Merge m-c to m-i
|
@ -401,8 +401,6 @@
|
||||||
@RESPATH@/components/SiteSpecificUserAgent.manifest
|
@RESPATH@/components/SiteSpecificUserAgent.manifest
|
||||||
@RESPATH@/components/storage-json.js
|
@RESPATH@/components/storage-json.js
|
||||||
@RESPATH@/components/crypto-SDR.js
|
@RESPATH@/components/crypto-SDR.js
|
||||||
@RESPATH@/components/jsconsole-clhandler.manifest
|
|
||||||
@RESPATH@/components/jsconsole-clhandler.js
|
|
||||||
@RESPATH@/components/Downloads.manifest
|
@RESPATH@/components/Downloads.manifest
|
||||||
@RESPATH@/components/DownloadLegacy.js
|
@RESPATH@/components/DownloadLegacy.js
|
||||||
@RESPATH@/components/nsSidebar.manifest
|
@RESPATH@/components/nsSidebar.manifest
|
||||||
|
|
|
@ -1432,3 +1432,5 @@ pref("browser.migration.automigrate", false);
|
||||||
pref("dom.mozBrowserFramesEnabled", true);
|
pref("dom.mozBrowserFramesEnabled", true);
|
||||||
|
|
||||||
pref("extensions.pocket.enabled", true);
|
pref("extensions.pocket.enabled", true);
|
||||||
|
|
||||||
|
pref("signon.schemeUpgrades", true);
|
||||||
|
|
|
@ -3911,11 +3911,6 @@ function addToUrlbarHistory(aUrlToAdd) {
|
||||||
PlacesUIUtils.markPageAsTyped(aUrlToAdd);
|
PlacesUIUtils.markPageAsTyped(aUrlToAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toJavaScriptConsole()
|
|
||||||
{
|
|
||||||
toOpenWindowByType("global:console", "chrome://global/content/console.xul");
|
|
||||||
}
|
|
||||||
|
|
||||||
function BrowserDownloadsUI()
|
function BrowserDownloadsUI()
|
||||||
{
|
{
|
||||||
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
|
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
|
||||||
|
@ -7242,8 +7237,13 @@ var gIdentityHandler = {
|
||||||
label.setAttribute("control", menulist.getAttribute("id"));
|
label.setAttribute("control", menulist.getAttribute("id"));
|
||||||
label.textContent = aPermission.label;
|
label.textContent = aPermission.label;
|
||||||
|
|
||||||
|
let img = document.createElement("image");
|
||||||
|
img.setAttribute("class",
|
||||||
|
"identity-popup-permission-icon " + aPermission.id + "-icon");
|
||||||
|
|
||||||
let container = document.createElement("hbox");
|
let container = document.createElement("hbox");
|
||||||
container.setAttribute("align", "center");
|
container.setAttribute("align", "center");
|
||||||
|
container.appendChild(img);
|
||||||
container.appendChild(label);
|
container.appendChild(label);
|
||||||
container.appendChild(menulist);
|
container.appendChild(menulist);
|
||||||
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.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/.
|
|
||||||
|
|
||||||
<?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?>
|
|
||||||
|
|
||||||
<overlay id="jsConsoleOverlay"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
|
||||||
|
|
||||||
<window id="JSConsoleWindow">
|
|
||||||
|
|
||||||
#include browserMountPoints.inc
|
|
||||||
|
|
||||||
</window>
|
|
||||||
|
|
||||||
</overlay>
|
|
|
@ -216,8 +216,6 @@ skip-if = buildapp == 'mulet' # Bug 1066070 - I don't think either popup notific
|
||||||
skip-if = true # bug 1057615
|
skip-if = true # bug 1057615
|
||||||
[browser_bug563588.js]
|
[browser_bug563588.js]
|
||||||
[browser_bug565575.js]
|
[browser_bug565575.js]
|
||||||
[browser_bug565667.js]
|
|
||||||
skip-if = toolkit != "cocoa"
|
|
||||||
[browser_bug567306.js]
|
[browser_bug567306.js]
|
||||||
subsuite = clipboard
|
subsuite = clipboard
|
||||||
[browser_bug575561.js]
|
[browser_bug575561.js]
|
||||||
|
|
|
@ -1,59 +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/. */
|
|
||||||
|
|
||||||
var fm = Services.focus;
|
|
||||||
|
|
||||||
function test() {
|
|
||||||
waitForExplicitFinish();
|
|
||||||
// Open the javascript console. It has the mac menu overlay, so browser.js is
|
|
||||||
// loaded in it.
|
|
||||||
let consoleWin = window.open("chrome://global/content/console.xul", "_blank",
|
|
||||||
"chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
|
|
||||||
testWithOpenWindow(consoleWin);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testWithOpenWindow(consoleWin) {
|
|
||||||
// Add a tab so we don't open the url into the current tab
|
|
||||||
let newTab = gBrowser.addTab("http://example.com");
|
|
||||||
gBrowser.selectedTab = newTab;
|
|
||||||
|
|
||||||
let numTabs = gBrowser.tabs.length;
|
|
||||||
|
|
||||||
waitForFocus(function() {
|
|
||||||
// Sanity check
|
|
||||||
is(fm.activeWindow, consoleWin,
|
|
||||||
"the console window is focused");
|
|
||||||
|
|
||||||
gBrowser.tabContainer.addEventListener("TabOpen", function(aEvent) {
|
|
||||||
gBrowser.tabContainer.removeEventListener("TabOpen", arguments.callee, true);
|
|
||||||
let browser = aEvent.originalTarget.linkedBrowser;
|
|
||||||
browser.addEventListener("pageshow", function(event) {
|
|
||||||
if (event.target.location.href != "about:addons")
|
|
||||||
return;
|
|
||||||
browser.removeEventListener("pageshow", arguments.callee, true);
|
|
||||||
|
|
||||||
is(fm.activeWindow, window,
|
|
||||||
"the browser window was focused");
|
|
||||||
is(browser.currentURI.spec, "about:addons",
|
|
||||||
"about:addons was loaded in the window");
|
|
||||||
is(gBrowser.tabs.length, numTabs + 1,
|
|
||||||
"a new tab was added");
|
|
||||||
|
|
||||||
// Cleanup.
|
|
||||||
executeSoon(function() {
|
|
||||||
consoleWin.close();
|
|
||||||
gBrowser.removeTab(gBrowser.selectedTab);
|
|
||||||
gBrowser.removeTab(newTab);
|
|
||||||
finish();
|
|
||||||
});
|
|
||||||
}, true);
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
// Open the addons manager, uses switchToTabHavingURI.
|
|
||||||
consoleWin.BrowserOpenAddonsMgr();
|
|
||||||
}, consoleWin);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ideally we'd also check that the case for no open windows works, but we can't
|
|
||||||
// due to limitations with the testing framework.
|
|
|
@ -45,6 +45,10 @@ add_task(function* testMainViewVisible() {
|
||||||
is(menulists[0].value, "1", "Correct value on install menulist");
|
is(menulists[0].value, "1", "Correct value on install menulist");
|
||||||
gIdentityHandler._identityPopup.hidden = true;
|
gIdentityHandler._identityPopup.hidden = true;
|
||||||
|
|
||||||
|
let img = menulists[0].parentNode.querySelector("image");
|
||||||
|
ok(img, "There is an image for the permissions");
|
||||||
|
ok(img.classList.contains("install-icon"), "proper class is in image class");
|
||||||
|
|
||||||
gIdentityHandler.setPermission("install", SitePermissions.getDefault("install"));
|
gIdentityHandler.setPermission("install", SitePermissions.getDefault("install"));
|
||||||
|
|
||||||
gIdentityHandler._identityBox.click();
|
gIdentityHandler._identityBox.click();
|
||||||
|
|
|
@ -5,7 +5,6 @@ browser.jar:
|
||||||
% content browser %content/browser/ contentaccessible=yes
|
% content browser %content/browser/ contentaccessible=yes
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
% overlay chrome://mozapps/content/downloads/downloads.xul chrome://browser/content/downloadManagerOverlay.xul
|
% overlay chrome://mozapps/content/downloads/downloads.xul chrome://browser/content/downloadManagerOverlay.xul
|
||||||
% overlay chrome://global/content/console.xul chrome://browser/content/jsConsoleOverlay.xul
|
|
||||||
% overlay chrome://mozapps/content/update/updates.xul chrome://browser/content/softwareUpdateOverlay.xul
|
% overlay chrome://mozapps/content/update/updates.xul chrome://browser/content/softwareUpdateOverlay.xul
|
||||||
#endif
|
#endif
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
|
@ -180,7 +179,6 @@ browser.jar:
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
* content/browser/macBrowserOverlay.xul (content/macBrowserOverlay.xul)
|
* content/browser/macBrowserOverlay.xul (content/macBrowserOverlay.xul)
|
||||||
* content/browser/downloadManagerOverlay.xul (content/downloadManagerOverlay.xul)
|
* content/browser/downloadManagerOverlay.xul (content/downloadManagerOverlay.xul)
|
||||||
* content/browser/jsConsoleOverlay.xul (content/jsConsoleOverlay.xul)
|
|
||||||
* content/browser/softwareUpdateOverlay.xul (content/softwareUpdateOverlay.xul)
|
* content/browser/softwareUpdateOverlay.xul (content/softwareUpdateOverlay.xul)
|
||||||
#endif
|
#endif
|
||||||
* content/browser/viewSourceOverlay.xul (content/viewSourceOverlay.xul)
|
* content/browser/viewSourceOverlay.xul (content/viewSourceOverlay.xul)
|
||||||
|
|
|
@ -87,7 +87,8 @@
|
||||||
<!-- Permissions Section -->
|
<!-- Permissions Section -->
|
||||||
<hbox class="identity-popup-section">
|
<hbox class="identity-popup-section">
|
||||||
<vbox id="identity-popup-permissions-content" flex="1">
|
<vbox id="identity-popup-permissions-content" flex="1">
|
||||||
<label class="identity-popup-headline"
|
<label id="identity-popup-permissions-headline"
|
||||||
|
class="identity-popup-headline"
|
||||||
value="&identity.permissions;"/>
|
value="&identity.permissions;"/>
|
||||||
<vbox id="identity-popup-permission-list"/>
|
<vbox id="identity-popup-permission-list"/>
|
||||||
<description>&identity.permissionsEmpty;</description>
|
<description>&identity.permissionsEmpty;</description>
|
||||||
|
|
|
@ -302,9 +302,8 @@ HistoryDownloadElementShell.prototype = {
|
||||||
|
|
||||||
if (this.element.selected) {
|
if (this.element.selected) {
|
||||||
goUpdateDownloadCommands();
|
goUpdateDownloadCommands();
|
||||||
} else {
|
|
||||||
goUpdateCommand("downloadsCmd_clearDownloads");
|
|
||||||
}
|
}
|
||||||
|
goUpdateCommand("downloadsCmd_clearDownloads");
|
||||||
},
|
},
|
||||||
|
|
||||||
onChanged() {
|
onChanged() {
|
||||||
|
|
|
@ -15,12 +15,13 @@ function test() {
|
||||||
let win = OpenBrowserWindow({private: true});
|
let win = OpenBrowserWindow({private: true});
|
||||||
win.addEventListener("load", function onLoad() {
|
win.addEventListener("load", function onLoad() {
|
||||||
win.removeEventListener("load", onLoad, false);
|
win.removeEventListener("load", onLoad, false);
|
||||||
let consoleWin = win.open("chrome://global/content/console.xul", "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
|
let chromeWin = win.open("chrome://browser/content/places/places.xul",
|
||||||
consoleWin.addEventListener("load", function consoleLoad() {
|
"_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
|
||||||
consoleWin.removeEventListener("load", consoleLoad, false);
|
chromeWin.addEventListener("load", function chromeWinLoad() {
|
||||||
|
chromeWin.removeEventListener("load", chromeWinLoad, false);
|
||||||
win.close();
|
win.close();
|
||||||
}, false);
|
}, false);
|
||||||
windowsToClose.push(consoleWin);
|
windowsToClose.push(chromeWin);
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
let observer = function() {
|
let observer = function() {
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||||
|
|
||||||
Current extension version is: 1.5.281
|
Current extension version is: 1.5.305
|
||||||
|
|
|
@ -65,22 +65,15 @@ function getFindBar(domWindow) {
|
||||||
if (PdfjsContentUtils.isRemote) {
|
if (PdfjsContentUtils.isRemote) {
|
||||||
throw new Error('FindBar is not accessible from the content process.');
|
throw new Error('FindBar is not accessible from the content process.');
|
||||||
}
|
}
|
||||||
var browser = getContainingBrowser(domWindow);
|
|
||||||
try {
|
try {
|
||||||
|
var browser = getContainingBrowser(domWindow);
|
||||||
var tabbrowser = browser.getTabBrowser();
|
var tabbrowser = browser.getTabBrowser();
|
||||||
var tab;
|
var tab = tabbrowser.getTabForBrowser(browser);
|
||||||
tab = tabbrowser.getTabForBrowser(browser);
|
|
||||||
return tabbrowser.getFindBar(tab);
|
return tabbrowser.getFindBar(tab);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
try {
|
// Suppress errors for PDF files opened in the bookmark sidebar, see
|
||||||
// FF22 has no _getTabForBrowser, and FF24 has no getFindBar
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1248959.
|
||||||
var chromeWindow = browser.ownerDocument.defaultView;
|
return null;
|
||||||
return chromeWindow.gFindBar;
|
|
||||||
} catch (ex) {
|
|
||||||
// Suppress errors for PDF files opened in the bookmark sidebar, see
|
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1248959.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,18 +148,17 @@ function getLocalizedString(strings, id, property) {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeContentReadable(obj, window) {
|
function createNewChannel(uri, node) {
|
||||||
/* jshint -W027 */
|
|
||||||
return Cu.cloneInto(obj, window);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createNewChannel(uri) {
|
|
||||||
return NetUtil.newChannel({
|
return NetUtil.newChannel({
|
||||||
uri: uri,
|
uri: uri,
|
||||||
loadUsingSystemPrincipal: true
|
loadUsingSystemPrincipal: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function asyncOpenChannel(channel, listener, context) {
|
||||||
|
return channel.asyncOpen2(listener);
|
||||||
|
}
|
||||||
|
|
||||||
function asyncFetchChannel(channel, callback) {
|
function asyncFetchChannel(channel, callback) {
|
||||||
return NetUtil.asyncFetch(channel, callback);
|
return NetUtil.asyncFetch(channel, callback);
|
||||||
}
|
}
|
||||||
|
@ -264,7 +256,7 @@ ChromeActions.prototype = {
|
||||||
getService(Ci.nsIExternalHelperAppService);
|
getService(Ci.nsIExternalHelperAppService);
|
||||||
|
|
||||||
var docIsPrivate = this.isInPrivateBrowsing();
|
var docIsPrivate = this.isInPrivateBrowsing();
|
||||||
var netChannel = createNewChannel(blobUri);
|
var netChannel = createNewChannel(blobUri, this.domWindow.document);
|
||||||
if ('nsIPrivateBrowsingChannel' in Ci &&
|
if ('nsIPrivateBrowsingChannel' in Ci &&
|
||||||
netChannel instanceof Ci.nsIPrivateBrowsingChannel) {
|
netChannel instanceof Ci.nsIPrivateBrowsingChannel) {
|
||||||
netChannel.setPrivate(docIsPrivate);
|
netChannel.setPrivate(docIsPrivate);
|
||||||
|
@ -327,7 +319,7 @@ ChromeActions.prototype = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
channel.asyncOpen2(listener);
|
asyncOpenChannel(channel, listener, null);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getLocale: function() {
|
getLocale: function() {
|
||||||
|
@ -367,11 +359,7 @@ ChromeActions.prototype = {
|
||||||
return (!!prefBrowser && prefGfx);
|
return (!!prefBrowser && prefGfx);
|
||||||
},
|
},
|
||||||
supportsDocumentColors: function() {
|
supportsDocumentColors: function() {
|
||||||
if (getIntPref('browser.display.document_color_use', 0) === 2 ||
|
return getIntPref('browser.display.document_color_use', 0) !== 2;
|
||||||
!getBoolPref('browser.display.use_document_colors', true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
supportedMouseWheelZoomModifierKeys: function() {
|
supportedMouseWheelZoomModifierKeys: function() {
|
||||||
return {
|
return {
|
||||||
|
@ -783,7 +771,7 @@ RequestListener.prototype.receive = function(event) {
|
||||||
var response;
|
var response;
|
||||||
if (sync) {
|
if (sync) {
|
||||||
response = actions[action].call(this.actions, data);
|
response = actions[action].call(this.actions, data);
|
||||||
event.detail.response = makeContentReadable(response, doc.defaultView);
|
event.detail.response = Cu.cloneInto(response, doc.defaultView);
|
||||||
} else {
|
} else {
|
||||||
if (!event.detail.responseExpected) {
|
if (!event.detail.responseExpected) {
|
||||||
doc.documentElement.removeChild(message);
|
doc.documentElement.removeChild(message);
|
||||||
|
@ -792,8 +780,7 @@ RequestListener.prototype.receive = function(event) {
|
||||||
response = function sendResponse(response) {
|
response = function sendResponse(response) {
|
||||||
try {
|
try {
|
||||||
var listener = doc.createEvent('CustomEvent');
|
var listener = doc.createEvent('CustomEvent');
|
||||||
let detail = makeContentReadable({response: response},
|
let detail = Cu.cloneInto({ response: response }, doc.defaultView);
|
||||||
doc.defaultView);
|
|
||||||
listener.initCustomEvent('pdf.js.response', true, false, detail);
|
listener.initCustomEvent('pdf.js.response', true, false, detail);
|
||||||
return message.dispatchEvent(listener);
|
return message.dispatchEvent(listener);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -837,7 +824,7 @@ FindEventManager.prototype.receiveMessage = function(msg) {
|
||||||
var type = msg.data.type;
|
var type = msg.data.type;
|
||||||
var contentWindow = this.contentWindow;
|
var contentWindow = this.contentWindow;
|
||||||
|
|
||||||
detail = makeContentReadable(detail, contentWindow);
|
detail = Cu.cloneInto(detail, contentWindow);
|
||||||
var forward = contentWindow.document.createEvent('CustomEvent');
|
var forward = contentWindow.document.createEvent('CustomEvent');
|
||||||
forward.initCustomEvent(type, true, true, detail);
|
forward.initCustomEvent(type, true, true, detail);
|
||||||
contentWindow.dispatchEvent(forward);
|
contentWindow.dispatchEvent(forward);
|
||||||
|
@ -972,7 +959,7 @@ PdfStreamConverter.prototype = {
|
||||||
.createInstance(Ci.nsIBinaryInputStream);
|
.createInstance(Ci.nsIBinaryInputStream);
|
||||||
|
|
||||||
// Create a new channel that is viewer loaded as a resource.
|
// Create a new channel that is viewer loaded as a resource.
|
||||||
var channel = createNewChannel(PDF_VIEWER_WEB_PAGE);
|
var channel = createNewChannel(PDF_VIEWER_WEB_PAGE, null);
|
||||||
|
|
||||||
var listener = this.listener;
|
var listener = this.listener;
|
||||||
var dataListener = this.dataListener;
|
var dataListener = this.dataListener;
|
||||||
|
@ -1024,18 +1011,17 @@ PdfStreamConverter.prototype = {
|
||||||
channel.loadGroup = aRequest.loadGroup;
|
channel.loadGroup = aRequest.loadGroup;
|
||||||
channel.loadInfo.originAttributes = aRequest.loadInfo.originAttributes;
|
channel.loadInfo.originAttributes = aRequest.loadInfo.originAttributes;
|
||||||
|
|
||||||
// We can use resource principal when data is fetched by the chrome
|
// We can use the resource principal when data is fetched by the chrome,
|
||||||
// make sure we reuse the origin attributes from the request channel to keep
|
// e.g. useful for NoScript. Make make sure we reuse the origin attributes
|
||||||
// isolation consistent.
|
// from the request channel to keep isolation consistent.
|
||||||
// e.g. useful for NoScript
|
|
||||||
var ssm = Cc['@mozilla.org/scriptsecuritymanager;1']
|
var ssm = Cc['@mozilla.org/scriptsecuritymanager;1']
|
||||||
.getService(Ci.nsIScriptSecurityManager);
|
.getService(Ci.nsIScriptSecurityManager);
|
||||||
var uri = NetUtil.newURI(PDF_VIEWER_WEB_PAGE, null, null);
|
var uri = NetUtil.newURI(PDF_VIEWER_WEB_PAGE, null, null);
|
||||||
var attrs = aRequest.loadInfo.originAttributes;
|
|
||||||
var resourcePrincipal;
|
var resourcePrincipal;
|
||||||
resourcePrincipal = ssm.createCodebasePrincipal(uri, attrs);
|
resourcePrincipal =
|
||||||
|
ssm.createCodebasePrincipal(uri, aRequest.loadInfo.originAttributes);
|
||||||
aRequest.owner = resourcePrincipal;
|
aRequest.owner = resourcePrincipal;
|
||||||
channel.asyncOpen2(proxy);
|
asyncOpenChannel(channel, proxy, aContext);
|
||||||
},
|
},
|
||||||
|
|
||||||
// nsIRequestObserver::onStopRequest
|
// nsIRequestObserver::onStopRequest
|
||||||
|
|
|
@ -181,8 +181,7 @@ var PdfjsChromeUtils = {
|
||||||
_findbarFromMessage: function(aMsg) {
|
_findbarFromMessage: function(aMsg) {
|
||||||
let browser = aMsg.target;
|
let browser = aMsg.target;
|
||||||
let tabbrowser = browser.getTabBrowser();
|
let tabbrowser = browser.getTabBrowser();
|
||||||
let tab;
|
let tab = tabbrowser.getTabForBrowser(browser);
|
||||||
tab = tabbrowser.getTabForBrowser(browser);
|
|
||||||
return tabbrowser.getFindBar(tab);
|
return tabbrowser.getFindBar(tab);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdf = {}));
|
||||||
// Use strict in our context only - users might not want it
|
// Use strict in our context only - users might not want it
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var pdfjsVersion = '1.5.281';
|
var pdfjsVersion = '1.5.305';
|
||||||
var pdfjsBuild = '5a5bb99';
|
var pdfjsBuild = '546e223';
|
||||||
|
|
||||||
var pdfjsFilePath =
|
var pdfjsFilePath =
|
||||||
typeof document !== 'undefined' && document.currentScript ?
|
typeof document !== 'undefined' && document.currentScript ?
|
||||||
|
@ -1070,6 +1070,11 @@ function isArrayBuffer(v) {
|
||||||
return typeof v === 'object' && v !== null && v.byteLength !== undefined;
|
return typeof v === 'object' && v !== null && v.byteLength !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if ch is one of the following characters: SPACE, TAB, CR or LF.
|
||||||
|
function isSpace(ch) {
|
||||||
|
return (ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Promise Capability object.
|
* Promise Capability object.
|
||||||
*
|
*
|
||||||
|
@ -1441,6 +1446,7 @@ exports.isEmptyObj = isEmptyObj;
|
||||||
exports.isInt = isInt;
|
exports.isInt = isInt;
|
||||||
exports.isNum = isNum;
|
exports.isNum = isNum;
|
||||||
exports.isString = isString;
|
exports.isString = isString;
|
||||||
|
exports.isSpace = isSpace;
|
||||||
exports.isSameOrigin = isSameOrigin;
|
exports.isSameOrigin = isSameOrigin;
|
||||||
exports.isValidUrl = isValidUrl;
|
exports.isValidUrl = isValidUrl;
|
||||||
exports.isLittleEndian = isLittleEndian;
|
exports.isLittleEndian = isLittleEndian;
|
||||||
|
@ -7815,7 +7821,12 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
||||||
},
|
},
|
||||||
|
|
||||||
getPageIndex: function WorkerTransport_getPageIndexByRef(ref) {
|
getPageIndex: function WorkerTransport_getPageIndexByRef(ref) {
|
||||||
return this.messageHandler.sendWithPromise('GetPageIndex', { ref: ref });
|
return this.messageHandler.sendWithPromise('GetPageIndex', { ref: ref }).
|
||||||
|
then(function (pageIndex) {
|
||||||
|
return pageIndex;
|
||||||
|
}, function (reason) {
|
||||||
|
return Promise.reject(new Error(reason));
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getAnnotations: function WorkerTransport_getAnnotations(pageIndex, intent) {
|
getAnnotations: function WorkerTransport_getAnnotations(pageIndex, intent) {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
|
var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
|
||||||
|
|
||||||
|
|
||||||
var pdfjsWebLibs = {
|
var pdfjsWebLibs = {
|
||||||
pdfjsWebPDFJS: window.pdfjsDistBuildPdf
|
pdfjsWebPDFJS: window.pdfjsDistBuildPdf
|
||||||
};
|
};
|
||||||
|
@ -4590,6 +4591,11 @@ exports.PDFFindBar = PDFFindBar;
|
||||||
|
|
||||||
var parseQueryString = uiUtils.parseQueryString;
|
var parseQueryString = uiUtils.parseQueryString;
|
||||||
|
|
||||||
|
var PageNumberRegExp = /^\d+$/;
|
||||||
|
function isPageNumber(str) {
|
||||||
|
return PageNumberRegExp.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} PDFLinkServiceOptions
|
* @typedef {Object} PDFLinkServiceOptions
|
||||||
* @property {EventBus} eventBus - The application event bus.
|
* @property {EventBus} eventBus - The application event bus.
|
||||||
|
@ -4601,7 +4607,7 @@ var parseQueryString = uiUtils.parseQueryString;
|
||||||
* @class
|
* @class
|
||||||
* @implements {IPDFLinkService}
|
* @implements {IPDFLinkService}
|
||||||
*/
|
*/
|
||||||
var PDFLinkService = (function () {
|
var PDFLinkService = (function PDFLinkServiceClosure() {
|
||||||
/**
|
/**
|
||||||
* @constructs PDFLinkService
|
* @constructs PDFLinkService
|
||||||
* @param {PDFLinkServiceOptions} options
|
* @param {PDFLinkServiceOptions} options
|
||||||
|
@ -4661,7 +4667,7 @@ var PDFLinkService = (function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var goToDestination = function(destRef) {
|
var goToDestination = function(destRef) {
|
||||||
// dest array looks like that: <page-ref> </XYZ|FitXXX> <args..>
|
// dest array looks like that: <page-ref> </XYZ|/FitXXX> <args..>
|
||||||
var pageNumber = destRef instanceof Object ?
|
var pageNumber = destRef instanceof Object ?
|
||||||
self._pagesRefCache[destRef.num + ' ' + destRef.gen + ' R'] :
|
self._pagesRefCache[destRef.num + ' ' + destRef.gen + ' R'] :
|
||||||
(destRef + 1);
|
(destRef + 1);
|
||||||
|
@ -4711,30 +4717,15 @@ var PDFLinkService = (function () {
|
||||||
*/
|
*/
|
||||||
getDestinationHash: function PDFLinkService_getDestinationHash(dest) {
|
getDestinationHash: function PDFLinkService_getDestinationHash(dest) {
|
||||||
if (typeof dest === 'string') {
|
if (typeof dest === 'string') {
|
||||||
return this.getAnchorUrl('#' + escape(dest));
|
// In practice, a named destination may contain only a number.
|
||||||
|
// If that happens, use the '#nameddest=' form to avoid the link
|
||||||
|
// redirecting to a page, instead of the correct destination.
|
||||||
|
return this.getAnchorUrl(
|
||||||
|
'#' + (isPageNumber(dest) ? 'nameddest=' : '') + escape(dest));
|
||||||
}
|
}
|
||||||
if (dest instanceof Array) {
|
if (dest instanceof Array) {
|
||||||
var destRef = dest[0]; // see navigateTo method for dest format
|
var str = JSON.stringify(dest);
|
||||||
var pageNumber = destRef instanceof Object ?
|
return this.getAnchorUrl('#' + escape(str));
|
||||||
this._pagesRefCache[destRef.num + ' ' + destRef.gen + ' R'] :
|
|
||||||
(destRef + 1);
|
|
||||||
if (pageNumber) {
|
|
||||||
var pdfOpenParams = this.getAnchorUrl('#page=' + pageNumber);
|
|
||||||
var destKind = dest[1];
|
|
||||||
if (typeof destKind === 'object' && 'name' in destKind &&
|
|
||||||
destKind.name === 'XYZ') {
|
|
||||||
var scale = (dest[4] || this.pdfViewer.currentScaleValue);
|
|
||||||
var scaleNumber = parseFloat(scale);
|
|
||||||
if (scaleNumber) {
|
|
||||||
scale = scaleNumber * 100;
|
|
||||||
}
|
|
||||||
pdfOpenParams += '&zoom=' + scale;
|
|
||||||
if (dest[2] || dest[3]) {
|
|
||||||
pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pdfOpenParams;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return this.getAnchorUrl('');
|
return this.getAnchorUrl('');
|
||||||
},
|
},
|
||||||
|
@ -4753,6 +4744,7 @@ var PDFLinkService = (function () {
|
||||||
* @param {string} hash
|
* @param {string} hash
|
||||||
*/
|
*/
|
||||||
setHash: function PDFLinkService_setHash(hash) {
|
setHash: function PDFLinkService_setHash(hash) {
|
||||||
|
var pageNumber, dest;
|
||||||
if (hash.indexOf('=') >= 0) {
|
if (hash.indexOf('=') >= 0) {
|
||||||
var params = parseQueryString(hash);
|
var params = parseQueryString(hash);
|
||||||
if ('search' in params) {
|
if ('search' in params) {
|
||||||
|
@ -4770,7 +4762,6 @@ var PDFLinkService = (function () {
|
||||||
this.navigateTo(params.nameddest);
|
this.navigateTo(params.nameddest);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var pageNumber, dest;
|
|
||||||
if ('page' in params) {
|
if ('page' in params) {
|
||||||
pageNumber = (params.page | 0) || 1;
|
pageNumber = (params.page | 0) || 1;
|
||||||
}
|
}
|
||||||
|
@ -4820,13 +4811,23 @@ var PDFLinkService = (function () {
|
||||||
mode: params.pagemode
|
mode: params.pagemode
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (/^\d+$/.test(hash)) { // page number
|
} else if (isPageNumber(hash)) { // Page number.
|
||||||
this.page = hash;
|
this.page = hash | 0;
|
||||||
} else { // named destination
|
} else { // Named (or explicit) destination.
|
||||||
if (this.pdfHistory) {
|
dest = unescape(hash);
|
||||||
this.pdfHistory.updateNextHashParam(unescape(hash));
|
try {
|
||||||
|
dest = JSON.parse(dest);
|
||||||
|
} catch (ex) {}
|
||||||
|
|
||||||
|
if (typeof dest === 'string' || isValidExplicitDestination(dest)) {
|
||||||
|
if (this.pdfHistory) {
|
||||||
|
this.pdfHistory.updateNextHashParam(dest);
|
||||||
|
}
|
||||||
|
this.navigateTo(dest);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
this.navigateTo(unescape(hash));
|
console.error('PDFLinkService_setHash: \'' + unescape(hash) +
|
||||||
|
'\' is not a valid destination.');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -4884,6 +4885,60 @@ var PDFLinkService = (function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isValidExplicitDestination(dest) {
|
||||||
|
if (!(dest instanceof Array)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var destLength = dest.length, allowNull = true;
|
||||||
|
if (destLength < 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var page = dest[0];
|
||||||
|
if (!(typeof page === 'object' &&
|
||||||
|
typeof page.num === 'number' && (page.num | 0) === page.num &&
|
||||||
|
typeof page.gen === 'number' && (page.gen | 0) === page.gen) &&
|
||||||
|
!(typeof page === 'number' && (page | 0) === page && page >= 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var zoom = dest[1];
|
||||||
|
if (!(typeof zoom === 'object' && typeof zoom.name === 'string')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (zoom.name) {
|
||||||
|
case 'XYZ':
|
||||||
|
if (destLength !== 5) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Fit':
|
||||||
|
case 'FitB':
|
||||||
|
return destLength === 2;
|
||||||
|
case 'FitH':
|
||||||
|
case 'FitBH':
|
||||||
|
case 'FitV':
|
||||||
|
case 'FitBV':
|
||||||
|
if (destLength !== 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'FitR':
|
||||||
|
if (destLength !== 6) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
allowNull = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (var i = 2; i < destLength; i++) {
|
||||||
|
var param = dest[i];
|
||||||
|
if (!(typeof param === 'number' || (allowNull && param === null))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return PDFLinkService;
|
return PDFLinkService;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -6723,6 +6778,8 @@ var PDFViewer = (function pdfViewer() {
|
||||||
scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
|
scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
console.error('PDFViewer_scrollPageIntoView: \'' + dest[1].name +
|
||||||
|
'\' is not a valid destination type.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7692,6 +7749,7 @@ var PDFViewerApplication = {
|
||||||
pdfViewer.setDocument(pdfDocument);
|
pdfViewer.setDocument(pdfDocument);
|
||||||
var firstPagePromise = pdfViewer.firstPagePromise;
|
var firstPagePromise = pdfViewer.firstPagePromise;
|
||||||
var pagesPromise = pdfViewer.pagesPromise;
|
var pagesPromise = pdfViewer.pagesPromise;
|
||||||
|
var onePageRendered = pdfViewer.onePageRendered;
|
||||||
|
|
||||||
this.pageRotation = 0;
|
this.pageRotation = 0;
|
||||||
|
|
||||||
|
@ -7797,9 +7855,8 @@ var PDFViewerApplication = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// outline depends on pagesRefMap
|
Promise.all([onePageRendered, this.animationStartedPromise]).then(
|
||||||
var promises = [pagesPromise, this.animationStartedPromise];
|
function() {
|
||||||
Promise.all(promises).then(function() {
|
|
||||||
pdfDocument.getOutline().then(function(outline) {
|
pdfDocument.getOutline().then(function(outline) {
|
||||||
self.pdfOutlineViewer.render({ outline: outline });
|
self.pdfOutlineViewer.render({ outline: outline });
|
||||||
});
|
});
|
||||||
|
@ -9226,7 +9283,6 @@ exports.FirefoxCom = FirefoxCom;
|
||||||
// FIXME the l10n.js file in the Firefox extension needs global FirefoxCom.
|
// FIXME the l10n.js file in the Firefox extension needs global FirefoxCom.
|
||||||
window.FirefoxCom = pdfjsWebLibs.pdfjsWebFirefoxCom.FirefoxCom;
|
window.FirefoxCom = pdfjsWebLibs.pdfjsWebFirefoxCom.FirefoxCom;
|
||||||
|
|
||||||
|
|
||||||
function getViewerConfiguration() {
|
function getViewerConfiguration() {
|
||||||
return {
|
return {
|
||||||
appContainer: document.body,
|
appContainer: document.body,
|
||||||
|
|
|
@ -392,8 +392,6 @@
|
||||||
@RESPATH@/components/crypto-SDR.js
|
@RESPATH@/components/crypto-SDR.js
|
||||||
@RESPATH@/components/TooltipTextProvider.js
|
@RESPATH@/components/TooltipTextProvider.js
|
||||||
@RESPATH@/components/TooltipTextProvider.manifest
|
@RESPATH@/components/TooltipTextProvider.manifest
|
||||||
@RESPATH@/components/jsconsole-clhandler.manifest
|
|
||||||
@RESPATH@/components/jsconsole-clhandler.js
|
|
||||||
@RESPATH@/components/webvtt.xpt
|
@RESPATH@/components/webvtt.xpt
|
||||||
@RESPATH@/components/WebVTT.manifest
|
@RESPATH@/components/WebVTT.manifest
|
||||||
@RESPATH@/components/WebVTTParserWrapper.js
|
@RESPATH@/components/WebVTTParserWrapper.js
|
||||||
|
|
|
@ -253,4 +253,3 @@ var gPermissionObject = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const kPermissionIDs = Object.keys(gPermissionObject);
|
const kPermissionIDs = Object.keys(gPermissionObject);
|
||||||
|
|
||||||
|
|
|
@ -349,6 +349,19 @@ description#identity-popup-content-verifier,
|
||||||
background-image: url(chrome://browser/skin/controlcenter/permissions.svg);
|
background-image: url(chrome://browser/skin/controlcenter/permissions.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#identity-popup-permissions-headline {
|
||||||
|
/* Make sure the label is as tall as the icon so that the permission list
|
||||||
|
which is aligned with the icon doesn't cover it up. */
|
||||||
|
min-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#identity-popup-permission-list {
|
||||||
|
/* Offset the padding set on #identity-popup-permissions-content so that it
|
||||||
|
shows up just below the section. The permission icons are 16px wide and
|
||||||
|
should be right aligned with the section icon. */
|
||||||
|
margin-inline-start: calc(-1em - 16px);
|
||||||
|
}
|
||||||
|
|
||||||
#identity-popup-permission-list menulist {
|
#identity-popup-permission-list menulist {
|
||||||
min-width: 60px;
|
min-width: 60px;
|
||||||
}
|
}
|
||||||
|
@ -361,8 +374,12 @@ description#identity-popup-content-verifier,
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.identity-popup-permission-label {
|
.identity-popup-permission-icon {
|
||||||
margin-inline-start: 0;
|
width: 16px;
|
||||||
word-wrap: break-word;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.identity-popup-permission-label {
|
||||||
|
margin-inline-start: 1em;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
|
@ -22,5 +22,6 @@
|
||||||
<path id="microphone" d="m 8,14 0,4 a 8,8 0 0 0 6,7.7 l 0,2.3 -2,0 a 2,2 0 0 0 -2,2 l 12,0 a 2,2 0 0 0 -2,-2 l -2,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 -2,0 0,4 a 6,6 0 0 1 -12,0 l 0,-4 z m 4,4 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
|
<path id="microphone" d="m 8,14 0,4 a 8,8 0 0 0 6,7.7 l 0,2.3 -2,0 a 2,2 0 0 0 -2,2 l 12,0 a 2,2 0 0 0 -2,-2 l -2,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 -2,0 0,4 a 6,6 0 0 1 -12,0 l 0,-4 z m 4,4 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
|
||||||
<path id="microphone-detailed" d="m 8,18 a 8,8 0 0 0 6,7.7 l 0,2.3 -1,0 a 3,2 0 0 0 -3,2 l 12,0 a 3,2 0 0 0 -3,-2 l -1,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 a 1,1 0 0 0 -2,0 l 0,4 a 6,6 0 0 1 -12,0 l 0,-4 a 1,1 0 0 0 -2,0 z m 4,0 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
|
<path id="microphone-detailed" d="m 8,18 a 8,8 0 0 0 6,7.7 l 0,2.3 -1,0 a 3,2 0 0 0 -3,2 l 12,0 a 3,2 0 0 0 -3,-2 l -1,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 a 1,1 0 0 0 -2,0 l 0,4 a 6,6 0 0 1 -12,0 l 0,-4 a 1,1 0 0 0 -2,0 z m 4,0 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
|
||||||
<path id="pointerLock" d="m 8,24 6,-5 5,10 4,-2 -5,-10 7,-1 -17,-14 z" />
|
<path id="pointerLock" d="m 8,24 6,-5 5,10 4,-2 -5,-10 7,-1 -17,-14 z" />
|
||||||
|
<path id="popup" d="m 2,24 a 4,4 0 0 0 4,4 l 8,0 a 10,10 0 0 1 -2,-4 l -4,0 a 2,2 0 0 1 -2,-2 l 0,-12 18,0 0,2 a 10,10 0 0 1 4,2 l 0,-8 a 4,4 0 0 0 -4,-4 l -18,0 a 4,4 0 0 0 -4,4 z m 12,-2.1 a 8,8 0 1 1 0,0.2 m 10.7,-4.3 a 5,5 0 0 0 -6.9,6.9 z m -5.4,8.4 a 5,5 0 0 0 6.9,-6.9 z" />
|
||||||
<path id="screen" d="m 2,18 a 2,2 0 0 0 2,2 l 2,0 0,-6 a 4,4 0 0 1 4,-4 l 14,0 0,-6 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z m 6,10 a 2,2 0 0 0 2,2 l 18,0 a 2,2 0 0 0 2,-2 l 0,-14 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z" />
|
<path id="screen" d="m 2,18 a 2,2 0 0 0 2,2 l 2,0 0,-6 a 4,4 0 0 1 4,-4 l 14,0 0,-6 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z m 6,10 a 2,2 0 0 0 2,2 l 18,0 a 2,2 0 0 0 2,-2 l 0,-14 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
До Ширина: | Высота: | Размер: 3.4 KiB После Ширина: | Высота: | Размер: 3.7 KiB |
|
@ -81,6 +81,7 @@
|
||||||
.login-icon,
|
.login-icon,
|
||||||
.microphone-icon,
|
.microphone-icon,
|
||||||
.pointerLock-icon,
|
.pointerLock-icon,
|
||||||
|
.popup-icon,
|
||||||
.screen-icon,
|
.screen-icon,
|
||||||
.desktop-notification-icon,
|
.desktop-notification-icon,
|
||||||
.popup-notification-icon[popupid="geolocation"],
|
.popup-notification-icon[popupid="geolocation"],
|
||||||
|
@ -179,6 +180,10 @@
|
||||||
list-style-image: url(chrome://browser/skin/glyphs.svg#pointerLock);
|
list-style-image: url(chrome://browser/skin/glyphs.svg#pointerLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popup-icon {
|
||||||
|
list-style-image: url("chrome://browser/skin/glyphs.svg#popup");
|
||||||
|
}
|
||||||
|
|
||||||
/* EME */
|
/* EME */
|
||||||
|
|
||||||
.popup-notification-icon[popupid="drmContentPlaying"],
|
.popup-notification-icon[popupid="drmContentPlaying"],
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
component {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32} devtools-startup.js
|
component {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32} devtools-startup.js
|
||||||
contract @mozilla.org/devtools/startup-clh;1 {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}
|
contract @mozilla.org/devtools/startup-clh;1 {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}
|
||||||
# We want this to override toolkit's --jsconsole handling, so it must have a
|
|
||||||
# a higher priority than the entry in jsconsole-clhandler.manifest. Higher
|
|
||||||
# priority means the "m-devtools" value below needs to be something that sorts
|
|
||||||
# before the one in jsconsole-clhandler.manifest. See details in
|
|
||||||
# nsICommandLineHandler.idl.
|
|
||||||
category command-line-handler m-devtools @mozilla.org/devtools/startup-clh;1
|
category command-line-handler m-devtools @mozilla.org/devtools/startup-clh;1
|
||||||
|
|
|
@ -424,7 +424,7 @@ TabTarget.prototype = {
|
||||||
this._title = this._form.title;
|
this._title = this._form.title;
|
||||||
|
|
||||||
attachTab();
|
attachTab();
|
||||||
});
|
}, e => this._remote.reject(e));
|
||||||
} else if (this.isTabActor) {
|
} else if (this.isTabActor) {
|
||||||
// In the remote debugging case, the protocol connection will have been
|
// In the remote debugging case, the protocol connection will have been
|
||||||
// already initialized in the connection screen code.
|
// already initialized in the connection screen code.
|
||||||
|
|
|
@ -6,4 +6,6 @@
|
||||||
|
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
'swap.js',
|
'swap.js',
|
||||||
|
'tunnel.js',
|
||||||
|
'web-navigation.js',
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
const promise = require("promise");
|
const promise = require("promise");
|
||||||
const { Task } = require("devtools/shared/task");
|
const { Task } = require("devtools/shared/task");
|
||||||
|
const { tunnelToInnerBrowser } = require("./tunnel");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Swap page content from an existing tab into a new browser within a container
|
* Swap page content from an existing tab into a new browser within a container
|
||||||
|
@ -32,10 +33,14 @@ const { Task } = require("devtools/shared/task");
|
||||||
function swapToInnerBrowser({ tab, containerURL, getInnerBrowser }) {
|
function swapToInnerBrowser({ tab, containerURL, getInnerBrowser }) {
|
||||||
let gBrowser = tab.ownerDocument.defaultView.gBrowser;
|
let gBrowser = tab.ownerDocument.defaultView.gBrowser;
|
||||||
let innerBrowser;
|
let innerBrowser;
|
||||||
|
let tunnel;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
||||||
start: Task.async(function* () {
|
start: Task.async(function* () {
|
||||||
|
// Freeze navigation temporarily to avoid "blinking" in the location bar.
|
||||||
|
freezeNavigationState(tab);
|
||||||
|
|
||||||
// 1. Create a temporary, hidden tab to load the tool UI.
|
// 1. Create a temporary, hidden tab to load the tool UI.
|
||||||
let containerTab = gBrowser.addTab(containerURL, {
|
let containerTab = gBrowser.addTab(containerURL, {
|
||||||
skipAnimation: true,
|
skipAnimation: true,
|
||||||
|
@ -78,32 +83,47 @@ function swapToInnerBrowser({ tab, containerURL, getInnerBrowser }) {
|
||||||
// original browser tab and close the temporary tab used to load the
|
// original browser tab and close the temporary tab used to load the
|
||||||
// tool via `swapBrowsersAndCloseOther`.
|
// tool via `swapBrowsersAndCloseOther`.
|
||||||
gBrowser.swapBrowsersAndCloseOther(tab, containerTab);
|
gBrowser.swapBrowsersAndCloseOther(tab, containerTab);
|
||||||
|
|
||||||
|
// 7. Start a tunnel from the tool tab's browser to the viewport browser
|
||||||
|
// so that some browser UI functions, like navigation, are connected to
|
||||||
|
// the content in the viewport, instead of the tool page.
|
||||||
|
tunnel = tunnelToInnerBrowser(tab.linkedBrowser, innerBrowser);
|
||||||
|
yield tunnel.start();
|
||||||
|
|
||||||
|
// Force the browser UI to match the new state of the tab and browser.
|
||||||
|
thawNavigationState(tab);
|
||||||
|
gBrowser.setTabTitle(tab);
|
||||||
|
gBrowser.updateCurrentBrowser(true);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
// 1. Create a temporary, hidden tab to hold the content.
|
// 1. Stop the tunnel between outer and inner browsers.
|
||||||
|
tunnel.stop();
|
||||||
|
tunnel = null;
|
||||||
|
|
||||||
|
// 2. Create a temporary, hidden tab to hold the content.
|
||||||
let contentTab = gBrowser.addTab("about:blank", {
|
let contentTab = gBrowser.addTab("about:blank", {
|
||||||
skipAnimation: true,
|
skipAnimation: true,
|
||||||
});
|
});
|
||||||
gBrowser.hideTab(contentTab);
|
gBrowser.hideTab(contentTab);
|
||||||
let contentBrowser = contentTab.linkedBrowser;
|
let contentBrowser = contentTab.linkedBrowser;
|
||||||
|
|
||||||
// 2. Mark the content tab browser's docshell as active so the frame
|
// 3. Mark the content tab browser's docshell as active so the frame
|
||||||
// is created eagerly and will be ready to swap.
|
// is created eagerly and will be ready to swap.
|
||||||
contentBrowser.docShellIsActive = true;
|
contentBrowser.docShellIsActive = true;
|
||||||
|
|
||||||
// 3. Swap tab content from the browser within the viewport in the tool UI
|
// 4. Swap tab content from the browser within the viewport in the tool UI
|
||||||
// to the regular browser tab, preserving all state via
|
// to the regular browser tab, preserving all state via
|
||||||
// `gBrowser._swapBrowserDocShells`.
|
// `gBrowser._swapBrowserDocShells`.
|
||||||
gBrowser._swapBrowserDocShells(contentTab, innerBrowser);
|
gBrowser._swapBrowserDocShells(contentTab, innerBrowser);
|
||||||
innerBrowser = null;
|
innerBrowser = null;
|
||||||
|
|
||||||
// 4. Force the original browser tab to be remote since web content is
|
// 5. Force the original browser tab to be remote since web content is
|
||||||
// loaded in the child process, and we're about to swap the content
|
// loaded in the child process, and we're about to swap the content
|
||||||
// into this tab.
|
// into this tab.
|
||||||
gBrowser.updateBrowserRemoteness(tab.linkedBrowser, true);
|
gBrowser.updateBrowserRemoteness(tab.linkedBrowser, true);
|
||||||
|
|
||||||
// 5. Swap the content into the original browser tab and close the
|
// 6. Swap the content into the original browser tab and close the
|
||||||
// temporary tab used to hold the content via
|
// temporary tab used to hold the content via
|
||||||
// `swapBrowsersAndCloseOther`.
|
// `swapBrowsersAndCloseOther`.
|
||||||
gBrowser.swapBrowsersAndCloseOther(tab, contentTab);
|
gBrowser.swapBrowsersAndCloseOther(tab, contentTab);
|
||||||
|
@ -113,6 +133,40 @@ function swapToInnerBrowser({ tab, containerURL, getInnerBrowser }) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Browser navigation properties we'll freeze temporarily to avoid "blinking" in the
|
||||||
|
* location bar, etc. caused by the containerURL peeking through before the swap is
|
||||||
|
* complete.
|
||||||
|
*/
|
||||||
|
const NAVIGATION_PROPERTIES = [
|
||||||
|
"currentURI",
|
||||||
|
"contentTitle",
|
||||||
|
"securityUI",
|
||||||
|
];
|
||||||
|
|
||||||
|
function freezeNavigationState(tab) {
|
||||||
|
// Browser navigation properties we'll freeze temporarily to avoid "blinking" in the
|
||||||
|
// location bar, etc. caused by the containerURL peeking through before the swap is
|
||||||
|
// complete.
|
||||||
|
for (let property of NAVIGATION_PROPERTIES) {
|
||||||
|
let value = tab.linkedBrowser[property];
|
||||||
|
Object.defineProperty(tab.linkedBrowser, property, {
|
||||||
|
get() {
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function thawNavigationState(tab) {
|
||||||
|
// Thaw out the properties we froze at the beginning now that the swap is complete.
|
||||||
|
for (let property of NAVIGATION_PROPERTIES) {
|
||||||
|
delete tab.linkedBrowser[property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Browser elements that are passed to `gBrowser._swapBrowserDocShells` are
|
* Browser elements that are passed to `gBrowser._swapBrowserDocShells` are
|
||||||
* expected to have certain properties that currently exist only on
|
* expected to have certain properties that currently exist only on
|
||||||
|
|
|
@ -0,0 +1,401 @@
|
||||||
|
/* 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";
|
||||||
|
|
||||||
|
const { Ci } = require("chrome");
|
||||||
|
const Services = require("Services");
|
||||||
|
const { Task } = require("devtools/shared/task");
|
||||||
|
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||||
|
const { BrowserElementWebNavigation } = require("./web-navigation");
|
||||||
|
|
||||||
|
function debug(msg) {
|
||||||
|
// console.log(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties swapped between browsers by browser.xml's `swapDocShells`. See also the
|
||||||
|
* list at /devtools/client/responsive.html/docs/browser-swap.md.
|
||||||
|
*/
|
||||||
|
const SWAPPED_BROWSER_STATE = [
|
||||||
|
"_securityUI",
|
||||||
|
"_documentURI",
|
||||||
|
"_documentContentType",
|
||||||
|
"_contentTitle",
|
||||||
|
"_characterSet",
|
||||||
|
"_contentPrincipal",
|
||||||
|
"_imageDocument",
|
||||||
|
"_fullZoom",
|
||||||
|
"_textZoom",
|
||||||
|
"_isSyntheticDocument",
|
||||||
|
"_innerWindowID",
|
||||||
|
"_manifestURI",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This module takes an "outer" <xul:browser> from a browser tab as described by
|
||||||
|
* Firefox's tabbrowser.xml and wires it up to an "inner" <iframe mozbrowser>
|
||||||
|
* browser element containing arbitrary page content of interest.
|
||||||
|
*
|
||||||
|
* The inner <iframe mozbrowser> element is _just_ the page content. It is not
|
||||||
|
* enough to to replace <xul:browser> on its own. <xul:browser> comes along
|
||||||
|
* with lots of associated functionality via XBL bindings defined for such
|
||||||
|
* elements in browser.xml and remote-browser.xml, and the Firefox UI depends on
|
||||||
|
* these various things to make the UI function.
|
||||||
|
*
|
||||||
|
* By mapping various methods, properties, and messages from the outer browser
|
||||||
|
* to the inner browser, we can control the content inside the inner browser
|
||||||
|
* using the standard Firefox UI elements for navigation, reloading, and more.
|
||||||
|
*
|
||||||
|
* The approaches used in this module were chosen to avoid needing changes to
|
||||||
|
* the core browser for this specialized use case. If we start to increase
|
||||||
|
* usage of <iframe mozbrowser> in the core browser, we should avoid this module
|
||||||
|
* and instead refactor things to work with mozbrowser directly.
|
||||||
|
*
|
||||||
|
* For the moment though, this serves as a sufficient path to connect the
|
||||||
|
* Firefox UI to a mozbrowser.
|
||||||
|
*
|
||||||
|
* @param outer
|
||||||
|
* A <xul:browser> from a regular browser tab.
|
||||||
|
* @param inner
|
||||||
|
* A <iframe mozbrowser> containing page content to be wired up to the
|
||||||
|
* primary browser UI via the outer browser.
|
||||||
|
*/
|
||||||
|
function tunnelToInnerBrowser(outer, inner) {
|
||||||
|
let browserWindow = outer.ownerDocument.defaultView;
|
||||||
|
let gBrowser = browserWindow.gBrowser;
|
||||||
|
let mmTunnel;
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
start: Task.async(function* () {
|
||||||
|
if (outer.isRemoteBrowser) {
|
||||||
|
throw new Error("The outer browser must be non-remote.");
|
||||||
|
}
|
||||||
|
if (!inner.isRemoteBrowser) {
|
||||||
|
throw new Error("The inner browser must be remote.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The `permanentKey` property on a <xul:browser> is used to index into various maps
|
||||||
|
// held by the session store. When you swap content around with
|
||||||
|
// `_swapBrowserDocShells`, these keys are also swapped so they follow the content.
|
||||||
|
// This means the key that matches the content is on the inner browser. Since we
|
||||||
|
// want the browser UI to believe the page content is part of the outer browser, we
|
||||||
|
// copy the content's `permanentKey` up to the outer browser.
|
||||||
|
copyPermanentKey(outer, inner);
|
||||||
|
|
||||||
|
// Replace the outer browser's native messageManager with a message manager tunnel
|
||||||
|
// which we can use to route messages of interest to the inner browser instead.
|
||||||
|
// Note: The _actual_ messageManager accessible from
|
||||||
|
// `browser.frameLoader.messageManager` is not overridable and is left unchanged.
|
||||||
|
// Only the XBL getter `browser.messageManager` is overridden. Browser UI code
|
||||||
|
// always uses this getter instead of `browser.frameLoader.messageManager` directly,
|
||||||
|
// so this has the effect of overriding the message manager for browser UI code.
|
||||||
|
mmTunnel = new MessageManagerTunnel(outer, inner);
|
||||||
|
Object.defineProperty(outer, "messageManager", {
|
||||||
|
value: mmTunnel,
|
||||||
|
writable: false,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// We are tunneling to an inner browser with a specific remoteness, so it is simpler
|
||||||
|
// for the logic of the browser UI to assume this tab has taken on that remoteness,
|
||||||
|
// even though it's not true. Since the actions the browser UI performs are sent
|
||||||
|
// down to the inner browser by this tunnel, the tab's remoteness effectively is the
|
||||||
|
// remoteness of the inner browser.
|
||||||
|
Object.defineProperty(outer, "isRemoteBrowser", {
|
||||||
|
get() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear out any cached state that references the current non-remote XBL binding,
|
||||||
|
// such as form fill controllers. Otherwise they will remain in place and leak the
|
||||||
|
// outer docshell.
|
||||||
|
outer.destroy();
|
||||||
|
// The XBL binding for remote browsers uses the message manager for many actions in
|
||||||
|
// the UI and that works well here, since it gives us one main thing we need to
|
||||||
|
// route to the inner browser (the messages), instead of having to tweak many
|
||||||
|
// different browser properties. It is safe to alter a XBL binding dynamically.
|
||||||
|
// The content within is not reloaded.
|
||||||
|
outer.style.MozBinding = "url(chrome://browser/content/tabbrowser.xml" +
|
||||||
|
"#tabbrowser-remote-browser)";
|
||||||
|
|
||||||
|
// The constructor of the new XBL binding is run asynchronously and there is no
|
||||||
|
// event to signal its completion. Spin an event loop to watch for properties that
|
||||||
|
// are set by the contructor.
|
||||||
|
while (!outer._remoteWebNavigation) {
|
||||||
|
Services.tm.currentThread.processNextEvent(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the `webNavigation` object with our own version which tries to use
|
||||||
|
// mozbrowser APIs where possible. This replaces the webNavigation object that the
|
||||||
|
// remote-browser.xml binding creates. We do not care about it's original value
|
||||||
|
// because stop() will remove the remote-browser.xml binding and these will no
|
||||||
|
// longer be used.
|
||||||
|
let webNavigation = new BrowserElementWebNavigation(inner);
|
||||||
|
webNavigation.copyStateFrom(inner._remoteWebNavigationImpl);
|
||||||
|
outer._remoteWebNavigation = webNavigation;
|
||||||
|
outer._remoteWebNavigationImpl = webNavigation;
|
||||||
|
|
||||||
|
// Now that we've flipped to the remote browser XBL binding, add `progressListener`
|
||||||
|
// onto the remote version of `webProgress`. Normally tabbrowser.xml does this step
|
||||||
|
// when it creates a new browser, etc. Since we manually changed the XBL binding
|
||||||
|
// above, it caused a fresh webProgress object to be created which does not have any
|
||||||
|
// listeners added. So, we get the listener that gBrowser is using for the tab and
|
||||||
|
// reattach it here.
|
||||||
|
let tab = gBrowser.getTabForBrowser(outer);
|
||||||
|
let filteredProgressListener = gBrowser._tabFilters.get(tab);
|
||||||
|
outer.webProgress.addProgressListener(filteredProgressListener);
|
||||||
|
|
||||||
|
// All of the browser state from content was swapped onto the inner browser. Pull
|
||||||
|
// this state up to the outer browser.
|
||||||
|
for (let property of SWAPPED_BROWSER_STATE) {
|
||||||
|
outer[property] = inner[property];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wants to access the content's `frameLoader`, so we'll redirect it to
|
||||||
|
// inner browser.
|
||||||
|
Object.defineProperty(outer, "hasContentOpener", {
|
||||||
|
get() {
|
||||||
|
return inner.frameLoader.tabParent.hasContentOpener;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wants to access the content's `frameLoader`, so we'll redirect it to
|
||||||
|
// inner browser.
|
||||||
|
Object.defineProperty(outer, "docShellIsActive", {
|
||||||
|
get() {
|
||||||
|
return inner.frameLoader.tabParent.docShellIsActive;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
inner.frameLoader.tabParent.docShellIsActive = value;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wants to access the content's `frameLoader`, so we'll redirect it to
|
||||||
|
// inner browser.
|
||||||
|
outer.setDocShellIsActiveAndForeground = value => {
|
||||||
|
inner.frameLoader.tabParent.setDocShellIsActiveAndForeground(value);
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
let tab = gBrowser.getTabForBrowser(outer);
|
||||||
|
let filteredProgressListener = gBrowser._tabFilters.get(tab);
|
||||||
|
browserWindow = null;
|
||||||
|
gBrowser = null;
|
||||||
|
|
||||||
|
// The browser's state has changed over time while the tunnel was active. Push the
|
||||||
|
// the current state down to the inner browser, so that it follows the content in
|
||||||
|
// case that browser will be swapped elsewhere.
|
||||||
|
for (let property of SWAPPED_BROWSER_STATE) {
|
||||||
|
inner[property] = outer[property];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the progress listener we added manually.
|
||||||
|
outer.webProgress.removeProgressListener(filteredProgressListener);
|
||||||
|
|
||||||
|
// Reset the XBL binding back to the default.
|
||||||
|
outer.destroy();
|
||||||
|
outer.style.MozBinding = "";
|
||||||
|
|
||||||
|
// Reset overridden XBL properties and methods. Deleting the override
|
||||||
|
// means it will fallback to the original XBL binding definitions which
|
||||||
|
// are on the prototype.
|
||||||
|
delete outer.messageManager;
|
||||||
|
delete outer.isRemoteBrowser;
|
||||||
|
delete outer.hasContentOpener;
|
||||||
|
delete outer.docShellIsActive;
|
||||||
|
delete outer.setDocShellIsActiveAndForeground;
|
||||||
|
|
||||||
|
mmTunnel.destroy();
|
||||||
|
mmTunnel = null;
|
||||||
|
|
||||||
|
// Invalidate outer's permanentKey so that SessionStore stops associating
|
||||||
|
// things that happen to the outer browser with the content inside in the
|
||||||
|
// inner browser.
|
||||||
|
outer.permanentKey = { id: "zombie" };
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.tunnelToInnerBrowser = tunnelToInnerBrowser;
|
||||||
|
|
||||||
|
function copyPermanentKey(outer, inner) {
|
||||||
|
// When we're in the process of swapping content around, we end up receiving a
|
||||||
|
// SessionStore:update message which lists the container page that is loaded into the
|
||||||
|
// outer browser (that we're hiding the inner browser within) as part of its history.
|
||||||
|
// We want SessionStore's view of the history for our tab to only have the page content
|
||||||
|
// of the inner browser, so we want to hide this message from SessionStore, but we have
|
||||||
|
// no direct mechanism to do so. As a workaround, we wait until the one errant message
|
||||||
|
// has gone by, and then we copy the permanentKey after that, since the permanentKey is
|
||||||
|
// what SessionStore uses to identify each browser.
|
||||||
|
let outerMM = outer.frameLoader.messageManager;
|
||||||
|
let onHistoryEntry = message => {
|
||||||
|
let history = message.data.data.history;
|
||||||
|
if (!history || !history.entries) {
|
||||||
|
// Wait for a message that contains history data
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
outerMM.removeMessageListener("SessionStore:update", onHistoryEntry);
|
||||||
|
debug("Got session update for outer browser");
|
||||||
|
DevToolsUtils.executeSoon(() => {
|
||||||
|
debug("Copy inner permanentKey to outer browser");
|
||||||
|
outer.permanentKey = inner.permanentKey;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
outerMM.addMessageListener("SessionStore:update", onHistoryEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This module allows specific messages of interest to be directed from the
|
||||||
|
* outer browser to the inner browser (and vice versa) in a targetted fashion
|
||||||
|
* without having to touch the original code paths that use them.
|
||||||
|
*/
|
||||||
|
function MessageManagerTunnel(outer, inner) {
|
||||||
|
if (outer.isRemoteBrowser) {
|
||||||
|
throw new Error("The outer browser must be non-remote.");
|
||||||
|
}
|
||||||
|
this.outer = outer;
|
||||||
|
this.inner = inner;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageManagerTunnel.prototype = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Most message manager methods are left alone and are just passed along to
|
||||||
|
* the outer browser's real message manager. `sendAsyncMessage` is only one
|
||||||
|
* with special behavior.
|
||||||
|
*/
|
||||||
|
PASS_THROUGH_METHODS: [
|
||||||
|
"addMessageListener",
|
||||||
|
"loadFrameScript",
|
||||||
|
"killChild",
|
||||||
|
"assertPermission",
|
||||||
|
"assertContainApp",
|
||||||
|
"assertAppHasPermission",
|
||||||
|
"assertAppHasStatus",
|
||||||
|
"removeDelayedFrameScript",
|
||||||
|
"getDelayedFrameScripts",
|
||||||
|
"loadProcessScript",
|
||||||
|
"removeDelayedProcessScript",
|
||||||
|
"getDelayedProcessScripts",
|
||||||
|
"removeMessageListener",
|
||||||
|
"addWeakMessageListener",
|
||||||
|
"removeWeakMessageListener",
|
||||||
|
],
|
||||||
|
|
||||||
|
OUTER_TO_INNER_MESSAGES: [
|
||||||
|
// Messages sent from remote-browser.xml
|
||||||
|
"Browser:PurgeSessionHistory",
|
||||||
|
"InPermitUnload",
|
||||||
|
"PermitUnload",
|
||||||
|
// Messages sent from browser.js
|
||||||
|
"Browser:Reload",
|
||||||
|
// Messages sent from SelectParentHelper.jsm
|
||||||
|
"Forms:DismissedDropDown",
|
||||||
|
"Forms:MouseOut",
|
||||||
|
"Forms:MouseOver",
|
||||||
|
"Forms:SelectDropDownItem",
|
||||||
|
// Messages sent from SessionStore.jsm
|
||||||
|
"SessionStore:flush",
|
||||||
|
],
|
||||||
|
|
||||||
|
INNER_TO_OUTER_MESSAGES: [
|
||||||
|
// Messages sent to RemoteWebProgress.jsm
|
||||||
|
"Content:LoadURIResult",
|
||||||
|
"Content:LocationChange",
|
||||||
|
"Content:ProgressChange",
|
||||||
|
"Content:SecurityChange",
|
||||||
|
"Content:StateChange",
|
||||||
|
"Content:StatusChange",
|
||||||
|
// Messages sent to remote-browser.xml
|
||||||
|
"DOMTitleChanged",
|
||||||
|
"ImageDocumentLoaded",
|
||||||
|
"Forms:ShowDropDown",
|
||||||
|
"Forms:HideDropDown",
|
||||||
|
"InPermitUnload",
|
||||||
|
"PermitUnload",
|
||||||
|
// Messages sent to SelectParentHelper.jsm
|
||||||
|
"Forms:UpdateDropDown",
|
||||||
|
// Messages sent to browser.js
|
||||||
|
"PageVisibility:Hide",
|
||||||
|
"PageVisibility:Show",
|
||||||
|
// Messages sent to SessionStore.jsm
|
||||||
|
"SessionStore:update",
|
||||||
|
// Messages sent to BrowserTestUtils.jsm
|
||||||
|
"browser-test-utils:loadEvent",
|
||||||
|
],
|
||||||
|
|
||||||
|
get outerParentMM() {
|
||||||
|
return this.outer.frameLoader.messageManager;
|
||||||
|
},
|
||||||
|
|
||||||
|
get outerChildMM() {
|
||||||
|
// This is only possible because we require the outer browser to be
|
||||||
|
// non-remote, so we're able to reach into its window and use the child
|
||||||
|
// side message manager there.
|
||||||
|
let docShell = this.outer.frameLoader.docShell;
|
||||||
|
return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIContentFrameMessageManager);
|
||||||
|
},
|
||||||
|
|
||||||
|
get innerParentMM() {
|
||||||
|
return this.inner.frameLoader.messageManager;
|
||||||
|
},
|
||||||
|
|
||||||
|
sendAsyncMessage(name, ...args) {
|
||||||
|
debug(`Calling sendAsyncMessage for ${name}`);
|
||||||
|
|
||||||
|
if (!this.OUTER_TO_INNER_MESSAGES.includes(name)) {
|
||||||
|
debug(`Should ${name} go to inner?`);
|
||||||
|
this.outerParentMM.sendAsyncMessage(name, ...args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(`${name} outer -> inner`);
|
||||||
|
this.innerParentMM.sendAsyncMessage(name, ...args);
|
||||||
|
},
|
||||||
|
|
||||||
|
init() {
|
||||||
|
for (let method of this.PASS_THROUGH_METHODS) {
|
||||||
|
// Workaround bug 449811 to ensure a fresh binding each time through the loop
|
||||||
|
let _method = method;
|
||||||
|
this[_method] = (...args) => {
|
||||||
|
return this.outerParentMM[_method](...args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let message of this.INNER_TO_OUTER_MESSAGES) {
|
||||||
|
this.innerParentMM.addMessageListener(message, this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
for (let message of this.INNER_TO_OUTER_MESSAGES) {
|
||||||
|
this.innerParentMM.removeMessageListener(message, this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
receiveMessage({ name, data, objects, principal }) {
|
||||||
|
if (!this.INNER_TO_OUTER_MESSAGES.includes(name)) {
|
||||||
|
debug(`Received unexpected message ${name}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(`${name} inner -> outer`);
|
||||||
|
this.outerChildMM.sendAsyncMessage(name, data, objects, principal);
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,179 @@
|
||||||
|
/* 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";
|
||||||
|
|
||||||
|
const { Ci, Cu, Cr } = require("chrome");
|
||||||
|
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
const Services = require("Services");
|
||||||
|
const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
|
||||||
|
|
||||||
|
function readInputStreamToString(stream) {
|
||||||
|
return NetUtil.readInputStreamToString(stream, stream.available());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object aims to provide the nsIWebNavigation interface for mozbrowser elements.
|
||||||
|
* nsIWebNavigation is one of the interfaces expected on <xul:browser>s, so this wrapper
|
||||||
|
* helps mozbrowser elements support this.
|
||||||
|
*
|
||||||
|
* It attempts to use the mozbrowser API wherever possible, however some methods don't
|
||||||
|
* exist yet, so we fallback to the WebNavigation frame script messages in those cases.
|
||||||
|
* Ideally the mozbrowser API would eventually be extended to cover all properties and
|
||||||
|
* methods used here.
|
||||||
|
*
|
||||||
|
* This is largely copied from RemoteWebNavigation.js, which uses the message manager to
|
||||||
|
* perform all actions.
|
||||||
|
*/
|
||||||
|
function BrowserElementWebNavigation(browser) {
|
||||||
|
this._browser = browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserElementWebNavigation.prototype = {
|
||||||
|
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([
|
||||||
|
Ci.nsIWebNavigation,
|
||||||
|
Ci.nsISupports
|
||||||
|
]),
|
||||||
|
|
||||||
|
get _mm() {
|
||||||
|
return this._browser.frameLoader.messageManager;
|
||||||
|
},
|
||||||
|
|
||||||
|
canGoBack: false,
|
||||||
|
canGoForward: false,
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
this._browser.goBack();
|
||||||
|
},
|
||||||
|
|
||||||
|
goForward() {
|
||||||
|
this._browser.goForward();
|
||||||
|
},
|
||||||
|
|
||||||
|
gotoIndex(index) {
|
||||||
|
// No equivalent in the current BrowserElement API
|
||||||
|
this._sendMessage("WebNavigation:GotoIndex", { index });
|
||||||
|
},
|
||||||
|
|
||||||
|
loadURI(uri, flags, referrer, postData, headers) {
|
||||||
|
// No equivalent in the current BrowserElement API
|
||||||
|
this.loadURIWithOptions(uri, flags, referrer,
|
||||||
|
Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT,
|
||||||
|
postData, headers, null);
|
||||||
|
},
|
||||||
|
|
||||||
|
loadURIWithOptions(uri, flags, referrer, referrerPolicy, postData, headers,
|
||||||
|
baseURI) {
|
||||||
|
// No equivalent in the current BrowserElement API
|
||||||
|
this._sendMessage("WebNavigation:LoadURI", {
|
||||||
|
uri,
|
||||||
|
flags,
|
||||||
|
referrer: referrer ? referrer.spec : null,
|
||||||
|
referrerPolicy: referrerPolicy,
|
||||||
|
postData: postData ? readInputStreamToString(postData) : null,
|
||||||
|
headers: headers ? readInputStreamToString(headers) : null,
|
||||||
|
baseURI: baseURI ? baseURI.spec : null,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setOriginAttributesBeforeLoading(originAttributes) {
|
||||||
|
// No equivalent in the current BrowserElement API
|
||||||
|
this._sendMessage("WebNavigation:SetOriginAttributes", {
|
||||||
|
originAttributes,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
reload(flags) {
|
||||||
|
let hardReload = false;
|
||||||
|
if (flags & this.LOAD_FLAGS_BYPASS_PROXY ||
|
||||||
|
flags & this.LOAD_FLAGS_BYPASS_CACHE) {
|
||||||
|
hardReload = true;
|
||||||
|
}
|
||||||
|
this._browser.reload(hardReload);
|
||||||
|
},
|
||||||
|
|
||||||
|
stop(flags) {
|
||||||
|
// No equivalent in the current BrowserElement API
|
||||||
|
this._sendMessage("WebNavigation:Stop", { flags });
|
||||||
|
},
|
||||||
|
|
||||||
|
get document() {
|
||||||
|
return this._browser.contentDocument;
|
||||||
|
},
|
||||||
|
|
||||||
|
_currentURI: null,
|
||||||
|
get currentURI() {
|
||||||
|
if (!this._currentURI) {
|
||||||
|
this._currentURI = Services.io.newURI("about:blank", null, null);
|
||||||
|
}
|
||||||
|
return this._currentURI;
|
||||||
|
},
|
||||||
|
set currentURI(uri) {
|
||||||
|
this._browser.src = uri.spec;
|
||||||
|
},
|
||||||
|
|
||||||
|
referringURI: null,
|
||||||
|
|
||||||
|
// Bug 1233803 - accessing the sessionHistory of remote browsers should be
|
||||||
|
// done in content scripts.
|
||||||
|
get sessionHistory() {
|
||||||
|
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
},
|
||||||
|
set sessionHistory(value) {
|
||||||
|
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
},
|
||||||
|
|
||||||
|
_sendMessage(message, data) {
|
||||||
|
try {
|
||||||
|
this._mm.sendAsyncMessage(message, data);
|
||||||
|
} catch (e) {
|
||||||
|
Cu.reportError(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
swapBrowser(browser) {
|
||||||
|
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
},
|
||||||
|
|
||||||
|
copyStateFrom(otherWebNavigation) {
|
||||||
|
const state = [
|
||||||
|
"canGoBack",
|
||||||
|
"canGoForward",
|
||||||
|
"_currentURI",
|
||||||
|
];
|
||||||
|
for (let property of state) {
|
||||||
|
this[property] = otherWebNavigation[property];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const FLAGS = [
|
||||||
|
"LOAD_FLAGS_MASK",
|
||||||
|
"LOAD_FLAGS_NONE",
|
||||||
|
"LOAD_FLAGS_IS_REFRESH",
|
||||||
|
"LOAD_FLAGS_IS_LINK",
|
||||||
|
"LOAD_FLAGS_BYPASS_HISTORY",
|
||||||
|
"LOAD_FLAGS_REPLACE_HISTORY",
|
||||||
|
"LOAD_FLAGS_BYPASS_CACHE",
|
||||||
|
"LOAD_FLAGS_BYPASS_PROXY",
|
||||||
|
"LOAD_FLAGS_CHARSET_CHANGE",
|
||||||
|
"LOAD_FLAGS_STOP_CONTENT",
|
||||||
|
"LOAD_FLAGS_FROM_EXTERNAL",
|
||||||
|
"LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP",
|
||||||
|
"LOAD_FLAGS_FIRST_LOAD",
|
||||||
|
"LOAD_FLAGS_ALLOW_POPUPS",
|
||||||
|
"LOAD_FLAGS_BYPASS_CLASSIFIER",
|
||||||
|
"LOAD_FLAGS_FORCE_ALLOW_COOKIES",
|
||||||
|
"STOP_NETWORK",
|
||||||
|
"STOP_CONTENT",
|
||||||
|
"STOP_ALL",
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let flag of FLAGS) {
|
||||||
|
BrowserElementWebNavigation.prototype[flag] = Ci.nsIWebNavigation[flag];
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.BrowserElementWebNavigation = BrowserElementWebNavigation;
|
|
@ -19,6 +19,7 @@ support-files =
|
||||||
[browser_menu_item_01.js]
|
[browser_menu_item_01.js]
|
||||||
[browser_menu_item_02.js]
|
[browser_menu_item_02.js]
|
||||||
[browser_mouse_resize.js]
|
[browser_mouse_resize.js]
|
||||||
|
[browser_navigation.js]
|
||||||
[browser_page_state.js]
|
[browser_page_state.js]
|
||||||
[browser_resize_cmd.js]
|
[browser_resize_cmd.js]
|
||||||
skip-if = true # GCLI target confused after swap, will fix in bug 1240907
|
skip-if = true # GCLI target confused after swap, will fix in bug 1240907
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test the primary browser navigation UI to verify it's connected to the viewport.
|
||||||
|
|
||||||
|
const DUMMY_1_URL = "http://example.com/";
|
||||||
|
const TEST_URL = `${URL_ROOT}doc_page_state.html`;
|
||||||
|
const DUMMY_2_URL = "http://example.com/browser/";
|
||||||
|
const DUMMY_3_URL = "http://example.com/browser/devtools/";
|
||||||
|
|
||||||
|
add_task(function* () {
|
||||||
|
// Load up a sequence of pages:
|
||||||
|
// 0. DUMMY_1_URL
|
||||||
|
// 1. TEST_URL
|
||||||
|
// 2. DUMMY_2_URL
|
||||||
|
let tab = yield addTab(DUMMY_1_URL);
|
||||||
|
let browser = tab.linkedBrowser;
|
||||||
|
yield load(browser, TEST_URL);
|
||||||
|
yield load(browser, DUMMY_2_URL);
|
||||||
|
|
||||||
|
// Check session history state
|
||||||
|
let history = yield getSessionHistory(browser);
|
||||||
|
is(history.index, 2, "At page 2 in history");
|
||||||
|
is(history.entries.length, 3, "3 pages in history");
|
||||||
|
is(history.entries[0].uri, DUMMY_1_URL, "Page 0 URL matches");
|
||||||
|
is(history.entries[1].uri, TEST_URL, "Page 1 URL matches");
|
||||||
|
is(history.entries[2].uri, DUMMY_2_URL, "Page 2 URL matches");
|
||||||
|
|
||||||
|
// Go back one so we're at the test page
|
||||||
|
yield back(browser);
|
||||||
|
|
||||||
|
// Check session history state
|
||||||
|
history = yield getSessionHistory(browser);
|
||||||
|
is(history.index, 1, "At page 1 in history");
|
||||||
|
is(history.entries.length, 3, "3 pages in history");
|
||||||
|
is(history.entries[0].uri, DUMMY_1_URL, "Page 0 URL matches");
|
||||||
|
is(history.entries[1].uri, TEST_URL, "Page 1 URL matches");
|
||||||
|
is(history.entries[2].uri, DUMMY_2_URL, "Page 2 URL matches");
|
||||||
|
|
||||||
|
yield openRDM(tab);
|
||||||
|
|
||||||
|
ok(browser.webNavigation.canGoBack, "Going back is allowed");
|
||||||
|
ok(browser.webNavigation.canGoForward, "Going forward is allowed");
|
||||||
|
is(browser.documentURI.spec, TEST_URL, "documentURI matches page 1");
|
||||||
|
is(browser.contentTitle, "Page State Test", "contentTitle matches page 1");
|
||||||
|
|
||||||
|
yield forward(browser);
|
||||||
|
|
||||||
|
ok(browser.webNavigation.canGoBack, "Going back is allowed");
|
||||||
|
ok(!browser.webNavigation.canGoForward, "Going forward is not allowed");
|
||||||
|
is(browser.documentURI.spec, DUMMY_2_URL, "documentURI matches page 2");
|
||||||
|
is(browser.contentTitle, "mochitest index /browser/", "contentTitle matches page 2");
|
||||||
|
|
||||||
|
yield back(browser);
|
||||||
|
yield back(browser);
|
||||||
|
|
||||||
|
ok(!browser.webNavigation.canGoBack, "Going back is not allowed");
|
||||||
|
ok(browser.webNavigation.canGoForward, "Going forward is allowed");
|
||||||
|
is(browser.documentURI.spec, DUMMY_1_URL, "documentURI matches page 0");
|
||||||
|
is(browser.contentTitle, "mochitest index /", "contentTitle matches page 0");
|
||||||
|
|
||||||
|
let receivedStatusChanges = new Promise(resolve => {
|
||||||
|
let statusChangesSeen = 0;
|
||||||
|
let statusChangesExpected = 2;
|
||||||
|
let progressListener = {
|
||||||
|
onStatusChange(webProgress, request, status, message) {
|
||||||
|
info(message);
|
||||||
|
if (++statusChangesSeen == statusChangesExpected) {
|
||||||
|
gBrowser.removeProgressListener(progressListener);
|
||||||
|
ok(true, `${statusChangesExpected} status changes while loading`);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
gBrowser.addProgressListener(progressListener);
|
||||||
|
});
|
||||||
|
yield load(browser, DUMMY_3_URL);
|
||||||
|
yield receivedStatusChanges;
|
||||||
|
|
||||||
|
ok(browser.webNavigation.canGoBack, "Going back is allowed");
|
||||||
|
ok(!browser.webNavigation.canGoForward, "Going forward is not allowed");
|
||||||
|
is(browser.documentURI.spec, DUMMY_3_URL, "documentURI matches page 3");
|
||||||
|
is(browser.contentTitle, "mochitest index /browser/devtools/",
|
||||||
|
"contentTitle matches page 3");
|
||||||
|
|
||||||
|
yield closeRDM(tab);
|
||||||
|
|
||||||
|
// Check session history state
|
||||||
|
history = yield getSessionHistory(browser);
|
||||||
|
is(history.index, 1, "At page 1 in history");
|
||||||
|
is(history.entries.length, 2, "2 pages in history");
|
||||||
|
is(history.entries[0].uri, DUMMY_1_URL, "Page 0 URL matches");
|
||||||
|
is(history.entries[1].uri, DUMMY_3_URL, "Page 1 URL matches");
|
||||||
|
|
||||||
|
yield removeTab(tab);
|
||||||
|
});
|
|
@ -17,14 +17,8 @@ add_task(function* () {
|
||||||
// 2. DUMMY_2_URL
|
// 2. DUMMY_2_URL
|
||||||
let tab = yield addTab(DUMMY_1_URL);
|
let tab = yield addTab(DUMMY_1_URL);
|
||||||
let browser = tab.linkedBrowser;
|
let browser = tab.linkedBrowser;
|
||||||
|
yield load(browser, TEST_URL);
|
||||||
let loaded = BrowserTestUtils.browserLoaded(browser, false, TEST_URL);
|
yield load(browser, DUMMY_2_URL);
|
||||||
browser.loadURI(TEST_URL, null, null);
|
|
||||||
yield loaded;
|
|
||||||
|
|
||||||
loaded = BrowserTestUtils.browserLoaded(browser, false, DUMMY_2_URL);
|
|
||||||
browser.loadURI(DUMMY_2_URL, null, null);
|
|
||||||
yield loaded;
|
|
||||||
|
|
||||||
// Check session history state
|
// Check session history state
|
||||||
let history = yield getSessionHistory(browser);
|
let history = yield getSessionHistory(browser);
|
||||||
|
@ -35,9 +29,7 @@ add_task(function* () {
|
||||||
is(history.entries[2].uri, DUMMY_2_URL, "Page 2 URL matches");
|
is(history.entries[2].uri, DUMMY_2_URL, "Page 2 URL matches");
|
||||||
|
|
||||||
// Go back one so we're at the test page
|
// Go back one so we're at the test page
|
||||||
let shown = waitForPageShow(browser);
|
yield back(browser);
|
||||||
browser.goBack();
|
|
||||||
yield shown;
|
|
||||||
|
|
||||||
// Check session history state
|
// Check session history state
|
||||||
history = yield getSessionHistory(browser);
|
history = yield getSessionHistory(browser);
|
||||||
|
@ -82,41 +74,3 @@ add_task(function* () {
|
||||||
|
|
||||||
yield removeTab(tab);
|
yield removeTab(tab);
|
||||||
});
|
});
|
||||||
|
|
||||||
function getSessionHistory(browser) {
|
|
||||||
return ContentTask.spawn(browser, {}, function* () {
|
|
||||||
/* eslint-disable no-undef */
|
|
||||||
let { interfaces: Ci } = Components;
|
|
||||||
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
|
|
||||||
let sessionHistory = webNav.sessionHistory;
|
|
||||||
let result = {
|
|
||||||
index: sessionHistory.index,
|
|
||||||
entries: []
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let i = 0; i < sessionHistory.count; i++) {
|
|
||||||
let entry = sessionHistory.getEntryAtIndex(i, false);
|
|
||||||
result.entries.push({
|
|
||||||
uri: entry.URI.spec,
|
|
||||||
title: entry.title
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
/* eslint-enable no-undef */
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function waitForPageShow(browser) {
|
|
||||||
let mm = browser.messageManager;
|
|
||||||
return new Promise(resolve => {
|
|
||||||
let onShow = message => {
|
|
||||||
if (message.target != browser) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mm.removeMessageListener("PageVisibility:Show", onShow);
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
mm.addMessageListener("PageVisibility:Show", onShow);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<style>
|
<head>
|
||||||
body {
|
<title>Page State Test</title>
|
||||||
height: 100vh;
|
<style>
|
||||||
background: red;
|
body {
|
||||||
}
|
height: 100vh;
|
||||||
body.modified {
|
background: red;
|
||||||
background: green;
|
}
|
||||||
}
|
body.modified {
|
||||||
</style>
|
background: green;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
<body onclick="this.classList.add('modified')"/>
|
<body onclick="this.classList.add('modified')"/>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -153,3 +153,59 @@ function openDeviceModal(ui) {
|
||||||
ok(!modal.classList.contains("hidden"),
|
ok(!modal.classList.contains("hidden"),
|
||||||
"The device modal is displayed.");
|
"The device modal is displayed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSessionHistory(browser) {
|
||||||
|
return ContentTask.spawn(browser, {}, function* () {
|
||||||
|
/* eslint-disable no-undef */
|
||||||
|
let { interfaces: Ci } = Components;
|
||||||
|
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||||
|
let sessionHistory = webNav.sessionHistory;
|
||||||
|
let result = {
|
||||||
|
index: sessionHistory.index,
|
||||||
|
entries: []
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < sessionHistory.count; i++) {
|
||||||
|
let entry = sessionHistory.getEntryAtIndex(i, false);
|
||||||
|
result.entries.push({
|
||||||
|
uri: entry.URI.spec,
|
||||||
|
title: entry.title
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
/* eslint-enable no-undef */
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForPageShow(browser) {
|
||||||
|
let mm = browser.messageManager;
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let onShow = message => {
|
||||||
|
if (message.target != browser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mm.removeMessageListener("PageVisibility:Show", onShow);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
mm.addMessageListener("PageVisibility:Show", onShow);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function load(browser, url) {
|
||||||
|
let loaded = BrowserTestUtils.browserLoaded(browser, false, url);
|
||||||
|
browser.loadURI(url, null, null);
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
function back(browser) {
|
||||||
|
let shown = waitForPageShow(browser);
|
||||||
|
browser.goBack();
|
||||||
|
return shown;
|
||||||
|
}
|
||||||
|
|
||||||
|
function forward(browser) {
|
||||||
|
let shown = waitForPageShow(browser);
|
||||||
|
browser.goForward();
|
||||||
|
return shown;
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,9 @@ support-files =
|
||||||
[test_notification_box_01.html]
|
[test_notification_box_01.html]
|
||||||
[test_notification_box_02.html]
|
[test_notification_box_02.html]
|
||||||
[test_notification_box_03.html]
|
[test_notification_box_03.html]
|
||||||
|
[test_reps_attribute.html]
|
||||||
[test_reps_date-time.html]
|
[test_reps_date-time.html]
|
||||||
|
[test_reps_grip.html]
|
||||||
[test_reps_object-with-url.html]
|
[test_reps_object-with-url.html]
|
||||||
[test_reps_stylesheet.html]
|
[test_reps_stylesheet.html]
|
||||||
[test_reps_undefined.html]
|
[test_reps_undefined.html]
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
Test Attribute rep
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Rep test - Attribute</title>
|
||||||
|
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="test">
|
||||||
|
<script src="head.js" type="application/javascript;version=1.8"></script>
|
||||||
|
<script type="application/javascript;version=1.8">
|
||||||
|
window.onload = Task.async(function* () {
|
||||||
|
try {
|
||||||
|
let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
|
||||||
|
let { Attribute } = browserRequire("devtools/client/shared/components/reps/attribute");
|
||||||
|
|
||||||
|
let gripStub = {
|
||||||
|
"type": "object",
|
||||||
|
"class": "Attr",
|
||||||
|
"actor": "server1.conn19.obj65",
|
||||||
|
"extensible": true,
|
||||||
|
"frozen": false,
|
||||||
|
"sealed": false,
|
||||||
|
"ownPropertyLength": 0,
|
||||||
|
"preview": {
|
||||||
|
"kind": "DOMNode",
|
||||||
|
"nodeType": 2,
|
||||||
|
"nodeName": "class",
|
||||||
|
"value": "autocomplete-suggestions"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test that correct rep is chosen
|
||||||
|
const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
|
||||||
|
is(renderedRep.type, Attribute.rep, `Rep correctly selects ${Attribute.rep.displayName}`);
|
||||||
|
|
||||||
|
// Test rendering
|
||||||
|
const renderedComponent = renderComponent(Attribute.rep, { object: gripStub });
|
||||||
|
is(renderedComponent.textContent, "class=\"autocomplete-suggestions\"", "Attribute rep has expected text content");
|
||||||
|
} catch(e) {
|
||||||
|
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||||
|
} finally {
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,435 @@
|
||||||
|
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
Test grip rep
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Rep test - grip</title>
|
||||||
|
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="test">
|
||||||
|
<script src="head.js" type="application/javascript;version=1.8"></script>
|
||||||
|
<script type="application/javascript;version=1.8">
|
||||||
|
window.onload = Task.async(function* () {
|
||||||
|
let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
|
||||||
|
let { Grip } = browserRequire("devtools/client/shared/components/reps/grip");
|
||||||
|
|
||||||
|
try {
|
||||||
|
yield testBasic();
|
||||||
|
|
||||||
|
// Test property iterator
|
||||||
|
yield testMaxProps();
|
||||||
|
yield testMoreThanMaxProps();
|
||||||
|
yield testUninterestingProps();
|
||||||
|
|
||||||
|
// Test that properties are rendered as expected by PropRep
|
||||||
|
yield testNestedObject();
|
||||||
|
yield testNestedArray();
|
||||||
|
} catch(e) {
|
||||||
|
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||||
|
} finally {
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBasic() {
|
||||||
|
// Test object: `{}`
|
||||||
|
const testName = "testBasic";
|
||||||
|
|
||||||
|
// Test that correct rep is chosen
|
||||||
|
const gripStub = getGripStub("testBasic");
|
||||||
|
const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
|
||||||
|
is(renderedRep.type, Grip.rep, `Rep correctly selects ${Grip.rep.displayName}`);
|
||||||
|
|
||||||
|
// Test rendering
|
||||||
|
const defaultOutput = `Object`;
|
||||||
|
|
||||||
|
const modeTests = [
|
||||||
|
{
|
||||||
|
mode: undefined,
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "tiny",
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "short",
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "long",
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
testRenderingInMode(modeTests, testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testMaxProps() {
|
||||||
|
// Test object: `{a: "a", b: "b", c: "c"}`;
|
||||||
|
const testName = "testMaxProps";
|
||||||
|
|
||||||
|
const defaultOutput = `Object {a: "a", b: "b", c: "c"}`;
|
||||||
|
|
||||||
|
const modeTests = [
|
||||||
|
{
|
||||||
|
mode: undefined,
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "tiny",
|
||||||
|
expectedOutput: `Object`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "short",
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "long",
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
testRenderingInMode(modeTests, testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testMoreThanMaxProps() {
|
||||||
|
// Test object = `{a: "a", b: "b", c: "c", d: "d", e: "e"}`
|
||||||
|
const testName = "testMoreThanMaxProps";
|
||||||
|
|
||||||
|
const defaultOutput = `Object {a: "a", b: "b", c: "c", more...}`;
|
||||||
|
|
||||||
|
const modeTests = [
|
||||||
|
{
|
||||||
|
mode: undefined,
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "tiny",
|
||||||
|
expectedOutput: `Object`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "short",
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
},
|
||||||
|
// @TODO Fix this test case.
|
||||||
|
// See Bug 1281489 - Reps: Grips rep renders only 3 properties in long mode
|
||||||
|
{
|
||||||
|
mode: "long",
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
testRenderingInMode(modeTests, testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testUninterestingProps() {
|
||||||
|
// Test object: `{a: undefined, b: undefined, c: "c", d: 1}`
|
||||||
|
// @TODO This is not how we actually want the preview to be output.
|
||||||
|
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1276376
|
||||||
|
const expectedOutput = `Object {a: undefined, b: undefined, c: "c", more...}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testNestedObject() {
|
||||||
|
// Test object: `{objProp: {id: 1}, strProp: "test string"}`
|
||||||
|
const testName = "testNestedObject";
|
||||||
|
|
||||||
|
const defaultOutput = `Object {objProp: Object, strProp: "test string"}`;
|
||||||
|
|
||||||
|
const modeTests = [
|
||||||
|
{
|
||||||
|
mode: undefined,
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "tiny",
|
||||||
|
expectedOutput: `Object`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "short",
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "long",
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
testRenderingInMode(modeTests, testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testNestedArray() {
|
||||||
|
// Test object: `{arrProp: ["foo", "bar", "baz"]}`
|
||||||
|
const testName = "testNestedArray";
|
||||||
|
|
||||||
|
const defaultOutput = `Object {arrProp: [3]}`;
|
||||||
|
|
||||||
|
const modeTests = [
|
||||||
|
{
|
||||||
|
mode: undefined,
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "tiny",
|
||||||
|
expectedOutput: `Object`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "short",
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "long",
|
||||||
|
expectedOutput: defaultOutput,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
testRenderingInMode(modeTests, testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRenderingInMode(modeTests, testName) {
|
||||||
|
modeTests.forEach(({mode, expectedOutput, message}) => {
|
||||||
|
const modeString = typeof mode === "undefined" ? "no mode" : mode;
|
||||||
|
if (!message) {
|
||||||
|
message = `${testName}: ${modeString} renders correctly.`
|
||||||
|
}
|
||||||
|
const gripStub = getGripStub(testName);
|
||||||
|
|
||||||
|
const rendered = renderComponent(Grip.rep, { object: gripStub, mode });
|
||||||
|
is(rendered.textContent, expectedOutput, message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGripStub(functionName) {
|
||||||
|
switch (functionName) {
|
||||||
|
case "testBasic":
|
||||||
|
return {
|
||||||
|
"type": "object",
|
||||||
|
"class": "Object",
|
||||||
|
"actor": "server1.conn0.obj304",
|
||||||
|
"extensible": true,
|
||||||
|
"frozen": false,
|
||||||
|
"sealed": false,
|
||||||
|
"ownPropertyLength": 0,
|
||||||
|
"preview": {
|
||||||
|
"kind": "Object",
|
||||||
|
"ownProperties": {},
|
||||||
|
"ownPropertiesLength": 0,
|
||||||
|
"safeGetterValues": {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
case "testMaxProps":
|
||||||
|
return {
|
||||||
|
"type": "object",
|
||||||
|
"class": "Object",
|
||||||
|
"actor": "server1.conn0.obj337",
|
||||||
|
"extensible": true,
|
||||||
|
"frozen": false,
|
||||||
|
"sealed": false,
|
||||||
|
"ownPropertyLength": 3,
|
||||||
|
"preview": {
|
||||||
|
"kind": "Object",
|
||||||
|
"ownProperties": {
|
||||||
|
"a": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": "a"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": "b"
|
||||||
|
},
|
||||||
|
"c": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": "c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ownPropertiesLength": 3,
|
||||||
|
"safeGetterValues": {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
case "testMoreThanMaxProps":
|
||||||
|
return {
|
||||||
|
"type": "object",
|
||||||
|
"class": "Object",
|
||||||
|
"actor": "server1.conn0.obj332",
|
||||||
|
"extensible": true,
|
||||||
|
"frozen": false,
|
||||||
|
"sealed": false,
|
||||||
|
"ownPropertyLength": 5,
|
||||||
|
"preview": {
|
||||||
|
"kind": "Object",
|
||||||
|
"ownProperties": {
|
||||||
|
"a": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": "a"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": "b"
|
||||||
|
},
|
||||||
|
"c": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": "c"
|
||||||
|
},
|
||||||
|
"d": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": "d"
|
||||||
|
},
|
||||||
|
"e": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": "e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ownPropertiesLength": 5,
|
||||||
|
"safeGetterValues": {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
case "testUninterestingProps":
|
||||||
|
return {
|
||||||
|
"type": "object",
|
||||||
|
"class": "Object",
|
||||||
|
"actor": "server1.conn0.obj342",
|
||||||
|
"extensible": true,
|
||||||
|
"frozen": false,
|
||||||
|
"sealed": false,
|
||||||
|
"ownPropertyLength": 4,
|
||||||
|
"preview": {
|
||||||
|
"kind": "Object",
|
||||||
|
"ownProperties": {
|
||||||
|
"a": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": {
|
||||||
|
"type": "undefined"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": {
|
||||||
|
"type": "undefined"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"c": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": "c"
|
||||||
|
},
|
||||||
|
"d": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ownPropertiesLength": 4,
|
||||||
|
"safeGetterValues": {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
case "testNestedObject":
|
||||||
|
return {
|
||||||
|
"type": "object",
|
||||||
|
"class": "Object",
|
||||||
|
"actor": "server1.conn0.obj145",
|
||||||
|
"extensible": true,
|
||||||
|
"frozen": false,
|
||||||
|
"sealed": false,
|
||||||
|
"ownPropertyLength": 2,
|
||||||
|
"preview": {
|
||||||
|
"kind": "Object",
|
||||||
|
"ownProperties": {
|
||||||
|
"objProp": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": {
|
||||||
|
"type": "object",
|
||||||
|
"class": "Object",
|
||||||
|
"actor": "server1.conn0.obj146",
|
||||||
|
"extensible": true,
|
||||||
|
"frozen": false,
|
||||||
|
"sealed": false,
|
||||||
|
"ownPropertyLength": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strProp": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": "test string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ownPropertiesLength": 2,
|
||||||
|
"safeGetterValues": {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
case "testNestedArray":
|
||||||
|
return {
|
||||||
|
"type": "object",
|
||||||
|
"class": "Object",
|
||||||
|
"actor": "server1.conn0.obj326",
|
||||||
|
"extensible": true,
|
||||||
|
"frozen": false,
|
||||||
|
"sealed": false,
|
||||||
|
"ownPropertyLength": 1,
|
||||||
|
"preview": {
|
||||||
|
"kind": "Object",
|
||||||
|
"ownProperties": {
|
||||||
|
"arrProp": {
|
||||||
|
"configurable": true,
|
||||||
|
"enumerable": true,
|
||||||
|
"writable": true,
|
||||||
|
"value": {
|
||||||
|
"type": "object",
|
||||||
|
"class": "Array",
|
||||||
|
"actor": "server1.conn0.obj327",
|
||||||
|
"extensible": true,
|
||||||
|
"frozen": false,
|
||||||
|
"sealed": false,
|
||||||
|
"ownPropertyLength": 4,
|
||||||
|
"preview": {
|
||||||
|
"kind": "ArrayLike",
|
||||||
|
"length": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ownPropertiesLength": 1,
|
||||||
|
"safeGetterValues": {}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -33,21 +33,25 @@ extra state that may be set on tab by add-ons or others.
|
||||||
6. Swap the tool UI (with viewport showing the content) into the original
|
6. Swap the tool UI (with viewport showing the content) into the original
|
||||||
browser tab and close the temporary tab used to load the tool via
|
browser tab and close the temporary tab used to load the tool via
|
||||||
`swapBrowsersAndCloseOther`.
|
`swapBrowsersAndCloseOther`.
|
||||||
|
7. Start a tunnel from the tool tab's browser to the viewport browser
|
||||||
|
so that some browser UI functions, like navigation, are connected to
|
||||||
|
the content in the viewport, instead of the tool page.
|
||||||
|
|
||||||
## Closing RDM During Current Firefox Session
|
## Closing RDM During Current Firefox Session
|
||||||
|
|
||||||
To close RDM, we follow a similar process to the one from opening RDM so we can
|
To close RDM, we follow a similar process to the one from opening RDM so we can
|
||||||
restore the content back to a normal tab.
|
restore the content back to a normal tab.
|
||||||
|
|
||||||
1. Create a temporary, hidden tab to hold the content.
|
1. Stop the tunnel between outer and inner browsers.
|
||||||
2. Mark the content tab browser's docshell as active so the frame is created
|
2. Create a temporary, hidden tab to hold the content.
|
||||||
|
3. Mark the content tab browser's docshell as active so the frame is created
|
||||||
eagerly and will be ready to swap.
|
eagerly and will be ready to swap.
|
||||||
3. Swap tab content from the browser within the viewport in the tool UI to the
|
4. Swap tab content from the browser within the viewport in the tool UI to the
|
||||||
regular browser tab, preserving all state via
|
regular browser tab, preserving all state via
|
||||||
`gBrowser._swapBrowserDocShells`.
|
`gBrowser._swapBrowserDocShells`.
|
||||||
4. Force the original browser tab to be remote since web content is loaded in
|
5. Force the original browser tab to be remote since web content is loaded in
|
||||||
the child process, and we're about to swap the content into this tab.
|
the child process, and we're about to swap the content into this tab.
|
||||||
5. Swap the content into the original browser tab and close the temporary tab
|
6. Swap the content into the original browser tab and close the temporary tab
|
||||||
used to hold the content via `swapBrowsersAndCloseOther`.
|
used to hold the content via `swapBrowsersAndCloseOther`.
|
||||||
|
|
||||||
## Session Restore
|
## Session Restore
|
||||||
|
|
|
@ -1638,11 +1638,14 @@ RootClient.prototype = {
|
||||||
if (browser.frameLoader.tabParent) {
|
if (browser.frameLoader.tabParent) {
|
||||||
// Tabs in child process
|
// Tabs in child process
|
||||||
packet.tabId = browser.frameLoader.tabParent.tabId;
|
packet.tabId = browser.frameLoader.tabParent.tabId;
|
||||||
|
} else if (browser.outerWindowID) {
|
||||||
|
// <xul:browser> tabs in parent process
|
||||||
|
packet.outerWindowID = browser.outerWindowID;
|
||||||
} else {
|
} else {
|
||||||
// Tabs in parent process
|
// <iframe mozbrowser> tabs in parent process
|
||||||
let windowUtils = browser.contentWindow
|
let windowUtils = browser.contentWindow
|
||||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
.getInterface(Ci.nsIDOMWindowUtils);
|
.getInterface(Ci.nsIDOMWindowUtils);
|
||||||
packet.outerWindowID = windowUtils.outerWindowID;
|
packet.outerWindowID = windowUtils.outerWindowID;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -117,13 +117,6 @@
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<!-- Notification API V2 -->
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="@ANDROID_PACKAGE_NAME@.helperBroadcastAction" />
|
|
||||||
<data android:scheme="moz-notification" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.mozilla.gecko.UPDATE"/>
|
<action android:name="org.mozilla.gecko.UPDATE"/>
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
@ -247,6 +240,17 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name="org.mozilla.gecko.NotificationReceiver"
|
||||||
|
android:exported="false">
|
||||||
|
<!-- Notification API V2 -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="@ANDROID_PACKAGE_NAME@.helperBroadcastAction" />
|
||||||
|
<data android:scheme="moz-notification" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
#include ../services/manifests/FxAccountAndroidManifest_activities.xml.in
|
#include ../services/manifests/FxAccountAndroidManifest_activities.xml.in
|
||||||
#ifdef MOZ_ANDROID_SEARCH_ACTIVITY
|
#ifdef MOZ_ANDROID_SEARCH_ACTIVITY
|
||||||
#include ../search/manifests/SearchAndroidManifest_activities.xml.in
|
#include ../search/manifests/SearchAndroidManifest_activities.xml.in
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.mozilla.gecko.db.BrowserContract;
|
||||||
import org.mozilla.gecko.db.BrowserDB;
|
import org.mozilla.gecko.db.BrowserDB;
|
||||||
import org.mozilla.gecko.db.SuggestedSites;
|
import org.mozilla.gecko.db.SuggestedSites;
|
||||||
import org.mozilla.gecko.delegates.BrowserAppDelegate;
|
import org.mozilla.gecko.delegates.BrowserAppDelegate;
|
||||||
|
import org.mozilla.gecko.delegates.OfflineTabStatusDelegate;
|
||||||
import org.mozilla.gecko.delegates.ScreenshotDelegate;
|
import org.mozilla.gecko.delegates.ScreenshotDelegate;
|
||||||
import org.mozilla.gecko.distribution.Distribution;
|
import org.mozilla.gecko.distribution.Distribution;
|
||||||
import org.mozilla.gecko.distribution.DistributionStoreCallback;
|
import org.mozilla.gecko.distribution.DistributionStoreCallback;
|
||||||
|
@ -312,7 +313,8 @@ public class BrowserApp extends GeckoApp
|
||||||
(BrowserAppDelegate) new ReaderViewBookmarkPromotion(),
|
(BrowserAppDelegate) new ReaderViewBookmarkPromotion(),
|
||||||
(BrowserAppDelegate) new ContentNotificationsDelegate(),
|
(BrowserAppDelegate) new ContentNotificationsDelegate(),
|
||||||
(BrowserAppDelegate) new PostUpdateHandler(),
|
(BrowserAppDelegate) new PostUpdateHandler(),
|
||||||
new TelemetryCorePingDelegate()
|
new TelemetryCorePingDelegate(),
|
||||||
|
new OfflineTabStatusDelegate()
|
||||||
));
|
));
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|
|
@ -1630,8 +1630,6 @@ public abstract class GeckoApp
|
||||||
|
|
||||||
if (ACTION_ALERT_CALLBACK.equals(action)) {
|
if (ACTION_ALERT_CALLBACK.equals(action)) {
|
||||||
processAlertCallback(intent);
|
processAlertCallback(intent);
|
||||||
} else if (NotificationHelper.HELPER_BROADCAST_ACTION.equals(action)) {
|
|
||||||
NotificationHelper.getInstance(getApplicationContext()).handleNotificationIntent(intent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.mozilla.gecko.mozglue.SafeIntent;
|
||||||
import org.mozilla.gecko.util.GeckoEventListener;
|
import org.mozilla.gecko.util.GeckoEventListener;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
@ -45,7 +46,6 @@ public final class NotificationHelper implements GeckoEventListener {
|
||||||
private static final String WHEN_ATTR = "when";
|
private static final String WHEN_ATTR = "when";
|
||||||
private static final String PRIORITY_ATTR = "priority";
|
private static final String PRIORITY_ATTR = "priority";
|
||||||
private static final String LARGE_ICON_ATTR = "largeIcon";
|
private static final String LARGE_ICON_ATTR = "largeIcon";
|
||||||
private static final String EVENT_TYPE_ATTR = "eventType";
|
|
||||||
private static final String ACTIONS_ATTR = "actions";
|
private static final String ACTIONS_ATTR = "actions";
|
||||||
private static final String ACTION_ID_ATTR = "buttonId";
|
private static final String ACTION_ID_ATTR = "buttonId";
|
||||||
private static final String ACTION_TITLE_ATTR = "title";
|
private static final String ACTION_TITLE_ATTR = "title";
|
||||||
|
@ -53,13 +53,16 @@ public final class NotificationHelper implements GeckoEventListener {
|
||||||
private static final String PERSISTENT_ATTR = "persistent";
|
private static final String PERSISTENT_ATTR = "persistent";
|
||||||
private static final String HANDLER_ATTR = "handlerKey";
|
private static final String HANDLER_ATTR = "handlerKey";
|
||||||
private static final String COOKIE_ATTR = "cookie";
|
private static final String COOKIE_ATTR = "cookie";
|
||||||
|
static final String EVENT_TYPE_ATTR = "eventType";
|
||||||
|
|
||||||
private static final String NOTIFICATION_SCHEME = "moz-notification";
|
private static final String NOTIFICATION_SCHEME = "moz-notification";
|
||||||
|
|
||||||
private static final String BUTTON_EVENT = "notification-button-clicked";
|
private static final String BUTTON_EVENT = "notification-button-clicked";
|
||||||
private static final String CLICK_EVENT = "notification-clicked";
|
private static final String CLICK_EVENT = "notification-clicked";
|
||||||
private static final String CLEARED_EVENT = "notification-cleared";
|
|
||||||
private static final String CLOSED_EVENT = "notification-closed";
|
private static final String CLOSED_EVENT = "notification-closed";
|
||||||
|
static final String CLEARED_EVENT = "notification-cleared";
|
||||||
|
|
||||||
|
static final String ORIGINAL_EXTRA_COMPONENT = "originalComponent";
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
|
||||||
|
@ -107,40 +110,17 @@ public final class NotificationHelper implements GeckoEventListener {
|
||||||
return i.getBooleanExtra(HELPER_NOTIFICATION, false);
|
return i.getBooleanExtra(HELPER_NOTIFICATION, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleNotificationIntent(SafeIntent i) {
|
public static void getArgsAndSendNotificationIntent(SafeIntent intent) {
|
||||||
final Uri data = i.getData();
|
final JSONObject args = new JSONObject();
|
||||||
if (data == null) {
|
final Uri data = intent.getData();
|
||||||
Log.e(LOGTAG, "handleNotificationEvent: empty data");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final String id = data.getQueryParameter(ID_ATTR);
|
|
||||||
final String notificationType = data.getQueryParameter(EVENT_TYPE_ATTR);
|
final String notificationType = data.getQueryParameter(EVENT_TYPE_ATTR);
|
||||||
if (id == null || notificationType == null) {
|
|
||||||
Log.e(LOGTAG, "handleNotificationEvent: invalid intent parameters");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case the user swiped out the notification, we empty the id set.
|
|
||||||
if (CLEARED_EVENT.equals(notificationType)) {
|
|
||||||
mClearableNotifications.remove(id);
|
|
||||||
// If Gecko isn't running, we throw away events where the notification was cancelled.
|
|
||||||
// i.e. Don't bug the user if they're just closing a bunch of notifications.
|
|
||||||
if (!GeckoThread.isRunning()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject args = new JSONObject();
|
|
||||||
|
|
||||||
// The handler and cookie parameters are optional.
|
|
||||||
final String handler = data.getQueryParameter(HANDLER_ATTR);
|
|
||||||
final String cookie = i.getStringExtra(COOKIE_ATTR);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
args.put(ID_ATTR, id);
|
args.put(ID_ATTR, data.getQueryParameter(ID_ATTR));
|
||||||
args.put(EVENT_TYPE_ATTR, notificationType);
|
args.put(EVENT_TYPE_ATTR, notificationType);
|
||||||
args.put(HANDLER_ATTR, handler);
|
args.put(HANDLER_ATTR, data.getQueryParameter(HANDLER_ATTR));
|
||||||
args.put(COOKIE_ATTR, cookie);
|
args.put(COOKIE_ATTR, data.getQueryParameter(COOKIE_ATTR));
|
||||||
|
|
||||||
if (BUTTON_EVENT.equals(notificationType)) {
|
if (BUTTON_EVENT.equals(notificationType)) {
|
||||||
final String actionName = data.getQueryParameter(ACTION_ID_ATTR);
|
final String actionName = data.getQueryParameter(ACTION_ID_ATTR);
|
||||||
|
@ -152,14 +132,28 @@ public final class NotificationHelper implements GeckoEventListener {
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
Log.e(LOGTAG, "Error building JSON notification arguments.", e);
|
Log.e(LOGTAG, "Error building JSON notification arguments.", e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleNotificationIntent(SafeIntent i) {
|
||||||
|
final Uri data = i.getData();
|
||||||
|
final String notificationType = data.getQueryParameter(EVENT_TYPE_ATTR);
|
||||||
|
final String id = data.getQueryParameter(ID_ATTR);
|
||||||
|
if (id == null || notificationType == null) {
|
||||||
|
Log.e(LOGTAG, "handleNotificationEvent: invalid intent parameters");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getArgsAndSendNotificationIntent(i);
|
||||||
|
|
||||||
// If the notification was clicked, we are closing it. This must be executed after
|
// If the notification was clicked, we are closing it. This must be executed after
|
||||||
// sending the event to js side because when the notification is canceled no event can be
|
// sending the event to js side because when the notification is canceled no event can be
|
||||||
// handled.
|
// handled.
|
||||||
if (CLICK_EVENT.equals(notificationType) && !i.getBooleanExtra(ONGOING_ATTR, false)) {
|
if (CLICK_EVENT.equals(notificationType) && !i.getBooleanExtra(ONGOING_ATTR, false)) {
|
||||||
|
// The handler and cookie parameters are optional.
|
||||||
|
final String handler = data.getQueryParameter(HANDLER_ATTR);
|
||||||
|
final String cookie = i.getStringExtra(COOKIE_ATTR);
|
||||||
hideNotification(id, handler, cookie);
|
hideNotification(id, handler, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Uri.Builder getNotificationBuilder(JSONObject message, String type) {
|
private Uri.Builder getNotificationBuilder(JSONObject message, String type) {
|
||||||
|
@ -192,15 +186,18 @@ public final class NotificationHelper implements GeckoEventListener {
|
||||||
notificationIntent.setData(dataUri);
|
notificationIntent.setData(dataUri);
|
||||||
notificationIntent.putExtra(HELPER_NOTIFICATION, true);
|
notificationIntent.putExtra(HELPER_NOTIFICATION, true);
|
||||||
notificationIntent.putExtra(COOKIE_ATTR, message.optString(COOKIE_ATTR));
|
notificationIntent.putExtra(COOKIE_ATTR, message.optString(COOKIE_ATTR));
|
||||||
notificationIntent.setClass(mContext, GeckoAppShell.getGeckoInterface().getActivity().getClass());
|
|
||||||
|
// All intents get routed through the notificationReceiver. That lets us bail if we don't want to start Gecko
|
||||||
|
final ComponentName name = new ComponentName(mContext, GeckoAppShell.getGeckoInterface().getActivity().getClass());
|
||||||
|
notificationIntent.putExtra(ORIGINAL_EXTRA_COMPONENT, name);
|
||||||
|
|
||||||
return notificationIntent;
|
return notificationIntent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PendingIntent buildNotificationPendingIntent(JSONObject message, String type) {
|
private PendingIntent buildNotificationPendingIntent(JSONObject message, String type) {
|
||||||
Uri.Builder builder = getNotificationBuilder(message, type);
|
Uri.Builder builder = getNotificationBuilder(message, type);
|
||||||
final Intent notificationIntent = buildNotificationIntent(message, builder);
|
final Intent notificationIntent = buildNotificationIntent(message, builder);
|
||||||
PendingIntent pi = PendingIntent.getActivity(mContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
return PendingIntent.getBroadcast(mContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
return pi;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PendingIntent buildButtonClickPendingIntent(JSONObject message, JSONObject action) {
|
private PendingIntent buildButtonClickPendingIntent(JSONObject message, JSONObject action) {
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.mozglue.SafeIntent;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast receiver for Notifications. Will forward them to GeckoApp (and start Gecko) if they're clicked.
|
||||||
|
* If they're being dismissed, it will not start Gecko, but may forward them to JS if Gecko is running.
|
||||||
|
* This is also the only entry point for notification intents.
|
||||||
|
*/
|
||||||
|
public class NotificationReceiver extends BroadcastReceiver {
|
||||||
|
private static final String LOGTAG = "Gecko" + NotificationReceiver.class.getSimpleName();
|
||||||
|
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
final Uri data = intent.getData();
|
||||||
|
if (data == null) {
|
||||||
|
Log.e(LOGTAG, "handleNotificationEvent: empty data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String notificationType = data.getQueryParameter(NotificationHelper.EVENT_TYPE_ATTR);
|
||||||
|
if (notificationType == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case the user swiped out the notification, we empty the id set.
|
||||||
|
if (NotificationHelper.CLEARED_EVENT.equals(notificationType)) {
|
||||||
|
// If Gecko isn't running, we throw away events where the notification was cancelled.
|
||||||
|
// i.e. Don't bug the user if they're just closing a bunch of notifications.
|
||||||
|
if (GeckoThread.isRunning()) {
|
||||||
|
NotificationHelper.getArgsAndSendNotificationIntent(new SafeIntent(intent));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
forwardMessageToActivity(intent, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forwardMessageToActivity(final Intent intent, final Context context) {
|
||||||
|
final ComponentName name = intent.getExtras().getParcelable(NotificationHelper.ORIGINAL_EXTRA_COMPONENT);
|
||||||
|
intent.setComponent(name);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
|
@ -104,6 +104,9 @@ public class Tab {
|
||||||
private boolean mIsEditing;
|
private boolean mIsEditing;
|
||||||
private final TabEditingState mEditingState = new TabEditingState();
|
private final TabEditingState mEditingState = new TabEditingState();
|
||||||
|
|
||||||
|
// Will be true when tab is loaded from cache while device was offline.
|
||||||
|
private boolean mLoadedFromCache;
|
||||||
|
|
||||||
public static final int STATE_DELAYED = 0;
|
public static final int STATE_DELAYED = 0;
|
||||||
public static final int STATE_LOADING = 1;
|
public static final int STATE_LOADING = 1;
|
||||||
public static final int STATE_SUCCESS = 2;
|
public static final int STATE_SUCCESS = 2;
|
||||||
|
@ -301,6 +304,10 @@ public class Tab {
|
||||||
return mHasOpenSearch;
|
return mHasOpenSearch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasLoadedFromCache() {
|
||||||
|
return mLoadedFromCache;
|
||||||
|
}
|
||||||
|
|
||||||
public SiteIdentity getSiteIdentity() {
|
public SiteIdentity getSiteIdentity() {
|
||||||
return mSiteIdentity;
|
return mSiteIdentity;
|
||||||
}
|
}
|
||||||
|
@ -536,6 +543,10 @@ public class Tab {
|
||||||
mHasOpenSearch = hasOpenSearch;
|
mHasOpenSearch = hasOpenSearch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLoadedFromCache(boolean loadedFromCache) {
|
||||||
|
mLoadedFromCache = loadedFromCache;
|
||||||
|
}
|
||||||
|
|
||||||
public void updateIdentityData(JSONObject identityData) {
|
public void updateIdentityData(JSONObject identityData) {
|
||||||
mSiteIdentity.update(identityData);
|
mSiteIdentity.update(identityData);
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,7 @@ public class Tabs implements GeckoEventListener {
|
||||||
"Tab:Added",
|
"Tab:Added",
|
||||||
"Tab:Close",
|
"Tab:Close",
|
||||||
"Tab:Select",
|
"Tab:Select",
|
||||||
|
"Tab:LoadedFromCache",
|
||||||
"Content:LocationChange",
|
"Content:LocationChange",
|
||||||
"Content:SecurityChange",
|
"Content:SecurityChange",
|
||||||
"Content:StateChange",
|
"Content:StateChange",
|
||||||
|
@ -505,8 +506,9 @@ public class Tabs implements GeckoEventListener {
|
||||||
tab.handleContentLoaded();
|
tab.handleContentLoaded();
|
||||||
notifyListeners(tab, Tabs.TabEvents.LOAD_ERROR);
|
notifyListeners(tab, Tabs.TabEvents.LOAD_ERROR);
|
||||||
} else if (event.equals("Content:PageShow")) {
|
} else if (event.equals("Content:PageShow")) {
|
||||||
notifyListeners(tab, TabEvents.PAGE_SHOW);
|
tab.setLoadedFromCache(message.getBoolean("fromCache"));
|
||||||
tab.updateUserRequested(message.getString("userRequested"));
|
tab.updateUserRequested(message.getString("userRequested"));
|
||||||
|
notifyListeners(tab, TabEvents.PAGE_SHOW);
|
||||||
} else if (event.equals("DOMContentLoaded")) {
|
} else if (event.equals("DOMContentLoaded")) {
|
||||||
tab.handleContentLoaded();
|
tab.handleContentLoaded();
|
||||||
String backgroundColor = message.getString("bgColor");
|
String backgroundColor = message.getString("bgColor");
|
||||||
|
|
|
@ -104,6 +104,9 @@ public interface TelemetryContract {
|
||||||
// Stop holding a resource (reader, bookmark, etc) for viewing later.
|
// Stop holding a resource (reader, bookmark, etc) for viewing later.
|
||||||
UNSAVE("unsave.1"),
|
UNSAVE("unsave.1"),
|
||||||
|
|
||||||
|
// When the user performs actions on the in-content network error page.
|
||||||
|
NETERROR("neterror.1"),
|
||||||
|
|
||||||
// VALUES BELOW THIS LINE ARE EXCLUSIVE TO TESTING.
|
// VALUES BELOW THIS LINE ARE EXCLUSIVE TO TESTING.
|
||||||
_TEST1("_test_event_1.1"),
|
_TEST1("_test_event_1.1"),
|
||||||
_TEST2("_test_event_2.1"),
|
_TEST2("_test_event_2.1"),
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.delegates;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.CallSuper;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.AboutPages;
|
||||||
|
import org.mozilla.gecko.BrowserApp;
|
||||||
|
import org.mozilla.gecko.R;
|
||||||
|
import org.mozilla.gecko.SnackbarHelper;
|
||||||
|
import org.mozilla.gecko.Tab;
|
||||||
|
import org.mozilla.gecko.Tabs;
|
||||||
|
import org.mozilla.gecko.Telemetry;
|
||||||
|
import org.mozilla.gecko.TelemetryContract;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays "Showing offline version" message when tabs are loaded from cache while offline.
|
||||||
|
*/
|
||||||
|
public class OfflineTabStatusDelegate extends TabsTrayVisibilityAwareDelegate implements Tabs.OnTabsChangedListener {
|
||||||
|
private WeakReference<Activity> activityReference;
|
||||||
|
private WeakHashMap<Tab, Void> tabsQueuedForOfflineSnackbar = new WeakHashMap<>();
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
@Override
|
||||||
|
public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
|
||||||
|
super.onCreate(browserApp, savedInstanceState);
|
||||||
|
activityReference = new WeakReference<Activity>(browserApp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume(BrowserApp browserApp) {
|
||||||
|
Tabs.registerOnTabsChangedListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause(BrowserApp browserApp) {
|
||||||
|
Tabs.unregisterOnTabsChangedListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTabChanged(final Tab tab, Tabs.TabEvents event, String data) {
|
||||||
|
if (tab == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore tabs loaded regularly.
|
||||||
|
if (!tab.hasLoadedFromCache()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore tabs displaying about pages
|
||||||
|
if (AboutPages.isAboutPage(tab.getURL())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
// Show offline notification if tab is visible, or queue it for display later.
|
||||||
|
case PAGE_SHOW:
|
||||||
|
if (!isTabsTrayVisible() && Tabs.getInstance().isSelectedTab(tab)) {
|
||||||
|
showLoadedOfflineSnackbar(activityReference.get());
|
||||||
|
} else {
|
||||||
|
tabsQueuedForOfflineSnackbar.put(tab, null);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// When tab is selected and offline notification was queued, display it if possible.
|
||||||
|
// SELECTED event might also fire when we're on a TabStrip, so check first.
|
||||||
|
case SELECTED:
|
||||||
|
if (isTabsTrayVisible()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (tabsQueuedForOfflineSnackbar.containsKey(tab)) {
|
||||||
|
showLoadedOfflineSnackbar(activityReference.get());
|
||||||
|
tabsQueuedForOfflineSnackbar.remove(tab);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the notification snackbar and logs a telemetry event.
|
||||||
|
*
|
||||||
|
* @param activity which will be used for displaying the snackbar.
|
||||||
|
*/
|
||||||
|
private static void showLoadedOfflineSnackbar(final Activity activity) {
|
||||||
|
if (activity == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Telemetry.sendUIEvent(TelemetryContract.Event.NETERROR, TelemetryContract.Method.TOAST, "usecache");
|
||||||
|
|
||||||
|
SnackbarHelper.showSnackbarWithActionAndColors(
|
||||||
|
activity,
|
||||||
|
activity.getResources().getString(R.string.tab_offline_version),
|
||||||
|
Snackbar.LENGTH_INDEFINITE,
|
||||||
|
null, null, null,
|
||||||
|
ContextCompat.getColor(activity, R.color.link_blue),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.delegates;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.CallSuper;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.BrowserApp;
|
||||||
|
import org.mozilla.gecko.tabs.TabsPanel;
|
||||||
|
|
||||||
|
public abstract class TabsTrayVisibilityAwareDelegate extends BrowserAppDelegate {
|
||||||
|
private boolean tabsTrayVisible;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CallSuper
|
||||||
|
public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
|
||||||
|
tabsTrayVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CallSuper
|
||||||
|
public void onTabsTrayShown(BrowserApp browserApp, TabsPanel tabsPanel) {
|
||||||
|
tabsTrayVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CallSuper
|
||||||
|
public void onTabsTrayHidden(BrowserApp browserApp, TabsPanel tabsPanel) {
|
||||||
|
tabsTrayVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isTabsTrayVisible() {
|
||||||
|
return tabsTrayVisible;
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,9 +24,10 @@ public class DownloadContent {
|
||||||
public @interface Type {}
|
public @interface Type {}
|
||||||
public static final String TYPE_ASSET_ARCHIVE = "asset-archive";
|
public static final String TYPE_ASSET_ARCHIVE = "asset-archive";
|
||||||
|
|
||||||
@StringDef({KIND_FONT})
|
@StringDef({KIND_FONT, KIND_HYPHENATION_DICTIONARY})
|
||||||
public @interface Kind {}
|
public @interface Kind {}
|
||||||
public static final String KIND_FONT = "font";
|
public static final String KIND_FONT = "font";
|
||||||
|
public static final String KIND_HYPHENATION_DICTIONARY = "hyphenation";
|
||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
private final String location;
|
private final String location;
|
||||||
|
@ -126,6 +127,19 @@ public class DownloadContent {
|
||||||
return KIND_FONT.equals(kind);
|
return KIND_FONT.equals(kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isHyphenationDictionary() {
|
||||||
|
return KIND_HYPHENATION_DICTIONARY.equals(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*Checks whether the content to be downloaded is a known content.
|
||||||
|
*Currently it checks whether the type is "Asset Archive" and is of kind
|
||||||
|
*"Font" or "Hyphenation Dictionary".
|
||||||
|
*/
|
||||||
|
public boolean isKnownContent() {
|
||||||
|
return ((isFont() || isHyphenationDictionary()) && isAssetArchive());
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAssetArchive() {
|
public boolean isAssetArchive() {
|
||||||
return TYPE_ASSET_ARCHIVE.equals(type);
|
return TYPE_ASSET_ARCHIVE.equals(type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.CallSuper;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.keepsafe.switchboard.SwitchBoard;
|
import com.keepsafe.switchboard.SwitchBoard;
|
||||||
|
@ -17,14 +18,13 @@ import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.mozilla.gecko.AboutPages;
|
import org.mozilla.gecko.AboutPages;
|
||||||
import org.mozilla.gecko.BrowserApp;
|
import org.mozilla.gecko.BrowserApp;
|
||||||
import org.mozilla.gecko.delegates.BrowserAppDelegate;
|
|
||||||
import org.mozilla.gecko.GeckoProfile;
|
import org.mozilla.gecko.GeckoProfile;
|
||||||
import org.mozilla.gecko.Tab;
|
import org.mozilla.gecko.Tab;
|
||||||
import org.mozilla.gecko.Tabs;
|
import org.mozilla.gecko.Tabs;
|
||||||
import org.mozilla.gecko.db.BrowserContract;
|
import org.mozilla.gecko.db.BrowserContract;
|
||||||
import org.mozilla.gecko.db.BrowserDB;
|
import org.mozilla.gecko.db.BrowserDB;
|
||||||
import org.mozilla.gecko.db.UrlAnnotations;
|
import org.mozilla.gecko.db.UrlAnnotations;
|
||||||
import org.mozilla.gecko.tabs.TabsPanel;
|
import org.mozilla.gecko.delegates.TabsTrayVisibilityAwareDelegate;
|
||||||
import org.mozilla.gecko.util.Experiments;
|
import org.mozilla.gecko.util.Experiments;
|
||||||
import org.mozilla.gecko.util.ThreadUtils;
|
import org.mozilla.gecko.util.ThreadUtils;
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ import ch.boye.httpclientandroidlib.util.TextUtils;
|
||||||
/**
|
/**
|
||||||
* Promote "Add to home screen" if user visits website often.
|
* Promote "Add to home screen" if user visits website often.
|
||||||
*/
|
*/
|
||||||
public class AddToHomeScreenPromotion extends BrowserAppDelegate implements Tabs.OnTabsChangedListener {
|
public class AddToHomeScreenPromotion extends TabsTrayVisibilityAwareDelegate implements Tabs.OnTabsChangedListener {
|
||||||
private static class URLHistory {
|
private static class URLHistory {
|
||||||
public final long visits;
|
public final long visits;
|
||||||
public final long lastVisit;
|
public final long lastVisit;
|
||||||
|
@ -54,15 +54,15 @@ public class AddToHomeScreenPromotion extends BrowserAppDelegate implements Tabs
|
||||||
|
|
||||||
private WeakReference<Activity> activityReference;
|
private WeakReference<Activity> activityReference;
|
||||||
private boolean isEnabled;
|
private boolean isEnabled;
|
||||||
private boolean isInForeground;
|
|
||||||
private int minimumVisits;
|
private int minimumVisits;
|
||||||
private int lastVisitMinimumAgeMs;
|
private int lastVisitMinimumAgeMs;
|
||||||
private int lastVisitMaximumAgeMs;
|
private int lastVisitMaximumAgeMs;
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
|
public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
|
||||||
|
super.onCreate(browserApp, savedInstanceState);
|
||||||
activityReference = new WeakReference<Activity>(browserApp);
|
activityReference = new WeakReference<Activity>(browserApp);
|
||||||
isInForeground = true;
|
|
||||||
|
|
||||||
initializeExperiment(browserApp);
|
initializeExperiment(browserApp);
|
||||||
}
|
}
|
||||||
|
@ -77,16 +77,6 @@ public class AddToHomeScreenPromotion extends BrowserAppDelegate implements Tabs
|
||||||
Tabs.unregisterOnTabsChangedListener(this);
|
Tabs.unregisterOnTabsChangedListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTabsTrayShown(BrowserApp browserApp, TabsPanel tabsPanel) {
|
|
||||||
isInForeground = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTabsTrayHidden(BrowserApp browserApp, TabsPanel tabsPanel) {
|
|
||||||
isInForeground = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeExperiment(Context context) {
|
private void initializeExperiment(Context context) {
|
||||||
if (!SwitchBoard.isInExperiment(context, Experiments.PROMOTE_ADD_TO_HOMESCREEN)) {
|
if (!SwitchBoard.isInExperiment(context, Experiments.PROMOTE_ADD_TO_HOMESCREEN)) {
|
||||||
Log.v(LOGTAG, "Experiment not enabled");
|
Log.v(LOGTAG, "Experiment not enabled");
|
||||||
|
@ -139,7 +129,7 @@ public class AddToHomeScreenPromotion extends BrowserAppDelegate implements Tabs
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isInForeground) {
|
if (isTabsTrayVisible()) {
|
||||||
// We only want to show this prompt if this tab is in the foreground and not on top
|
// We only want to show this prompt if this tab is in the foreground and not on top
|
||||||
// of the tabs tray.
|
// of the tabs tray.
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -47,6 +47,9 @@
|
||||||
|
|
||||||
<!ENTITY switch_to_tab "Switch to tab">
|
<!ENTITY switch_to_tab "Switch to tab">
|
||||||
|
|
||||||
|
<!-- Localization note: Shown in a snackbar when tab is loaded from cache while device was offline. -->
|
||||||
|
<!ENTITY tab_offline_version "Showing offline version">
|
||||||
|
|
||||||
<!ENTITY crash_reporter_title "&brandShortName; Crash Reporter">
|
<!ENTITY crash_reporter_title "&brandShortName; Crash Reporter">
|
||||||
<!ENTITY crash_message2 "&brandShortName; had a problem and crashed. Your tabs should be listed on the &brandShortName; Start page when you restart.">
|
<!ENTITY crash_message2 "&brandShortName; had a problem and crashed. Your tabs should be listed on the &brandShortName; Start page when you restart.">
|
||||||
<!ENTITY crash_send_report_message3 "Tell &vendorShortName; about this crash so they can fix it">
|
<!ENTITY crash_send_report_message3 "Tell &vendorShortName; about this crash so they can fix it">
|
||||||
|
|
|
@ -262,7 +262,9 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
|
||||||
'delegates/BookmarkStateChangeDelegate.java',
|
'delegates/BookmarkStateChangeDelegate.java',
|
||||||
'delegates/BrowserAppDelegate.java',
|
'delegates/BrowserAppDelegate.java',
|
||||||
'delegates/BrowserAppDelegateWithReference.java',
|
'delegates/BrowserAppDelegateWithReference.java',
|
||||||
|
'delegates/OfflineTabStatusDelegate.java',
|
||||||
'delegates/ScreenshotDelegate.java',
|
'delegates/ScreenshotDelegate.java',
|
||||||
|
'delegates/TabsTrayVisibilityAwareDelegate.java',
|
||||||
'DevToolsAuthHelper.java',
|
'DevToolsAuthHelper.java',
|
||||||
'distribution/Distribution.java',
|
'distribution/Distribution.java',
|
||||||
'distribution/DistributionStoreCallback.java',
|
'distribution/DistributionStoreCallback.java',
|
||||||
|
@ -479,6 +481,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
|
||||||
'NotificationClient.java',
|
'NotificationClient.java',
|
||||||
'NotificationHandler.java',
|
'NotificationHandler.java',
|
||||||
'NotificationHelper.java',
|
'NotificationHelper.java',
|
||||||
|
'NotificationReceiver.java',
|
||||||
'notifications/WhatsNewReceiver.java',
|
'notifications/WhatsNewReceiver.java',
|
||||||
'NotificationService.java',
|
'NotificationService.java',
|
||||||
'NSSBridge.java',
|
'NSSBridge.java',
|
||||||
|
|
|
@ -68,6 +68,8 @@
|
||||||
|
|
||||||
<string name="switch_to_tab">&switch_to_tab;</string>
|
<string name="switch_to_tab">&switch_to_tab;</string>
|
||||||
|
|
||||||
|
<string name="tab_offline_version">&tab_offline_version;</string>
|
||||||
|
|
||||||
<string name="crash_reporter_title">&crash_reporter_title;</string>
|
<string name="crash_reporter_title">&crash_reporter_title;</string>
|
||||||
<string name="crash_message2">&crash_message2;</string>
|
<string name="crash_message2">&crash_message2;</string>
|
||||||
<string name="crash_send_report_message3">&crash_send_report_message3;</string>
|
<string name="crash_send_report_message3">&crash_send_report_message3;</string>
|
||||||
|
|
|
@ -4297,7 +4297,8 @@ Tab.prototype = {
|
||||||
Messaging.sendRequest({
|
Messaging.sendRequest({
|
||||||
type: "Content:PageShow",
|
type: "Content:PageShow",
|
||||||
tabID: this.id,
|
tabID: this.id,
|
||||||
userRequested: this.userRequested
|
userRequested: this.userRequested,
|
||||||
|
fromCache: Tabs.useCache
|
||||||
});
|
});
|
||||||
|
|
||||||
this.isSearch = false;
|
this.isSearch = false;
|
||||||
|
@ -7377,25 +7378,6 @@ var Tabs = {
|
||||||
// Clear the domain cache whenever a page is loaded into any browser.
|
// Clear the domain cache whenever a page is loaded into any browser.
|
||||||
this._domains.clear();
|
this._domains.clear();
|
||||||
|
|
||||||
// Notify if we are loading a page from cache.
|
|
||||||
if (this._useCache) {
|
|
||||||
let targetDoc = aEvent.originalTarget;
|
|
||||||
let isTopLevel = (targetDoc.defaultView.parent === targetDoc.defaultView);
|
|
||||||
|
|
||||||
// Ignore any about: pages, especially about:neterror since it means we failed to find the page in cache.
|
|
||||||
let targetURI = targetDoc.documentURI;
|
|
||||||
if (isTopLevel && !targetURI.startsWith("about:")) {
|
|
||||||
UITelemetry.addEvent("neterror.1", "toast", null, "usecache");
|
|
||||||
Snackbars.show(
|
|
||||||
Strings.browser.GetStringFromName("networkOffline.message2"),
|
|
||||||
Snackbars.LENGTH_INDEFINITE,
|
|
||||||
{
|
|
||||||
// link_blue
|
|
||||||
backgroundColor: "#0096DD"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case "TabOpen":
|
case "TabOpen":
|
||||||
// Use opening a new tab as a trigger to expire the most stale tab.
|
// Use opening a new tab as a trigger to expire the most stale tab.
|
||||||
|
|
|
@ -434,9 +434,6 @@ openInApp.pageAction = Open in App
|
||||||
openInApp.ok = OK
|
openInApp.ok = OK
|
||||||
openInApp.cancel = Cancel
|
openInApp.cancel = Cancel
|
||||||
|
|
||||||
#Network Offline
|
|
||||||
networkOffline.message2 = Showing offline version
|
|
||||||
|
|
||||||
#Tab sharing
|
#Tab sharing
|
||||||
tabshare.title = "Choose a tab to stream"
|
tabshare.title = "Choose a tab to stream"
|
||||||
#Tabs in context menus
|
#Tabs in context menus
|
||||||
|
|
|
@ -492,6 +492,56 @@ public class TestDownloadAction {
|
||||||
verify(catalog, times(11)).rememberFailure(eq(content), anyInt());
|
verify(catalog, times(11)).rememberFailure(eq(content), anyInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: If the file to be downloaded is of kind - "hyphenation"
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * isHyphenationDictionary returns true for a download content with kind "hyphenation"
|
||||||
|
* * isHyphenationDictionary returns false for a download content with unknown/different kind like "Font"
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testIsHyphenationDictionary() throws Exception {
|
||||||
|
DownloadContent hyphenationContent = createHyphenationDictionary();
|
||||||
|
Assert.assertTrue(hyphenationContent.isHyphenationDictionary());
|
||||||
|
DownloadContent fontContent = createFont();
|
||||||
|
Assert.assertFalse(fontContent.isHyphenationDictionary());
|
||||||
|
DownloadContent unknownContent = createUnknownContent(1024L);
|
||||||
|
Assert.assertFalse(unknownContent.isHyphenationDictionary());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scenario: If the content to be downloaded is known
|
||||||
|
*
|
||||||
|
* Verify that:
|
||||||
|
* * isKnownContent returns true for a downloadable content with a known kind and type.
|
||||||
|
* * isKnownContent returns false for a downloadable content with unknown kind and type.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testIsKnownContent() throws Exception {
|
||||||
|
DownloadContent fontContent = createFontWithSize(1024L);
|
||||||
|
DownloadContent hyphenationContent = createHyphenationDictionaryWithSize(1024L);
|
||||||
|
DownloadContent unknownContent = createUnknownContent(1024L);
|
||||||
|
DownloadContent contentWithUnknownType = createContentWithoutType(1024L);
|
||||||
|
|
||||||
|
Assert.assertTrue(fontContent.isKnownContent());
|
||||||
|
Assert.assertTrue(hyphenationContent.isKnownContent());
|
||||||
|
Assert.assertFalse(unknownContent.isKnownContent());
|
||||||
|
Assert.assertFalse(contentWithUnknownType.isKnownContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
private DownloadContent createUnknownContent(long size) {
|
||||||
|
return new DownloadContentBuilder()
|
||||||
|
.setSize(size)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DownloadContent createContentWithoutType(long size) {
|
||||||
|
return new DownloadContentBuilder()
|
||||||
|
.setKind(DownloadContent.KIND_HYPHENATION_DICTIONARY)
|
||||||
|
.setSize(size)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private DownloadContent createFont() {
|
private DownloadContent createFont() {
|
||||||
return createFontWithSize(102400L);
|
return createFontWithSize(102400L);
|
||||||
}
|
}
|
||||||
|
@ -504,6 +554,18 @@ public class TestDownloadAction {
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DownloadContent createHyphenationDictionary() {
|
||||||
|
return createHyphenationDictionaryWithSize(102400L);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DownloadContent createHyphenationDictionaryWithSize(long size) {
|
||||||
|
return new DownloadContentBuilder()
|
||||||
|
.setKind(DownloadContent.KIND_HYPHENATION_DICTIONARY)
|
||||||
|
.setType(DownloadContent.TYPE_ASSET_ARCHIVE)
|
||||||
|
.setSize(size)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private DownloadContentCatalog mockCatalogWithScheduledDownloads(DownloadContent... content) {
|
private DownloadContentCatalog mockCatalogWithScheduledDownloads(DownloadContent... content) {
|
||||||
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
|
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
|
||||||
doReturn(Arrays.asList(content)).when(catalog).getScheduledDownloads();
|
doReturn(Arrays.asList(content)).when(catalog).getScheduledDownloads();
|
||||||
|
|
|
@ -485,6 +485,7 @@ class JsapiTestsCommand(MachCommandBase):
|
||||||
return jsapi_tests_result
|
return jsapi_tests_result
|
||||||
|
|
||||||
def autotry_parser():
|
def autotry_parser():
|
||||||
|
print("mach try is under development, please file bugs blocking 1149670.")
|
||||||
from autotry import arg_parser
|
from autotry import arg_parser
|
||||||
return arg_parser()
|
return arg_parser()
|
||||||
|
|
||||||
|
@ -614,8 +615,6 @@ class PushToTry(MachCommandBase):
|
||||||
from mozbuild.testing import TestResolver
|
from mozbuild.testing import TestResolver
|
||||||
from autotry import AutoTry
|
from autotry import AutoTry
|
||||||
|
|
||||||
print("mach try is under development, please file bugs blocking 1149670.")
|
|
||||||
|
|
||||||
resolver_func = lambda: self._spawn(TestResolver)
|
resolver_func = lambda: self._spawn(TestResolver)
|
||||||
at = AutoTry(self.topsrcdir, resolver_func, self._mach_context)
|
at = AutoTry(self.topsrcdir, resolver_func, self._mach_context)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=549539
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=549539
|
||||||
// https://bug549539.bugzilla.mozilla.org/attachment.cgi?id=429661
|
// https://bug549539.bugzilla.mozilla.org/attachment.cgi?id=429661
|
||||||
// https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_1.9.3
|
// https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_1.9.3
|
||||||
// http://mxr.mozilla.org/mozilla-central/source/toolkit/components/console/hudservice/HUDService.jsm#3240
|
|
||||||
// https://developer.mozilla.org/en/how_to_build_an_xpcom_component_in_javascript
|
// https://developer.mozilla.org/en/how_to_build_an_xpcom_component_in_javascript
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["SpecialPowersObserver", "SpecialPowersObserverFactory"];
|
var EXPORTED_SYMBOLS = ["SpecialPowersObserver", "SpecialPowersObserverFactory"];
|
||||||
|
|
|
@ -9,16 +9,29 @@ import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import which
|
import which
|
||||||
|
import difflib
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
|
||||||
def validate_choices(values, choices):
|
def validate_choices(values, choices):
|
||||||
|
valid = True
|
||||||
for value in values:
|
for value in values:
|
||||||
if value not in choices:
|
corrections = difflib.get_close_matches(value, choices)
|
||||||
print 'Invalid choice {v!r}. Allowed choices: {c!r}'.format(v=value, c=choices)
|
if len(corrections) == 0:
|
||||||
sys.exit(1)
|
print 'Potentially invalid choice {v!r}. Neither the requested value nor a similar value was found.'.format(v=value)
|
||||||
|
print 'List of possible values: {c!r}'.format(c=choices)
|
||||||
|
result = raw_input('Are you sure you want to continue? [y/N] ').strip()
|
||||||
|
if not 'y' in result.lower():
|
||||||
|
valid = False
|
||||||
|
elif corrections[0] == value:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
valid = False
|
||||||
|
print 'Invalid choice {v!r}. Some suggestions (limit three): {c!r}?'.format(v=value, c=corrections)
|
||||||
|
if not valid:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
class ValidatePlatforms(argparse.Action):
|
class ValidatePlatforms(argparse.Action):
|
||||||
def __call__(self, parser, args, values, option_string=None):
|
def __call__(self, parser, args, values, option_string=None):
|
||||||
|
|
|
@ -1,75 +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/. */
|
|
||||||
|
|
||||||
|
|
||||||
.console-box {
|
|
||||||
-moz-binding: url("chrome://global/content/consoleBindings.xml#console-box");
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-rows {
|
|
||||||
-moz-user-focus: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row[type="error"],
|
|
||||||
.console-row[type="warning"],
|
|
||||||
.console-row[type="message"][typetext] {
|
|
||||||
-moz-binding: url("chrome://global/content/consoleBindings.xml#error");
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row[type="message"] {
|
|
||||||
-moz-binding: url("chrome://global/content/consoleBindings.xml#message");
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-msg-text,
|
|
||||||
.console-error-msg {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-error-source {
|
|
||||||
-moz-binding: url("chrome://global/content/consoleBindings.xml#console-error-source");
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-dots {
|
|
||||||
width: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* :::::::::: hiding and showing of rows for each mode :::::::::: */
|
|
||||||
|
|
||||||
.console-box[mode="Warnings"] > .console-box-internal > .console-rows
|
|
||||||
> .console-row[type="error"],
|
|
||||||
.console-box[mode="Messages"] > .console-box-internal > .console-rows
|
|
||||||
> .console-row[type="error"]
|
|
||||||
{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-box[mode="Errors"] > .console-box-internal > .console-rows
|
|
||||||
> .console-row[type="warning"],
|
|
||||||
.console-box[mode="Messages"] > .console-box-internal > .console-rows
|
|
||||||
> .console-row[type="warning"]
|
|
||||||
{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-box[mode="Errors"] > .console-box-internal > .console-rows
|
|
||||||
> .console-row[type="message"],
|
|
||||||
.console-box[mode="Warnings"] > .console-box-internal > .console-rows
|
|
||||||
> .console-row[type="message"]
|
|
||||||
{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filtered-by-string {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If line number is 0, hide the line number section */
|
|
||||||
.lineNumberRow[line="0"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#TextboxEval {
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
// -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
|
||||||
|
|
||||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
|
||||||
|
|
||||||
var gConsole, gConsoleBundle, gTextBoxEval, gEvaluator, gCodeToEvaluate;
|
|
||||||
var gFilter;
|
|
||||||
|
|
||||||
/* :::::::: Console Initialization ::::::::::::::: */
|
|
||||||
|
|
||||||
window.onload = function()
|
|
||||||
{
|
|
||||||
gConsole = document.getElementById("ConsoleBox");
|
|
||||||
gConsoleBundle = document.getElementById("ConsoleBundle");
|
|
||||||
gTextBoxEval = document.getElementById("TextboxEval");
|
|
||||||
gEvaluator = document.getElementById("Evaluator");
|
|
||||||
gFilter = document.getElementById("Filter");
|
|
||||||
|
|
||||||
updateSortCommand(gConsole.sortOrder);
|
|
||||||
updateModeCommand(gConsole.mode);
|
|
||||||
|
|
||||||
gEvaluator.addEventListener("load", loadOrDisplayResult, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* :::::::: Console UI Functions ::::::::::::::: */
|
|
||||||
|
|
||||||
function changeFilter()
|
|
||||||
{
|
|
||||||
gConsole.filter = gFilter.value;
|
|
||||||
|
|
||||||
document.persist("ConsoleBox", "filter");
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeMode(aMode)
|
|
||||||
{
|
|
||||||
switch (aMode) {
|
|
||||||
case "Errors":
|
|
||||||
case "Warnings":
|
|
||||||
case "Messages":
|
|
||||||
gConsole.mode = aMode;
|
|
||||||
break;
|
|
||||||
case "All":
|
|
||||||
gConsole.mode = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.persist("ConsoleBox", "mode");
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearConsole()
|
|
||||||
{
|
|
||||||
gConsole.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeSortOrder(aOrder)
|
|
||||||
{
|
|
||||||
updateSortCommand(gConsole.sortOrder = aOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSortCommand(aOrder)
|
|
||||||
{
|
|
||||||
var orderString = aOrder == 'reverse' ? "Descend" : "Ascend";
|
|
||||||
var bc = document.getElementById("Console:sort"+orderString);
|
|
||||||
bc.setAttribute("checked", true);
|
|
||||||
|
|
||||||
orderString = aOrder == 'reverse' ? "Ascend" : "Descend";
|
|
||||||
bc = document.getElementById("Console:sort"+orderString);
|
|
||||||
bc.setAttribute("checked", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateModeCommand(aMode)
|
|
||||||
{
|
|
||||||
/* aMode can end up invalid if it set by an extension that replaces */
|
|
||||||
/* mode and then it is uninstalled or disabled */
|
|
||||||
var bc = document.getElementById("Console:mode" + aMode) ||
|
|
||||||
document.getElementById("Console:modeAll");
|
|
||||||
bc.setAttribute("checked", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEvalKeyPress(aEvent)
|
|
||||||
{
|
|
||||||
if (aEvent.keyCode == 13)
|
|
||||||
evaluateTypein();
|
|
||||||
}
|
|
||||||
|
|
||||||
function evaluateTypein()
|
|
||||||
{
|
|
||||||
gCodeToEvaluate = gTextBoxEval.value;
|
|
||||||
// reset the iframe first; the code will be evaluated in loadOrDisplayResult
|
|
||||||
// below, once about:blank has completed loading (see bug 385092)
|
|
||||||
gEvaluator.contentWindow.location = "about:blank";
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadOrDisplayResult()
|
|
||||||
{
|
|
||||||
if (gCodeToEvaluate) {
|
|
||||||
gEvaluator.contentWindow.location = "javascript: " +
|
|
||||||
gCodeToEvaluate.replace(/%/g, "%25");
|
|
||||||
gCodeToEvaluate = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var resultRange = gEvaluator.contentDocument.createRange();
|
|
||||||
resultRange.selectNode(gEvaluator.contentDocument.documentElement);
|
|
||||||
var result = resultRange.toString();
|
|
||||||
if (result)
|
|
||||||
Services.console.logStringMessage(result);
|
|
||||||
// or could use appendMessage which doesn't persist
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
<?xml version="1.0"?> <!-- -*- tab-width: 4; indent-tabs-mode: nil -*- -->
|
|
||||||
|
|
||||||
<!-- 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/. -->
|
|
||||||
|
|
||||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
|
||||||
<?xml-stylesheet href="chrome://global/skin/console/console.css" type="text/css"?>
|
|
||||||
<?xml-stylesheet href="chrome://global/content/console.css" type="text/css"?>
|
|
||||||
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
|
|
||||||
|
|
||||||
<!DOCTYPE window [
|
|
||||||
<!ENTITY % console SYSTEM "chrome://global/locale/console.dtd"> %console;
|
|
||||||
]>
|
|
||||||
|
|
||||||
<window id="JSConsoleWindow"
|
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
||||||
title="&errorConsole.title;"
|
|
||||||
windowtype="global:console"
|
|
||||||
width="640" height="480"
|
|
||||||
screenX="10" screenY="10"
|
|
||||||
persist="screenX screenY width height sizemode"
|
|
||||||
onclose="return closeWindow(false);">
|
|
||||||
|
|
||||||
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
|
|
||||||
<script type="application/javascript" src="chrome://global/content/console.js"/>
|
|
||||||
<script type="application/javascript" src="chrome://global/content/viewSourceUtils.js"/>
|
|
||||||
|
|
||||||
<stringbundle id="ConsoleBundle" src="chrome://global/locale/console.properties"/>
|
|
||||||
|
|
||||||
<commandset id="editMenuCommands"/>
|
|
||||||
|
|
||||||
<commandset id="consoleCommands">
|
|
||||||
<command id="cmd_close" oncommand="closeWindow(true)"/>
|
|
||||||
</commandset>
|
|
||||||
|
|
||||||
<keyset id="consoleKeys">
|
|
||||||
<key id="key_close" key="&closeCmd.commandkey;" modifiers="accel"
|
|
||||||
command="cmd_close"/>
|
|
||||||
<key id="key_close2" keycode="VK_ESCAPE" command="cmd_close"/>
|
|
||||||
<key id="key_focus1" key="&focus1.commandkey;" modifiers="accel"
|
|
||||||
oncommand="gTextBoxEval.focus()"/>
|
|
||||||
<key id="key_focus2" key="&focus2.commandkey;" modifiers="alt"
|
|
||||||
oncommand="gTextBoxEval.focus()"/>
|
|
||||||
</keyset>
|
|
||||||
|
|
||||||
<popupset id="ContextMenus">
|
|
||||||
<menupopup id="ConsoleContext">
|
|
||||||
<menuitem type="radio" id="Console:sortAscend"
|
|
||||||
label="&sortFirst.label;" accesskey="&sortFirst.accesskey;"
|
|
||||||
oncommand="changeSortOrder('forward');"/>
|
|
||||||
<menuitem type="radio" id="Console:sortDescend"
|
|
||||||
label="&sortLast.label;" accesskey="&sortLast.accesskey;"
|
|
||||||
oncommand="changeSortOrder('reverse');"/>
|
|
||||||
<menuseparator/>
|
|
||||||
<menuitem id="menu_copy_cm" command="cmd_copy"
|
|
||||||
label="©Cmd.label;" accesskey="©Cmd.accesskey;"/>
|
|
||||||
</menupopup>
|
|
||||||
</popupset>
|
|
||||||
|
|
||||||
<toolbox id="console-toolbox">
|
|
||||||
<toolbar class="chromeclass-toolbar" id="ToolbarMode">
|
|
||||||
<hbox id="viewGroup">
|
|
||||||
<toolbarbutton type="radio" group="mode" id="Console:modeAll"
|
|
||||||
label="&all.label;" accesskey="&all.accesskey;"
|
|
||||||
oncommand="changeMode('All');"/>
|
|
||||||
<toolbarbutton type="radio" group="mode" id="Console:modeErrors"
|
|
||||||
label="&errors.label;" accesskey="&errors.accesskey;"
|
|
||||||
oncommand="changeMode('Errors');"/>
|
|
||||||
<toolbarbutton type="radio" group="mode" id="Console:modeWarnings"
|
|
||||||
label="&warnings.label;" accesskey="&warnings.accesskey;"
|
|
||||||
oncommand="changeMode('Warnings');"/>
|
|
||||||
<toolbarbutton type="radio" group="mode" id="Console:modeMessages"
|
|
||||||
label="&messages.label;" accesskey="&messages.accesskey;"
|
|
||||||
oncommand="changeMode('Messages');"/>
|
|
||||||
</hbox>
|
|
||||||
<toolbarseparator/>
|
|
||||||
<toolbarbutton id="Console:clear" oncommand="clearConsole();"
|
|
||||||
label="&clear.label;" accesskey="&clear.accesskey;"/>
|
|
||||||
</toolbar>
|
|
||||||
|
|
||||||
<toolbar class="chromeclass-toolbar" id="ToolbarEval" align="center" nowindowdrag="true">
|
|
||||||
<label value="&codeEval.label;" accesskey="&codeEval.accesskey;" control="TextboxEval"/>
|
|
||||||
<textbox id="TextboxEval" class="toolbar" value="" onkeypress="onEvalKeyPress(event)" flex="1"/>
|
|
||||||
<toolbarbutton id="ButtonEval" label="&evaluate.label;"
|
|
||||||
accesskey="&evaluate.accesskey;" oncommand="evaluateTypein()"/>
|
|
||||||
</toolbar>
|
|
||||||
</toolbox>
|
|
||||||
|
|
||||||
<vbox id="ConsoleBox" class="console-box" flex="1" context="ConsoleContext" persist="sortOrder"/>
|
|
||||||
|
|
||||||
<iframe name="Evaluator" id="Evaluator" collapsed="true"/>
|
|
||||||
|
|
||||||
<statusbar>
|
|
||||||
<statusbarpanel flex="1" pack="start">
|
|
||||||
<label value="&filter2.label;" control="Filter"/>
|
|
||||||
<textbox accesskey="&filter2.accesskey;" type="search"
|
|
||||||
id="Filter" oncommand="changeFilter();"/>
|
|
||||||
</statusbarpanel>
|
|
||||||
</statusbar>
|
|
||||||
</window>
|
|
|
@ -1,547 +0,0 @@
|
||||||
<?xml version="1.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/. -->
|
|
||||||
|
|
||||||
|
|
||||||
<!DOCTYPE bindings SYSTEM "chrome://global/locale/console.dtd">
|
|
||||||
|
|
||||||
<bindings id="consoleBindings"
|
|
||||||
xmlns="http://www.mozilla.org/xbl"
|
|
||||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
||||||
xmlns:xbl="http://www.mozilla.org/xbl">
|
|
||||||
|
|
||||||
<binding id="console-box" extends="xul:box">
|
|
||||||
<content>
|
|
||||||
<xul:stringbundle src="chrome://global/locale/console.properties" role="string-bundle"/>
|
|
||||||
<xul:vbox class="console-box-internal">
|
|
||||||
<xul:vbox class="console-rows" role="console-rows" xbl:inherits="dir=sortOrder"/>
|
|
||||||
</xul:vbox>
|
|
||||||
</content>
|
|
||||||
|
|
||||||
<implementation>
|
|
||||||
<field name="limit" readonly="true">
|
|
||||||
250
|
|
||||||
</field>
|
|
||||||
|
|
||||||
<field name="fieldMaxLength" readonly="true">
|
|
||||||
<!-- Limit displayed string lengths to avoid performance issues. (Bug 796179 and 831020) -->
|
|
||||||
200
|
|
||||||
</field>
|
|
||||||
|
|
||||||
<field name="showChromeErrors" readonly="true">
|
|
||||||
Services.prefs.getBoolPref("javascript.options.showInConsole");
|
|
||||||
</field>
|
|
||||||
|
|
||||||
<property name="count" readonly="true">
|
|
||||||
<getter>return this.mCount</getter>
|
|
||||||
</property>
|
|
||||||
|
|
||||||
<property name="mode">
|
|
||||||
<getter>return this.mMode;</getter>
|
|
||||||
<setter><![CDATA[
|
|
||||||
if (this.mode != val) {
|
|
||||||
this.mMode = val || "All";
|
|
||||||
this.setAttribute("mode", this.mMode);
|
|
||||||
this.selectedItem = null;
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
]]></setter>
|
|
||||||
</property>
|
|
||||||
|
|
||||||
<property name="filter">
|
|
||||||
<getter>return this.mFilter;</getter>
|
|
||||||
<setter><![CDATA[
|
|
||||||
val = val.toLowerCase();
|
|
||||||
if (this.mFilter != val) {
|
|
||||||
this.mFilter = val;
|
|
||||||
for (let aRow of this.mConsoleRowBox.children) {
|
|
||||||
this.filterElement(aRow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
]]></setter>
|
|
||||||
</property>
|
|
||||||
|
|
||||||
<property name="sortOrder">
|
|
||||||
<getter>return this.getAttribute("sortOrder");</getter>
|
|
||||||
<setter>this.setAttribute("sortOrder", val); return val;</setter>
|
|
||||||
</property>
|
|
||||||
<field name="mSelectedItem">null</field>
|
|
||||||
<property name="selectedItem">
|
|
||||||
<getter>return this.mSelectedItem</getter>
|
|
||||||
<setter><![CDATA[
|
|
||||||
if (this.mSelectedItem)
|
|
||||||
this.mSelectedItem.removeAttribute("selected");
|
|
||||||
|
|
||||||
this.mSelectedItem = val;
|
|
||||||
if (val)
|
|
||||||
val.setAttribute("selected", "true");
|
|
||||||
|
|
||||||
// Update edit commands
|
|
||||||
window.updateCommands("focus");
|
|
||||||
return val;
|
|
||||||
]]></setter>
|
|
||||||
</property>
|
|
||||||
|
|
||||||
<method name="init">
|
|
||||||
<body><![CDATA[
|
|
||||||
this.mCount = 0;
|
|
||||||
|
|
||||||
this.mConsoleListener = {
|
|
||||||
console: this,
|
|
||||||
observe : function(aObject) {
|
|
||||||
// The message can arrive a little bit after the xbl binding has been
|
|
||||||
// unbind. So node.appendItem will not be available anymore.
|
|
||||||
if ('appendItem' in this.console)
|
|
||||||
this.console.appendItem(aObject);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.mConsoleRowBox = document.getAnonymousElementByAttribute(this, "role", "console-rows");
|
|
||||||
this.mStrBundle = document.getAnonymousElementByAttribute(this, "role", "string-bundle");
|
|
||||||
|
|
||||||
try {
|
|
||||||
Services.console.registerListener(this.mConsoleListener);
|
|
||||||
} catch (ex) {
|
|
||||||
appendItem(
|
|
||||||
"Unable to display errors - couldn't get Console Service component. " +
|
|
||||||
"(Missing @mozilla.org/consoleservice;1)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mMode = this.getAttribute("mode") || "All";
|
|
||||||
this.mFilter = "";
|
|
||||||
|
|
||||||
this.appendInitialItems();
|
|
||||||
window.controllers.insertControllerAt(0, this._controller);
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="destroy">
|
|
||||||
<body><![CDATA[
|
|
||||||
Services.console.unregisterListener(this.mConsoleListener);
|
|
||||||
window.controllers.removeController(this._controller);
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="appendInitialItems">
|
|
||||||
<body><![CDATA[
|
|
||||||
var messages = Services.console.getMessageArray();
|
|
||||||
|
|
||||||
// In case getMessageArray returns 0-length array as null
|
|
||||||
if (!messages)
|
|
||||||
messages = [];
|
|
||||||
|
|
||||||
var limit = messages.length - this.limit;
|
|
||||||
if (limit < 0) limit = 0;
|
|
||||||
|
|
||||||
// Checks if console ever been cleared
|
|
||||||
for (var i = messages.length - 1; i >= limit; --i)
|
|
||||||
if (!messages[i].message)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Populate with messages after latest "clear"
|
|
||||||
while (++i < messages.length)
|
|
||||||
this.appendItem(messages[i]);
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="appendItem">
|
|
||||||
<parameter name="aObject"/>
|
|
||||||
<body><![CDATA[
|
|
||||||
try {
|
|
||||||
// Try to QI it to a script error to get more info
|
|
||||||
var scriptError = aObject.QueryInterface(Components.interfaces.nsIScriptError);
|
|
||||||
|
|
||||||
// filter chrome urls
|
|
||||||
if (!this.showChromeErrors && scriptError.sourceName.substr(0, 9) == "chrome://")
|
|
||||||
return;
|
|
||||||
|
|
||||||
// filter private windows
|
|
||||||
if (scriptError.isFromPrivateWindow)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.appendError(scriptError);
|
|
||||||
} catch (ex) {
|
|
||||||
try {
|
|
||||||
// Try to QI it to a console message
|
|
||||||
var msg = aObject.QueryInterface(Components.interfaces.nsIConsoleMessage);
|
|
||||||
if (msg.message)
|
|
||||||
this.appendMessage(msg.message);
|
|
||||||
else // observed a null/"clear" message
|
|
||||||
this.clearConsole();
|
|
||||||
} catch (ex2) {
|
|
||||||
// Give up and append the object itself as a string
|
|
||||||
this.appendMessage(aObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="_truncateIfNecessary">
|
|
||||||
<parameter name="aString"/>
|
|
||||||
<parameter name="aMiddleCharacter"/>
|
|
||||||
<body><![CDATA[
|
|
||||||
if (!aString || aString.length <= this.fieldMaxLength)
|
|
||||||
return {string: aString, column: aMiddleCharacter};
|
|
||||||
let halfLimit = this.fieldMaxLength / 2;
|
|
||||||
if (!aMiddleCharacter || aMiddleCharacter < 0 || aMiddleCharacter > aString.length)
|
|
||||||
aMiddleCharacter = halfLimit;
|
|
||||||
|
|
||||||
let startPosition = 0;
|
|
||||||
let endPosition = aString.length;
|
|
||||||
if (aMiddleCharacter - halfLimit >= 0)
|
|
||||||
startPosition = aMiddleCharacter - halfLimit;
|
|
||||||
if (aMiddleCharacter + halfLimit <= aString.length)
|
|
||||||
endPosition = aMiddleCharacter + halfLimit;
|
|
||||||
if (endPosition - startPosition < this.fieldMaxLength)
|
|
||||||
endPosition += this.fieldMaxLength - (endPosition - startPosition);
|
|
||||||
let truncatedString = aString.substring(startPosition, endPosition);
|
|
||||||
let Ci = Components.interfaces;
|
|
||||||
let ellipsis = Services.prefs.getComplexValue("intl.ellipsis",
|
|
||||||
Ci.nsIPrefLocalizedString).data;
|
|
||||||
if (startPosition > 0) {
|
|
||||||
truncatedString = ellipsis + truncatedString;
|
|
||||||
aMiddleCharacter += ellipsis.length;
|
|
||||||
}
|
|
||||||
if (endPosition < aString.length)
|
|
||||||
truncatedString = truncatedString + ellipsis;
|
|
||||||
|
|
||||||
return {
|
|
||||||
string: truncatedString,
|
|
||||||
column: aMiddleCharacter - startPosition
|
|
||||||
};
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="appendError">
|
|
||||||
<parameter name="aObject"/>
|
|
||||||
<body><![CDATA[
|
|
||||||
var row = this.createConsoleRow();
|
|
||||||
var nsIScriptError = Components.interfaces.nsIScriptError;
|
|
||||||
|
|
||||||
// nsIConsoleMessage constants: debug, info, warn, error
|
|
||||||
var typetext = ["typeMessage", "typeMessage", "typeWarning", "typeError"][aObject.logLevel];
|
|
||||||
var type = ["message", "message", "warning", "error"][aObject.logLevel];
|
|
||||||
|
|
||||||
row.setAttribute("typetext", this.mStrBundle.getString(typetext));
|
|
||||||
row.setAttribute("type", type);
|
|
||||||
row.setAttribute("msg", aObject.errorMessage);
|
|
||||||
row.setAttribute("category", aObject.category);
|
|
||||||
row.setAttribute("time", this.properFormatTime(aObject.timeStamp));
|
|
||||||
if (aObject.lineNumber || aObject.sourceName) {
|
|
||||||
row.setAttribute("href", this._truncateIfNecessary(aObject.sourceName).string);
|
|
||||||
row.mSourceName = aObject.sourceName;
|
|
||||||
row.setAttribute("line", aObject.lineNumber);
|
|
||||||
} else {
|
|
||||||
row.setAttribute("hideSource", "true");
|
|
||||||
}
|
|
||||||
if (aObject.sourceLine) {
|
|
||||||
let sourceLine = aObject.sourceLine.replace(/\s/g, " ");
|
|
||||||
let truncatedLineObj = this._truncateIfNecessary(sourceLine, aObject.columnNumber);
|
|
||||||
row.setAttribute("code", truncatedLineObj.string);
|
|
||||||
row.mSourceLine = sourceLine;
|
|
||||||
if (aObject.columnNumber) {
|
|
||||||
row.setAttribute("col", aObject.columnNumber);
|
|
||||||
row.setAttribute("errorDots", this.repeatChar(" ", truncatedLineObj.column));
|
|
||||||
row.setAttribute("errorCaret", " ");
|
|
||||||
} else {
|
|
||||||
row.setAttribute("hideCaret", "true");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row.setAttribute("hideCode", "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.appendConsoleRow(row);
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="appendMessage">
|
|
||||||
<parameter name="aMessage"/>
|
|
||||||
<parameter name="aType"/>
|
|
||||||
<body><![CDATA[
|
|
||||||
var row = this.createConsoleRow();
|
|
||||||
row.setAttribute("type", aType || "message");
|
|
||||||
row.setAttribute("msg", aMessage);
|
|
||||||
this.appendConsoleRow(row);
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="clear">
|
|
||||||
<body><![CDATA[
|
|
||||||
// add a "clear" message (mainly for other listeners)
|
|
||||||
Services.console.logStringMessage(null);
|
|
||||||
Services.console.reset();
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="properFormatTime">
|
|
||||||
<parameter name="aTime"/>
|
|
||||||
<body><![CDATA[
|
|
||||||
const dateServ = Components.classes["@mozilla.org/intl/scriptabledateformat;1"]
|
|
||||||
.getService(Components.interfaces.nsIScriptableDateFormat);
|
|
||||||
let errorTime = new Date(aTime);
|
|
||||||
return dateServ.FormatDateTime("", dateServ.dateFormatShort, dateServ.timeFormatSeconds,
|
|
||||||
errorTime.getFullYear(), errorTime.getMonth() + 1, errorTime.getDate(),
|
|
||||||
errorTime.getHours(), errorTime.getMinutes(), errorTime.getSeconds());
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="copySelectedItem">
|
|
||||||
<body><![CDATA[
|
|
||||||
if (this.mSelectedItem) try {
|
|
||||||
const clipURI = "@mozilla.org/widget/clipboardhelper;1";
|
|
||||||
const clipI = Components.interfaces.nsIClipboardHelper;
|
|
||||||
var clipboard = Components.classes[clipURI].getService(clipI);
|
|
||||||
|
|
||||||
clipboard.copyString(this.mSelectedItem.toString());
|
|
||||||
} catch (ex) {
|
|
||||||
// Unable to copy anything, die quietly
|
|
||||||
}
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="createConsoleRow">
|
|
||||||
<body><![CDATA[
|
|
||||||
var row = document.createElement("box");
|
|
||||||
row.setAttribute("class", "console-row");
|
|
||||||
row._IsConsoleRow = true;
|
|
||||||
row._ConsoleBox = this;
|
|
||||||
return row;
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="appendConsoleRow">
|
|
||||||
<parameter name="aRow"/>
|
|
||||||
<body><![CDATA[
|
|
||||||
this.filterElement(aRow);
|
|
||||||
this.mConsoleRowBox.appendChild(aRow);
|
|
||||||
if (++this.mCount > this.limit) this.deleteFirst();
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="deleteFirst">
|
|
||||||
<body><![CDATA[
|
|
||||||
var node = this.mConsoleRowBox.firstChild;
|
|
||||||
this.mConsoleRowBox.removeChild(node);
|
|
||||||
--this.mCount;
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="clearConsole">
|
|
||||||
<body><![CDATA[
|
|
||||||
if (this.mCount == 0) // already clear
|
|
||||||
return;
|
|
||||||
this.mCount = 0;
|
|
||||||
|
|
||||||
var newRows = this.mConsoleRowBox.cloneNode(false);
|
|
||||||
this.mConsoleRowBox.parentNode.replaceChild(newRows, this.mConsoleRowBox);
|
|
||||||
this.mConsoleRowBox = newRows;
|
|
||||||
this.selectedItem = null;
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="filterElement">
|
|
||||||
<parameter name="aRow" />
|
|
||||||
<body><![CDATA[
|
|
||||||
let anyMatch = ["msg", "line", "code"].some(function (key) {
|
|
||||||
return (aRow.hasAttribute(key) &&
|
|
||||||
this.stringMatchesFilters(aRow.getAttribute(key), this.mFilter));
|
|
||||||
}, this) || (aRow.mSourceName &&
|
|
||||||
this.stringMatchesFilters(aRow.mSourceName, this.mFilter));
|
|
||||||
|
|
||||||
if (anyMatch) {
|
|
||||||
aRow.classList.remove("filtered-by-string")
|
|
||||||
} else {
|
|
||||||
aRow.classList.add("filtered-by-string")
|
|
||||||
}
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<!-- UTILITY FUNCTIONS -->
|
|
||||||
|
|
||||||
<method name="repeatChar">
|
|
||||||
<parameter name="aChar"/>
|
|
||||||
<parameter name="aCol"/>
|
|
||||||
<body><![CDATA[
|
|
||||||
if (--aCol <= 0)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
for (var i = 2; i < aCol; i += i)
|
|
||||||
aChar += aChar;
|
|
||||||
|
|
||||||
return aChar + aChar.slice(0, aCol - aChar.length);
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="stringMatchesFilters">
|
|
||||||
<parameter name="aString"/>
|
|
||||||
<parameter name="aFilter"/>
|
|
||||||
<body><![CDATA[
|
|
||||||
if (!aString || !aFilter) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let searchStr = aString.toLowerCase();
|
|
||||||
let filterStrings = aFilter.split(/\s+/);
|
|
||||||
return !filterStrings.some(function (f) {
|
|
||||||
return searchStr.indexOf(f) == -1;
|
|
||||||
});
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<constructor>this.init();</constructor>
|
|
||||||
<destructor>this.destroy();</destructor>
|
|
||||||
|
|
||||||
<!-- Command controller for the copy command -->
|
|
||||||
<field name="_controller"><![CDATA[({
|
|
||||||
_outer: this,
|
|
||||||
|
|
||||||
QueryInterface: function(aIID) {
|
|
||||||
if (aIID.equals(Components.interfaces.nsIController) ||
|
|
||||||
aIID.equals(Components.interfaces.nsISupports))
|
|
||||||
return this;
|
|
||||||
throw Components.results.NS_NOINTERFACE;
|
|
||||||
},
|
|
||||||
|
|
||||||
supportsCommand: function(aCommand) {
|
|
||||||
return aCommand == "cmd_copy";
|
|
||||||
},
|
|
||||||
|
|
||||||
isCommandEnabled: function(aCommand) {
|
|
||||||
return aCommand == "cmd_copy" && this._outer.selectedItem;
|
|
||||||
},
|
|
||||||
|
|
||||||
doCommand: function(aCommand) {
|
|
||||||
if (aCommand == "cmd_copy")
|
|
||||||
this._outer.copySelectedItem();
|
|
||||||
},
|
|
||||||
|
|
||||||
onEvent: function() { }
|
|
||||||
});]]></field>
|
|
||||||
</implementation>
|
|
||||||
|
|
||||||
<handlers>
|
|
||||||
<handler event="mousedown"><![CDATA[
|
|
||||||
if (event.button == 0 || event.button == 2) {
|
|
||||||
var target = event.originalTarget;
|
|
||||||
|
|
||||||
while (target && !("_IsConsoleRow" in target))
|
|
||||||
target = target.parentNode;
|
|
||||||
|
|
||||||
if (target)
|
|
||||||
this.selectedItem = target;
|
|
||||||
}
|
|
||||||
]]></handler>
|
|
||||||
</handlers>
|
|
||||||
</binding>
|
|
||||||
|
|
||||||
<binding id="error" extends="xul:box">
|
|
||||||
<content>
|
|
||||||
<xul:box class="console-row-internal-box" flex="1">
|
|
||||||
<xul:box class="console-row-icon" align="center" xbl:inherits="selected">
|
|
||||||
<xul:image class="console-icon" xbl:inherits="src,type"/>
|
|
||||||
</xul:box>
|
|
||||||
<xul:vbox class="console-row-content" xbl:inherits="selected" flex="1">
|
|
||||||
<xul:box class="console-row-msg" align="start">
|
|
||||||
<xul:label class="label" xbl:inherits="value=typetext"/>
|
|
||||||
<xul:description class="console-error-msg" xbl:inherits="xbl:text=msg" flex="1"/>
|
|
||||||
<xul:label class="label console-time" xbl:inherits="value=time"/>
|
|
||||||
</xul:box>
|
|
||||||
<xul:box class="console-row-file" xbl:inherits="hidden=hideSource">
|
|
||||||
<xul:label class="label" value="&errFile.label;"/>
|
|
||||||
<xul:box class="console-error-source" xbl:inherits="href,line"/>
|
|
||||||
<xul:spacer flex="1"/>
|
|
||||||
<xul:hbox class="lineNumberRow" xbl:inherits="line">
|
|
||||||
<xul:label class="label" value="&errLine.label;"/>
|
|
||||||
<xul:label class="label" xbl:inherits="value=line"/>
|
|
||||||
</xul:hbox>
|
|
||||||
</xul:box>
|
|
||||||
<xul:vbox class="console-row-code" xbl:inherits="selected,hidden=hideCode">
|
|
||||||
<xul:label class="monospace console-code" xbl:inherits="value=code" crop="end"/>
|
|
||||||
<xul:box xbl:inherits="hidden=hideCaret">
|
|
||||||
<xul:label class="monospace console-dots" xbl:inherits="value=errorDots"/>
|
|
||||||
<xul:label class="monospace console-caret" xbl:inherits="value=errorCaret"/>
|
|
||||||
<xul:spacer flex="1"/>
|
|
||||||
</xul:box>
|
|
||||||
</xul:vbox>
|
|
||||||
</xul:vbox>
|
|
||||||
</xul:box>
|
|
||||||
</content>
|
|
||||||
|
|
||||||
<implementation>
|
|
||||||
<field name="mSourceName">null</field>
|
|
||||||
<field name="mSourceLine">null</field>
|
|
||||||
|
|
||||||
<method name="toString">
|
|
||||||
<body><![CDATA[
|
|
||||||
let msg = "";
|
|
||||||
let strBundle = this._ConsoleBox.mStrBundle;
|
|
||||||
|
|
||||||
if (this.hasAttribute("time"))
|
|
||||||
msg += strBundle.getFormattedString("errTime", [this.getAttribute("time")]) + "\n";
|
|
||||||
|
|
||||||
msg += this.getAttribute("typetext") + " " + this.getAttribute("msg");
|
|
||||||
|
|
||||||
if (this.hasAttribute("line") && this.mSourceName) {
|
|
||||||
msg += "\n" + strBundle.getFormattedString("errFile",
|
|
||||||
[this.mSourceName]) + "\n";
|
|
||||||
if (this.hasAttribute("col")) {
|
|
||||||
msg += strBundle.getFormattedString("errLineCol",
|
|
||||||
[this.getAttribute("line"), this.getAttribute("col")]);
|
|
||||||
} else
|
|
||||||
msg += strBundle.getFormattedString("errLine", [this.getAttribute("line")]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.hasAttribute("code"))
|
|
||||||
msg += "\n" + strBundle.getString("errCode") + "\n" + this.mSourceLine;
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
</implementation>
|
|
||||||
|
|
||||||
</binding>
|
|
||||||
|
|
||||||
<binding id="message" extends="xul:box">
|
|
||||||
<content>
|
|
||||||
<xul:box class="console-internal-box" flex="1">
|
|
||||||
<xul:box class="console-row-icon" align="center">
|
|
||||||
<xul:image class="console-icon" xbl:inherits="src,type"/>
|
|
||||||
</xul:box>
|
|
||||||
<xul:vbox class="console-row-content" xbl:inherits="selected" flex="1">
|
|
||||||
<xul:vbox class="console-row-msg" flex="1">
|
|
||||||
<xul:description class="console-msg-text" xbl:inherits="xbl:text=msg"/>
|
|
||||||
</xul:vbox>
|
|
||||||
</xul:vbox>
|
|
||||||
</xul:box>
|
|
||||||
</content>
|
|
||||||
|
|
||||||
<implementation>
|
|
||||||
<method name="toString">
|
|
||||||
<body><![CDATA[
|
|
||||||
return this.getAttribute("msg");
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
</implementation>
|
|
||||||
</binding>
|
|
||||||
|
|
||||||
<binding id="console-error-source" extends="xul:box">
|
|
||||||
<content>
|
|
||||||
<xul:label class="text-link" xbl:inherits="value=href" crop="right"/>
|
|
||||||
</content>
|
|
||||||
|
|
||||||
<handlers>
|
|
||||||
<handler event="click" phase="capturing" button="0" preventdefault="true">
|
|
||||||
<![CDATA[
|
|
||||||
var url = document.getBindingParent(this).mSourceName;
|
|
||||||
url = url.substring(url.lastIndexOf(" ") + 1);
|
|
||||||
var line = getAttribute("line");
|
|
||||||
gViewSourceUtils.viewSource({URL: url, lineNumber: line});
|
|
||||||
]]>
|
|
||||||
</handler>
|
|
||||||
</handlers>
|
|
||||||
</binding>
|
|
||||||
|
|
||||||
</bindings>
|
|
|
@ -1,9 +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/.
|
|
||||||
|
|
||||||
toolkit.jar:
|
|
||||||
content/global/console.js (content/console.js)
|
|
||||||
content/global/console.xul (content/console.xul)
|
|
||||||
content/global/console.css (content/console.css)
|
|
||||||
content/global/consoleBindings.xml (content/consoleBindings.xml)
|
|
|
@ -1,40 +0,0 @@
|
||||||
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
|
|
||||||
/* vim:sw=4:sr:sta:et:sts: */
|
|
||||||
|
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
const Cc = Components.classes;
|
|
||||||
const Ci = Components.interfaces;
|
|
||||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
|
|
||||||
function jsConsoleHandler() {}
|
|
||||||
jsConsoleHandler.prototype = {
|
|
||||||
handle: function clh_handle(cmdLine) {
|
|
||||||
if (!cmdLine.handleFlag("jsconsole", false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
|
|
||||||
getService(Ci.nsIWindowMediator);
|
|
||||||
var console = wm.getMostRecentWindow("global:console");
|
|
||||||
if (!console) {
|
|
||||||
var wwatch = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
|
||||||
getService(Ci.nsIWindowWatcher);
|
|
||||||
wwatch.openWindow(null, "chrome://global/content/console.xul", "_blank",
|
|
||||||
"chrome,dialog=no,all", cmdLine);
|
|
||||||
} else {
|
|
||||||
console.focus(); // the Error console was already open
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO)
|
|
||||||
cmdLine.preventDefault = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
helpInfo : " --jsconsole Open the Error console.\n",
|
|
||||||
|
|
||||||
classID: Components.ID("{2cd0c310-e127-44d0-88fc-4435c9ab4d4b}"),
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
|
|
||||||
};
|
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([jsConsoleHandler]);
|
|
|
@ -1,3 +0,0 @@
|
||||||
component {2cd0c310-e127-44d0-88fc-4435c9ab4d4b} jsconsole-clhandler.js
|
|
||||||
contract @mozilla.org/toolkit/console-clh;1 {2cd0c310-e127-44d0-88fc-4435c9ab4d4b}
|
|
||||||
category command-line-handler t-jsconsole @mozilla.org/toolkit/console-clh;1
|
|
|
@ -1,17 +0,0 @@
|
||||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
|
||||||
# vim: set filetype=python:
|
|
||||||
# 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/.
|
|
||||||
|
|
||||||
MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
|
|
||||||
|
|
||||||
EXTRA_COMPONENTS += [
|
|
||||||
'jsconsole-clhandler.js',
|
|
||||||
'jsconsole-clhandler.manifest',
|
|
||||||
]
|
|
||||||
|
|
||||||
JAR_MANIFESTS += ['jar.mn']
|
|
||||||
|
|
||||||
with Files('**'):
|
|
||||||
BUG_COMPONENT = ('Toolkit', 'Error Console')
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"extends": [
|
|
||||||
"../../../../testing/mochitest/chrome.eslintrc"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
[DEFAULT]
|
|
||||||
skip-if = buildapp == 'b2g'
|
|
||||||
|
|
||||||
[test_hugeURIs.xul]
|
|
|
@ -1,64 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
|
||||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
|
||||||
<!--
|
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=796179
|
|
||||||
-->
|
|
||||||
<window title="Mozilla Bug 796179"
|
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
||||||
onload="RunTest();">
|
|
||||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
|
||||||
|
|
||||||
<!-- Detect severe performance and memory issues when large amounts of errors
|
|
||||||
are reported from CSS embedded in a file with a long data URI. Addressed
|
|
||||||
by 786108 for issues internal to the style system and by 796179 for issues
|
|
||||||
related to the error console. This error console test should finish quickly
|
|
||||||
with those patches and run for a very long time or OOM otherwise. -->
|
|
||||||
|
|
||||||
<!-- test results are displayed in the html:body -->
|
|
||||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=796179"
|
|
||||||
target="_blank">Mozilla Bug 796179</a>
|
|
||||||
<div id="badSVG" style="max-width: 1; max-height: 1; overflow: hidden"></div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<!-- display the error console so we can test its reaction to the test -->
|
|
||||||
<iframe id="errorConsoleFrame" height="400" src="chrome://global/content/console.xul"></iframe>
|
|
||||||
|
|
||||||
<!-- test code -->
|
|
||||||
<script type="application/javascript">
|
|
||||||
<![CDATA[
|
|
||||||
function RunTest()
|
|
||||||
{
|
|
||||||
// Create the bad SVG and add it to the document.
|
|
||||||
var img = new Array;
|
|
||||||
img.push('<img src="data:image/svg+xml,');
|
|
||||||
img.push(encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="300px" height="300px">'));
|
|
||||||
|
|
||||||
for (var i = 0 ; i < 10000 ; i++)
|
|
||||||
img.push(encodeURIComponent('<circle cx="0" cy="0" r="1" style="xxx-invalid-property: 0;"/>'));
|
|
||||||
|
|
||||||
img.push(encodeURIComponent('</svg>'));
|
|
||||||
img.push('" />');
|
|
||||||
|
|
||||||
document.getElementById('badSVG').innerHTML = img.join('');
|
|
||||||
|
|
||||||
// We yield control of the thread, allowing the error console to render.
|
|
||||||
// If we get control back without timing out or OOMing then the test passed.
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
|
||||||
SimpleTest.executeSoon(function() {
|
|
||||||
// Clean up.
|
|
||||||
var elem = document.getElementById('errorConsoleFrame');
|
|
||||||
elem.parentNode.removeChild(elem);
|
|
||||||
elem = document.getElementById('badSVG');
|
|
||||||
elem.parentNode.removeChild(elem);
|
|
||||||
elem = null;
|
|
||||||
|
|
||||||
// Finish the test with a pass.
|
|
||||||
ok(true, 'Error console rendered OK.');
|
|
||||||
SimpleTest.finish();
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
]]>
|
|
||||||
</script>
|
|
||||||
</window>
|
|
|
@ -18,7 +18,6 @@ DIRS += [
|
||||||
'apppicker',
|
'apppicker',
|
||||||
'asyncshutdown',
|
'asyncshutdown',
|
||||||
'commandlines',
|
'commandlines',
|
||||||
'console',
|
|
||||||
'contentprefs',
|
'contentprefs',
|
||||||
'cookie',
|
'cookie',
|
||||||
'crashmonitor',
|
'crashmonitor',
|
||||||
|
|
|
@ -204,6 +204,42 @@ this.LoginHelper = {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
doLoginsMatch(aLogin1, aLogin2, {
|
||||||
|
ignorePassword = false,
|
||||||
|
ignoreSchemes = false,
|
||||||
|
}) {
|
||||||
|
if (aLogin1.httpRealm != aLogin2.httpRealm ||
|
||||||
|
aLogin1.username != aLogin2.username)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ignorePassword && aLogin1.password != aLogin2.password)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ignoreSchemes) {
|
||||||
|
let hostname1URI = Services.io.newURI(aLogin1.hostname, null, null);
|
||||||
|
let hostname2URI = Services.io.newURI(aLogin2.hostname, null, null);
|
||||||
|
if (hostname1URI.hostPort != hostname2URI.hostPort)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (aLogin1.formSubmitURL != "" && aLogin2.formSubmitURL != "" &&
|
||||||
|
Services.io.newURI(aLogin1.formSubmitURL, null, null).hostPort !=
|
||||||
|
Services.io.newURI(aLogin2.formSubmitURL, null, null).hostPort)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (aLogin1.hostname != aLogin2.hostname)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If either formSubmitURL is blank (but not null), then match.
|
||||||
|
if (aLogin1.formSubmitURL != "" && aLogin2.formSubmitURL != "" &&
|
||||||
|
aLogin1.formSubmitURL != aLogin2.formSubmitURL)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The .usernameField and .passwordField values are ignored.
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new login object that results by modifying the given object with
|
* Creates a new login object that results by modifying the given object with
|
||||||
* the provided data.
|
* the provided data.
|
||||||
|
|
|
@ -334,6 +334,15 @@ var LoginManagerParent = {
|
||||||
schemeUpgrades: LoginHelper.schemeUpgrades,
|
schemeUpgrades: LoginHelper.schemeUpgrades,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Dedupe so the length checks below still make sense with scheme upgrades.
|
||||||
|
// Below here we have one login per hostPort + action + username with the
|
||||||
|
// matching scheme being preferred.
|
||||||
|
let resolveBy = [
|
||||||
|
"scheme",
|
||||||
|
"timePasswordChanged",
|
||||||
|
];
|
||||||
|
logins = LoginHelper.dedupeLogins(logins, ["username"], resolveBy, hostname);
|
||||||
|
|
||||||
// If we didn't find a username field, but seem to be changing a
|
// If we didn't find a username field, but seem to be changing a
|
||||||
// password, allow the user to select from a list of applicable
|
// password, allow the user to select from a list of applicable
|
||||||
// logins to update the password for.
|
// logins to update the password for.
|
||||||
|
@ -355,6 +364,10 @@ var LoginManagerParent = {
|
||||||
|
|
||||||
prompter.promptToChangePassword(oldLogin, formLogin);
|
prompter.promptToChangePassword(oldLogin, formLogin);
|
||||||
} else {
|
} else {
|
||||||
|
// Note: It's possible that that we already have the correct u+p saved
|
||||||
|
// but since we don't have the username, we don't know if the user is
|
||||||
|
// changing a second account to the new password so we ask anyways.
|
||||||
|
|
||||||
prompter.promptToChangePasswordWithUsernames(
|
prompter.promptToChangePasswordWithUsernames(
|
||||||
logins, logins.length, formLogin);
|
logins, logins.length, formLogin);
|
||||||
}
|
}
|
||||||
|
@ -365,8 +378,8 @@ var LoginManagerParent = {
|
||||||
|
|
||||||
var existingLogin = null;
|
var existingLogin = null;
|
||||||
// Look for an existing login that matches the form login.
|
// Look for an existing login that matches the form login.
|
||||||
for (var i = 0; i < logins.length; i++) {
|
for (let login of logins) {
|
||||||
var same, login = logins[i];
|
let same;
|
||||||
|
|
||||||
// If one login has a username but the other doesn't, ignore
|
// If one login has a username but the other doesn't, ignore
|
||||||
// the username when comparing and only match if they have the
|
// the username when comparing and only match if they have the
|
||||||
|
@ -375,14 +388,23 @@ var LoginManagerParent = {
|
||||||
if (!login.username && formLogin.username) {
|
if (!login.username && formLogin.username) {
|
||||||
var restoreMe = formLogin.username;
|
var restoreMe = formLogin.username;
|
||||||
formLogin.username = "";
|
formLogin.username = "";
|
||||||
same = formLogin.matches(login, false);
|
same = LoginHelper.doLoginsMatch(formLogin, login, {
|
||||||
|
ignorePassword: false,
|
||||||
|
ignoreSchemes: LoginHelper.schemeUpgrades,
|
||||||
|
});
|
||||||
formLogin.username = restoreMe;
|
formLogin.username = restoreMe;
|
||||||
} else if (!formLogin.username && login.username) {
|
} else if (!formLogin.username && login.username) {
|
||||||
formLogin.username = login.username;
|
formLogin.username = login.username;
|
||||||
same = formLogin.matches(login, false);
|
same = LoginHelper.doLoginsMatch(formLogin, login, {
|
||||||
|
ignorePassword: false,
|
||||||
|
ignoreSchemes: LoginHelper.schemeUpgrades,
|
||||||
|
});
|
||||||
formLogin.username = ""; // we know it's always blank.
|
formLogin.username = ""; // we know it's always blank.
|
||||||
} else {
|
} else {
|
||||||
same = formLogin.matches(login, true);
|
same = LoginHelper.doLoginsMatch(formLogin, login, {
|
||||||
|
ignorePassword: true,
|
||||||
|
ignoreSchemes: LoginHelper.schemeUpgrades,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (same) {
|
if (same) {
|
||||||
|
|
|
@ -6,6 +6,10 @@ const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
|
||||||
|
"resource://gre/modules/LoginHelper.jsm");
|
||||||
|
|
||||||
|
|
||||||
function nsLoginInfo() {}
|
function nsLoginInfo() {}
|
||||||
|
|
||||||
nsLoginInfo.prototype = {
|
nsLoginInfo.prototype = {
|
||||||
|
@ -38,22 +42,9 @@ nsLoginInfo.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
matches(aLogin, ignorePassword) {
|
matches(aLogin, ignorePassword) {
|
||||||
if (this.hostname != aLogin.hostname ||
|
return LoginHelper.doLoginsMatch(this, aLogin, {
|
||||||
this.httpRealm != aLogin.httpRealm ||
|
ignorePassword,
|
||||||
this.username != aLogin.username)
|
});
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!ignorePassword && this.password != aLogin.password)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// If either formSubmitURL is blank (but not null), then match.
|
|
||||||
if (this.formSubmitURL != "" && aLogin.formSubmitURL != "" &&
|
|
||||||
this.formSubmitURL != aLogin.formSubmitURL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// The .usernameField and .passwordField values are ignored.
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
equals : function (aLogin) {
|
equals : function (aLogin) {
|
||||||
|
|
|
@ -27,9 +27,7 @@ const PROMPT_ADD_OR_UPDATE = 1;
|
||||||
const PROMPT_NOTNOW = 2;
|
const PROMPT_NOTNOW = 2;
|
||||||
const PROMPT_NEVER = 3;
|
const PROMPT_NEVER = 3;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* LoginManagerPromptFactory
|
|
||||||
*
|
|
||||||
* Implements nsIPromptFactory
|
* Implements nsIPromptFactory
|
||||||
*
|
*
|
||||||
* Invoked by [toolkit/components/prompts/src/nsPrompter.js]
|
* Invoked by [toolkit/components/prompts/src/nsPrompter.js]
|
||||||
|
@ -186,9 +184,7 @@ XPCOMUtils.defineLazyGetter(this.LoginManagerPromptFactory.prototype, "log", ()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* LoginManagerPrompter
|
|
||||||
*
|
|
||||||
* Implements interfaces for prompting the user to enter/save/change auth info.
|
* Implements interfaces for prompting the user to enter/save/change auth info.
|
||||||
*
|
*
|
||||||
* nsIAuthPrompt: Used by SeaMonkey, Thunderbird, but not Firefox.
|
* nsIAuthPrompt: Used by SeaMonkey, Thunderbird, but not Firefox.
|
||||||
|
@ -279,9 +275,7 @@ LoginManagerPrompter.prototype = {
|
||||||
/* ---------- nsIAuthPrompt prompts ---------- */
|
/* ---------- nsIAuthPrompt prompts ---------- */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* prompt
|
|
||||||
*
|
|
||||||
* Wrapper around the prompt service prompt. Saving random fields here
|
* Wrapper around the prompt service prompt. Saving random fields here
|
||||||
* doesn't really make sense and therefore isn't implemented.
|
* doesn't really make sense and therefore isn't implemented.
|
||||||
*/
|
*/
|
||||||
|
@ -302,9 +296,7 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* promptUsernameAndPassword
|
|
||||||
*
|
|
||||||
* Looks up a username and password in the database. Will prompt the user
|
* Looks up a username and password in the database. Will prompt the user
|
||||||
* with a dialog, even if a username and password are found.
|
* with a dialog, even if a username and password are found.
|
||||||
*/
|
*/
|
||||||
|
@ -401,9 +393,7 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* promptPassword
|
|
||||||
*
|
|
||||||
* If a password is found in the database for the password realm, it is
|
* If a password is found in the database for the password realm, it is
|
||||||
* returned straight away without displaying a dialog.
|
* returned straight away without displaying a dialog.
|
||||||
*
|
*
|
||||||
|
@ -508,14 +498,12 @@ LoginManagerPrompter.prototype = {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* promptAuth
|
|
||||||
*
|
|
||||||
* Implementation of nsIAuthPrompt2.
|
* Implementation of nsIAuthPrompt2.
|
||||||
*
|
*
|
||||||
* nsIChannel aChannel
|
* @param {nsIChannel} aChannel
|
||||||
* int aLevel
|
* @param {int} aLevel
|
||||||
* nsIAuthInformation aAuthInfo
|
* @param {nsIAuthInformation} aAuthInfo
|
||||||
*/
|
*/
|
||||||
promptAuth : function (aChannel, aLevel, aAuthInfo) {
|
promptAuth : function (aChannel, aLevel, aAuthInfo) {
|
||||||
var selectedLogin = null;
|
var selectedLogin = null;
|
||||||
|
@ -694,11 +682,6 @@ LoginManagerPrompter.prototype = {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* init
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
init : function (aWindow, aFactory) {
|
init : function (aWindow, aFactory) {
|
||||||
this._window = aWindow;
|
this._window = aWindow;
|
||||||
this._factory = aFactory || null;
|
this._factory = aFactory || null;
|
||||||
|
@ -715,11 +698,6 @@ LoginManagerPrompter.prototype = {
|
||||||
this._opener = aOpener;
|
this._opener = aOpener;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* promptToSavePassword
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
promptToSavePassword : function (aLogin) {
|
promptToSavePassword : function (aLogin) {
|
||||||
this.log("promptToSavePassword");
|
this.log("promptToSavePassword");
|
||||||
var notifyObj = this._getPopupNote() || this._getNotifyBox();
|
var notifyObj = this._getPopupNote() || this._getNotifyBox();
|
||||||
|
@ -729,12 +707,8 @@ LoginManagerPrompter.prototype = {
|
||||||
this._showSaveLoginDialog(aLogin);
|
this._showSaveLoginDialog(aLogin);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
/*
|
|
||||||
* _showLoginNotification
|
|
||||||
*
|
|
||||||
* Displays a notification bar.
|
* Displays a notification bar.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
_showLoginNotification : function (aNotifyBox, aName, aText, aButtons) {
|
_showLoginNotification : function (aNotifyBox, aName, aText, aButtons) {
|
||||||
var oldBar = aNotifyBox.getNotificationWithValue(aName);
|
var oldBar = aNotifyBox.getNotificationWithValue(aName);
|
||||||
|
@ -820,9 +794,13 @@ LoginManagerPrompter.prototype = {
|
||||||
};
|
};
|
||||||
|
|
||||||
let updateButtonLabel = () => {
|
let updateButtonLabel = () => {
|
||||||
let foundLogins = Services.logins.findLogins({}, login.hostname,
|
let foundLogins = LoginHelper.searchLoginsWithObject({
|
||||||
login.formSubmitURL,
|
formSubmitURL: login.formSubmitURL,
|
||||||
login.httpRealm);
|
hostname: login.hostname,
|
||||||
|
httpRealm: login.httpRealm,
|
||||||
|
schemeUpgrades: LoginHelper.schemeUpgrades,
|
||||||
|
});
|
||||||
|
|
||||||
let logins = this._filterUpdatableLogins(login, foundLogins);
|
let logins = this._filterUpdatableLogins(login, foundLogins);
|
||||||
let msgNames = (logins.length == 0) ? saveMsgNames : changeMsgNames;
|
let msgNames = (logins.length == 0) ? saveMsgNames : changeMsgNames;
|
||||||
|
|
||||||
|
@ -886,9 +864,13 @@ LoginManagerPrompter.prototype = {
|
||||||
};
|
};
|
||||||
|
|
||||||
let persistData = () => {
|
let persistData = () => {
|
||||||
let foundLogins = Services.logins.findLogins({}, login.hostname,
|
let foundLogins = LoginHelper.searchLoginsWithObject({
|
||||||
login.formSubmitURL,
|
formSubmitURL: login.formSubmitURL,
|
||||||
login.httpRealm);
|
hostname: login.hostname,
|
||||||
|
httpRealm: login.httpRealm,
|
||||||
|
schemeUpgrades: LoginHelper.schemeUpgrades,
|
||||||
|
});
|
||||||
|
|
||||||
let logins = this._filterUpdatableLogins(login, foundLogins);
|
let logins = this._filterUpdatableLogins(login, foundLogins);
|
||||||
|
|
||||||
if (logins.length == 0) {
|
if (logins.length == 0) {
|
||||||
|
@ -1002,15 +984,15 @@ LoginManagerPrompter.prototype = {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* _showSaveLoginNotification
|
|
||||||
*
|
|
||||||
* Displays a notification bar or a popup notification, to allow the user
|
* Displays a notification bar or a popup notification, to allow the user
|
||||||
* to save the specified login. This allows the user to see the results of
|
* to save the specified login. This allows the user to see the results of
|
||||||
* their login, and only save a login which they know worked.
|
* their login, and only save a login which they know worked.
|
||||||
*
|
*
|
||||||
* @param aNotifyObj
|
* @param aNotifyObj
|
||||||
* A notification box or a popup notification.
|
* A notification box or a popup notification.
|
||||||
|
* @param aLogin
|
||||||
|
* The login captured from the form.
|
||||||
*/
|
*/
|
||||||
_showSaveLoginNotification : function (aNotifyObj, aLogin) {
|
_showSaveLoginNotification : function (aNotifyObj, aLogin) {
|
||||||
// Ugh. We can't use the strings from the popup window, because they
|
// Ugh. We can't use the strings from the popup window, because they
|
||||||
|
@ -1079,11 +1061,6 @@ LoginManagerPrompter.prototype = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* _removeLoginNotifications
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
_removeLoginNotifications : function () {
|
_removeLoginNotifications : function () {
|
||||||
var popupNote = this._getPopupNote();
|
var popupNote = this._getPopupNote();
|
||||||
if (popupNote)
|
if (popupNote)
|
||||||
|
@ -1108,12 +1085,9 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* _showSaveLoginDialog
|
|
||||||
*
|
|
||||||
* Called when we detect a new login in a form submission,
|
* Called when we detect a new login in a form submission,
|
||||||
* asks the user what to do.
|
* asks the user what to do.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
_showSaveLoginDialog : function (aLogin) {
|
_showSaveLoginDialog : function (aLogin) {
|
||||||
const buttonFlags = Ci.nsIPrompt.BUTTON_POS_1_DEFAULT +
|
const buttonFlags = Ci.nsIPrompt.BUTTON_POS_1_DEFAULT +
|
||||||
|
@ -1189,9 +1163,7 @@ LoginManagerPrompter.prototype = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* _showChangeLoginNotification
|
|
||||||
*
|
|
||||||
* Shows the Change Password notification bar or popup notification.
|
* Shows the Change Password notification bar or popup notification.
|
||||||
*
|
*
|
||||||
* @param aNotifyObj
|
* @param aNotifyObj
|
||||||
|
@ -1202,7 +1174,6 @@ LoginManagerPrompter.prototype = {
|
||||||
*
|
*
|
||||||
* @param aNewLogin
|
* @param aNewLogin
|
||||||
* The login object with the changes we want to make.
|
* The login object with the changes we want to make.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
_showChangeLoginNotification(aNotifyObj, aOldLogin, aNewLogin) {
|
_showChangeLoginNotification(aNotifyObj, aOldLogin, aNewLogin) {
|
||||||
var changeButtonText =
|
var changeButtonText =
|
||||||
|
@ -1223,6 +1194,8 @@ LoginManagerPrompter.prototype = {
|
||||||
|
|
||||||
// Notification is a PopupNotification
|
// Notification is a PopupNotification
|
||||||
if (aNotifyObj == this._getPopupNote()) {
|
if (aNotifyObj == this._getPopupNote()) {
|
||||||
|
aOldLogin.hostname = aNewLogin.hostname;
|
||||||
|
aOldLogin.formSubmitURL = aNewLogin.formSubmitURL;
|
||||||
aOldLogin.password = aNewLogin.password;
|
aOldLogin.password = aNewLogin.password;
|
||||||
aOldLogin.username = aNewLogin.username;
|
aOldLogin.username = aNewLogin.username;
|
||||||
this._showLoginCaptureDoorhanger(aOldLogin, "password-change");
|
this._showLoginCaptureDoorhanger(aOldLogin, "password-change");
|
||||||
|
@ -1259,11 +1232,8 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* _showChangeLoginDialog
|
|
||||||
*
|
|
||||||
* Shows the Change Password dialog.
|
* Shows the Change Password dialog.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
_showChangeLoginDialog(aOldLogin, aNewLogin) {
|
_showChangeLoginDialog(aOldLogin, aNewLogin) {
|
||||||
const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
|
const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
|
||||||
|
@ -1292,9 +1262,7 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* promptToChangePasswordWithUsernames
|
|
||||||
*
|
|
||||||
* Called when we detect a password change in a form submission, but we
|
* Called when we detect a password change in a form submission, but we
|
||||||
* don't know which existing login (username) it's for. Asks the user
|
* don't know which existing login (username) it's for. Asks the user
|
||||||
* to select a username and confirm the password change.
|
* to select a username and confirm the password change.
|
||||||
|
@ -1347,6 +1315,8 @@ LoginManagerPrompter.prototype = {
|
||||||
var propBag = Cc["@mozilla.org/hash-property-bag;1"].
|
var propBag = Cc["@mozilla.org/hash-property-bag;1"].
|
||||||
createInstance(Ci.nsIWritablePropertyBag);
|
createInstance(Ci.nsIWritablePropertyBag);
|
||||||
if (aNewLogin) {
|
if (aNewLogin) {
|
||||||
|
propBag.setProperty("formSubmitURL", aNewLogin.formSubmitURL);
|
||||||
|
propBag.setProperty("hostname", aNewLogin.hostname);
|
||||||
propBag.setProperty("password", aNewLogin.password);
|
propBag.setProperty("password", aNewLogin.password);
|
||||||
propBag.setProperty("username", aNewLogin.username);
|
propBag.setProperty("username", aNewLogin.username);
|
||||||
// Explicitly set the password change time here (even though it would
|
// Explicitly set the password change time here (even though it would
|
||||||
|
@ -1360,9 +1330,7 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* _getChromeWindow
|
|
||||||
*
|
|
||||||
* Given a content DOM window, returns the chrome window it's in.
|
* Given a content DOM window, returns the chrome window it's in.
|
||||||
*/
|
*/
|
||||||
_getChromeWindow: function (aWindow) {
|
_getChromeWindow: function (aWindow) {
|
||||||
|
@ -1377,9 +1345,6 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* _getNotifyWindow
|
|
||||||
*/
|
|
||||||
_getNotifyWindow: function () {
|
_getNotifyWindow: function () {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1445,9 +1410,7 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* _getPopupNote
|
|
||||||
*
|
|
||||||
* Returns the popup notification to this prompter,
|
* Returns the popup notification to this prompter,
|
||||||
* or null if there isn't one available.
|
* or null if there isn't one available.
|
||||||
*/
|
*/
|
||||||
|
@ -1470,9 +1433,7 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* _getNotifyBox
|
|
||||||
*
|
|
||||||
* Returns the notification box to this prompter, or null if there isn't
|
* Returns the notification box to this prompter, or null if there isn't
|
||||||
* a notification box available.
|
* a notification box available.
|
||||||
*/
|
*/
|
||||||
|
@ -1495,9 +1456,7 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* _repickSelectedLogin
|
|
||||||
*
|
|
||||||
* The user might enter a login that isn't the one we prefilled, but
|
* The user might enter a login that isn't the one we prefilled, but
|
||||||
* is the same as some other existing login. So, pick a login with a
|
* is the same as some other existing login. So, pick a login with a
|
||||||
* matching username, or return null.
|
* matching username, or return null.
|
||||||
|
@ -1510,9 +1469,7 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* _getLocalizedString
|
|
||||||
*
|
|
||||||
* Can be called as:
|
* Can be called as:
|
||||||
* _getLocalizedString("key1");
|
* _getLocalizedString("key1");
|
||||||
* _getLocalizedString("key2", ["arg1"]);
|
* _getLocalizedString("key2", ["arg1"]);
|
||||||
|
@ -1532,9 +1489,7 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* _sanitizeUsername
|
|
||||||
*
|
|
||||||
* Sanitizes the specified username, by stripping quotes and truncating if
|
* Sanitizes the specified username, by stripping quotes and truncating if
|
||||||
* it's too long. This helps prevent an evil site from messing with the
|
* it's too long. This helps prevent an evil site from messing with the
|
||||||
* "save password?" prompt too much.
|
* "save password?" prompt too much.
|
||||||
|
@ -1566,9 +1521,7 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* _getShortDisplayHost
|
|
||||||
*
|
|
||||||
* Converts a login's hostname field (a URL) to a short string for
|
* Converts a login's hostname field (a URL) to a short string for
|
||||||
* prompting purposes. Eg, "http://foo.com" --> "foo.com", or
|
* prompting purposes. Eg, "http://foo.com" --> "foo.com", or
|
||||||
* "ftp://www.site.co.uk" --> "site.co.uk".
|
* "ftp://www.site.co.uk" --> "site.co.uk".
|
||||||
|
@ -1595,9 +1548,7 @@ LoginManagerPrompter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* _getAuthTarget
|
|
||||||
*
|
|
||||||
* Returns the hostname and realm for which authentication is being
|
* Returns the hostname and realm for which authentication is being
|
||||||
* requested, in the format expected to be used with nsILoginInfo.
|
* requested, in the format expected to be used with nsILoginInfo.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -359,7 +359,8 @@ this.LoginManagerStorage_json.prototype = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.log("_searchLogins: returning", foundLogins.length, "logins for", matchData);
|
this.log("_searchLogins: returning", foundLogins.length, "logins for", matchData,
|
||||||
|
"with options", aOptions);
|
||||||
return [foundLogins, foundIds];
|
return [foundLogins, foundIds];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,15 @@ const BRAND_SHORT_NAME = BRAND_BUNDLE.GetStringFromName("brandShortName");
|
||||||
|
|
||||||
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||||
Ci.nsILoginInfo, "init");
|
Ci.nsILoginInfo, "init");
|
||||||
let login1 = new nsLoginInfo("http://mochi.test:8888", "http://mochi.test:8888", null,
|
let login1 = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||||
"notifyu1", "notifyp1", "user", "pass");
|
"notifyu1", "notifyp1", "user", "pass");
|
||||||
let login2 = new nsLoginInfo("http://mochi.test:8888", "http://mochi.test:8888", null,
|
let login1HTTPS = new nsLoginInfo("https://example.com", "https://example.com", null,
|
||||||
|
"notifyu1", "notifyp1", "user", "pass");
|
||||||
|
let login2 = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||||
"", "notifyp1", "", "pass");
|
"", "notifyp1", "", "pass");
|
||||||
let login1B = new nsLoginInfo("http://mochi.test:8888", "http://mochi.test:8888", null,
|
let login1B = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||||
"notifyu1B", "notifyp1B", "user", "pass");
|
"notifyu1B", "notifyp1B", "user", "pass");
|
||||||
let login2B = new nsLoginInfo("http://mochi.test:8888", "http://mochi.test:8888", null,
|
let login2B = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||||
"", "notifyp1B", "", "pass");
|
"", "notifyp1B", "", "pass");
|
||||||
|
|
||||||
requestLongerTimeout(2);
|
requestLongerTimeout(2);
|
||||||
|
@ -46,7 +48,7 @@ add_task(function* test_clickNever() {
|
||||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||||
let notif = getCaptureDoorhanger("password-save");
|
let notif = getCaptureDoorhanger("password-save");
|
||||||
ok(notif, "got notification popup");
|
ok(notif, "got notification popup");
|
||||||
is(true, Services.logins.getLoginSavingEnabled("http://mochi.test:8888"),
|
is(true, Services.logins.getLoginSavingEnabled("http://example.com"),
|
||||||
"Checking for login saving enabled");
|
"Checking for login saving enabled");
|
||||||
clickDoorhangerButton(notif, NEVER_BUTTON);
|
clickDoorhangerButton(notif, NEVER_BUTTON);
|
||||||
});
|
});
|
||||||
|
@ -59,9 +61,9 @@ add_task(function* test_clickNever() {
|
||||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||||
let notif = getCaptureDoorhanger("password-save");
|
let notif = getCaptureDoorhanger("password-save");
|
||||||
ok(!notif, "checking for no notification popup");
|
ok(!notif, "checking for no notification popup");
|
||||||
is(false, Services.logins.getLoginSavingEnabled("http://mochi.test:8888"),
|
is(false, Services.logins.getLoginSavingEnabled("http://example.com"),
|
||||||
"Checking for login saving disabled");
|
"Checking for login saving disabled");
|
||||||
Services.logins.setLoginSavingEnabled("http://mochi.test:8888", true);
|
Services.logins.setLoginSavingEnabled("http://example.com", true);
|
||||||
});
|
});
|
||||||
|
|
||||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||||
|
@ -80,7 +82,7 @@ add_task(function* test_clickRemember() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "notifyu1", "Check the username used on the new entry");
|
is(login.username, "notifyu1", "Check the username used on the new entry");
|
||||||
is(login.password, "notifyp1", "Check the password used on the new entry");
|
is(login.password, "notifyp1", "Check the password used on the new entry");
|
||||||
is(login.timesUsed, 1, "Check times used on new entry");
|
is(login.timesUsed, 1, "Check times used on new entry");
|
||||||
|
@ -95,7 +97,7 @@ add_task(function* test_clickRemember() {
|
||||||
|
|
||||||
logins = Services.logins.getAllLogins();
|
logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "notifyu1", "Check the username used");
|
is(login.username, "notifyu1", "Check the username used");
|
||||||
is(login.password, "notifyp1", "Check the password used");
|
is(login.password, "notifyp1", "Check the password used");
|
||||||
is(login.timesUsed, 2, "Check times used incremented");
|
is(login.timesUsed, 2, "Check times used incremented");
|
||||||
|
@ -209,7 +211,7 @@ add_task(function* test_pwOnlyLoginMatchesForm() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "", "Check the username");
|
is(login.username, "", "Check the username");
|
||||||
is(login.password, "notifyp1", "Check the password");
|
is(login.password, "notifyp1", "Check the password");
|
||||||
is(login.timesUsed, 1, "Check times used");
|
is(login.timesUsed, 1, "Check times used");
|
||||||
|
@ -230,7 +232,7 @@ add_task(function* test_pwOnlyFormMatchesLogin() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "notifyu1", "Check the username");
|
is(login.username, "notifyu1", "Check the username");
|
||||||
is(login.password, "notifyp1", "Check the password");
|
is(login.password, "notifyp1", "Check the password");
|
||||||
is(login.timesUsed, 2, "Check times used");
|
is(login.timesUsed, 2, "Check times used");
|
||||||
|
@ -252,7 +254,7 @@ add_task(function* test_pwOnlyFormDoesntMatchExisting() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "notifyu1B", "Check the username unchanged");
|
is(login.username, "notifyu1B", "Check the username unchanged");
|
||||||
is(login.password, "notifyp1B", "Check the password unchanged");
|
is(login.password, "notifyp1B", "Check the password unchanged");
|
||||||
is(login.timesUsed, 1, "Check times used");
|
is(login.timesUsed, 1, "Check times used");
|
||||||
|
@ -274,7 +276,7 @@ add_task(function* test_changeUPLoginOnUPForm_dont() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "notifyu1", "Check the username unchanged");
|
is(login.username, "notifyu1", "Check the username unchanged");
|
||||||
is(login.password, "notifyp1", "Check the password unchanged");
|
is(login.password, "notifyp1", "Check the password unchanged");
|
||||||
is(login.timesUsed, 1, "Check times used");
|
is(login.timesUsed, 1, "Check times used");
|
||||||
|
@ -297,7 +299,7 @@ add_task(function* test_changeUPLoginOnUPForm_change() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "notifyu1", "Check the username unchanged");
|
is(login.username, "notifyu1", "Check the username unchanged");
|
||||||
is(login.password, "pass2", "Check the password changed");
|
is(login.password, "pass2", "Check the password changed");
|
||||||
is(login.timesUsed, 2, "Check times used");
|
is(login.timesUsed, 2, "Check times used");
|
||||||
|
@ -325,7 +327,7 @@ add_task(function* test_changePLoginOnUPForm() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "", "Check the username unchanged");
|
is(login.username, "", "Check the username unchanged");
|
||||||
is(login.password, "pass2", "Check the password changed");
|
is(login.password, "pass2", "Check the password changed");
|
||||||
is(login.timesUsed, 2, "Check times used");
|
is(login.timesUsed, 2, "Check times used");
|
||||||
|
@ -347,7 +349,7 @@ add_task(function* test_changePLoginOnPForm() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "", "Check the username unchanged");
|
is(login.username, "", "Check the username unchanged");
|
||||||
is(login.password, "notifyp1", "Check the password changed");
|
is(login.password, "notifyp1", "Check the password changed");
|
||||||
is(login.timesUsed, 3, "Check times used");
|
is(login.timesUsed, 3, "Check times used");
|
||||||
|
@ -422,7 +424,7 @@ add_task(function* test_change2pw0unExistingDifferentUP() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "notifyu1B", "Check the username unchanged");
|
is(login.username, "notifyu1B", "Check the username unchanged");
|
||||||
is(login.password, "notifyp1B", "Check the password unchanged");
|
is(login.password, "notifyp1B", "Check the password unchanged");
|
||||||
is(login.timesUsed, 1, "Check times used");
|
is(login.timesUsed, 1, "Check times used");
|
||||||
|
@ -446,7 +448,7 @@ add_task(function* test_change2pw0unExistingDifferentP() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "", "Check the username unchanged");
|
is(login.username, "", "Check the username unchanged");
|
||||||
is(login.password, "notifyp1B", "Check the password unchanged");
|
is(login.password, "notifyp1B", "Check the password unchanged");
|
||||||
is(login.timesUsed, 1, "Check times used");
|
is(login.timesUsed, 1, "Check times used");
|
||||||
|
@ -469,7 +471,7 @@ add_task(function* test_change2pw0unExistingWithSameP() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "", "Check the username unchanged");
|
is(login.username, "", "Check the username unchanged");
|
||||||
is(login.password, "notifyp1", "Check the password unchanged");
|
is(login.password, "notifyp1", "Check the password unchanged");
|
||||||
is(login.timesUsed, 2, "Check times used incremented");
|
is(login.timesUsed, 2, "Check times used incremented");
|
||||||
|
@ -494,7 +496,7 @@ add_task(function* test_changeUPLoginOnPUpdateForm() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "notifyu1", "Check the username unchanged");
|
is(login.username, "notifyu1", "Check the username unchanged");
|
||||||
is(login.password, "pass2", "Check the password changed");
|
is(login.password, "pass2", "Check the password changed");
|
||||||
is(login.timesUsed, 2, "Check times used");
|
is(login.timesUsed, 2, "Check times used");
|
||||||
|
@ -525,7 +527,7 @@ add_task(function* test_recipeCaptureFields_NewLogin() {
|
||||||
|
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "notifyu1", "Check the username unchanged");
|
is(login.username, "notifyu1", "Check the username unchanged");
|
||||||
is(login.password, "notifyp1", "Check the password unchanged");
|
is(login.password, "notifyp1", "Check the password unchanged");
|
||||||
is(login.timesUsed, 1, "Check times used");
|
is(login.timesUsed, 1, "Check times used");
|
||||||
|
@ -545,7 +547,7 @@ add_task(function* test_recipeCaptureFields_ExistingLogin() {
|
||||||
checkOnlyLoginWasUsedTwice({ justChanged: false });
|
checkOnlyLoginWasUsedTwice({ justChanged: false });
|
||||||
let logins = Services.logins.getAllLogins();
|
let logins = Services.logins.getAllLogins();
|
||||||
is(logins.length, 1, "Should only have 1 login");
|
is(logins.length, 1, "Should only have 1 login");
|
||||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
is(login.username, "notifyu1", "Check the username unchanged");
|
is(login.username, "notifyu1", "Check the username unchanged");
|
||||||
is(login.password, "notifyp1", "Check the password unchanged");
|
is(login.password, "notifyp1", "Check the password unchanged");
|
||||||
is(login.timesUsed, 2, "Check times used incremented");
|
is(login.timesUsed, 2, "Check times used incremented");
|
||||||
|
@ -576,5 +578,114 @@ add_task(function* test_noShowPasswordOnDismissal() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add_task(function* test_httpsUpgradeCaptureFields_noChange() {
|
||||||
|
info("Check that we don't prompt to remember when capturing an upgraded login with no change");
|
||||||
|
Services.logins.addLogin(login1);
|
||||||
|
// Sanity check the HTTP login exists.
|
||||||
|
let logins = Services.logins.getAllLogins();
|
||||||
|
is(logins.length, 1, "Should have the HTTP login");
|
||||||
|
|
||||||
|
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||||
|
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||||
|
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||||
|
let notif = getCaptureDoorhanger("password-save");
|
||||||
|
ok(!notif, "checking for no notification popup");
|
||||||
|
}, "https://example.com"); // This is HTTPS whereas the saved login is HTTP
|
||||||
|
|
||||||
|
logins = Services.logins.getAllLogins();
|
||||||
|
is(logins.length, 1, "Should only have 1 login still");
|
||||||
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
|
is(login.hostname, "http://example.com", "Check the hostname is unchanged");
|
||||||
|
is(login.username, "notifyu1", "Check the username is unchanged");
|
||||||
|
is(login.password, "notifyp1", "Check the password is unchanged");
|
||||||
|
is(login.timesUsed, 2, "Check times used increased");
|
||||||
|
|
||||||
|
Services.logins.removeLogin(login1);
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* test_httpsUpgradeCaptureFields_changePW() {
|
||||||
|
info("Check that we prompt to change when capturing an upgraded login with a new PW");
|
||||||
|
Services.logins.addLogin(login1);
|
||||||
|
// Sanity check the HTTP login exists.
|
||||||
|
let logins = Services.logins.getAllLogins();
|
||||||
|
is(logins.length, 1, "Should have the HTTP login");
|
||||||
|
|
||||||
|
yield testSubmittingLoginForm("subtst_notifications_8.html", function*(fieldValues) {
|
||||||
|
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||||
|
is(fieldValues.password, "pass2", "Checking submitted password");
|
||||||
|
let notif = getCaptureDoorhanger("password-change");
|
||||||
|
ok(notif, "checking for a change popup");
|
||||||
|
clickDoorhangerButton(notif, CHANGE_BUTTON);
|
||||||
|
ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
|
||||||
|
}, "https://example.com"); // This is HTTPS whereas the saved login is HTTP
|
||||||
|
|
||||||
|
checkOnlyLoginWasUsedTwice({ justChanged: true });
|
||||||
|
logins = Services.logins.getAllLogins();
|
||||||
|
is(logins.length, 1, "Should only have 1 login still");
|
||||||
|
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
|
is(login.hostname, "https://example.com", "Check the hostname is upgraded");
|
||||||
|
is(login.formSubmitURL, "https://example.com", "Check the formSubmitURL is upgraded");
|
||||||
|
is(login.username, "notifyu1", "Check the username is unchanged");
|
||||||
|
is(login.password, "pass2", "Check the password changed");
|
||||||
|
is(login.timesUsed, 2, "Check times used increased");
|
||||||
|
|
||||||
|
Services.logins.removeAllLogins();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* test_httpsUpgradeCaptureFields_captureMatchingHTTP() {
|
||||||
|
info("Capture a new HTTP login which matches a stored HTTPS one.");
|
||||||
|
Services.logins.addLogin(login1HTTPS);
|
||||||
|
|
||||||
|
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||||
|
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||||
|
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||||
|
let notif = getCaptureDoorhanger("password-save");
|
||||||
|
ok(notif, "got notification popup");
|
||||||
|
|
||||||
|
is(Services.logins.getAllLogins().length, 1, "Should only have the HTTPS login");
|
||||||
|
clickDoorhangerButton(notif, REMEMBER_BUTTON);
|
||||||
|
});
|
||||||
|
|
||||||
|
let logins = Services.logins.getAllLogins();
|
||||||
|
is(logins.length, 2, "Should have both HTTP and HTTPS logins");
|
||||||
|
for (let login of logins) {
|
||||||
|
login = login.QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
|
is(login.username, "notifyu1", "Check the username used on the new entry");
|
||||||
|
is(login.password, "notifyp1", "Check the password used on the new entry");
|
||||||
|
is(login.timesUsed, 1, "Check times used on entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
info("Make sure Remember took effect and we don't prompt for an existing HTTP login");
|
||||||
|
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||||
|
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||||
|
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||||
|
let notif = getCaptureDoorhanger("password-save");
|
||||||
|
ok(!notif, "checking for no notification popup");
|
||||||
|
});
|
||||||
|
|
||||||
|
logins = Services.logins.getAllLogins();
|
||||||
|
is(logins.length, 2, "Should have both HTTP and HTTPS still");
|
||||||
|
|
||||||
|
let httpsLogins = LoginHelper.searchLoginsWithObject({
|
||||||
|
hostname: "https://example.com",
|
||||||
|
});
|
||||||
|
is(httpsLogins.length, 1, "Check https logins count");
|
||||||
|
let httpsLogin = httpsLogins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
|
ok(httpsLogin.equals(login1HTTPS), "Check HTTPS login didn't change");
|
||||||
|
is(httpsLogin.timesUsed, 1, "Check times used");
|
||||||
|
|
||||||
|
let httpLogins = LoginHelper.searchLoginsWithObject({
|
||||||
|
hostname: "http://example.com",
|
||||||
|
});
|
||||||
|
is(httpLogins.length, 1, "Check http logins count");
|
||||||
|
let httpLogin = httpLogins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||||
|
ok(httpLogin.equals(login1), "Check HTTP login is as expected");
|
||||||
|
is(httpLogin.timesUsed, 2, "Check times used increased");
|
||||||
|
|
||||||
|
Services.logins.removeLogin(login1);
|
||||||
|
Services.logins.removeLogin(login1HTTPS);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// * existing login test, form has different password --> change password, no save prompt
|
// * existing login test, form has different password --> change password, no save prompt
|
||||||
|
|
|
@ -39,9 +39,9 @@ function getSelectDialogDoc() {
|
||||||
|
|
||||||
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||||
Ci.nsILoginInfo, "init");
|
Ci.nsILoginInfo, "init");
|
||||||
let login1 = new nsLoginInfo("http://mochi.test:8888", "http://mochi.test:8888", null,
|
let login1 = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||||
"notifyu1", "notifyp1", "user", "pass");
|
"notifyu1", "notifyp1", "user", "pass");
|
||||||
let login1B = new nsLoginInfo("http://mochi.test:8888", "http://mochi.test:8888", null,
|
let login1B = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||||
"notifyu1B", "notifyp1B", "user", "pass");
|
"notifyu1B", "notifyp1B", "user", "pass");
|
||||||
|
|
||||||
add_task(function* test_changeUPLoginOnPUpdateForm_accept() {
|
add_task(function* test_changeUPLoginOnPUpdateForm_accept() {
|
||||||
|
|
|
@ -20,10 +20,10 @@ registerCleanupFunction(function* cleanup_removeAllLoginsAndResetRecipes() {
|
||||||
*
|
*
|
||||||
* @param {String} aPageFile - test page file name which auto-submits to formsubmit.sjs
|
* @param {String} aPageFile - test page file name which auto-submits to formsubmit.sjs
|
||||||
* @param {Function} aTaskFn - task which can be run before the tab closes.
|
* @param {Function} aTaskFn - task which can be run before the tab closes.
|
||||||
* @param {String} [aOrigin="http://mochi.test:8888"] - origin of the server to
|
* @param {String} [aOrigin="http://example.com"] - origin of the server to use
|
||||||
* use to load `aPageFile`.
|
* to load `aPageFile`.
|
||||||
*/
|
*/
|
||||||
function testSubmittingLoginForm(aPageFile, aTaskFn, aOrigin = "http://mochi.test:8888") {
|
function testSubmittingLoginForm(aPageFile, aTaskFn, aOrigin = "http://example.com") {
|
||||||
return BrowserTestUtils.withNewTab({
|
return BrowserTestUtils.withNewTab({
|
||||||
gBrowser,
|
gBrowser,
|
||||||
url: aOrigin + DIRECTORY_PATH + aPageFile,
|
url: aOrigin + DIRECTORY_PATH + aPageFile,
|
||||||
|
|
|
@ -1222,14 +1222,17 @@
|
||||||
event.preventDefault(); // Prevent page scrolling
|
event.preventDefault(); // Prevent page scrolling
|
||||||
},
|
},
|
||||||
|
|
||||||
get videoSubtitles() {
|
isSupportedTextTrack : function(textTrack) {
|
||||||
return Array.prototype.filter.call(this.video.textTracks, function (tt) {
|
return textTrack.kind == "subtitles" ||
|
||||||
return tt.kind === "subtitles";
|
textTrack.kind == "captions";
|
||||||
});
|
},
|
||||||
|
|
||||||
|
get overlayableTextTracks() {
|
||||||
|
return Array.prototype.filter.call(this.video.textTracks, this.isSupportedTextTrack);
|
||||||
},
|
},
|
||||||
|
|
||||||
isClosedCaptionOn : function () {
|
isClosedCaptionOn : function () {
|
||||||
for (let tt of this.videoSubtitles) {
|
for (let tt of this.overlayableTextTracks) {
|
||||||
if (tt.mode === "showing") {
|
if (tt.mode === "showing") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1239,7 +1242,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
setClosedCaptionButtonState : function () {
|
setClosedCaptionButtonState : function () {
|
||||||
if (!this.videoSubtitles.length || this.videocontrols.isTouchControl) {
|
if (!this.overlayableTextTracks.length || this.videocontrols.isTouchControl) {
|
||||||
this.closedCaptionButton.setAttribute("hidden", "true");
|
this.closedCaptionButton.setAttribute("hidden", "true");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1266,7 +1269,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
addNewTextTrack : function (tt) {
|
addNewTextTrack : function (tt) {
|
||||||
if (tt.kind !== "subtitles") {
|
if (!this.isSupportedTextTrack(tt)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1299,7 +1302,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
changeTextTrack : function (index) {
|
changeTextTrack : function (index) {
|
||||||
for (let tt of this.videoSubtitles) {
|
for (let tt of this.overlayableTextTracks) {
|
||||||
if (tt.index === index) {
|
if (tt.index === index) {
|
||||||
tt.mode = "showing";
|
tt.mode = "showing";
|
||||||
|
|
||||||
|
@ -1323,8 +1326,8 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleClosedCaption : function () {
|
toggleClosedCaption : function () {
|
||||||
if (this.videoSubtitles.length === 1) {
|
if (this.overlayableTextTracks.length === 1) {
|
||||||
const lastTTIdx = this.videoSubtitles[0].index;
|
const lastTTIdx = this.overlayableTextTracks[0].index;
|
||||||
|
|
||||||
return this.changeTextTrack(this.isClosedCaptionOn() ? 0 : lastTTIdx);
|
return this.changeTextTrack(this.isClosedCaptionOn() ? 0 : lastTTIdx);
|
||||||
}
|
}
|
||||||
|
@ -1391,7 +1394,7 @@
|
||||||
kind: "subtitles"
|
kind: "subtitles"
|
||||||
});
|
});
|
||||||
|
|
||||||
for (let tt of this.videoSubtitles) {
|
for (let tt of this.overlayableTextTracks) {
|
||||||
this.addNewTextTrack(tt);
|
this.addNewTextTrack(tt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,6 @@
|
||||||
locale/@AB_CD@/global/commonDialogs.properties (%chrome/global/commonDialogs.properties)
|
locale/@AB_CD@/global/commonDialogs.properties (%chrome/global/commonDialogs.properties)
|
||||||
locale/@AB_CD@/global/config.dtd (%chrome/global/config.dtd)
|
locale/@AB_CD@/global/config.dtd (%chrome/global/config.dtd)
|
||||||
locale/@AB_CD@/global/config.properties (%chrome/global/config.properties)
|
locale/@AB_CD@/global/config.properties (%chrome/global/config.properties)
|
||||||
locale/@AB_CD@/global/console.dtd (%chrome/global/console.dtd)
|
|
||||||
locale/@AB_CD@/global/console.properties (%chrome/global/console.properties)
|
|
||||||
locale/@AB_CD@/global/contentAreaCommands.properties (%chrome/global/contentAreaCommands.properties)
|
locale/@AB_CD@/global/contentAreaCommands.properties (%chrome/global/contentAreaCommands.properties)
|
||||||
#ifndef MOZ_FENNEC
|
#ifndef MOZ_FENNEC
|
||||||
locale/@AB_CD@/global/customizeToolbar.dtd (%chrome/global/customizeToolbar.dtd)
|
locale/@AB_CD@/global/customizeToolbar.dtd (%chrome/global/customizeToolbar.dtd)
|
||||||
|
|
Двоичные данные
toolkit/themes/linux/global/console/console-toolbar.png
До Ширина: | Высота: | Размер: 659 B |
|
@ -1,156 +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/. */
|
|
||||||
|
|
||||||
/* ===== console.css ====================================================
|
|
||||||
== Styles used by the Error Console window.
|
|
||||||
====================================================================== */
|
|
||||||
|
|
||||||
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
|
|
||||||
|
|
||||||
.console-box {
|
|
||||||
background-color: -moz-Field;
|
|
||||||
color: -moz-FieldText;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ::::: console rows ::::: */
|
|
||||||
|
|
||||||
.console-row {
|
|
||||||
border-bottom: 1px dotted threedshadow;
|
|
||||||
padding: 4px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row-icon {
|
|
||||||
padding: 4px;
|
|
||||||
padding-inline-start: 5px;
|
|
||||||
-moz-box-align: start !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row-msg > label:first-child,
|
|
||||||
.console-row-file > label:first-child {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-time {
|
|
||||||
font-weight: normal !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-icon {
|
|
||||||
list-style-image: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-error-msg {
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ..... error rows ..... */
|
|
||||||
|
|
||||||
.console-row-code {
|
|
||||||
padding-top: 3px;
|
|
||||||
padding-bottom: 3px;
|
|
||||||
padding-inline-start: 3px;
|
|
||||||
padding-inline-end: 0px;
|
|
||||||
color: #0000BB;
|
|
||||||
font-size: larger;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-dots,
|
|
||||||
.console-caret {
|
|
||||||
height: 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-dots {
|
|
||||||
background: url("chrome://global/skin/console/console-error-dash.gif") repeat-x top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-caret {
|
|
||||||
width: 7px;
|
|
||||||
background: url("chrome://global/skin/console/console-error-caret.gif") no-repeat top;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ..... message rows ..... */
|
|
||||||
|
|
||||||
.console-row[type="message"] {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ..... selected state ..... */
|
|
||||||
|
|
||||||
.console-row[selected="true"] {
|
|
||||||
background-image: url("chrome://global/skin/console/itemSelected.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row-code[selected="true"],
|
|
||||||
.console-row-content[selected="true"] > .console-row-file > .console-error-source > .text-link {
|
|
||||||
color: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ::::: icons ::::: */
|
|
||||||
|
|
||||||
.console-row[type="error"],
|
|
||||||
.console-row[type="exception"] {
|
|
||||||
list-style-image: url("moz-icon://stock/gtk-dialog-error?size=menu");
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row[type="error"] .console-row-msg,
|
|
||||||
.console-row[type="exception"] .console-row-msg {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row[type="warning"] {
|
|
||||||
list-style-image: url("moz-icon://stock/gtk-dialog-warning?size=menu");
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row[type="message"] {
|
|
||||||
list-style-image: url("moz-icon://stock/gtk-dialog-info?size=menu");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ::::: toolbars ::::: */
|
|
||||||
|
|
||||||
#TextboxEval {
|
|
||||||
margin: 2px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ButtonEval {
|
|
||||||
margin-top: 2px !important;
|
|
||||||
margin-bottom: 2px !important;
|
|
||||||
margin-inline-start: 0px !important;
|
|
||||||
margin-inline-end: 2px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Toolbar icons */
|
|
||||||
|
|
||||||
toolbar#ToolbarMode toolbarbutton {
|
|
||||||
-moz-box-orient: horizontal;
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeAll {
|
|
||||||
list-style-image: url("chrome://global/skin/console/console-toolbar.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeErrors {
|
|
||||||
list-style-image: url("moz-icon://stock/gtk-dialog-error?size=toolbar");
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeWarnings {
|
|
||||||
list-style-image: url("moz-icon://stock/gtk-dialog-warning?size=toolbar");
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeMessages {
|
|
||||||
list-style-image: url("moz-icon://stock/gtk-dialog-info?size=toolbar");
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:clear {
|
|
||||||
list-style-image: url("moz-icon://stock/gtk-clear?size=toolbar");
|
|
||||||
}
|
|
||||||
|
|
||||||
toolbar#ToolbarMode .toolbarbutton-text {
|
|
||||||
padding-inline-end: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ::::: Fix Error Console toolbar button text spacing ::::: */
|
|
||||||
|
|
||||||
.toolbarbutton-text {
|
|
||||||
padding-inline-start: 0px;
|
|
||||||
padding-inline-end: 5px;
|
|
||||||
}
|
|
Двоичные данные
toolkit/themes/linux/global/console/console.png
До Ширина: | Высота: | Размер: 516 B |
|
@ -35,9 +35,6 @@ toolkit.jar:
|
||||||
skin/classic/global/toolbarbutton.css
|
skin/classic/global/toolbarbutton.css
|
||||||
skin/classic/global/tree.css
|
skin/classic/global/tree.css
|
||||||
skin/classic/global/alerts/alert.css (alerts/alert.css)
|
skin/classic/global/alerts/alert.css (alerts/alert.css)
|
||||||
skin/classic/global/console/console.css (console/console.css)
|
|
||||||
skin/classic/global/console/console.png (console/console.png)
|
|
||||||
skin/classic/global/console/console-toolbar.png (console/console-toolbar.png)
|
|
||||||
skin/classic/global/dirListing/remote.png (dirListing/remote.png)
|
skin/classic/global/dirListing/remote.png (dirListing/remote.png)
|
||||||
|
|
||||||
skin/classic/global/icons/Authentication.png (icons/Authentication.png)
|
skin/classic/global/icons/Authentication.png (icons/Authentication.png)
|
||||||
|
|
Двоичные данные
toolkit/themes/osx/global/console/console-error-caret.gif
До Ширина: | Высота: | Размер: 55 B |
Двоичные данные
toolkit/themes/osx/global/console/console-error-dash.gif
До Ширина: | Высота: | Размер: 48 B |
|
@ -1,165 +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/. */
|
|
||||||
|
|
||||||
/* ===== console.css ====================================================
|
|
||||||
== Styles used by the Error Console window.
|
|
||||||
======================================================================= */
|
|
||||||
|
|
||||||
/* View buttons */
|
|
||||||
@import "chrome://global/skin/viewbuttons.css";
|
|
||||||
|
|
||||||
%include ../shared.inc
|
|
||||||
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
|
|
||||||
|
|
||||||
.console-box {
|
|
||||||
background-color: -moz-Field;
|
|
||||||
color: -moz-FieldText;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ::::: console rows ::::: */
|
|
||||||
|
|
||||||
.console-row {
|
|
||||||
border-bottom: 1px solid #A3A3A3;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row-file {
|
|
||||||
color: #505050;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row-msg > label:first-child {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row-msg > label, .comsole-row-msg > description, .console-error-msg, .console-row-file, .console-row-code {
|
|
||||||
margin: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row-file > label {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-msg-text {
|
|
||||||
white-space: pre-wrap !important;
|
|
||||||
}
|
|
||||||
.console-icon {
|
|
||||||
list-style-image: inherit;
|
|
||||||
padding-right: 6px;
|
|
||||||
padding-left: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ..... error rows ..... */
|
|
||||||
|
|
||||||
.console-row-code {
|
|
||||||
color: #0000BB;
|
|
||||||
font-size: larger;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-dots,
|
|
||||||
.console-caret {
|
|
||||||
height: 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-dots {
|
|
||||||
background: url("chrome://global/skin/console/console-error-dash.gif") repeat-x top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-caret {
|
|
||||||
width: 7px;
|
|
||||||
background: url("chrome://global/skin/console/console-error-caret.gif") no-repeat top;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ..... message rows ..... */
|
|
||||||
|
|
||||||
.console-row[type="message"] {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ..... selected state ..... */
|
|
||||||
|
|
||||||
.console-row[selected="true"] {
|
|
||||||
background-color: #3D80DF !important;
|
|
||||||
color: #FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row-code[selected="true"],
|
|
||||||
.console-row-content[selected="true"] > .console-row-file,
|
|
||||||
.console-row-content[selected="true"] > .console-row-file > .console-error-source > .text-link {
|
|
||||||
color: #FFF !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ::::: row colors ::::: */
|
|
||||||
|
|
||||||
.console-row[type="error"],
|
|
||||||
.console-row[type="exception"] {
|
|
||||||
background-color: #FFD0DC;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row[type="warning"] {
|
|
||||||
background-color: #F8F3CC;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row[type="message"] {
|
|
||||||
background-color: #D3EDFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ::::: toolbars ::::: */
|
|
||||||
|
|
||||||
#ToolbarEval {
|
|
||||||
-moz-appearance: none;
|
|
||||||
background: @scopeBarBackground@;
|
|
||||||
border-bottom: @scopeBarSeparatorBorder@;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ToolbarEval > label {
|
|
||||||
font-weight: bold;
|
|
||||||
color: @scopeBarTitleColor@;
|
|
||||||
}
|
|
||||||
|
|
||||||
#TextfieldEval {
|
|
||||||
margin: 2px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ButtonEval {
|
|
||||||
margin: 0 4px;
|
|
||||||
padding: 1px 10px;
|
|
||||||
-moz-appearance: none;
|
|
||||||
border-radius: 10000px;
|
|
||||||
border: @roundButtonBorder@;
|
|
||||||
background: @roundButtonBackground@;
|
|
||||||
box-shadow: @roundButtonShadow@;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ButtonEval:hover:active {
|
|
||||||
text-shadow: @loweredShadow@;
|
|
||||||
background: @roundButtonPressedBackground@;
|
|
||||||
box-shadow: @roundButtonPressedShadow@;
|
|
||||||
}
|
|
||||||
|
|
||||||
toolbarseparator {
|
|
||||||
min-height: 1em;
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Toolbar icons */
|
|
||||||
|
|
||||||
#ToolbarMode {
|
|
||||||
-moz-box-pack: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ToolbarMode toolbarbutton > .toolbarbutton-icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:clear {
|
|
||||||
-moz-box-orient: vertical;
|
|
||||||
-moz-box-align: center;
|
|
||||||
-moz-appearance: toolbarbutton;
|
|
||||||
font: menu;
|
|
||||||
text-shadow: @loweredShadow@;
|
|
||||||
margin: 4px 0 9px;
|
|
||||||
padding: 0 1px;
|
|
||||||
}
|
|
|
@ -74,9 +74,6 @@ toolkit.jar:
|
||||||
skin/classic/global/arrow/panelarrow-vertical@2x.png (arrow/panelarrow-vertical@2x.png)
|
skin/classic/global/arrow/panelarrow-vertical@2x.png (arrow/panelarrow-vertical@2x.png)
|
||||||
skin/classic/global/checkbox/cbox-check.gif (checkbox/cbox-check.gif)
|
skin/classic/global/checkbox/cbox-check.gif (checkbox/cbox-check.gif)
|
||||||
skin/classic/global/checkbox/cbox-check-dis.gif (checkbox/cbox-check-dis.gif)
|
skin/classic/global/checkbox/cbox-check-dis.gif (checkbox/cbox-check-dis.gif)
|
||||||
skin/classic/global/console/console-error-caret.gif (console/console-error-caret.gif)
|
|
||||||
skin/classic/global/console/console-error-dash.gif (console/console-error-dash.gif)
|
|
||||||
* skin/classic/global/console/console.css (console/console.css)
|
|
||||||
skin/classic/global/dirListing/dirListing.css (dirListing/dirListing.css)
|
skin/classic/global/dirListing/dirListing.css (dirListing/dirListing.css)
|
||||||
skin/classic/global/dirListing/folder.png (dirListing/folder.png)
|
skin/classic/global/dirListing/folder.png (dirListing/folder.png)
|
||||||
skin/classic/global/dirListing/remote.png (dirListing/remote.png)
|
skin/classic/global/dirListing/remote.png (dirListing/remote.png)
|
||||||
|
|
|
@ -53,9 +53,6 @@
|
||||||
|
|
||||||
skin/classic/global/checkbox/cbox-check.gif (../../windows/global/checkbox/cbox-check.gif)
|
skin/classic/global/checkbox/cbox-check.gif (../../windows/global/checkbox/cbox-check.gif)
|
||||||
skin/classic/global/checkbox/cbox-check-dis.gif (../../windows/global/checkbox/cbox-check-dis.gif)
|
skin/classic/global/checkbox/cbox-check-dis.gif (../../windows/global/checkbox/cbox-check-dis.gif)
|
||||||
skin/classic/global/console/console-error-caret.gif (../../windows/global/console/console-error-caret.gif)
|
|
||||||
skin/classic/global/console/console-error-dash.gif (../../windows/global/console/console-error-dash.gif)
|
|
||||||
skin/classic/global/console/itemSelected.png (../../windows/global/console/itemSelected.png)
|
|
||||||
* skin/classic/global/dirListing/dirListing.css (../../windows/global/dirListing/dirListing.css)
|
* skin/classic/global/dirListing/dirListing.css (../../windows/global/dirListing/dirListing.css)
|
||||||
skin/classic/global/dirListing/folder.png (../../windows/global/dirListing/folder.png)
|
skin/classic/global/dirListing/folder.png (../../windows/global/dirListing/folder.png)
|
||||||
skin/classic/global/dirListing/local.png (../../windows/global/dirListing/local.png)
|
skin/classic/global/dirListing/local.png (../../windows/global/dirListing/local.png)
|
||||||
|
|
Двоичные данные
toolkit/themes/windows/global/console/console-error-caret.gif
До Ширина: | Высота: | Размер: 55 B |
Двоичные данные
toolkit/themes/windows/global/console/console-error-dash.gif
До Ширина: | Высота: | Размер: 48 B |
Двоичные данные
toolkit/themes/windows/global/console/console-toolbar-XP.png
До Ширина: | Высота: | Размер: 9.1 KiB |
Двоичные данные
toolkit/themes/windows/global/console/console-toolbar.png
До Ширина: | Высота: | Размер: 5.7 KiB |
|
@ -1,220 +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/. */
|
|
||||||
|
|
||||||
/* ===== console.css ====================================================
|
|
||||||
== Styles used by the Error Console window.
|
|
||||||
====================================================================== */
|
|
||||||
|
|
||||||
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
|
|
||||||
|
|
||||||
.console-box {
|
|
||||||
background-color: -moz-Field;
|
|
||||||
color: -moz-FieldText;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ::::: console rows ::::: */
|
|
||||||
|
|
||||||
.console-row {
|
|
||||||
border-bottom: 1px solid ThreeDLightShadow;
|
|
||||||
padding: 4px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row-icon {
|
|
||||||
padding: 4px;
|
|
||||||
padding-inline-start: 5px;
|
|
||||||
-moz-box-align: start !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row-msg > label:first-child,
|
|
||||||
.console-row-file > label:first-child {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-time {
|
|
||||||
font-weight: normal !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-icon {
|
|
||||||
list-style-image: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-error-msg {
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ..... error rows ..... */
|
|
||||||
|
|
||||||
.console-row-code {
|
|
||||||
padding-top: 3px;
|
|
||||||
padding-bottom: 3px;
|
|
||||||
padding-inline-start: 3px;
|
|
||||||
padding-inline-end: 0px;
|
|
||||||
color: #0000BB;
|
|
||||||
font-size: larger;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-dots,
|
|
||||||
.console-caret {
|
|
||||||
height: 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-dots {
|
|
||||||
background: url("chrome://global/skin/console/console-error-dash.gif") repeat-x top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-caret {
|
|
||||||
width: 7px;
|
|
||||||
background: url("chrome://global/skin/console/console-error-caret.gif") no-repeat top;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ..... message rows ..... */
|
|
||||||
|
|
||||||
.console-row[type="message"] {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ..... selected state ..... */
|
|
||||||
|
|
||||||
.console-row[selected="true"] {
|
|
||||||
background-image: url("chrome://global/skin/console/itemSelected.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row-code[selected="true"],
|
|
||||||
.console-row-content[selected="true"] > .console-row-file > .console-error-source > .text-link {
|
|
||||||
color: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ::::: icons ::::: */
|
|
||||||
|
|
||||||
.console-row[type="error"],
|
|
||||||
.console-row[type="exception"] {
|
|
||||||
list-style-image: url("chrome://global/skin/icons/error-16.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row[type="error"] .console-row-msg,
|
|
||||||
.console-row[type="exception"] .console-row-msg {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row[type="warning"] {
|
|
||||||
list-style-image: url("chrome://global/skin/icons/warning-16.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-row[type="message"] {
|
|
||||||
list-style-image: url("chrome://global/skin/icons/information-16.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ::::: toolbars ::::: */
|
|
||||||
|
|
||||||
#TextboxEval {
|
|
||||||
margin: 2px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ButtonEval {
|
|
||||||
margin-top: 2px !important;
|
|
||||||
margin-bottom: 2px !important;
|
|
||||||
margin-inline-start: 0px !important;
|
|
||||||
margin-inline-end: 2px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
toolbarseparator {
|
|
||||||
min-height: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Toolbar icons */
|
|
||||||
|
|
||||||
#ToolbarMode toolbarbutton {
|
|
||||||
min-width: 57px;
|
|
||||||
padding: 4px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
toolbar#ToolbarMode toolbarbutton:active,
|
|
||||||
toolbar#ToolbarMode toolbarbutton[checked="true"] {
|
|
||||||
padding-inline-start: 5px !important;
|
|
||||||
padding-inline-end: 3px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
toolbar#ToolbarMode toolbarbutton {
|
|
||||||
list-style-image: url("chrome://global/skin/console/console-toolbar.png");
|
|
||||||
-moz-box-orient: horizontal;
|
|
||||||
padding: 4px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeAll {
|
|
||||||
-moz-image-region: rect(0px 24px 24px 0px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeAll {
|
|
||||||
-moz-image-region: rect(0px 24px 24px 0px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeAll:hover,
|
|
||||||
#Console\:modeAll[checked="true"] {
|
|
||||||
-moz-image-region: rect(24px 24px 48px 0px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeErrors {
|
|
||||||
-moz-image-region: rect(0px 96px 24px 72px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeErrors:hover,
|
|
||||||
#Console\:modeErrors[checked="true"] {
|
|
||||||
-moz-image-region: rect(24px 96px 48px 72px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeWarnings {
|
|
||||||
-moz-image-region: rect(0px 72px 24px 48px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeWarnings:hover,
|
|
||||||
#Console\:modeWarnings[checked="true"] {
|
|
||||||
-moz-image-region: rect(24px 72px 48px 48px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeMessages {
|
|
||||||
-moz-image-region: rect(0px 48px 24px 24px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:modeMessages:hover,
|
|
||||||
#Console\:modeMessages[checked="true"] {
|
|
||||||
-moz-image-region: rect(24px 48px 48px 24px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:clear {
|
|
||||||
-moz-image-region: rect(0px 120px 24px 96px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#Console\:clear:hover,
|
|
||||||
#Console\:clear[checked="true"] {
|
|
||||||
-moz-image-region: rect(24px 120px 48px 96px);
|
|
||||||
}
|
|
||||||
|
|
||||||
toolbar#ToolbarMode .toolbarbutton-icon {
|
|
||||||
padding: 2px 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
toolbar#ToolbarMode .toolbarbutton-text {
|
|
||||||
padding-inline-end: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ::::: Fix Error Console toolbar button text spacing ::::: */
|
|
||||||
|
|
||||||
.toolbarbutton-text {
|
|
||||||
padding-inline-start: 0px;
|
|
||||||
padding-inline-end: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
%ifdef XP_WIN
|
|
||||||
@media not all and (-moz-os-version: windows-xp) {
|
|
||||||
#ToolbarMode {
|
|
||||||
-moz-appearance: -moz-win-browsertabbar-toolbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ToolbarEval {
|
|
||||||
-moz-appearance: toolbox;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
%endif
|
|
Двоичные данные
toolkit/themes/windows/global/console/itemSelected.png
До Ширина: | Высота: | Размер: 459 B |
|
@ -37,8 +37,6 @@ toolkit.jar:
|
||||||
skin/classic/global/toolbarbutton.css
|
skin/classic/global/toolbarbutton.css
|
||||||
* skin/classic/global/tree.css
|
* skin/classic/global/tree.css
|
||||||
skin/classic/global/alerts/alert.css (alerts/alert.css)
|
skin/classic/global/alerts/alert.css (alerts/alert.css)
|
||||||
* skin/classic/global/console/console.css (console/console.css)
|
|
||||||
skin/classic/global/console/console-toolbar.png (console/console-toolbar.png)
|
|
||||||
skin/classic/global/dirListing/remote.png (dirListing/remote.png)
|
skin/classic/global/dirListing/remote.png (dirListing/remote.png)
|
||||||
skin/classic/global/icons/autocomplete-search.svg (icons/autocomplete-search.svg)
|
skin/classic/global/icons/autocomplete-search.svg (icons/autocomplete-search.svg)
|
||||||
skin/classic/global/icons/blacklist_favicon.png (icons/blacklist_favicon.png)
|
skin/classic/global/icons/blacklist_favicon.png (icons/blacklist_favicon.png)
|
||||||
|
@ -55,7 +53,6 @@ toolkit.jar:
|
||||||
* skin/classic/global/in-content/info-pages.css (in-content/info-pages.css)
|
* skin/classic/global/in-content/info-pages.css (in-content/info-pages.css)
|
||||||
skin/classic/global/toolbar/spring.png (toolbar/spring.png)
|
skin/classic/global/toolbar/spring.png (toolbar/spring.png)
|
||||||
|
|
||||||
skin/classic/global/console/console-toolbar-XP.png (console/console-toolbar-XP.png)
|
|
||||||
skin/classic/global/dirListing/folder-XP.png (dirListing/folder-XP.png)
|
skin/classic/global/dirListing/folder-XP.png (dirListing/folder-XP.png)
|
||||||
skin/classic/global/dirListing/local-XP.png (dirListing/local-XP.png)
|
skin/classic/global/dirListing/local-XP.png (dirListing/local-XP.png)
|
||||||
skin/classic/global/dirListing/remote-XP.png (dirListing/remote-XP.png)
|
skin/classic/global/dirListing/remote-XP.png (dirListing/remote-XP.png)
|
||||||
|
@ -101,7 +98,6 @@ toolkit.jar:
|
||||||
#elif MOZ_SEPARATE_MANIFEST_FOR_THEME_OVERRIDES
|
#elif MOZ_SEPARATE_MANIFEST_FOR_THEME_OVERRIDES
|
||||||
[extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
|
[extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
|
||||||
#endif
|
#endif
|
||||||
% override chrome://global/skin/console/console-toolbar.png chrome://global/skin/console/console-toolbar-XP.png osversion<6
|
|
||||||
% override chrome://global/skin/dirListing/folder.png chrome://global/skin/dirListing/folder-XP.png osversion<6
|
% override chrome://global/skin/dirListing/folder.png chrome://global/skin/dirListing/folder-XP.png osversion<6
|
||||||
% override chrome://global/skin/dirListing/local.png chrome://global/skin/dirListing/local-XP.png osversion<6
|
% override chrome://global/skin/dirListing/local.png chrome://global/skin/dirListing/local-XP.png osversion<6
|
||||||
% override chrome://global/skin/dirListing/remote.png chrome://global/skin/dirListing/remote-XP.png osversion<6
|
% override chrome://global/skin/dirListing/remote.png chrome://global/skin/dirListing/remote-XP.png osversion<6
|
||||||
|
|