--- a/browser/base/content/newtab/grid.js
+++ b/browser/base/content/newtab/grid.js
@@ -178,18 +178,18 @@ var gGrid = {
let site = document.createElementNS(HTML_NAMESPACE, "div");
site.classList.add("newtab-site");
site.setAttribute("draggable", "true");
// Create the site's inner HTML code.
site.innerHTML =
'<a class="newtab-link">' +
' <span class="newtab-thumbnail placeholder"/>' +
- ' <span class="newtab-thumbnail thumbnail"/>' +
- ' <span class="newtab-thumbnail enhanced-content"/>' +
+ ' <img src="" alt="" class="newtab-thumbnail thumbnail"/>' +
+ ' <img src="" alt="" class="newtab-thumbnail enhanced-content"/>' +
' <span class="newtab-title"/>' +
'</a>' +
'<input type="button" title="' + newTabString("pin") + '"' +
' class="newtab-control newtab-control-pin"/>' +
'<input type="button" title="' + newTabString("block") + '"' +
' class="newtab-control newtab-control-block"/>';
this._siteFragment = document.createDocumentFragment();
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -181,17 +181,17 @@ Site.prototype = {
let link = gAllPages.enhanced && DirectoryLinksProvider.getEnhancedLink(this.link) ||
this.link;
let thumbnail = this._querySelector(".newtab-thumbnail.thumbnail");
if (link.bgColor) {
thumbnail.style.backgroundColor = link.bgColor;
}
let uri = link.imageURI || PageThumbs.getThumbnailURL(this.url);
- thumbnail.style.backgroundImage = 'url("' + uri + '")';
+ thumbnail.src = uri;
if (THUMBNAIL_PLACEHOLDER_ENABLED &&
link.type == "history" &&
link.baseDomain) {
let placeholder = this._querySelector(".newtab-thumbnail.placeholder");
let charCodeSum = 0;
for (let c of link.baseDomain) {
charCodeSum += c.charCodeAt(0);
@@ -199,17 +199,17 @@ Site.prototype = {
const COLORS = 16;
let hue = Math.round((charCodeSum % COLORS) / COLORS * 360);
placeholder.style.backgroundColor = "hsl(" + hue + ",80%,40%)";
placeholder.textContent = link.baseDomain.substr(0,1).toUpperCase();
}
if (link.enhancedImageURI) {
let enhanced = this._querySelector(".enhanced-content");
- enhanced.style.backgroundImage = 'url("' + link.enhancedImageURI + '")';
+ enhanced.src = link.enhancedImageURI;
}
},
/**
* Adds event handlers for the site and its buttons.
*/
_addEventHandlers: function Site_addEventHandlers() {
// Register drag-and-drop event handlers.
--- a/browser/base/content/test/newtab/browser_newtab_background_captures.js
+++ b/browser/base/content/test/newtab/browser_newtab_background_captures.js
@@ -3,26 +3,27 @@
/**
* Verifies that hidden, pre-loaded newtabs don't allow background captures, and
* when unhidden, do allow background captures.
*/
const CAPTURE_PREF = "browser.pagethumbnails.capturing_disabled";
+XPCOMUtils.defineLazyServiceGetter(this, "PageThumbsStorageService",
+ "@mozilla.org/thumbnails/pagethumbs-service;1",
+ "nsIPageThumbsStorageService");
+
add_task(async function() {
- let imports = {};
- Cu.import("resource://gre/modules/PageThumbs.jsm", imports);
-
// Disable captures.
await pushPrefs([CAPTURE_PREF, false]);
// Make sure the thumbnail doesn't exist yet.
let url = "http://example.com/";
- let path = imports.PageThumbsStorage.getFilePathForURL(url);
+ let path = PageThumbsStorageService.getFilePathForURL(url);
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(path);
try {
file.remove(false);
} catch (err) {}
// Add a top site.
await setLinks("-1");
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -381,17 +381,18 @@
@RESPATH@/browser/components/aboutdevtools.manifest
@RESPATH@/browser/components/Experiments.manifest
@RESPATH@/browser/components/ExperimentsService.js
@RESPATH@/browser/components/browser-newtab.xpt
@RESPATH@/browser/components/aboutNewTabService.js
@RESPATH@/browser/components/NewTabComponents.manifest
@RESPATH@/components/Downloads.manifest
@RESPATH@/components/DownloadLegacy.js
-@RESPATH@/components/BrowserPageThumbs.manifest
+@RESPATH@/components/thumbnails.xpt
+@RESPATH@/components/PageThumbsComponents.manifest
@RESPATH@/components/crashmonitor.manifest
@RESPATH@/components/nsCrashMonitor.js
@RESPATH@/components/toolkitsearch.manifest
@RESPATH@/components/nsSearchService.js
@RESPATH@/components/nsSearchSuggestions.js
@RESPATH@/components/nsSidebar.js
#ifdef NIGHTLY_BUILD
@RESPATH@/components/payments.manifest
@@ -451,17 +452,17 @@
@RESPATH@/components/toolkitplaces.manifest
@RESPATH@/components/nsLivemarkService.js
@RESPATH@/components/nsTaggingService.js
@RESPATH@/components/UnifiedComplete.js
@RESPATH@/components/nsPlacesExpiration.js
@RESPATH@/components/PageIconProtocolHandler.js
@RESPATH@/components/PlacesCategoriesStarter.js
@RESPATH@/components/ColorAnalyzer.js
-@RESPATH@/components/PageThumbsProtocol.js
+@RESPATH@/components/PageThumbsStorageService.js
@RESPATH@/components/mozProtocolHandler.js
@RESPATH@/components/mozProtocolHandler.manifest
@RESPATH@/components/nsDefaultCLH.manifest
@RESPATH@/components/nsDefaultCLH.js
@RESPATH@/components/ContentPrefService2.manifest
@RESPATH@/components/ContentPrefService2.js
@RESPATH@/components/nsContentDispatchChooser.manifest
@RESPATH@/components/nsContentDispatchChooser.js
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -2191,16 +2191,24 @@ imgLoader::LoadImage(nsIURI* aURI,
isPrivate = nsContentUtils::IsInPrivateBrowsing(aLoadGroup);
}
MOZ_ASSERT(isPrivate == mRespectPrivacy);
#endif
// Get the default load flags from the loadgroup (if possible)...
if (aLoadGroup) {
aLoadGroup->GetLoadFlags(&requestFlags);
+
+ // If we are trying to load a thumbnail via the moz-page-thumb:// protocol, load
+ // it directly from the cache to prevent re-decoding the image. See Bug 1373258
+ // TODO: Bug 1406134
+ bool isThumbnailScheme = false;
+ if (NS_SUCCEEDED(aURI->SchemeIs("moz-page-thumb", &isThumbnailScheme)) && isThumbnailScheme) {
+ requestFlags |= nsIRequest::LOAD_FROM_CACHE;
+ }
}
//
// Merge the default load flags with those passed in via aLoadFlags.
// Currently, *only* the caching, validation and background load flags
// are merged...
//
// The flags in aLoadFlags take precedence over the default flags!
//
@@ -2468,16 +2476,24 @@ imgLoader::LoadImageWithChannel(nsIChann
nsresult rv;
ImageCacheKey key(uri, attrs, doc, rv);
NS_ENSURE_SUCCESS(rv, rv);
nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
channel->GetLoadFlags(&requestFlags);
+ // If we are trying to load a thumbnail via the moz-page-thumb:// protocol, load
+ // it directly from the cache to prevent re-decoding the image. See Bug 1373258
+ // TODO: Bug 1406134
+ bool isThumbnailScheme = false;
+ if (NS_SUCCEEDED(uri->SchemeIs("moz-page-thumb", &isThumbnailScheme)) && isThumbnailScheme) {
+ requestFlags |= nsIRequest::LOAD_FROM_CACHE;
+ }
+
RefPtr<imgCacheEntry> entry;
if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) {
RemoveFromCache(key);
} else {
// Look in the cache for our URI, and then validate it.
// XXX For now ignore aCacheKey. We will need it in the future
// for correctly dealing with image load requests that are a result
deleted file mode 100644
--- a/toolkit/components/thumbnails/BrowserPageThumbs.manifest
+++ /dev/null
@@ -1,2 +0,0 @@
-component {5a4ae9b5-f475-48ae-9dce-0b4c1d347884} PageThumbsProtocol.js
-contract @mozilla.org/network/protocol;1?name=moz-page-thumb {5a4ae9b5-f475-48ae-9dce-0b4c1d347884}
--- a/toolkit/components/thumbnails/PageThumbs.jsm
+++ b/toolkit/components/thumbnails/PageThumbs.jsm
@@ -43,26 +43,18 @@ XPCOMUtils.defineLazyModuleGetters(this,
AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
PageThumbUtils: "resource://gre/modules/PageThumbUtils.jsm",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
});
XPCOMUtils.defineLazyServiceGetter(this, "gUpdateTimerManager",
"@mozilla.org/updates/timer-manager;1", "nsIUpdateTimerManager");
-XPCOMUtils.defineLazyGetter(this, "gCryptoHash", function() {
- return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
-});
-
-XPCOMUtils.defineLazyGetter(this, "gUnicodeConverter", function() {
- let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
- .createInstance(Ci.nsIScriptableUnicodeConverter);
- converter.charset = "utf8";
- return converter;
-});
+XPCOMUtils.defineLazyServiceGetter(this, "PageThumbsStorageService",
+ "@mozilla.org/thumbnails/pagethumbs-service;1", "nsIPageThumbsStorageService");
/**
* Utilities for dealing with promises and Task.jsm
*/
const TaskUtils = {
/**
* Read the bytes from a blob, asynchronously.
*
@@ -154,17 +146,17 @@ this.PageThumbs = {
* Gets the path of the thumbnail file for a given web page's
* url. This file may or may not exist depending on whether the
* thumbnail has been captured or not.
*
* @param aUrl The web page's url.
* @return The path of the thumbnail file.
*/
getThumbnailPath: function PageThumbs_getThumbnailPath(aUrl) {
- return PageThumbsStorage.getFilePathForURL(aUrl);
+ return PageThumbsStorageService.getFilePathForURL(aUrl);
},
/**
* Asynchronously returns a thumbnail as a blob for the given
* window.
*
* @param aBrowser The <browser> to capture a thumbnail from.
* @return {Promise}
@@ -502,49 +494,29 @@ this.PageThumbs = {
return !Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled");
} catch (e) {
return true;
}
},
};
this.PageThumbsStorage = {
- // The path for the storage
- _path: null,
- get path() {
- if (!this._path) {
- this._path = OS.Path.join(OS.Constants.Path.localProfileDir, THUMBNAIL_DIRECTORY);
- }
- return this._path;
- },
ensurePath: function Storage_ensurePath() {
// Create the directory (ignore any error if the directory
// already exists). As all writes are done from the PageThumbsWorker
// thread, which serializes its operations, this ensures that
// future operations can proceed without having to check whether
// the directory exists.
return PageThumbsWorker.post("makeDir",
- [this.path, {ignoreExisting: true}]).catch(function onError(aReason) {
+ [PageThumbsStorageService.path, {ignoreExisting: true}]).catch(function onError(aReason) {
Components.utils.reportError("Could not create thumbnails directory" + aReason);
});
},
- getLeafNameForURL: function Storage_getLeafNameForURL(aURL) {
- if (typeof aURL != "string") {
- throw new TypeError("Expecting a string");
- }
- let hash = this._calculateMD5Hash(aURL);
- return hash + ".png";
- },
-
- getFilePathForURL: function Storage_getFilePathForURL(aURL) {
- return OS.Path.join(this.path, this.getLeafNameForURL(aURL));
- },
-
_revisionTable: {},
// Generate an arbitrary revision tag, i.e. one that can't be used to
// infer URL frecency.
_updateRevision(aURL) {
// Initialize with a random value and increment on each update. Wrap around
// modulo _revisionRange, so that even small values carry no meaning.
let rev = this._revisionTable[aURL];
@@ -586,17 +558,17 @@ this.PageThumbsStorage = {
* an ArrayBuffer. This array buffer will be detached and cannot be
* reused after the copy.
* @param {boolean} aNoOverwrite If true and the thumbnail's file already
* exists, the file will not be overwritten.
*
* @return {Promise}
*/
writeData: function Storage_writeData(aURL, aData, aNoOverwrite) {
- let path = this.getFilePathForURL(aURL);
+ let path = PageThumbsStorageService.getFilePathForURL(aURL);
this.ensurePath();
aData = new Uint8Array(aData);
let msg = [
path,
aData,
{
tmpPath: path + ".tmp",
bytes: aData.byteLength,
@@ -617,30 +589,30 @@ this.PageThumbsStorage = {
* @param {string} aTargetURL The url of the target thumbnail.
* @param {boolean} aNoOverwrite If true and the target file already exists,
* the file will not be overwritten.
*
* @return {Promise}
*/
copy: function Storage_copy(aSourceURL, aTargetURL, aNoOverwrite) {
this.ensurePath();
- let sourceFile = this.getFilePathForURL(aSourceURL);
- let targetFile = this.getFilePathForURL(aTargetURL);
+ let sourceFile = PageThumbsStorageService.getFilePathForURL(aSourceURL);
+ let targetFile = PageThumbsStorageService.getFilePathForURL(aTargetURL);
let options = { noOverwrite: aNoOverwrite };
return PageThumbsWorker.post("copy", [sourceFile, targetFile, options]).
then(() => this._updateRevision(aTargetURL), this._eatNoOverwriteError(aNoOverwrite));
},
/**
* Remove a single thumbnail, off the main thread.
*
* @return {Promise}
*/
remove: function Storage_remove(aURL) {
- return PageThumbsWorker.post("remove", [this.getFilePathForURL(aURL)]);
+ return PageThumbsWorker.post("remove", [PageThumbsStorageService.getFilePathForURL(aURL)]);
},
/**
* Remove all thumbnails, off the main thread.
*
* @return {Promise}
*/
wipe: async function Storage_wipe() {
@@ -662,57 +634,41 @@ this.PageThumbsStorage = {
// to clear the thumbnail wipe.
AsyncShutdown.profileBeforeChange.addBlocker(
"PageThumbs: removing all thumbnails",
blocker);
// Start the work only now that `profileBeforeChange` has had
// a chance to throw an error.
- let promise = PageThumbsWorker.post("wipe", [this.path]);
+ let promise = PageThumbsWorker.post("wipe", [PageThumbsStorageService.path]);
try {
await promise;
} finally {
// Generally, we will be done much before profileBeforeChange,
// so let's not hoard blockers.
if ("removeBlocker" in AsyncShutdown.profileBeforeChange) {
// `removeBlocker` was added with bug 985655. In the interest
// of backporting, let's degrade gracefully if `removeBlocker`
// doesn't exist.
AsyncShutdown.profileBeforeChange.removeBlocker(blocker);
}
}
},
fileExistsForURL: function Storage_fileExistsForURL(aURL) {
- return PageThumbsWorker.post("exists", [this.getFilePathForURL(aURL)]);
+ return PageThumbsWorker.post("exists", [PageThumbsStorageService.getFilePathForURL(aURL)]);
},
isFileRecentForURL: function Storage_isFileRecentForURL(aURL) {
return PageThumbsWorker.post("isFileRecent",
- [this.getFilePathForURL(aURL),
+ [PageThumbsStorageService.getFilePathForURL(aURL),
MAX_THUMBNAIL_AGE_SECS]);
},
- _calculateMD5Hash: function Storage_calculateMD5Hash(aValue) {
- let hash = gCryptoHash;
- let value = gUnicodeConverter.convertToByteArray(aValue);
-
- hash.init(hash.MD5);
- hash.update(value, value.length);
- return this._convertToHexString(hash.finish(false));
- },
-
- _convertToHexString: function Storage_convertToHexString(aData) {
- let hex = "";
- for (let i = 0; i < aData.length; i++)
- hex += ("0" + aData.charCodeAt(i).toString(16)).slice(-2);
- return hex;
- },
-
/**
* For functions that take a noOverwrite option, OS.File throws an error if
* the target file exists and noOverwrite is true. We don't consider that an
* error, and we don't want such errors propagated.
*
* @param {aNoOverwrite} The noOverwrite option used in the OS.File operation.
*
* @return {function} A function that should be passed as the second argument
@@ -728,17 +684,17 @@ this.PageThumbsStorage = {
};
},
// Deprecated, please do not use
getFileForURL: function Storage_getFileForURL_DEPRECATED(aURL) {
Deprecated.warning("PageThumbs.getFileForURL is deprecated. Please use PageThumbs.getFilePathForURL and OS.File",
"https://developer.mozilla.org/docs/JavaScript_OS.File");
// Note: Once this method has been removed, we can get rid of the dependency towards FileUtils
- return new FileUtils.File(PageThumbsStorage.getFilePathForURL(aURL));
+ return new FileUtils.File(PageThumbsStorageService.getFilePathForURL(aURL));
}
};
var PageThumbsStorageMigrator = {
get currentVersion() {
try {
return Services.prefs.getIntPref(PREF_STORAGE_VERSION);
} catch (e) {
@@ -837,19 +793,19 @@ var PageThumbsExpiration = {
if (typeof filter == "function")
filter(filterCallback);
else
filter.filterForThumbnailExpiration(filterCallback);
}
},
expireThumbnails: function Expiration_expireThumbnails(aURLsToKeep) {
- let keep = aURLsToKeep.map(url => PageThumbsStorage.getLeafNameForURL(url));
+ let keep = aURLsToKeep.map(url => PageThumbsStorageService.getLeafNameForURL(url));
let msg = [
- PageThumbsStorage.path,
+ PageThumbsStorageService.path,
keep,
EXPIRATION_MIN_CHUNK_SIZE
];
return PageThumbsWorker.post(
"expireFilesInDirectory",
msg
);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/thumbnails/PageThumbsComponents.manifest
@@ -0,0 +1,2 @@
+component {97943eec-0e48-49ef-b7b7-cf4aa0109bb6} PageThumbsStorageService.js
+contract @mozilla.org/thumbnails/pagethumbs-service;1 {97943eec-0e48-49ef-b7b7-cf4aa0109bb6}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/thumbnails/PageThumbsProtocol.cpp
@@ -0,0 +1,189 @@
+//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/**
+ * Implementation of moz-page-thumb protocol. This accesses and displays
+ * screenshots for URLs that are stored in the profile directory.
+ */
+
+#include "nsIPageThumbsStorageService.h"
+#include "PageThumbsProtocol.h"
+#include "nsIURI.h"
+#include "nsIFileURL.h"
+#include "nsIFile.h"
+#include "nsIChannel.h"
+#include "nsComponentManagerUtils.h"
+#include "nsNetUtil.h"
+#include "mozilla/dom/URLSearchParams.h"
+#include "nsStandardURL.h"
+
+using mozilla::dom::URLParams;
+using mozilla::net::nsStandardURL;
+
+NS_IMPL_ISUPPORTS(PageThumbsProtocol, nsIProtocolHandler);
+
+// PageThumbsProtocol::GetScheme
+
+NS_IMETHODIMP
+PageThumbsProtocol::GetScheme(nsACString& aScheme)
+{
+ aScheme.AssignLiteral("moz-page-thumb");
+ return NS_OK;
+}
+
+// PageThumbsProtocol::GetDefaultPort
+
+NS_IMETHODIMP
+PageThumbsProtocol::GetDefaultPort(int32_t *aDefaultPort)
+{
+ *aDefaultPort = -1;
+ return NS_OK;
+}
+
+// PageThumbsProtocol::GetProtocolFlags
+
+NS_IMETHODIMP
+PageThumbsProtocol::GetProtocolFlags(uint32_t *aProtocolFlags)
+{
+ *aProtocolFlags = (URI_DANGEROUS_TO_LOAD | URI_IS_LOCAL_RESOURCE |
+ URI_NORELATIVE | URI_NOAUTH);
+ return NS_OK;
+}
+
+// PageThumbsProtocol::NewURI
+
+NS_IMETHODIMP
+PageThumbsProtocol::NewURI(const nsACString& aSpec,
+ const char *aOriginCharset,
+ nsIURI *aBaseURI, nsIURI **_retval)
+{
+ nsCOMPtr <nsIURI> uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID);
+ nsresult rv = uri->SetSpec(aSpec);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
+
+ uri.forget(_retval);
+ return rv;
+}
+
+// PageThumbsProtocol::NewChannel
+
+NS_IMETHODIMP
+PageThumbsProtocol::NewChannel2(nsIURI* aURI,
+ nsILoadInfo *aLoadInfo,
+ nsIChannel** _retval)
+{
+ // Get the file path for the URL
+ nsCOMPtr <nsIFile> filePath;
+ nsresult rv = GetFilePathForURL(aURI, getter_AddRefs(filePath));
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
+
+ // Get a file URI from the local file path
+ nsCOMPtr <nsIURI> fileURI;
+ rv = NS_NewFileURI(getter_AddRefs(fileURI), filePath);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
+
+ // Create a new channel with the file URI created
+ nsCOMPtr <nsIChannel> channel;
+ nsCOMPtr <nsIIOService> ios = do_GetIOService();
+ rv = ios->NewChannelFromURIWithLoadInfo(fileURI, aLoadInfo, getter_AddRefs(channel));
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
+
+ channel->SetOriginalURI(aURI);
+ channel.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PageThumbsProtocol::NewChannel(nsIURI* aURI, nsIChannel** _retval)
+{
+ return NewChannel2(aURI, nullptr, _retval);
+}
+
+// PageThumbsProtocol::AllowPort
+
+NS_IMETHODIMP
+PageThumbsProtocol::AllowPort(int32_t aPort, const char *aScheme, bool *_retval)
+{
+ *_retval = false;
+ return NS_OK;
+}
+
+// PageThumbsProtocol::ParseProtocolURL
+//
+// Extracts the URL from the query parameter. The URI is passed in in the form:
+// 'moz-page-thumb://thumbnail/?url=http%3A%2F%2Fwww.mozilla.org%2F'.
+
+nsresult
+PageThumbsProtocol::ParseProtocolURL(nsIURI* aURI, nsString& aParsedURL)
+{
+ nsAutoCString spec;
+ aURI->GetSpec(spec);
+
+ // Check that we have the correct host
+ nsAutoCString host;
+ host = Substring(spec, spec.FindChar(':') + 3, 9);
+ if (!host.EqualsLiteral("thumbnail")) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // Get the path out of the URI
+ nsAutoCString path;
+ nsresult rv = aURI->GetPathQueryRef(path);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
+
+ // Since this is a protocol URI and it doesn't parse nicely, we split on where
+ // the start of the query is and parse it from there
+ int32_t queryBegins = path.FindChar('?');
+ if (queryBegins <= 0) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ URLParams params;
+ params.ParseInput(Substring(path, queryBegins + 1));
+
+ params.Get(NS_LITERAL_STRING("url"), aParsedURL);
+
+ // If there's no URL as part of the query params, there will be no thumbnail
+ if (aParsedURL.IsVoid()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return NS_OK;
+}
+
+// PageThumbsProtocol::GetFilePathForURL
+//
+// Returns the thumbnail's file path for a given URL
+
+nsresult
+PageThumbsProtocol::GetFilePathForURL(nsIURI* aURI, nsIFile **_retval)
+{
+ nsresult rv;
+
+ // Use PageThumbsStorageService to get the local file path of the screenshot
+ // for the given URL
+ nsAutoString filePathForURL;
+ nsCOMPtr <nsIPageThumbsStorageService> pageThumbsStorage =
+ do_GetService("@mozilla.org/thumbnails/pagethumbs-service;1", &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
+
+ // Parse the protocol URL to extract the thumbnail's URL
+ nsAutoString parsedURL;
+ rv = ParseProtocolURL(aURI, parsedURL);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
+
+ rv = pageThumbsStorage->GetFilePathForURL(parsedURL, filePathForURL);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
+
+ // Find the local file containing the screenshot
+ nsCOMPtr <nsIFile> filePath = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+ rv = filePath->InitWithPath(filePathForURL);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
+
+ filePath.forget(_retval);
+ return NS_OK;
+}
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/thumbnails/PageThumbsProtocol.h
@@ -0,0 +1,28 @@
+//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef PageThumbsProtocol_h__
+#define PageThumbsProtocol_h__
+
+#include "nsIProtocolHandler.h"
+#include "nsString.h"
+
+// {5a4ae9b5-f475-48ae-9dce-0b4c1d347884}
+#define PAGETHUMBSPROTOCOL_CID \
+{ 0x5a4ae9b5, 0xf475, 0x48ae, { 0x9d, 0xce, 0x0b, 0x4c, 0x1d, 0x34, 0x78, 0x84 } }
+
+class PageThumbsProtocol final : public nsIProtocolHandler
+{
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPROTOCOLHANDLER
+
+ private:
+ ~PageThumbsProtocol() {}
+ nsresult GetFilePathForURL(nsIURI* aURI, nsIFile **_retval);
+ nsresult ParseProtocolURL(nsIURI* aURI, nsString& aParsedURL);
+};
+
+#endif /* PageThumbsProtocol_h__ */
deleted file mode 100644
--- a/toolkit/components/thumbnails/PageThumbsProtocol.js
+++ /dev/null
@@ -1,154 +0,0 @@
-/* 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&revision=XX
- *
- * This URL requests an image for 'http://www.mozilla.org/'.
- * The value of the revision key may change when the stored thumbnail changes.
- */
-
-"use strict";
-
-const Cu = Components.utils;
-const Cc = Components.classes;
-const Cr = Components.results;
-const Ci = Components.interfaces;
-
-Cu.import("resource://gre/modules/PageThumbs.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/osfile.jsm", this);
-
-XPCOMUtils.defineLazyModuleGetter(this, "Services",
- "resource://gre/modules/Services.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
- "resource://gre/modules/FileUtils.jsm");
-
-const SUBSTITUTING_URL_CID = "{dea9657c-18cf-4984-bde9-ccef5d8ab473}";
-
-/**
- * Implements the thumbnail protocol handler responsible for moz-page-thumb: URLs.
- */
-function Protocol() {
-}
-
-Protocol.prototype = {
- /**
- * The scheme used by this protocol.
- */
- get scheme() {
- return PageThumbs.scheme;
- },
-
- /**
- * The default port for this protocol (we don't support ports).
- */
- get defaultPort() {
- return -1;
- },
-
- /**
- * The flags specific to this protocol implementation.
- */
- get protocolFlags() {
- return Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
- Ci.nsIProtocolHandler.URI_IS_LOCAL_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 = Components.classesByID[SUBSTITUTING_URL_CID].createInstance(Ci.nsIURL);
- 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.
- * @param aLoadInfo The Loadinfo which to use on the channel.
- * @return The newly created channel.
- */
- newChannel2: function Proto_newChannel2(aURI, aLoadInfo) {
- let {file} = aURI.QueryInterface(Ci.nsIFileURL);
- let fileuri = Services.io.newFileURI(file);
- let channel = Services.io.newChannelFromURIWithLoadInfo(fileuri, aLoadInfo);
- channel.originalURI = aURI;
- return channel;
- },
-
- newChannel: function Proto_newChannel(aURI) {
- return this.newChannel2(aURI, null);
- },
-
- /**
- * Decides whether to allow a blacklisted port.
- * @return Always false, we'll never allow ports.
- */
- allowPort: () => false,
-
- // nsISubstitutingProtocolHandler methods
-
- /*
- * Substituting the scheme and host isn't enough, we also transform the path.
- * So declare no-op implementations for (get|set|has)Substitution methods and
- * do all the work in resolveURI.
- */
-
- setSubstitution(root, baseURI) {},
-
- getSubstitution(root) {
- throw Cr.NS_ERROR_NOT_AVAILABLE;
- },
-
- hasSubstitution(root) {
- return false;
- },
-
- resolveURI(resURI) {
- let {url} = parseURI(resURI);
- let path = PageThumbsStorage.getFilePathForURL(url);
- return OS.Path.toFileURI(path);
- },
-
- // xpcom machinery
- classID: Components.ID("{5a4ae9b5-f475-48ae-9dce-0b4c1d347884}"),
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler,
- Ci.nsISubstitutingProtocolHandler])
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Protocol]);
-
-/**
- * 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) {
- if (aURI.host != PageThumbs.staticHost)
- throw Cr.NS_ERROR_NOT_AVAILABLE;
-
- let {query} = aURI.QueryInterface(Ci.nsIURL);
- let params = {};
-
- query.split("&").forEach(function(aParam) {
- let [key, value] = aParam.split("=").map(decodeURIComponent);
- params[key.toLowerCase()] = value;
- });
-
- return params;
-}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/thumbnails/PageThumbsStorageService.js
@@ -0,0 +1,65 @@
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+const THUMBNAIL_DIRECTORY = "thumbnails";
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+Cu.import("resource://gre/modules/osfile.jsm", this);
+
+XPCOMUtils.defineLazyGetter(this, "gCryptoHash", function() {
+ return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
+});
+
+XPCOMUtils.defineLazyGetter(this, "gUnicodeConverter", function() {
+ let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "utf8";
+ return converter;
+});
+function PageThumbsStorageService() {}
+
+PageThumbsStorageService.prototype = {
+ classID: Components.ID("{97943eec-0e48-49ef-b7b7-cf4aa0109bb6}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPageThumbsStorageService]),
+ _xpcom_categories: [{
+ service: true
+ }],
+ // The path for the storage
+ _path: null,
+ get path() {
+ if (!this._path) {
+ this._path = OS.Path.join(OS.Constants.Path.localProfileDir, THUMBNAIL_DIRECTORY);
+ }
+ return this._path;
+ },
+
+ getLeafNameForURL(aURL) {
+ if (typeof aURL != "string") {
+ throw new TypeError("Expecting a string");
+ }
+ let hash = this._calculateMD5Hash(aURL);
+ return hash + ".png";
+ },
+
+ getFilePathForURL(aURL) {
+ return OS.Path.join(this.path, this.getLeafNameForURL(aURL));
+ },
+
+ _calculateMD5Hash(aValue) {
+ let hash = gCryptoHash;
+ let value = gUnicodeConverter.convertToByteArray(aValue);
+
+ hash.init(hash.MD5);
+ hash.update(value, value.length);
+ return this._convertToHexString(hash.finish(false));
+ },
+
+ _convertToHexString(aData) {
+ let hex = "";
+ for (let i = 0; i < aData.length; i++)
+ hex += ("0" + aData.charCodeAt(i).toString(16)).slice(-2);
+ return hex;
+ },
+
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PageThumbsStorageService]);
--- a/toolkit/components/thumbnails/moz.build
+++ b/toolkit/components/thumbnails/moz.build
@@ -6,20 +6,37 @@
with Files('**'):
BUG_COMPONENT = ('Firefox', 'New Tab Page')
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell.ini']
EXTRA_COMPONENTS += [
- 'BrowserPageThumbs.manifest',
- 'PageThumbsProtocol.js',
+ 'PageThumbsComponents.manifest',
+ 'PageThumbsStorageService.js',
]
EXTRA_JS_MODULES += [
'BackgroundPageThumbs.jsm',
'PageThumbs.jsm',
'PageThumbsWorker.js',
'PageThumbUtils.jsm',
]
+UNIFIED_SOURCES += [
+ 'nsPageThumbsModule.cpp',
+ 'PageThumbsProtocol.cpp'
+]
+
+XPIDL_SOURCES += [
+ 'nsIPageThumbsStorageService.idl',
+]
+
+LOCAL_INCLUDES += [
+ '/netwerk/base'
+]
+
+XPIDL_MODULE = 'thumbnails'
+
+FINAL_LIBRARY = 'xul'
+
JAR_MANIFESTS += ['jar.mn']
new file mode 100644
--- /dev/null
+++ b/toolkit/components/thumbnails/nsIPageThumbsStorageService.idl
@@ -0,0 +1,30 @@
+/* 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/. */
+
+#include "nsISupports.idl"
+
+/**
+ * A service which returns information about file paths where the
+ * screenshots for URLs are stored. These screenshots are used by the
+ * moz-page-thumb protocol
+ */
+
+[scriptable, uuid(97943eec-0e48-49ef-b7b7-cf4aa0109bb6)]
+interface nsIPageThumbsStorageService : nsISupports
+{
+ /**
+ * Returns the leaf name of the file containing the screenshot for a given URL
+ */
+ AString getLeafNameForURL(in AString aURL);
+
+ /**
+ * Returns the path where the thumbnails are stored
+ */
+ readonly attribute ACString path;
+
+ /**
+ * Returns the full file path containing the screenshot for a given URL
+ */
+ AString getFilePathForURL(in AString aURL);
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/components/thumbnails/nsPageThumbsModule.cpp
@@ -0,0 +1,25 @@
+#include "mozilla/ModuleUtils.h"
+#include "nsIClassInfoImpl.h"
+
+#include "PageThumbsProtocol.h"
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(PageThumbsProtocol)
+NS_DEFINE_NAMED_CID(PAGETHUMBSPROTOCOL_CID);
+
+const mozilla::Module::CIDEntry kPageThumbsCIDs[] = {
+ { &kPAGETHUMBSPROTOCOL_CID, false, nullptr, PageThumbsProtocolConstructor },
+ { nullptr }
+};
+
+const mozilla::Module::ContractIDEntry kPageThumbsContracts[] = {
+ { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-page-thumb", &kPAGETHUMBSPROTOCOL_CID },
+ { nullptr }
+};
+
+const mozilla::Module kPageThumbsModule = {
+ mozilla::Module::kVersion,
+ kPageThumbsCIDs,
+ kPageThumbsContracts
+};
+
+NSMODULE_DEFN(nsPageThumbsModule) = &kPageThumbsModule;
--- a/toolkit/components/thumbnails/test/browser_thumbnails_bug818225.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_bug818225.js
@@ -9,17 +9,17 @@ function* runTests() {
let path = PageThumbs.getThumbnailPath(URL);
yield testIfExists(path, false, "Thumbnail file does not exist");
yield addVisitsAndRepopulateNewTabLinks(URL, next);
yield createThumbnail(URL);
path = PageThumbs.getThumbnailPath(URL);
- let expectedPath = PageThumbsStorage.getFilePathForURL(URL);
+ let expectedPath = PageThumbsStorageService.getFilePathForURL(URL);
is(path, expectedPath, "Thumbnail file has correct path");
yield testIfExists(path, true, "Thumbnail file exists");
}
function createThumbnail(aURL) {
addTab(aURL, function() {
--- a/toolkit/components/thumbnails/test/browser_thumbnails_storage.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_storage.js
@@ -22,25 +22,25 @@ function* runTests() {
dontExpireThumbnailURLs([URL, URL_COPY]);
await promiseClearHistory();
await promiseAddVisitsAndRepopulateNewTabLinks(URL);
await promiseCreateThumbnail();
// Make sure Storage.copy() updates an existing file.
await PageThumbsStorage.copy(URL, URL_COPY);
- let copy = new FileUtils.File(PageThumbsStorage.getFilePathForURL(URL_COPY));
+ let copy = new FileUtils.File(PageThumbsStorageService.getFilePathForURL(URL_COPY));
let mtime = copy.lastModifiedTime -= 60;
await PageThumbsStorage.copy(URL, URL_COPY);
- isnot(new FileUtils.File(PageThumbsStorage.getFilePathForURL(URL_COPY)).lastModifiedTime, mtime,
+ isnot(new FileUtils.File(PageThumbsStorageService.getFilePathForURL(URL_COPY)).lastModifiedTime, mtime,
"thumbnail file was updated");
- let file = new FileUtils.File(PageThumbsStorage.getFilePathForURL(URL));
- let fileCopy = new FileUtils.File(PageThumbsStorage.getFilePathForURL(URL_COPY));
+ let file = new FileUtils.File(PageThumbsStorageService.getFilePathForURL(URL));
+ let fileCopy = new FileUtils.File(PageThumbsStorageService.getFilePathForURL(URL_COPY));
// Clear the browser history. Retry until the files are gone because Windows
// locks them sometimes.
info("Clearing history");
while (file.exists() || fileCopy.exists()) {
await promiseClearHistory();
}
info("History is clear");
--- a/toolkit/components/thumbnails/test/browser_thumbnails_storage_migrate3.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_storage_migrate3.js
@@ -24,31 +24,31 @@ XPCOMUtils.defineLazyServiceGetter(this,
function* runTests() {
// Prepare a local profile directory.
let localProfile = FileUtils.getDir("ProfD", ["local-test"], true);
changeLocation("ProfLD", localProfile);
let roaming = FileUtils.getDir("ProfD", [THUMBNAIL_DIRECTORY], true);
// Set up some data in the roaming profile.
- let name = PageThumbsStorage.getLeafNameForURL(URL);
+ let name = PageThumbsStorageService.getLeafNameForURL(URL);
let file = FileUtils.getFile("ProfD", [THUMBNAIL_DIRECTORY, name]);
writeDummyFile(file);
- name = PageThumbsStorage.getLeafNameForURL(URL2);
+ name = PageThumbsStorageService.getLeafNameForURL(URL2);
file = FileUtils.getFile("ProfD", [THUMBNAIL_DIRECTORY, name]);
writeDummyFile(file);
- name = PageThumbsStorage.getLeafNameForURL(URL3);
+ name = PageThumbsStorageService.getLeafNameForURL(URL3);
file = FileUtils.getFile("ProfD", [THUMBNAIL_DIRECTORY, name]);
writeDummyFile(file);
// Pretend to have one of the thumbnails
// already in place at the new storage site.
- name = PageThumbsStorage.getLeafNameForURL(URL3);
+ name = PageThumbsStorageService.getLeafNameForURL(URL3);
file = FileUtils.getFile("ProfLD", [THUMBNAIL_DIRECTORY, name]);
writeDummyFile(file, "no-overwrite-plz");
// Kick off thumbnail storage migration.
PageThumbsStorageMigrator.migrateToVersion3(localProfile.path);
ok(true, "migration finished");
// Wait until the first thumbnail was moved to its new location.
@@ -66,17 +66,17 @@ function* runTests() {
is(getFileContents(file), "no-overwrite-plz",
"existing thumbnail was not overwritten");
// Sanity check: ensure that, until it is removed, deprecated
// function |getFileForURL| points to the same path as
// |getFilePathForURL|.
if ("getFileForURL" in PageThumbsStorage) {
file = PageThumbsStorage.getFileForURL(URL);
- is(file.path, PageThumbsStorage.getFilePathForURL(URL),
+ is(file.path, PageThumbsStorageService.getFilePathForURL(URL),
"Deprecated getFileForURL and getFilePathForURL return the same path");
}
}
function changeLocation(aLocation, aNewDir) {
let oldDir = gDirSvc.get(aLocation, Ci.nsIFile);
gDirSvc.undefine(aLocation);
gDirSvc.set(aLocation, aNewDir);
--- a/toolkit/components/thumbnails/test/browser_thumbnails_update.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_update.js
@@ -19,26 +19,26 @@ function* runTests() {
for (let iterator of test())
yield iterator;
}
}
function ensureThumbnailStale(url) {
// We go behind the back of the thumbnail service and change the
// mtime of the file to be in the past.
- let fname = PageThumbsStorage.getFilePathForURL(url);
+ let fname = PageThumbsStorageService.getFilePathForURL(url);
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(fname);
ok(file.exists(), fname + " should exist");
// Set it as very stale...
file.lastModifiedTime = Date.now() - 1000000000;
}
function getThumbnailModifiedTime(url) {
- let fname = PageThumbsStorage.getFilePathForURL(url);
+ let fname = PageThumbsStorageService.getFilePathForURL(url);
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(fname);
return file.lastModifiedTime;
}
// The tests!
/* Check functionality of a normal captureAndStoreIfStale request */
function* simpleCaptureTest() {
--- a/toolkit/components/thumbnails/test/head.js
+++ b/toolkit/components/thumbnails/test/head.js
@@ -12,16 +12,20 @@ Cu.import("resource://gre/modules/NewTab
Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
Cu.import("resource://gre/modules/FileUtils.jsm", tmp);
Cu.import("resource://gre/modules/osfile.jsm", tmp);
var {PageThumbs, BackgroundPageThumbs, NewTabUtils, PageThumbsStorage, SessionStore, FileUtils, OS} = tmp;
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
"resource://testing-common/PlacesTestUtils.jsm");
+XPCOMUtils.defineLazyServiceGetter(this, "PageThumbsStorageService",
+ "@mozilla.org/thumbnails/pagethumbs-service;1",
+ "nsIPageThumbsStorageService");
+
var oldEnabledPref = Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled");
Services.prefs.setBoolPref("browser.pagethumbnails.capturing_disabled", false);
registerCleanupFunction(function() {
while (gBrowser.tabs.length > 1)
gBrowser.removeTab(gBrowser.tabs[1]);
Services.prefs.setBoolPref("browser.pagethumbnails.capturing_disabled", oldEnabledPref);
});
@@ -168,17 +172,17 @@ function retrieveImageDataForURL(aURL, a
});
}
/**
* Returns the file of the thumbnail with the given URL.
* @param aURL The URL of the thumbnail.
*/
function thumbnailFile(aURL) {
- return new FileUtils.File(PageThumbsStorage.getFilePathForURL(aURL));
+ return new FileUtils.File(PageThumbsStorageService.getFilePathForURL(aURL));
}
/**
* Checks if a thumbnail for the given URL exists.
* @param aURL The url associated to the thumbnail.
*/
function thumbnailExists(aURL) {
let file = thumbnailFile(aURL);
--- a/toolkit/components/thumbnails/test/test_thumbnails_interfaces.js
+++ b/toolkit/components/thumbnails/test/test_thumbnails_interfaces.js
@@ -1,29 +1,29 @@
-// tests to check that moz-page-thumb URLs correctly resolve as file:// URLS
"use strict";
const Cu = Components.utils;
-const Cc = Components.classes;
-const Cr = Components.results;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm");
-// need profile so that PageThumbsStorage can resolve the path to the underlying file
+// need profile so that PageThumbsStorageService can resolve the path to the underlying file
do_get_profile();
function run_test() {
- // first check the protocol handler implements the correct interface
+ // check the protocol handler implements the correct interface
let handler = Services.io.getProtocolHandler("moz-page-thumb");
- ok(handler instanceof Ci.nsISubstitutingProtocolHandler,
- "moz-page-thumb handler provides substituting interface");
+ ok(handler instanceof Ci.nsIProtocolHandler,
+ "moz-page-thumb handler provides a protocol handler interface");
- // then check that the file URL resolution works
- let uri = Services.io.newURI("moz-page-thumb://thumbnail/?url=http%3A%2F%2Fwww.mozilla.org%2F");
- ok(uri instanceof Ci.nsIFileURL, "moz-page-thumb:// is a FileURL");
- ok(uri.file, "This moz-page-thumb:// object is backed by a file");
+ // and check that the error cases work as specified
+ let badhost = Services.io.newURI("moz-page-thumb://wronghost/?url=http%3A%2F%2Fwww.mozilla.org%2F");
+ Assert.throws(() => handler.newChannel(badhost), /NS_ERROR_NOT_AVAILABLE/i,
+ "moz-page-thumb object with wrong host must not resolve to a file path");
- // and check that the error case works as specified
- let bad = Services.io.newURI("moz-page-thumb://wronghost/?url=http%3A%2F%2Fwww.mozilla.org%2F");
- Assert.throws(() => handler.resolveURI(bad), /NS_ERROR_NOT_AVAILABLE/i,
- "moz-page-thumb object with wrong host must not resolve to a file path");
+ let badQuery = Services.io.newURI("moz-page-thumb://thumbnail/http%3A%2F%2Fwww.mozilla.org%2F");
+ Assert.throws(() => handler.newChannel(badQuery), /NS_ERROR_MALFORMED_URI/i,
+ "moz-page-thumb object with malformed query parameters must not resolve to a file path");
+
+ let noURL = Services.io.newURI("moz-page-thumb://thumbnail/?badStuff");
+ Assert.throws(() => handler.newChannel(noURL), /NS_ERROR_NOT_AVAILABLE/i,
+ "moz-page-thumb object without a URL parameter must not resolve to a file path");
}