This commit is contained in:
Phil Ringnalda 2016-06-25 16:34:13 -07:00
Родитель 5bb4252ff6 98d7a9d9c5
Коммит 18e2692832
97 изменённых файлов: 3119 добавлений и 2986 удалений

Просмотреть файл

@ -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="&copyCmd.label;" accesskey="&copyCmd.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)

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 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)

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 55 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 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)

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 55 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 48 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 9.1 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 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

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 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