mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 497543 - Part 2 - moz-page-thumb:// Protocol and Channel; r=mak
This commit is contained in:
parent
450c992d83
commit
5e7ade0a16
@ -71,6 +71,7 @@ PARALLEL_DIRS = \
|
||||
shell \
|
||||
sidebar \
|
||||
tabview \
|
||||
thumbnails \
|
||||
migration \
|
||||
$(NULL)
|
||||
|
||||
|
2
browser/components/thumbnails/BrowserPageThumbs.manifest
Normal file
2
browser/components/thumbnails/BrowserPageThumbs.manifest
Normal file
@ -0,0 +1,2 @@
|
||||
component {5a4ae9b5-f475-48ae-9dce-0b4c1d347884} PageThumbsProtocol.js
|
||||
contract @mozilla.org/network/protocol;1?name=moz-page-thumb {5a4ae9b5-f475-48ae-9dce-0b4c1d347884}
|
23
browser/components/thumbnails/Makefile.in
Normal file
23
browser/components/thumbnails/Makefile.in
Normal file
@ -0,0 +1,23 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
EXTRA_COMPONENTS = \
|
||||
BrowserPageThumbs.manifest \
|
||||
PageThumbsProtocol.js \
|
||||
$(NULL)
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
DIRS += test
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
XPIDL_FLAGS += -I$(topsrcdir)/browser/components/
|
448
browser/components/thumbnails/PageThumbsProtocol.js
Normal file
448
browser/components/thumbnails/PageThumbsProtocol.js
Normal file
@ -0,0 +1,448 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* PageThumbsProtocol.js
|
||||
*
|
||||
* This file implements the moz-page-thumb:// protocol and the corresponding
|
||||
* channel delivering cached thumbnails.
|
||||
*
|
||||
* URL structure:
|
||||
*
|
||||
* moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.org%2F
|
||||
*
|
||||
* This URL requests an image for 'http://www.mozilla.org/'.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Cr = Components.results;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource:///modules/PageThumbs.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
/**
|
||||
* Implements the thumbnail protocol handler responsible for moz-page-thumb: URIs.
|
||||
*/
|
||||
function Protocol() {
|
||||
}
|
||||
|
||||
Protocol.prototype = {
|
||||
/**
|
||||
* The scheme used by this protocol.
|
||||
*/
|
||||
get scheme() PageThumbs.scheme,
|
||||
|
||||
/**
|
||||
* The default port for this protocol (we don't support ports).
|
||||
*/
|
||||
get defaultPort() -1,
|
||||
|
||||
/**
|
||||
* The flags specific to this protocol implementation.
|
||||
*/
|
||||
get protocolFlags() {
|
||||
return Ci.nsIProtocolHandler.URI_IS_UI_RESOURCE |
|
||||
Ci.nsIProtocolHandler.URI_NORELATIVE |
|
||||
Ci.nsIProtocolHandler.URI_NOAUTH;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a new URI object that is suitable for loading by this protocol.
|
||||
* @param aSpec The URI string in UTF8 encoding.
|
||||
* @param aOriginCharset The charset of the document from which the URI originated.
|
||||
* @return The newly created URI.
|
||||
*/
|
||||
newURI: function Proto_newURI(aSpec, aOriginCharset) {
|
||||
let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
|
||||
uri.spec = aSpec;
|
||||
return uri;
|
||||
},
|
||||
|
||||
/**
|
||||
* Constructs a new channel from the given URI for this protocol handler.
|
||||
* @param aURI The URI for which to construct a channel.
|
||||
* @return The newly created channel.
|
||||
*/
|
||||
newChannel: function Proto_newChannel(aURI) {
|
||||
return new Channel(aURI);
|
||||
},
|
||||
|
||||
/**
|
||||
* Decides whether to allow a blacklisted port.
|
||||
* @return Always false, we'll never allow ports.
|
||||
*/
|
||||
allowPort: function () false,
|
||||
|
||||
classID: Components.ID("{5a4ae9b5-f475-48ae-9dce-0b4c1d347884}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
|
||||
};
|
||||
|
||||
let NSGetFactory = XPCOMUtils.generateNSGetFactory([Protocol]);
|
||||
|
||||
/**
|
||||
* A channel implementation responsible for delivering cached thumbnails.
|
||||
*/
|
||||
function Channel(aURI) {
|
||||
this._uri = aURI;
|
||||
|
||||
// nsIChannel
|
||||
this.originalURI = aURI;
|
||||
|
||||
// nsIHttpChannel
|
||||
this._responseHeaders = {"content-type": PageThumbs.contentType};
|
||||
}
|
||||
|
||||
Channel.prototype = {
|
||||
/**
|
||||
* Tracks if the channel has been opened, yet.
|
||||
*/
|
||||
_wasOpened: false,
|
||||
|
||||
/**
|
||||
* Opens this channel asynchronously.
|
||||
* @param aListener The listener that receives the channel data when available.
|
||||
* @param aContext A custom context passed to the listener's methods.
|
||||
*/
|
||||
asyncOpen: function Channel_asyncOpen(aListener, aContext) {
|
||||
if (this._wasOpened)
|
||||
throw Cr.NS_ERROR_ALREADY_OPENED;
|
||||
|
||||
if (this.canceled)
|
||||
return;
|
||||
|
||||
this._listener = aListener;
|
||||
this._context = aContext;
|
||||
|
||||
this._isPending = true;
|
||||
this._wasOpened = true;
|
||||
|
||||
// Try to read the data from the thumbnail cache.
|
||||
this._readCache(function (aData) {
|
||||
// Update response if there's no data.
|
||||
if (!aData) {
|
||||
this._responseStatus = 404;
|
||||
this._responseText = "Not Found";
|
||||
}
|
||||
|
||||
this._startRequest();
|
||||
|
||||
if (!this.canceled) {
|
||||
this._addToLoadGroup();
|
||||
|
||||
if (aData)
|
||||
this._serveData(aData);
|
||||
|
||||
if (!this.canceled)
|
||||
this._stopRequest();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Reads a data stream from the cache entry.
|
||||
* @param aCallback The callback the data is passed to.
|
||||
*/
|
||||
_readCache: function Channel_readCache(aCallback) {
|
||||
let {url} = parseURI(this._uri);
|
||||
|
||||
// Return early if there's no valid URL given.
|
||||
if (!url) {
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to get a cache entry.
|
||||
PageThumbsCache.getReadEntry(url, function (aEntry) {
|
||||
let inputStream = aEntry && aEntry.openInputStream(0);
|
||||
|
||||
function closeEntryAndFinish(aData) {
|
||||
if (aEntry) {
|
||||
aEntry.close();
|
||||
}
|
||||
aCallback(aData);
|
||||
}
|
||||
|
||||
// Check if we have a valid entry and if it has any data.
|
||||
if (!inputStream || !inputStream.available()) {
|
||||
closeEntryAndFinish();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Read the cache entry's data.
|
||||
NetUtil.asyncFetch(inputStream, function (aData, aStatus) {
|
||||
// We might have been canceled while waiting.
|
||||
if (this.canceled)
|
||||
return;
|
||||
|
||||
// Check if we have a valid data stream.
|
||||
if (!Components.isSuccessCode(aStatus) || !aData.available())
|
||||
aData = null;
|
||||
|
||||
closeEntryAndFinish(aData);
|
||||
}.bind(this));
|
||||
} catch (e) {
|
||||
closeEntryAndFinish();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls onStartRequest on the channel listener.
|
||||
*/
|
||||
_startRequest: function Channel_startRequest() {
|
||||
try {
|
||||
this._listener.onStartRequest(this, this._context);
|
||||
} catch (e) {
|
||||
// The listener might throw if the request has been canceled.
|
||||
this.cancel(Cr.NS_BINDING_ABORTED);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls onDataAvailable on the channel listener and passes the data stream.
|
||||
* @param aData The data to be delivered.
|
||||
*/
|
||||
_serveData: function Channel_serveData(aData) {
|
||||
try {
|
||||
let available = aData.available();
|
||||
this._listener.onDataAvailable(this, this._context, aData, 0, available);
|
||||
} catch (e) {
|
||||
// The listener might throw if the request has been canceled.
|
||||
this.cancel(Cr.NS_BINDING_ABORTED);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls onStopRequest on the channel listener.
|
||||
*/
|
||||
_stopRequest: function Channel_stopRequest() {
|
||||
try {
|
||||
this._listener.onStopRequest(this, this._context, this.status);
|
||||
} catch (e) {
|
||||
// This might throw but is generally ignored.
|
||||
}
|
||||
|
||||
// The request has finished, clean up after ourselves.
|
||||
this._cleanup();
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds this request to the load group, if any.
|
||||
*/
|
||||
_addToLoadGroup: function Channel_addToLoadGroup() {
|
||||
if (this.loadGroup)
|
||||
this.loadGroup.addRequest(this, this._context);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes this request from its load group, if any.
|
||||
*/
|
||||
_removeFromLoadGroup: function Channel_removeFromLoadGroup() {
|
||||
if (!this.loadGroup)
|
||||
return;
|
||||
|
||||
try {
|
||||
this.loadGroup.removeRequest(this, this._context, this.status);
|
||||
} catch (e) {
|
||||
// This might throw but is ignored.
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleans up the channel when the request has finished.
|
||||
*/
|
||||
_cleanup: function Channel_cleanup() {
|
||||
this._removeFromLoadGroup();
|
||||
this.loadGroup = null;
|
||||
|
||||
this._isPending = false;
|
||||
|
||||
delete this._listener;
|
||||
delete this._context;
|
||||
},
|
||||
|
||||
/* :::::::: nsIChannel ::::::::::::::: */
|
||||
|
||||
contentType: PageThumbs.contentType,
|
||||
contentLength: -1,
|
||||
owner: null,
|
||||
contentCharset: null,
|
||||
notificationCallbacks: null,
|
||||
|
||||
get URI() this._uri,
|
||||
get securityInfo() null,
|
||||
|
||||
/**
|
||||
* Opens this channel synchronously. Not supported.
|
||||
*/
|
||||
open: function Channel_open() {
|
||||
// Synchronous data delivery is not implemented.
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
/* :::::::: nsIHttpChannel ::::::::::::::: */
|
||||
|
||||
redirectionLimit: 10,
|
||||
requestMethod: "GET",
|
||||
allowPipelining: true,
|
||||
referrer: null,
|
||||
|
||||
get requestSucceeded() true,
|
||||
|
||||
_responseStatus: 200,
|
||||
get responseStatus() this._responseStatus,
|
||||
|
||||
_responseText: "OK",
|
||||
get responseStatusText() this._responseText,
|
||||
|
||||
/**
|
||||
* Checks if the server sent the equivalent of a "Cache-control: no-cache"
|
||||
* response header.
|
||||
* @return Always false.
|
||||
*/
|
||||
isNoCacheResponse: function () false,
|
||||
|
||||
/**
|
||||
* Checks if the server sent the equivalent of a "Cache-control: no-cache"
|
||||
* response header.
|
||||
* @return Always false.
|
||||
*/
|
||||
isNoStoreResponse: function () false,
|
||||
|
||||
/**
|
||||
* Returns the value of a particular request header. Not implemented.
|
||||
*/
|
||||
getRequestHeader: function Channel_getRequestHeader() {
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method is called to set the value of a particular request header.
|
||||
* Not implemented.
|
||||
*/
|
||||
setRequestHeader: function Channel_setRequestHeader() {
|
||||
if (this._wasOpened)
|
||||
throw Cr.NS_ERROR_IN_PROGRESS;
|
||||
},
|
||||
|
||||
/**
|
||||
* Call this method to visit all request headers. Not implemented.
|
||||
*/
|
||||
visitRequestHeaders: function () {},
|
||||
|
||||
/**
|
||||
* Gets the value of a particular response header.
|
||||
* @param aHeader The case-insensitive name of the response header to query.
|
||||
* @return The header value.
|
||||
*/
|
||||
getResponseHeader: function Channel_getResponseHeader(aHeader) {
|
||||
let name = aHeader.toLowerCase();
|
||||
if (name in this._responseHeaders)
|
||||
return this._responseHeaders[name];
|
||||
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method is called to set the value of a particular response header.
|
||||
* @param aHeader The case-insensitive name of the response header to query.
|
||||
* @param aValue The response header value to set.
|
||||
*/
|
||||
setResponseHeader: function Channel_setResponseHeader(aHeader, aValue, aMerge) {
|
||||
let name = aHeader.toLowerCase();
|
||||
if (!aValue && !aMerge)
|
||||
delete this._responseHeaders[name];
|
||||
else
|
||||
this._responseHeaders[name] = aValue;
|
||||
},
|
||||
|
||||
/**
|
||||
* Call this method to visit all response headers.
|
||||
* @param aVisitor The header visitor.
|
||||
*/
|
||||
visitResponseHeaders: function Channel_visitResponseHeaders(aVisitor) {
|
||||
for (let name in this._responseHeaders) {
|
||||
let value = this._responseHeaders[name];
|
||||
|
||||
try {
|
||||
aVisitor.visitHeader(name, value);
|
||||
} catch (e) {
|
||||
// The visitor can throw to stop the iteration.
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/* :::::::: nsIRequest ::::::::::::::: */
|
||||
|
||||
loadFlags: Ci.nsIRequest.LOAD_NORMAL,
|
||||
loadGroup: null,
|
||||
|
||||
get name() this._uri.spec,
|
||||
|
||||
_status: Cr.NS_OK,
|
||||
get status() this._status,
|
||||
|
||||
_isPending: false,
|
||||
isPending: function () this._isPending,
|
||||
|
||||
resume: function () {},
|
||||
suspend: function () {},
|
||||
|
||||
/**
|
||||
* Cancels this request.
|
||||
* @param aStatus The reason for cancelling.
|
||||
*/
|
||||
cancel: function Channel_cancel(aStatus) {
|
||||
if (this.canceled)
|
||||
return;
|
||||
|
||||
this._isCanceled = true;
|
||||
this._status = aStatus;
|
||||
|
||||
this._cleanup();
|
||||
},
|
||||
|
||||
/* :::::::: nsIHttpChannelInternal ::::::::::::::: */
|
||||
|
||||
documentURI: null,
|
||||
|
||||
_isCanceled: false,
|
||||
get canceled() this._isCanceled,
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel,
|
||||
Ci.nsIHttpChannel,
|
||||
Ci.nsIHttpChannelInternal,
|
||||
Ci.nsIRequest])
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a given URI and extracts all parameters relevant to this protocol.
|
||||
* @param aURI The URI to parse.
|
||||
* @return The parsed parameters.
|
||||
*/
|
||||
function parseURI(aURI) {
|
||||
let {scheme, staticHost} = PageThumbs;
|
||||
let re = new RegExp("^" + scheme + "://" + staticHost + ".*?\\?");
|
||||
let query = aURI.spec.replace(re, "");
|
||||
let params = {};
|
||||
|
||||
query.split("&").forEach(function (aParam) {
|
||||
let [key, value] = aParam.split("=").map(decodeURIComponent);
|
||||
params[key.toLowerCase()] = value;
|
||||
});
|
||||
|
||||
return params;
|
||||
}
|
@ -284,6 +284,7 @@
|
||||
@BINPATH@/components/nsSetDefaultBrowser.manifest
|
||||
@BINPATH@/components/nsSetDefaultBrowser.js
|
||||
@BINPATH@/components/BrowserPlaces.manifest
|
||||
@BINPATH@/components/BrowserPageThumbs.manifest
|
||||
@BINPATH@/components/nsPrivateBrowsingService.manifest
|
||||
@BINPATH@/components/nsPrivateBrowsingService.js
|
||||
@BINPATH@/components/toolkitsearch.manifest
|
||||
@ -347,6 +348,7 @@
|
||||
@BINPATH@/components/nsPlacesExpiration.js
|
||||
@BINPATH@/components/PlacesProtocolHandler.js
|
||||
@BINPATH@/components/PlacesCategoriesStarter.js
|
||||
@BINPATH@/components/PageThumbsProtocol.js
|
||||
@BINPATH@/components/nsDefaultCLH.manifest
|
||||
@BINPATH@/components/nsDefaultCLH.js
|
||||
@BINPATH@/components/nsContentPrefService.manifest
|
||||
|
Loading…
Reference in New Issue
Block a user