mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
28b141d8f2
@ -1041,6 +1041,7 @@ pref("devtools.scratchpad.enabled", true);
|
||||
|
||||
// Enable the Style Editor.
|
||||
pref("devtools.styleeditor.enabled", true);
|
||||
pref("devtools.styleeditor.transitions", true);
|
||||
|
||||
// Enable tools for Chrome development.
|
||||
pref("devtools.chrome.enabled", false);
|
||||
|
119
browser/base/content/browser-thumbnails.js
Normal file
119
browser/base/content/browser-thumbnails.js
Normal file
@ -0,0 +1,119 @@
|
||||
#ifdef 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Keeps thumbnails of open web pages up-to-date.
|
||||
*/
|
||||
let gBrowserThumbnails = {
|
||||
_captureDelayMS: 2000,
|
||||
|
||||
/**
|
||||
* Map of capture() timeouts assigned to their browsers.
|
||||
*/
|
||||
_timeouts: null,
|
||||
|
||||
/**
|
||||
* Cache for the PageThumbs module.
|
||||
*/
|
||||
_pageThumbs: null,
|
||||
|
||||
/**
|
||||
* List of tab events we want to listen for.
|
||||
*/
|
||||
_tabEvents: ["TabClose", "TabSelect"],
|
||||
|
||||
init: function Thumbnails_init() {
|
||||
gBrowser.addTabsProgressListener(this);
|
||||
|
||||
this._tabEvents.forEach(function (aEvent) {
|
||||
gBrowser.tabContainer.addEventListener(aEvent, this, false);
|
||||
}, this);
|
||||
|
||||
this._timeouts = new WeakMap();
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "_pageThumbs",
|
||||
"resource:///modules/PageThumbs.jsm", "PageThumbs");
|
||||
},
|
||||
|
||||
uninit: function Thumbnails_uninit() {
|
||||
gBrowser.removeTabsProgressListener(this);
|
||||
|
||||
this._tabEvents.forEach(function (aEvent) {
|
||||
gBrowser.tabContainer.removeEventListener(aEvent, this, false);
|
||||
}, this);
|
||||
|
||||
this._timeouts = null;
|
||||
this._pageThumbs = null;
|
||||
},
|
||||
|
||||
handleEvent: function Thumbnails_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "TabSelect":
|
||||
this._delayedCapture(aEvent.target.linkedBrowser);
|
||||
break;
|
||||
case "TabClose": {
|
||||
let browser = aEvent.target.linkedBrowser;
|
||||
if (this._timeouts.has(browser)) {
|
||||
clearTimeout(this._timeouts.get(browser));
|
||||
this._timeouts.delete(browser);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* State change progress listener for all tabs.
|
||||
*/
|
||||
onStateChange: function Thumbnails_onStateChange(aBrowser, aWebProgress,
|
||||
aRequest, aStateFlags, aStatus) {
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)
|
||||
this._delayedCapture(aBrowser);
|
||||
},
|
||||
|
||||
_capture: function Thumbnails_capture(aBrowser) {
|
||||
if (this._shouldCapture(aBrowser)) {
|
||||
let canvas = this._pageThumbs.capture(aBrowser.contentWindow);
|
||||
this._pageThumbs.store(aBrowser.currentURI.spec, canvas);
|
||||
}
|
||||
},
|
||||
|
||||
_delayedCapture: function Thumbnails_delayedCapture(aBrowser) {
|
||||
if (this._timeouts.has(aBrowser))
|
||||
clearTimeout(this._timeouts.get(aBrowser));
|
||||
|
||||
let timeout = setTimeout(function () {
|
||||
this._timeouts.delete(aBrowser);
|
||||
this._capture(aBrowser);
|
||||
}.bind(this), this._captureDelayMS);
|
||||
|
||||
this._timeouts.set(aBrowser, timeout);
|
||||
},
|
||||
|
||||
_shouldCapture: function Thumbnails_shouldCapture(aBrowser) {
|
||||
// There's no point in taking screenshot of loading pages.
|
||||
if (aBrowser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
|
||||
return false;
|
||||
|
||||
// Don't take screenshots of about: pages.
|
||||
if (aBrowser.currentURI.schemeIs("about"))
|
||||
return false;
|
||||
|
||||
let channel = aBrowser.docShell.currentDocumentChannel;
|
||||
|
||||
try {
|
||||
// If the channel is a nsIHttpChannel get its http status code.
|
||||
let httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
||||
|
||||
// Continue only if we have a 2xx status code.
|
||||
return Math.floor(httpChannel.responseStatus / 100) == 2;
|
||||
} catch (e) {
|
||||
// Not a http channel, we just assume a success status code.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
@ -196,6 +196,7 @@ let gInitialPages = [
|
||||
#include browser-places.js
|
||||
#include browser-tabPreviews.js
|
||||
#include browser-tabview.js
|
||||
#include browser-thumbnails.js
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
#include browser-syncui.js
|
||||
@ -1699,6 +1700,7 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
|
||||
gSyncUI.init();
|
||||
#endif
|
||||
|
||||
gBrowserThumbnails.init();
|
||||
TabView.init();
|
||||
|
||||
setUrlAndSearchBarWidthForConditionalForwardButton();
|
||||
@ -1820,6 +1822,7 @@ function BrowserShutdown() {
|
||||
gPrefService.removeObserver(allTabs.prefName, allTabs);
|
||||
ctrlTab.uninit();
|
||||
TabView.uninit();
|
||||
gBrowserThumbnails.uninit();
|
||||
|
||||
try {
|
||||
FullZoom.destroy();
|
||||
|
@ -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;
|
||||
}
|
21
browser/components/thumbnails/test/Makefile.in
Normal file
21
browser/components/thumbnails/test/Makefile.in
Normal file
@ -0,0 +1,21 @@
|
||||
# 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@
|
||||
relativesrcdir = browser/components/thumbnails/test
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_BROWSER_FILES = \
|
||||
browser_thumbnails_cache.js \
|
||||
browser_thumbnails_capture.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_BROWSER_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
|
@ -0,0 +1,34 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* These tests ensure that saving a thumbnail to the cache works. They also
|
||||
* retrieve the thumbnail and display it using an <img> element to compare
|
||||
* its pixel colors.
|
||||
*/
|
||||
function runTests() {
|
||||
// Create a new tab with a red background.
|
||||
yield addTab("data:text/html,<body bgcolor=ff0000></body>");
|
||||
let cw = gBrowser.selectedTab.linkedBrowser.contentWindow;
|
||||
|
||||
// Capture a thumbnail for the tab.
|
||||
let canvas = PageThumbs.capture(cw);
|
||||
|
||||
// Store the tab into the thumbnail cache.
|
||||
yield PageThumbs.store("key", canvas, next);
|
||||
|
||||
let {width, height} = canvas;
|
||||
let thumb = PageThumbs.getThumbnailURL("key", width, height);
|
||||
|
||||
// Create a new tab with an image displaying the previously stored thumbnail.
|
||||
yield addTab("data:text/html,<img src='" + thumb + "'/>" +
|
||||
"<canvas width=" + width + " height=" + height + "/>");
|
||||
|
||||
cw = gBrowser.selectedTab.linkedBrowser.contentWindow;
|
||||
let [img, canvas] = cw.document.querySelectorAll("img, canvas");
|
||||
|
||||
// Draw the image to a canvas and compare the pixel color values.
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
checkCanvasColor(ctx, 255, 0, 0, "we have a red image and canvas");
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* These tests ensure that capturing a site's screenshot to a canvas actually
|
||||
* works.
|
||||
*/
|
||||
function runTests() {
|
||||
// Create a tab with a red background.
|
||||
yield addTab("data:text/html,<body bgcolor=ff0000></body>");
|
||||
checkCurrentThumbnailColor(255, 0, 0, "we have a red thumbnail");
|
||||
|
||||
// Load a page with a green background.
|
||||
yield navigateTo("data:text/html,<body bgcolor=00ff00></body>");
|
||||
checkCurrentThumbnailColor(0, 255, 0, "we have a green thumbnail");
|
||||
|
||||
// Load a page with a blue background.
|
||||
yield navigateTo("data:text/html,<body bgcolor=0000ff></body>");
|
||||
checkCurrentThumbnailColor(0, 0, 255, "we have a blue thumbnail");
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures a thumbnail of the currently selected tab and checks the color of
|
||||
* the resulting canvas.
|
||||
* @param aRed The red component's intensity.
|
||||
* @param aGreen The green component's intensity.
|
||||
* @param aBlue The blue component's intensity.
|
||||
* @param aMessage The info message to print when checking the pixel color.
|
||||
*/
|
||||
function checkCurrentThumbnailColor(aRed, aGreen, aBlue, aMessage) {
|
||||
let tab = gBrowser.selectedTab;
|
||||
let cw = tab.linkedBrowser.contentWindow;
|
||||
|
||||
let canvas = PageThumbs.capture(cw);
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
checkCanvasColor(ctx, aRed, aGreen, aBlue, aMessage);
|
||||
}
|
93
browser/components/thumbnails/test/head.js
Normal file
93
browser/components/thumbnails/test/head.js
Normal file
@ -0,0 +1,93 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource:///modules/PageThumbs.jsm");
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
while (gBrowser.tabs.length > 1)
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Provide the default test function to start our test runner.
|
||||
*/
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* The test runner that controls the execution flow of our tests.
|
||||
*/
|
||||
let TestRunner = {
|
||||
/**
|
||||
* Starts the test runner.
|
||||
*/
|
||||
run: function () {
|
||||
waitForExplicitFinish();
|
||||
|
||||
this._iter = runTests();
|
||||
this.next();
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs the next available test or finishes if there's no test left.
|
||||
*/
|
||||
next: function () {
|
||||
try {
|
||||
TestRunner._iter.next();
|
||||
} catch (e if e instanceof StopIteration) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Continues the current test execution.
|
||||
*/
|
||||
function next() {
|
||||
TestRunner.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tab with the given URI.
|
||||
* @param aURI The URI that's loaded in the tab.
|
||||
*/
|
||||
function addTab(aURI) {
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(aURI);
|
||||
whenBrowserLoaded(tab.linkedBrowser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a new URI into the currently selected tab.
|
||||
* @param aURI The URI to load.
|
||||
*/
|
||||
function navigateTo(aURI) {
|
||||
let browser = gBrowser.selectedTab.linkedBrowser;
|
||||
whenBrowserLoaded(browser);
|
||||
browser.loadURI(aURI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues the current test execution when a load event for the given browser
|
||||
* has been received
|
||||
* @param aBrowser The browser to listen on.
|
||||
*/
|
||||
function whenBrowserLoaded(aBrowser) {
|
||||
aBrowser.addEventListener("load", function onLoad() {
|
||||
aBrowser.removeEventListener("load", onLoad, true);
|
||||
executeSoon(next);
|
||||
}, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the top-left pixel of a given canvas' 2d context for a given color.
|
||||
* @param aContext The 2D context of a canvas.
|
||||
* @param aRed The red component's intensity.
|
||||
* @param aGreen The green component's intensity.
|
||||
* @param aBlue The blue component's intensity.
|
||||
* @param aMessage The info message to print when comparing the pixel color.
|
||||
*/
|
||||
function checkCanvasColor(aContext, aRed, aGreen, aBlue, aMessage) {
|
||||
let [r, g, b] = aContext.getImageData(0, 0, 1, 1).data;
|
||||
ok(r == aRed && g == aGreen && b == aBlue, aMessage);
|
||||
}
|
@ -59,6 +59,23 @@ const UPDATE_STYLESHEET_THROTTLE_DELAY = 500;
|
||||
// @see StyleEditor._persistExpando
|
||||
const STYLESHEET_EXPANDO = "-moz-styleeditor-stylesheet-";
|
||||
|
||||
const TRANSITIONS_PREF = "devtools.styleeditor.transitions";
|
||||
|
||||
const TRANSITION_CLASS = "moz-styleeditor-transitioning";
|
||||
const TRANSITION_DURATION_MS = 500;
|
||||
const TRANSITION_RULE = "\
|
||||
:root.moz-styleeditor-transitioning, :root.moz-styleeditor-transitioning * {\
|
||||
-moz-transition-duration: " + TRANSITION_DURATION_MS + "ms !important; \
|
||||
-moz-transition-delay: 0ms !important;\
|
||||
-moz-transition-timing-function: ease-out !important;\
|
||||
-moz-transition-property: all !important;\
|
||||
}";
|
||||
|
||||
/**
|
||||
* Style Editor module-global preferences
|
||||
*/
|
||||
const TRANSITIONS_ENABLED = Services.prefs.getBoolPref(TRANSITIONS_PREF);
|
||||
|
||||
|
||||
/**
|
||||
* StyleEditor constructor.
|
||||
@ -107,6 +124,9 @@ function StyleEditor(aDocument, aStyleSheet)
|
||||
|
||||
// this is to perform pending updates before editor closing
|
||||
this._onWindowUnloadBinding = this._onWindowUnload.bind(this);
|
||||
|
||||
this._transitionRefCount = 0;
|
||||
|
||||
this._focusOnSourceEditorReady = false;
|
||||
}
|
||||
|
||||
@ -405,8 +425,13 @@ StyleEditor.prototype = {
|
||||
* Arguments: (StyleEditor editor)
|
||||
* @see inputElement
|
||||
*
|
||||
* onCommit: Called when changes have been committed/applied
|
||||
* to the live DOM style sheet.
|
||||
* onUpdate: Called when changes are being applied to the live
|
||||
* DOM style sheet but might not be complete from
|
||||
* a WYSIWYG perspective (eg. transitioned update).
|
||||
* Arguments: (StyleEditor editor)
|
||||
*
|
||||
* onCommit: Called when changes have been completely committed
|
||||
* /applied to the live DOM style sheet.
|
||||
* Arguments: (StyleEditor editor)
|
||||
* }
|
||||
*
|
||||
@ -640,15 +665,50 @@ StyleEditor.prototype = {
|
||||
let source = this._state.text;
|
||||
let oldNode = this.styleSheet.ownerNode;
|
||||
let oldIndex = this.styleSheetIndex;
|
||||
|
||||
let newNode = this.contentDocument.createElement("style");
|
||||
let content = this.contentDocument;
|
||||
let newNode = content.createElement("style");
|
||||
newNode.setAttribute("type", "text/css");
|
||||
newNode.appendChild(this.contentDocument.createTextNode(source));
|
||||
newNode.appendChild(content.createTextNode(source));
|
||||
oldNode.parentNode.replaceChild(newNode, oldNode);
|
||||
|
||||
this._styleSheet = this.contentDocument.styleSheets[oldIndex];
|
||||
this._styleSheet = content.styleSheets[oldIndex];
|
||||
this._persistExpando();
|
||||
|
||||
if (!TRANSITIONS_ENABLED) {
|
||||
this._triggerAction("Update");
|
||||
this._triggerAction("Commit");
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert the global transition rule
|
||||
// Use a ref count to make sure we do not add it multiple times.. and remove
|
||||
// it only when all pending StyleEditor-generated transitions ended.
|
||||
if (!this._transitionRefCount) {
|
||||
this._styleSheet.insertRule(TRANSITION_RULE, 0);
|
||||
content.documentElement.classList.add(TRANSITION_CLASS);
|
||||
}
|
||||
|
||||
this._transitionRefCount++;
|
||||
|
||||
// Set up clean up and commit after transition duration (+10% buffer)
|
||||
// @see _onTransitionEnd
|
||||
content.defaultView.setTimeout(this._onTransitionEnd.bind(this),
|
||||
Math.floor(TRANSITION_DURATION_MS * 1.1));
|
||||
|
||||
this._triggerAction("Update");
|
||||
},
|
||||
|
||||
/**
|
||||
* This cleans up class and rule added for transition effect and then trigger
|
||||
* Commit as the changes have been completed.
|
||||
*/
|
||||
_onTransitionEnd: function SE__onTransitionEnd()
|
||||
{
|
||||
if (--this._transitionRefCount == 0) {
|
||||
this.contentDocument.documentElement.classList.remove(TRANSITION_CLASS);
|
||||
this.styleSheet.deleteRule(0);
|
||||
}
|
||||
|
||||
this._triggerAction("Commit");
|
||||
},
|
||||
|
||||
|
@ -156,11 +156,14 @@ StyleEditorChrome.prototype = {
|
||||
aContentWindow.addEventListener("unload", onContentUnload, false);
|
||||
|
||||
if (aContentWindow.document.readyState == "complete") {
|
||||
this._root.classList.remove("loading");
|
||||
this._populateChrome();
|
||||
return;
|
||||
} else {
|
||||
this._root.classList.add("loading");
|
||||
let onContentReady = function () {
|
||||
aContentWindow.removeEventListener("load", onContentReady, false);
|
||||
this._root.classList.remove("loading");
|
||||
this._populateChrome();
|
||||
}.bind(this);
|
||||
aContentWindow.addEventListener("load", onContentReady, false);
|
||||
@ -299,7 +302,7 @@ StyleEditorChrome.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the chrome UI to an empty state.
|
||||
* Reset the chrome UI to an empty and ready state.
|
||||
*/
|
||||
_resetChrome: function SEC__resetChrome()
|
||||
{
|
||||
@ -309,6 +312,12 @@ StyleEditorChrome.prototype = {
|
||||
this._editors = [];
|
||||
|
||||
this._view.removeAll();
|
||||
|
||||
// (re)enable UI
|
||||
let matches = this._root.querySelectorAll("toolbarbutton,input,select");
|
||||
for (let i = 0; i < matches.length; ++i) {
|
||||
matches[i].removeAttribute("disabled");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -46,6 +46,10 @@ box,
|
||||
-moz-box-pack: center;
|
||||
}
|
||||
|
||||
.loading .splitview-nav-container > .placeholder {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.splitview-controller,
|
||||
.splitview-main {
|
||||
-moz-box-flex: 0;
|
||||
|
@ -54,18 +54,20 @@
|
||||
persist="screenX screenY width height sizemode">
|
||||
<xul:script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
|
||||
|
||||
<xul:box id="style-editor-chrome" class="splitview-root">
|
||||
<xul:box id="style-editor-chrome" class="splitview-root loading">
|
||||
<xul:box class="splitview-controller" id="stylesheets-controller" persist="width height">
|
||||
<xul:box class="splitview-main">
|
||||
<xul:toolbar class="devtools-toolbar">
|
||||
<xul:toolbarbutton class="style-editor-newButton devtools-toolbarbutton"
|
||||
accesskey="&newButton.accesskey;"
|
||||
tooltiptext="&newButton.tooltip;"
|
||||
label="&newButton.label;"/>
|
||||
label="&newButton.label;"
|
||||
disabled="true"/>
|
||||
<xul:toolbarbutton class="style-editor-importButton devtools-toolbarbutton"
|
||||
accesskey="&importButton.accesskey;"
|
||||
tooltiptext="&importButton.tooltip;"
|
||||
label="&importButton.label;"/>
|
||||
label="&importButton.label;"
|
||||
disabled="true"/>
|
||||
<xul:spacer flex="1"/>
|
||||
<xul:textbox class="splitview-filter devtools-searchinput"
|
||||
type="search" flex="1"
|
||||
|
@ -18,6 +18,18 @@ function test()
|
||||
isnot(gBrowser.selectedBrowser.contentWindow.document.readyState, "complete",
|
||||
"content document is still loading");
|
||||
|
||||
let root = gChromeWindow.document.querySelector(".splitview-root");
|
||||
ok(root.classList.contains("loading"),
|
||||
"style editor root element has 'loading' class name");
|
||||
|
||||
let button = gChromeWindow.document.querySelector(".style-editor-newButton");
|
||||
ok(button.hasAttribute("disabled"),
|
||||
"new style sheet button is disabled");
|
||||
|
||||
button = gChromeWindow.document.querySelector(".style-editor-importButton");
|
||||
ok(button.hasAttribute("disabled"),
|
||||
"import button is disabled");
|
||||
|
||||
if (!aChrome.isContentAttached) {
|
||||
aChrome.addChromeListener({
|
||||
onContentAttach: run
|
||||
@ -35,5 +47,17 @@ function run(aChrome)
|
||||
is(aChrome.contentWindow.document.readyState, "complete",
|
||||
"content document is complete");
|
||||
|
||||
let root = gChromeWindow.document.querySelector(".splitview-root");
|
||||
ok(!root.classList.contains("loading"),
|
||||
"style editor root element does not have 'loading' class name anymore");
|
||||
|
||||
let button = gChromeWindow.document.querySelector(".style-editor-newButton");
|
||||
ok(!button.hasAttribute("disabled"),
|
||||
"new style sheet button is enabled");
|
||||
|
||||
button = gChromeWindow.document.querySelector(".style-editor-importButton");
|
||||
ok(!button.hasAttribute("disabled"),
|
||||
"import button is enabled");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "simple.html";
|
||||
|
||||
const TRANSITION_CLASS = "moz-styleeditor-transitioning";
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
@ -30,6 +32,7 @@ function run(aChrome)
|
||||
|
||||
let gAddedCount = 0; // to add new stylesheet after the 2 initial stylesheets
|
||||
let gNewEditor; // to make sure only one new stylesheet got created
|
||||
let gUpdateCount = 0; // to make sure only one Update event is triggered
|
||||
let gCommitCount = 0; // to make sure only one Commit event is triggered
|
||||
|
||||
function testEditorAdded(aChrome, aEditor)
|
||||
@ -82,6 +85,13 @@ function testEditorAdded(aChrome, aEditor)
|
||||
}, gChromeWindow) ;
|
||||
},
|
||||
|
||||
onUpdate: function (aEditor) {
|
||||
gUpdateCount++;
|
||||
|
||||
ok(content.document.documentElement.classList.contains(TRANSITION_CLASS),
|
||||
"StyleEditor's transition class has been added to content");
|
||||
},
|
||||
|
||||
onCommit: function (aEditor) {
|
||||
gCommitCount++;
|
||||
|
||||
@ -99,7 +109,11 @@ function testEditorAdded(aChrome, aEditor)
|
||||
is(computedStyle.backgroundColor, "rgb(255, 0, 0)",
|
||||
"content's background color has been updated to red");
|
||||
|
||||
ok(!content.document.documentElement.classList.contains(TRANSITION_CLASS),
|
||||
"StyleEditor's transition class has been removed from content");
|
||||
|
||||
executeSoon(function () {
|
||||
is(gUpdateCount, 1, "received only one Update event (throttle)");
|
||||
is(gCommitCount, 1, "received only one Commit event (throttle)");
|
||||
|
||||
aEditor.removeActionListener(listener);
|
||||
|
@ -37,7 +37,7 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const TEST_REPLACED_API_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console-replaced-api.html";
|
||||
const TEST_REPLACED_API_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-replaced-api.html";
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
@ -41,7 +41,7 @@
|
||||
// Tests that the page's resources are displayed in the console as they're
|
||||
// loaded
|
||||
|
||||
const TEST_NETWORK_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-network.html" + "?_date=" + Date.now();
|
||||
const TEST_NETWORK_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-network.html" + "?_date=" + Date.now();
|
||||
|
||||
function test() {
|
||||
addTab("data:text/html,Web Console basic network logging test");
|
||||
|
@ -39,7 +39,7 @@
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// Tests that the console object still exists after a page reload.
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
|
@ -40,7 +40,7 @@
|
||||
|
||||
// Tests that errors still show up in the Web Console after a page reload.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-error.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-error.html";
|
||||
|
||||
function test() {
|
||||
expectUncaughtException();
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
Cu.import("resource:///modules/HUDService.jsm");
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
// Tests that the Web Console close button functions.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
|
@ -12,7 +12,7 @@
|
||||
// Tests that the Web Console limits the number of lines displayed according to
|
||||
// the user's preferences.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
|
@ -36,7 +36,7 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-585956-console-trace.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-585956-console-trace.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
|
@ -40,7 +40,7 @@
|
||||
|
||||
// Tests that adding text to one of the output labels doesn't cause errors.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
|
@ -36,11 +36,11 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-593003-iframe-wrong-hud.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-593003-iframe-wrong-hud.html";
|
||||
|
||||
const TEST_IFRAME_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-593003-iframe-wrong-hud-iframe.html";
|
||||
const TEST_IFRAME_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html";
|
||||
|
||||
const TEST_DUMMY_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_DUMMY_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
|
||||
let tab1, tab2;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
|
||||
let tab1, tab2, win1, win2;
|
||||
let noErrors = true;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-network.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-network.html";
|
||||
|
||||
function tabLoad(aEvent) {
|
||||
browser.removeEventListener(aEvent.type, arguments.callee, true);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-597756-reopen-closed-tab.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-597756-reopen-closed-tab.html";
|
||||
|
||||
let newTabIsOpen = false;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-599725-response-headers.sjs";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-599725-response-headers.sjs";
|
||||
|
||||
let lastFinishedRequest = null;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-600183-charset.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-600183-charset.html";
|
||||
|
||||
let lastFinishedRequest = null;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-601177-log-levels.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-601177-log-levels.html";
|
||||
|
||||
let msgs;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-603750-websocket.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-603750-websocket.html";
|
||||
const pref_ws = "network.websocket.enabled";
|
||||
const pref_block = "network.websocket.override-security-block";
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-613013-console-api-iframe.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-613013-console-api-iframe.html";
|
||||
|
||||
let TestObserver = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Mihai Sucan <mihai.sucan@gmail.com>
|
||||
*/
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-630733-response-redirect-headers.sjs";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-630733-response-redirect-headers.sjs";
|
||||
|
||||
let lastFinishedRequests = {};
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
// Tests that network log messages bring up the network panel.
|
||||
|
||||
const TEST_NETWORK_REQUEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-network-request.html";
|
||||
const TEST_NETWORK_REQUEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-network-request.html";
|
||||
|
||||
const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test//test-image.png";
|
||||
const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test/test-image.png";
|
||||
|
||||
const TEST_DATA_JSON_CONTENT =
|
||||
'{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }';
|
||||
|
@ -37,7 +37,7 @@
|
||||
|
||||
// Tests that the basic console.log()-style APIs and filtering work.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console-extras.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-extras.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
|
@ -40,7 +40,7 @@
|
||||
|
||||
// Tests that the message type filter checkboxes work.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
|
@ -40,7 +40,7 @@
|
||||
|
||||
// Tests that the text filter box works.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
|
@ -41,7 +41,7 @@
|
||||
// Tests that console logging via the console API produces nodes of the correct
|
||||
// CSS classes.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
|
@ -35,7 +35,7 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
|
@ -12,9 +12,9 @@
|
||||
|
||||
// Tests that network log messages bring up the network panel.
|
||||
|
||||
const TEST_NETWORK_REQUEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-network-request.html";
|
||||
const TEST_NETWORK_REQUEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-network-request.html";
|
||||
|
||||
const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test//test-image.png";
|
||||
const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test/test-image.png";
|
||||
|
||||
const TEST_DATA_JSON_CONTENT =
|
||||
'{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }';
|
||||
|
@ -41,9 +41,9 @@
|
||||
|
||||
// Tests that the network panel works.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test//test-image.png";
|
||||
const TEST_ENCODING_ISO_8859_1 = "http://example.com/browser/browser/devtools/webconsole/test//test-encoding-ISO-8859-1.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test/test-image.png";
|
||||
const TEST_ENCODING_ISO_8859_1 = "http://example.com/browser/browser/devtools/webconsole/test/test-encoding-ISO-8859-1.html";
|
||||
|
||||
let testDriver;
|
||||
|
||||
|
@ -41,7 +41,7 @@
|
||||
// Tests that the HUD service keeps an accurate registry of all the Web Console
|
||||
// instances.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
|
@ -4,7 +4,7 @@
|
||||
// Tests that source URLs in the Web Console can be clicked to display the
|
||||
// standard View Source window.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-error.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-error.html";
|
||||
|
||||
function test() {
|
||||
expectUncaughtException();
|
||||
|
@ -8,6 +8,6 @@
|
||||
<body>
|
||||
<p>WebConsole test: iframe associated to the wrong HUD.</p>
|
||||
<iframe
|
||||
src="http://example.com/browser/browser/devtools/webconsole/test//test-bug-593003-iframe-wrong-hud-iframe.html"></iframe>
|
||||
src="http://example.com/browser/browser/devtools/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -27,7 +27,7 @@
|
||||
<h1>Heads Up Display HTTP Logging Testpage</h1>
|
||||
<h2>This page is used to test the HTTP logging.</h2>
|
||||
|
||||
<form action="http://example.com/browser/browser/devtools/webconsole/test//test-network-request.html" method="post">
|
||||
<form action="http://example.com/browser/browser/devtools/webconsole/test/test-network-request.html" method="post">
|
||||
<input name="name" type="text" value="foo bar"><br>
|
||||
<input name="age" type="text" value="144"><br>
|
||||
</form>
|
||||
|
@ -283,6 +283,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
|
||||
@ -346,6 +347,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
|
||||
|
@ -52,6 +52,7 @@ EXTRA_JS_MODULES = \
|
||||
openLocationLastURL.jsm \
|
||||
NetworkPrioritizer.jsm \
|
||||
offlineAppCache.jsm \
|
||||
PageThumbs.jsm \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
|
||||
|
265
browser/modules/PageThumbs.jsm
Normal file
265
browser/modules/PageThumbs.jsm
Normal file
@ -0,0 +1,265 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let EXPORTED_SYMBOLS = ["PageThumbs", "PageThumbsCache"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
/**
|
||||
* The default width for page thumbnails.
|
||||
*
|
||||
* Hint: This is the default value because the 'New Tab Page' is the only
|
||||
* client for now.
|
||||
*/
|
||||
const THUMBNAIL_WIDTH = 201;
|
||||
|
||||
/**
|
||||
* The default height for page thumbnails.
|
||||
*
|
||||
* Hint: This is the default value because the 'New Tab Page' is the only
|
||||
* client for now.
|
||||
*/
|
||||
const THUMBNAIL_HEIGHT = 127;
|
||||
|
||||
/**
|
||||
* The default background color for page thumbnails.
|
||||
*/
|
||||
const THUMBNAIL_BG_COLOR = "#fff";
|
||||
|
||||
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");
|
||||
|
||||
/**
|
||||
* Singleton providing functionality for capturing web page thumbnails and for
|
||||
* accessing them if already cached.
|
||||
*/
|
||||
let PageThumbs = {
|
||||
/**
|
||||
* The scheme to use for thumbnail urls.
|
||||
*/
|
||||
get scheme() "moz-page-thumb",
|
||||
|
||||
/**
|
||||
* The static host to use for thumbnail urls.
|
||||
*/
|
||||
get staticHost() "thumbnail",
|
||||
|
||||
/**
|
||||
* The thumbnails' image type.
|
||||
*/
|
||||
get contentType() "image/png",
|
||||
|
||||
/**
|
||||
* Gets the thumbnail image's url for a given web page's url.
|
||||
* @param aUrl The web page's url that is depicted in the thumbnail.
|
||||
* @return The thumbnail image's url.
|
||||
*/
|
||||
getThumbnailURL: function PageThumbs_getThumbnailURL(aUrl) {
|
||||
return this.scheme + "://" + this.staticHost +
|
||||
"?url=" + encodeURIComponent(aUrl);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a canvas containing a thumbnail depicting the given window.
|
||||
* @param aWindow The DOM window to capture a thumbnail from.
|
||||
* @return The newly created canvas containing the image data.
|
||||
*/
|
||||
capture: function PageThumbs_capture(aWindow) {
|
||||
let [sx, sy, sw, sh, scale] = this._determineCropRectangle(aWindow);
|
||||
|
||||
let canvas = this._createCanvas();
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
// Scale the canvas accordingly.
|
||||
ctx.scale(scale, scale);
|
||||
|
||||
try {
|
||||
// Draw the window contents to the canvas.
|
||||
ctx.drawWindow(aWindow, sx, sy, sw, sh, THUMBNAIL_BG_COLOR,
|
||||
ctx.DRAWWINDOW_DO_NOT_FLUSH);
|
||||
} catch (e) {
|
||||
// We couldn't draw to the canvas for some reason.
|
||||
}
|
||||
|
||||
return canvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* Stores the image data contained in the given canvas to the underlying
|
||||
* storage.
|
||||
* @param aKey The key to use for the storage.
|
||||
* @param aCanvas The canvas containing the thumbnail's image data.
|
||||
* @param aCallback The function to be called when the canvas data has been
|
||||
* stored (optional).
|
||||
*/
|
||||
store: function PageThumbs_store(aKey, aCanvas, aCallback) {
|
||||
let self = this;
|
||||
|
||||
function finish(aSuccessful) {
|
||||
if (aCallback)
|
||||
aCallback(aSuccessful);
|
||||
}
|
||||
|
||||
// Get a writeable cache entry.
|
||||
PageThumbsCache.getWriteEntry(aKey, function (aEntry) {
|
||||
if (!aEntry) {
|
||||
finish(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract image data from the canvas.
|
||||
self._readImageData(aCanvas, function (aData) {
|
||||
let outputStream = aEntry.openOutputStream(0);
|
||||
|
||||
// Write the image data to the cache entry.
|
||||
NetUtil.asyncCopy(aData, outputStream, function (aResult) {
|
||||
let success = Components.isSuccessCode(aResult);
|
||||
if (success)
|
||||
aEntry.markValid();
|
||||
|
||||
aEntry.close();
|
||||
finish(success);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Reads the image data from a given canvas and passes it to the callback.
|
||||
* @param aCanvas The canvas to read the image data from.
|
||||
* @param aCallback The function that the image data is passed to.
|
||||
*/
|
||||
_readImageData: function PageThumbs_readImageData(aCanvas, aCallback) {
|
||||
let dataUri = aCanvas.toDataURL(PageThumbs.contentType, "");
|
||||
let uri = Services.io.newURI(dataUri, "UTF8", null);
|
||||
|
||||
NetUtil.asyncFetch(uri, function (aData, aResult) {
|
||||
if (Components.isSuccessCode(aResult) && aData && aData.available())
|
||||
aCallback(aData);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines the crop rectangle for a given content window.
|
||||
* @param aWindow The content window.
|
||||
* @return An array containing x, y, width, heigh and the scale of the crop
|
||||
* rectangle.
|
||||
*/
|
||||
_determineCropRectangle: function PageThumbs_determineCropRectangle(aWindow) {
|
||||
let sx = 0;
|
||||
let sy = 0;
|
||||
let sw = aWindow.innerWidth;
|
||||
let sh = aWindow.innerHeight;
|
||||
|
||||
let scale = Math.max(THUMBNAIL_WIDTH / sw, THUMBNAIL_HEIGHT / sh);
|
||||
let scaledWidth = sw * scale;
|
||||
let scaledHeight = sh * scale;
|
||||
|
||||
if (scaledHeight > THUMBNAIL_HEIGHT) {
|
||||
sy = Math.floor(Math.abs((scaledHeight - THUMBNAIL_HEIGHT) / 2) / scale);
|
||||
sh -= 2 * sy;
|
||||
}
|
||||
|
||||
if (scaledWidth > THUMBNAIL_WIDTH) {
|
||||
sx = Math.floor(Math.abs((scaledWidth - THUMBNAIL_WIDTH) / 2) / scale);
|
||||
sw -= 2 * sx;
|
||||
}
|
||||
|
||||
return [sx, sy, sw, sh, scale];
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a new hidden canvas element.
|
||||
* @return The newly created canvas.
|
||||
*/
|
||||
_createCanvas: function PageThumbs_createCanvas() {
|
||||
let doc = Services.appShell.hiddenDOMWindow.document;
|
||||
let canvas = doc.createElementNS(HTML_NAMESPACE, "canvas");
|
||||
canvas.mozOpaque = true;
|
||||
canvas.width = THUMBNAIL_WIDTH;
|
||||
canvas.height = THUMBNAIL_HEIGHT;
|
||||
return canvas;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A singleton handling the storage of page thumbnails.
|
||||
*/
|
||||
let PageThumbsCache = {
|
||||
/**
|
||||
* Calls the given callback with a cache entry opened for reading.
|
||||
* @param aKey The key identifying the desired cache entry.
|
||||
* @param aCallback The callback that is called when the cache entry is ready.
|
||||
*/
|
||||
getReadEntry: function Cache_getReadEntry(aKey, aCallback) {
|
||||
// Try to open the desired cache entry.
|
||||
this._openCacheEntry(aKey, Ci.nsICache.ACCESS_READ, aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls the given callback with a cache entry opened for writing.
|
||||
* @param aKey The key identifying the desired cache entry.
|
||||
* @param aCallback The callback that is called when the cache entry is ready.
|
||||
*/
|
||||
getWriteEntry: function Cache_getWriteEntry(aKey, aCallback) {
|
||||
// Try to open the desired cache entry.
|
||||
this._openCacheEntry(aKey, Ci.nsICache.ACCESS_WRITE, aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens the cache entry identified by the given key.
|
||||
* @param aKey The key identifying the desired cache entry.
|
||||
* @param aAccess The desired access mode (see nsICache.ACCESS_* constants).
|
||||
* @param aCallback The function to be called when the cache entry was opened.
|
||||
*/
|
||||
_openCacheEntry: function Cache_openCacheEntry(aKey, aAccess, aCallback) {
|
||||
function onCacheEntryAvailable(aEntry, aAccessGranted, aStatus) {
|
||||
let validAccess = aAccess == aAccessGranted;
|
||||
let validStatus = Components.isSuccessCode(aStatus);
|
||||
|
||||
// Check if a valid entry was passed and if the
|
||||
// access we requested was actually granted.
|
||||
if (aEntry && !(validAccess && validStatus)) {
|
||||
aEntry.close();
|
||||
aEntry = null;
|
||||
}
|
||||
|
||||
aCallback(aEntry);
|
||||
}
|
||||
|
||||
let listener = this._createCacheListener(onCacheEntryAvailable);
|
||||
this._cacheSession.asyncOpenCacheEntry(aKey, aAccess, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a cache listener implementing the nsICacheListener interface.
|
||||
* @param aCallback The callback to be called when the cache entry is available.
|
||||
* @return The new cache listener.
|
||||
*/
|
||||
_createCacheListener: function Cache_createCacheListener(aCallback) {
|
||||
return {
|
||||
onCacheEntryAvailable: aCallback,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICacheListener])
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Define a lazy getter for the cache session.
|
||||
*/
|
||||
XPCOMUtils.defineLazyGetter(PageThumbsCache, "_cacheSession", function () {
|
||||
return Services.cache.createSession(PageThumbs.scheme,
|
||||
Ci.nsICache.STORE_ON_DISK, true);
|
||||
});
|
@ -41,6 +41,12 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
.loading .splitview-nav-container {
|
||||
background-image: url(chrome://global/skin/icons/loading_16.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.splitview-nav {
|
||||
-moz-appearance: none;
|
||||
margin: 0;
|
||||
@ -70,7 +76,6 @@
|
||||
|
||||
.placeholder {
|
||||
-moz-box-flex: 1;
|
||||
-moz-box-back: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,10 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#style-editor-chrome {
|
||||
background-color: hsl(208,11%,27%);
|
||||
}
|
||||
|
||||
.stylesheet-title,
|
||||
.stylesheet-name {
|
||||
text-decoration: none;
|
||||
|
@ -41,6 +41,12 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
.loading .splitview-nav-container {
|
||||
background-image: url(chrome://global/skin/icons/loading_16.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.splitview-nav {
|
||||
-moz-appearance: none;
|
||||
margin: 0;
|
||||
@ -70,7 +76,6 @@
|
||||
|
||||
.placeholder {
|
||||
-moz-box-flex: 1;
|
||||
-moz-box-back: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,10 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#style-editor-chrome {
|
||||
background-color: hsl(208,11%,27%);
|
||||
}
|
||||
|
||||
.stylesheet-title,
|
||||
.stylesheet-name {
|
||||
text-decoration: none;
|
||||
|
@ -41,6 +41,12 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
.loading .splitview-nav-container {
|
||||
background-image: url(chrome://global/skin/icons/loading_16.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.splitview-nav {
|
||||
-moz-appearance: none;
|
||||
margin: 0;
|
||||
@ -70,7 +76,6 @@
|
||||
|
||||
.placeholder {
|
||||
-moz-box-flex: 1;
|
||||
-moz-box-back: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,10 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#style-editor-chrome {
|
||||
background-color: hsl(211,21%,26%);
|
||||
}
|
||||
|
||||
.stylesheet-title,
|
||||
.stylesheet-name {
|
||||
text-decoration: none;
|
||||
|
@ -63,6 +63,8 @@ XPCOMUtils.defineLazyGetter(Services, "dirsvc", function () {
|
||||
});
|
||||
|
||||
let initTable = [
|
||||
["appShell", "@mozilla.org/appshell/appShellService;1", "nsIAppShellService"],
|
||||
["cache", "@mozilla.org/network/cache-service;1", "nsICacheService"],
|
||||
["console", "@mozilla.org/consoleservice;1", "nsIConsoleService"],
|
||||
["contentPrefs", "@mozilla.org/content-pref/service;1", "nsIContentPrefService"],
|
||||
["cookies", "@mozilla.org/cookiemanager;1", "nsICookieManager2"],
|
||||
|
Loading…
Reference in New Issue
Block a user