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/storage-json.js
@RESPATH@/components/crypto-SDR.js
@RESPATH@/components/jsconsole-clhandler.manifest
@RESPATH@/components/jsconsole-clhandler.js
@RESPATH@/components/Downloads.manifest
@RESPATH@/components/DownloadLegacy.js
@RESPATH@/components/nsSidebar.manifest

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

@ -1432,3 +1432,5 @@ pref("browser.migration.automigrate", false);
pref("dom.mozBrowserFramesEnabled", true);
pref("extensions.pocket.enabled", true);
pref("signon.schemeUpgrades", true);

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

@ -3911,11 +3911,6 @@ function addToUrlbarHistory(aUrlToAdd) {
PlacesUIUtils.markPageAsTyped(aUrlToAdd);
}
function toJavaScriptConsole()
{
toOpenWindowByType("global:console", "chrome://global/content/console.xul");
}
function BrowserDownloadsUI()
{
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
@ -7242,8 +7237,13 @@ var gIdentityHandler = {
label.setAttribute("control", menulist.getAttribute("id"));
label.textContent = aPermission.label;
let img = document.createElement("image");
img.setAttribute("class",
"identity-popup-permission-icon " + aPermission.id + "-icon");
let container = document.createElement("hbox");
container.setAttribute("align", "center");
container.appendChild(img);
container.appendChild(label);
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
[browser_bug563588.js]
[browser_bug565575.js]
[browser_bug565667.js]
skip-if = toolkit != "cocoa"
[browser_bug567306.js]
subsuite = clipboard
[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");
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._identityBox.click();

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

@ -5,7 +5,6 @@ browser.jar:
% content browser %content/browser/ contentaccessible=yes
#ifdef XP_MACOSX
% 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
#endif
#ifdef XP_WIN
@ -180,7 +179,6 @@ browser.jar:
#ifdef XP_MACOSX
* content/browser/macBrowserOverlay.xul (content/macBrowserOverlay.xul)
* content/browser/downloadManagerOverlay.xul (content/downloadManagerOverlay.xul)
* content/browser/jsConsoleOverlay.xul (content/jsConsoleOverlay.xul)
* content/browser/softwareUpdateOverlay.xul (content/softwareUpdateOverlay.xul)
#endif
* content/browser/viewSourceOverlay.xul (content/viewSourceOverlay.xul)

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

@ -87,7 +87,8 @@
<!-- Permissions Section -->
<hbox class="identity-popup-section">
<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;"/>
<vbox id="identity-popup-permission-list"/>
<description>&identity.permissionsEmpty;</description>

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

@ -302,9 +302,8 @@ HistoryDownloadElementShell.prototype = {
if (this.element.selected) {
goUpdateDownloadCommands();
} else {
goUpdateCommand("downloadsCmd_clearDownloads");
}
goUpdateCommand("downloadsCmd_clearDownloads");
},
onChanged() {

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

@ -15,12 +15,13 @@ function test() {
let win = OpenBrowserWindow({private: true});
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, false);
let consoleWin = win.open("chrome://global/content/console.xul", "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
consoleWin.addEventListener("load", function consoleLoad() {
consoleWin.removeEventListener("load", consoleLoad, false);
let chromeWin = win.open("chrome://browser/content/places/places.xul",
"_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
chromeWin.addEventListener("load", function chromeWinLoad() {
chromeWin.removeEventListener("load", chromeWinLoad, false);
win.close();
}, false);
windowsToClose.push(consoleWin);
windowsToClose.push(chromeWin);
}, false);
let observer = function() {

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

@ -1,3 +1,3 @@
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) {
throw new Error('FindBar is not accessible from the content process.');
}
var browser = getContainingBrowser(domWindow);
try {
var browser = getContainingBrowser(domWindow);
var tabbrowser = browser.getTabBrowser();
var tab;
tab = tabbrowser.getTabForBrowser(browser);
var tab = tabbrowser.getTabForBrowser(browser);
return tabbrowser.getFindBar(tab);
} catch (e) {
try {
// FF22 has no _getTabForBrowser, and FF24 has no getFindBar
var chromeWindow = browser.ownerDocument.defaultView;
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;
}
// 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;
}
function makeContentReadable(obj, window) {
/* jshint -W027 */
return Cu.cloneInto(obj, window);
}
function createNewChannel(uri) {
function createNewChannel(uri, node) {
return NetUtil.newChannel({
uri: uri,
loadUsingSystemPrincipal: true
loadUsingSystemPrincipal: true,
});
}
function asyncOpenChannel(channel, listener, context) {
return channel.asyncOpen2(listener);
}
function asyncFetchChannel(channel, callback) {
return NetUtil.asyncFetch(channel, callback);
}
@ -264,7 +256,7 @@ ChromeActions.prototype = {
getService(Ci.nsIExternalHelperAppService);
var docIsPrivate = this.isInPrivateBrowsing();
var netChannel = createNewChannel(blobUri);
var netChannel = createNewChannel(blobUri, this.domWindow.document);
if ('nsIPrivateBrowsingChannel' in Ci &&
netChannel instanceof Ci.nsIPrivateBrowsingChannel) {
netChannel.setPrivate(docIsPrivate);
@ -327,7 +319,7 @@ ChromeActions.prototype = {
}
};
channel.asyncOpen2(listener);
asyncOpenChannel(channel, listener, null);
});
},
getLocale: function() {
@ -367,11 +359,7 @@ ChromeActions.prototype = {
return (!!prefBrowser && prefGfx);
},
supportsDocumentColors: function() {
if (getIntPref('browser.display.document_color_use', 0) === 2 ||
!getBoolPref('browser.display.use_document_colors', true)) {
return false;
}
return true;
return getIntPref('browser.display.document_color_use', 0) !== 2;
},
supportedMouseWheelZoomModifierKeys: function() {
return {
@ -783,7 +771,7 @@ RequestListener.prototype.receive = function(event) {
var response;
if (sync) {
response = actions[action].call(this.actions, data);
event.detail.response = makeContentReadable(response, doc.defaultView);
event.detail.response = Cu.cloneInto(response, doc.defaultView);
} else {
if (!event.detail.responseExpected) {
doc.documentElement.removeChild(message);
@ -792,8 +780,7 @@ RequestListener.prototype.receive = function(event) {
response = function sendResponse(response) {
try {
var listener = doc.createEvent('CustomEvent');
let detail = makeContentReadable({response: response},
doc.defaultView);
let detail = Cu.cloneInto({ response: response }, doc.defaultView);
listener.initCustomEvent('pdf.js.response', true, false, detail);
return message.dispatchEvent(listener);
} catch (e) {
@ -837,7 +824,7 @@ FindEventManager.prototype.receiveMessage = function(msg) {
var type = msg.data.type;
var contentWindow = this.contentWindow;
detail = makeContentReadable(detail, contentWindow);
detail = Cu.cloneInto(detail, contentWindow);
var forward = contentWindow.document.createEvent('CustomEvent');
forward.initCustomEvent(type, true, true, detail);
contentWindow.dispatchEvent(forward);
@ -972,7 +959,7 @@ PdfStreamConverter.prototype = {
.createInstance(Ci.nsIBinaryInputStream);
// 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 dataListener = this.dataListener;
@ -1024,18 +1011,17 @@ PdfStreamConverter.prototype = {
channel.loadGroup = aRequest.loadGroup;
channel.loadInfo.originAttributes = aRequest.loadInfo.originAttributes;
// We can use resource principal when data is fetched by the chrome
// make sure we reuse the origin attributes from the request channel to keep
// isolation consistent.
// e.g. useful for NoScript
// We can use the resource principal when data is fetched by the chrome,
// e.g. useful for NoScript. Make make sure we reuse the origin attributes
// from the request channel to keep isolation consistent.
var ssm = Cc['@mozilla.org/scriptsecuritymanager;1']
.getService(Ci.nsIScriptSecurityManager);
var uri = NetUtil.newURI(PDF_VIEWER_WEB_PAGE, null, null);
var attrs = aRequest.loadInfo.originAttributes;
var resourcePrincipal;
resourcePrincipal = ssm.createCodebasePrincipal(uri, attrs);
resourcePrincipal =
ssm.createCodebasePrincipal(uri, aRequest.loadInfo.originAttributes);
aRequest.owner = resourcePrincipal;
channel.asyncOpen2(proxy);
asyncOpenChannel(channel, proxy, aContext);
},
// nsIRequestObserver::onStopRequest

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

@ -181,8 +181,7 @@ var PdfjsChromeUtils = {
_findbarFromMessage: function(aMsg) {
let browser = aMsg.target;
let tabbrowser = browser.getTabBrowser();
let tab;
tab = tabbrowser.getTabForBrowser(browser);
let tab = tabbrowser.getTabForBrowser(browser);
return tabbrowser.getFindBar(tab);
},

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

@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdf = {}));
// Use strict in our context only - users might not want it
'use strict';
var pdfjsVersion = '1.5.281';
var pdfjsBuild = '5a5bb99';
var pdfjsVersion = '1.5.305';
var pdfjsBuild = '546e223';
var pdfjsFilePath =
typeof document !== 'undefined' && document.currentScript ?
@ -1070,6 +1070,11 @@ function isArrayBuffer(v) {
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.
*
@ -1441,6 +1446,7 @@ exports.isEmptyObj = isEmptyObj;
exports.isInt = isInt;
exports.isNum = isNum;
exports.isString = isString;
exports.isSpace = isSpace;
exports.isSameOrigin = isSameOrigin;
exports.isValidUrl = isValidUrl;
exports.isLittleEndian = isLittleEndian;
@ -7815,7 +7821,12 @@ var WorkerTransport = (function WorkerTransportClosure() {
},
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) {

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -18,6 +18,7 @@
var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
var pdfjsWebLibs = {
pdfjsWebPDFJS: window.pdfjsDistBuildPdf
};
@ -4590,6 +4591,11 @@ exports.PDFFindBar = PDFFindBar;
var parseQueryString = uiUtils.parseQueryString;
var PageNumberRegExp = /^\d+$/;
function isPageNumber(str) {
return PageNumberRegExp.test(str);
}
/**
* @typedef {Object} PDFLinkServiceOptions
* @property {EventBus} eventBus - The application event bus.
@ -4601,7 +4607,7 @@ var parseQueryString = uiUtils.parseQueryString;
* @class
* @implements {IPDFLinkService}
*/
var PDFLinkService = (function () {
var PDFLinkService = (function PDFLinkServiceClosure() {
/**
* @constructs PDFLinkService
* @param {PDFLinkServiceOptions} options
@ -4661,7 +4667,7 @@ var PDFLinkService = (function () {
var self = this;
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 ?
self._pagesRefCache[destRef.num + ' ' + destRef.gen + ' R'] :
(destRef + 1);
@ -4711,30 +4717,15 @@ var PDFLinkService = (function () {
*/
getDestinationHash: function PDFLinkService_getDestinationHash(dest) {
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) {
var destRef = dest[0]; // see navigateTo method for dest format
var pageNumber = destRef instanceof Object ?
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;
}
var str = JSON.stringify(dest);
return this.getAnchorUrl('#' + escape(str));
}
return this.getAnchorUrl('');
},
@ -4753,6 +4744,7 @@ var PDFLinkService = (function () {
* @param {string} hash
*/
setHash: function PDFLinkService_setHash(hash) {
var pageNumber, dest;
if (hash.indexOf('=') >= 0) {
var params = parseQueryString(hash);
if ('search' in params) {
@ -4770,7 +4762,6 @@ var PDFLinkService = (function () {
this.navigateTo(params.nameddest);
return;
}
var pageNumber, dest;
if ('page' in params) {
pageNumber = (params.page | 0) || 1;
}
@ -4820,13 +4811,23 @@ var PDFLinkService = (function () {
mode: params.pagemode
});
}
} else if (/^\d+$/.test(hash)) { // page number
this.page = hash;
} else { // named destination
if (this.pdfHistory) {
this.pdfHistory.updateNextHashParam(unescape(hash));
} else if (isPageNumber(hash)) { // Page number.
this.page = hash | 0;
} else { // Named (or explicit) destination.
dest = 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;
})();
@ -6723,6 +6778,8 @@ var PDFViewer = (function pdfViewer() {
scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
break;
default:
console.error('PDFViewer_scrollPageIntoView: \'' + dest[1].name +
'\' is not a valid destination type.');
return;
}
@ -7692,6 +7749,7 @@ var PDFViewerApplication = {
pdfViewer.setDocument(pdfDocument);
var firstPagePromise = pdfViewer.firstPagePromise;
var pagesPromise = pdfViewer.pagesPromise;
var onePageRendered = pdfViewer.onePageRendered;
this.pageRotation = 0;
@ -7797,9 +7855,8 @@ var PDFViewerApplication = {
}
});
// outline depends on pagesRefMap
var promises = [pagesPromise, this.animationStartedPromise];
Promise.all(promises).then(function() {
Promise.all([onePageRendered, this.animationStartedPromise]).then(
function() {
pdfDocument.getOutline().then(function(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.
window.FirefoxCom = pdfjsWebLibs.pdfjsWebFirefoxCom.FirefoxCom;
function getViewerConfiguration() {
return {
appContainer: document.body,

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

@ -392,8 +392,6 @@
@RESPATH@/components/crypto-SDR.js
@RESPATH@/components/TooltipTextProvider.js
@RESPATH@/components/TooltipTextProvider.manifest
@RESPATH@/components/jsconsole-clhandler.manifest
@RESPATH@/components/jsconsole-clhandler.js
@RESPATH@/components/webvtt.xpt
@RESPATH@/components/WebVTT.manifest
@RESPATH@/components/WebVTTParserWrapper.js

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

@ -253,4 +253,3 @@ var gPermissionObject = {
};
const kPermissionIDs = Object.keys(gPermissionObject);

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

@ -349,6 +349,19 @@ description#identity-popup-content-verifier,
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 {
min-width: 60px;
}
@ -361,8 +374,12 @@ description#identity-popup-content-verifier,
display: none;
}
.identity-popup-permission-label {
margin-inline-start: 0;
word-wrap: break-word;
.identity-popup-permission-icon {
width: 16px;
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-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="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" />
</svg>

До

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

После

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

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

@ -81,6 +81,7 @@
.login-icon,
.microphone-icon,
.pointerLock-icon,
.popup-icon,
.screen-icon,
.desktop-notification-icon,
.popup-notification-icon[popupid="geolocation"],
@ -179,6 +180,10 @@
list-style-image: url(chrome://browser/skin/glyphs.svg#pointerLock);
}
.popup-icon {
list-style-image: url("chrome://browser/skin/glyphs.svg#popup");
}
/* EME */
.popup-notification-icon[popupid="drmContentPlaying"],

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

@ -1,8 +1,3 @@
component {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32} devtools-startup.js
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

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

@ -424,7 +424,7 @@ TabTarget.prototype = {
this._title = this._form.title;
attachTab();
});
}, e => this._remote.reject(e));
} else if (this.isTabActor) {
// In the remote debugging case, the protocol connection will have been
// already initialized in the connection screen code.

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

@ -6,4 +6,6 @@
DevToolsModules(
'swap.js',
'tunnel.js',
'web-navigation.js',
)

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

@ -6,6 +6,7 @@
const promise = require("promise");
const { Task } = require("devtools/shared/task");
const { tunnelToInnerBrowser } = require("./tunnel");
/**
* 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 }) {
let gBrowser = tab.ownerDocument.defaultView.gBrowser;
let innerBrowser;
let tunnel;
return {
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.
let containerTab = gBrowser.addTab(containerURL, {
skipAnimation: true,
@ -78,32 +83,47 @@ function swapToInnerBrowser({ tab, containerURL, getInnerBrowser }) {
// original browser tab and close the temporary tab used to load the
// tool via `swapBrowsersAndCloseOther`.
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() {
// 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", {
skipAnimation: true,
});
gBrowser.hideTab(contentTab);
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.
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
// `gBrowser._swapBrowserDocShells`.
gBrowser._swapBrowserDocShells(contentTab, innerBrowser);
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
// into this tab.
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
// `swapBrowsersAndCloseOther`.
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
* 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_02.js]
[browser_mouse_resize.js]
[browser_navigation.js]
[browser_page_state.js]
[browser_resize_cmd.js]
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
let tab = yield addTab(DUMMY_1_URL);
let browser = tab.linkedBrowser;
let loaded = BrowserTestUtils.browserLoaded(browser, false, TEST_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;
yield load(browser, TEST_URL);
yield load(browser, DUMMY_2_URL);
// Check session history state
let history = yield getSessionHistory(browser);
@ -35,9 +29,7 @@ add_task(function* () {
is(history.entries[2].uri, DUMMY_2_URL, "Page 2 URL matches");
// Go back one so we're at the test page
let shown = waitForPageShow(browser);
browser.goBack();
yield shown;
yield back(browser);
// Check session history state
history = yield getSessionHistory(browser);
@ -82,41 +74,3 @@ add_task(function* () {
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>
<html>
<style>
body {
height: 100vh;
background: red;
}
body.modified {
background: green;
}
</style>
<head>
<title>Page State Test</title>
<style>
body {
height: 100vh;
background: red;
}
body.modified {
background: green;
}
</style>
</head>
<body onclick="this.classList.add('modified')"/>
</html>

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

@ -153,3 +153,59 @@ function openDeviceModal(ui) {
ok(!modal.classList.contains("hidden"),
"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_02.html]
[test_notification_box_03.html]
[test_reps_attribute.html]
[test_reps_date-time.html]
[test_reps_grip.html]
[test_reps_object-with-url.html]
[test_reps_stylesheet.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
browser tab and close the temporary tab used to load the tool via
`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
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.
1. Create a temporary, hidden tab to hold the content.
2. Mark the content tab browser's docshell as active so the frame is created
1. Stop the tunnel between outer and inner browsers.
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.
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
`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.
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`.
## Session Restore

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

@ -1638,11 +1638,14 @@ RootClient.prototype = {
if (browser.frameLoader.tabParent) {
// Tabs in child process
packet.tabId = browser.frameLoader.tabParent.tabId;
} else if (browser.outerWindowID) {
// <xul:browser> tabs in parent process
packet.outerWindowID = browser.outerWindowID;
} else {
// Tabs in parent process
// <iframe mozbrowser> tabs in parent process
let windowUtils = browser.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
packet.outerWindowID = windowUtils.outerWindowID;
}
} else {

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

@ -117,13 +117,6 @@
<category android:name="android.intent.category.DEFAULT" />
</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>
<action android:name="org.mozilla.gecko.UPDATE"/>
<category android:name="android.intent.category.DEFAULT" />
@ -247,6 +240,17 @@
</intent-filter>
</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
#ifdef MOZ_ANDROID_SEARCH_ACTIVITY
#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.SuggestedSites;
import org.mozilla.gecko.delegates.BrowserAppDelegate;
import org.mozilla.gecko.delegates.OfflineTabStatusDelegate;
import org.mozilla.gecko.delegates.ScreenshotDelegate;
import org.mozilla.gecko.distribution.Distribution;
import org.mozilla.gecko.distribution.DistributionStoreCallback;
@ -312,7 +313,8 @@ public class BrowserApp extends GeckoApp
(BrowserAppDelegate) new ReaderViewBookmarkPromotion(),
(BrowserAppDelegate) new ContentNotificationsDelegate(),
(BrowserAppDelegate) new PostUpdateHandler(),
new TelemetryCorePingDelegate()
new TelemetryCorePingDelegate(),
new OfflineTabStatusDelegate()
));
@NonNull

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

@ -1630,8 +1630,6 @@ public abstract class GeckoApp
if (ACTION_ALERT_CALLBACK.equals(action)) {
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 android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
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 PRIORITY_ATTR = "priority";
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 ACTION_ID_ATTR = "buttonId";
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 HANDLER_ATTR = "handlerKey";
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 BUTTON_EVENT = "notification-button-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";
static final String CLEARED_EVENT = "notification-cleared";
static final String ORIGINAL_EXTRA_COMPONENT = "originalComponent";
private final Context mContext;
@ -107,40 +110,17 @@ public final class NotificationHelper implements GeckoEventListener {
return i.getBooleanExtra(HELPER_NOTIFICATION, false);
}
public void handleNotificationIntent(SafeIntent i) {
final Uri data = i.getData();
if (data == null) {
Log.e(LOGTAG, "handleNotificationEvent: empty data");
return;
}
final String id = data.getQueryParameter(ID_ATTR);
public static void getArgsAndSendNotificationIntent(SafeIntent intent) {
final JSONObject args = new JSONObject();
final Uri data = intent.getData();
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 {
args.put(ID_ATTR, id);
args.put(ID_ATTR, data.getQueryParameter(ID_ATTR));
args.put(EVENT_TYPE_ATTR, notificationType);
args.put(HANDLER_ATTR, handler);
args.put(COOKIE_ATTR, cookie);
args.put(HANDLER_ATTR, data.getQueryParameter(HANDLER_ATTR));
args.put(COOKIE_ATTR, data.getQueryParameter(COOKIE_ATTR));
if (BUTTON_EVENT.equals(notificationType)) {
final String actionName = data.getQueryParameter(ACTION_ID_ATTR);
@ -152,14 +132,28 @@ public final class NotificationHelper implements GeckoEventListener {
} catch (JSONException 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
// sending the event to js side because when the notification is canceled no event can be
// handled.
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);
}
}
private Uri.Builder getNotificationBuilder(JSONObject message, String type) {
@ -192,15 +186,18 @@ public final class NotificationHelper implements GeckoEventListener {
notificationIntent.setData(dataUri);
notificationIntent.putExtra(HELPER_NOTIFICATION, true);
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;
}
private PendingIntent buildNotificationPendingIntent(JSONObject message, String type) {
Uri.Builder builder = getNotificationBuilder(message, type);
final Intent notificationIntent = buildNotificationIntent(message, builder);
PendingIntent pi = PendingIntent.getActivity(mContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
return pi;
return PendingIntent.getBroadcast(mContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
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 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_LOADING = 1;
public static final int STATE_SUCCESS = 2;
@ -301,6 +304,10 @@ public class Tab {
return mHasOpenSearch;
}
public boolean hasLoadedFromCache() {
return mLoadedFromCache;
}
public SiteIdentity getSiteIdentity() {
return mSiteIdentity;
}
@ -536,6 +543,10 @@ public class Tab {
mHasOpenSearch = hasOpenSearch;
}
public void setLoadedFromCache(boolean loadedFromCache) {
mLoadedFromCache = loadedFromCache;
}
public void updateIdentityData(JSONObject identityData) {
mSiteIdentity.update(identityData);
}

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

@ -105,6 +105,7 @@ public class Tabs implements GeckoEventListener {
"Tab:Added",
"Tab:Close",
"Tab:Select",
"Tab:LoadedFromCache",
"Content:LocationChange",
"Content:SecurityChange",
"Content:StateChange",
@ -505,8 +506,9 @@ public class Tabs implements GeckoEventListener {
tab.handleContentLoaded();
notifyListeners(tab, Tabs.TabEvents.LOAD_ERROR);
} else if (event.equals("Content:PageShow")) {
notifyListeners(tab, TabEvents.PAGE_SHOW);
tab.setLoadedFromCache(message.getBoolean("fromCache"));
tab.updateUserRequested(message.getString("userRequested"));
notifyListeners(tab, TabEvents.PAGE_SHOW);
} else if (event.equals("DOMContentLoaded")) {
tab.handleContentLoaded();
String backgroundColor = message.getString("bgColor");

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

@ -104,6 +104,9 @@ public interface TelemetryContract {
// Stop holding a resource (reader, bookmark, etc) for viewing later.
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.
_TEST1("_test_event_1.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 static final String TYPE_ASSET_ARCHIVE = "asset-archive";
@StringDef({KIND_FONT})
@StringDef({KIND_FONT, KIND_HYPHENATION_DICTIONARY})
public @interface Kind {}
public static final String KIND_FONT = "font";
public static final String KIND_HYPHENATION_DICTIONARY = "hyphenation";
private final String id;
private final String location;
@ -126,6 +127,19 @@ public class DownloadContent {
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() {
return TYPE_ASSET_ARCHIVE.equals(type);
}

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

@ -9,6 +9,7 @@ import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.CallSuper;
import android.util.Log;
import com.keepsafe.switchboard.SwitchBoard;
@ -17,14 +18,13 @@ import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.AboutPages;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.delegates.BrowserAppDelegate;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserDB;
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.ThreadUtils;
@ -35,7 +35,7 @@ import ch.boye.httpclientandroidlib.util.TextUtils;
/**
* 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 {
public final long visits;
public final long lastVisit;
@ -54,15 +54,15 @@ public class AddToHomeScreenPromotion extends BrowserAppDelegate implements Tabs
private WeakReference<Activity> activityReference;
private boolean isEnabled;
private boolean isInForeground;
private int minimumVisits;
private int lastVisitMinimumAgeMs;
private int lastVisitMaximumAgeMs;
@CallSuper
@Override
public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
super.onCreate(browserApp, savedInstanceState);
activityReference = new WeakReference<Activity>(browserApp);
isInForeground = true;
initializeExperiment(browserApp);
}
@ -77,16 +77,6 @@ public class AddToHomeScreenPromotion extends BrowserAppDelegate implements Tabs
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) {
if (!SwitchBoard.isInExperiment(context, Experiments.PROMOTE_ADD_TO_HOMESCREEN)) {
Log.v(LOGTAG, "Experiment not enabled");
@ -139,7 +129,7 @@ public class AddToHomeScreenPromotion extends BrowserAppDelegate implements Tabs
return;
}
if (!isInForeground) {
if (isTabsTrayVisible()) {
// We only want to show this prompt if this tab is in the foreground and not on top
// of the tabs tray.
return;

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

@ -47,6 +47,9 @@
<!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_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">

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

@ -262,7 +262,9 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'delegates/BookmarkStateChangeDelegate.java',
'delegates/BrowserAppDelegate.java',
'delegates/BrowserAppDelegateWithReference.java',
'delegates/OfflineTabStatusDelegate.java',
'delegates/ScreenshotDelegate.java',
'delegates/TabsTrayVisibilityAwareDelegate.java',
'DevToolsAuthHelper.java',
'distribution/Distribution.java',
'distribution/DistributionStoreCallback.java',
@ -479,6 +481,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'NotificationClient.java',
'NotificationHandler.java',
'NotificationHelper.java',
'NotificationReceiver.java',
'notifications/WhatsNewReceiver.java',
'NotificationService.java',
'NSSBridge.java',

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

@ -68,6 +68,8 @@
<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_message2">&crash_message2;</string>
<string name="crash_send_report_message3">&crash_send_report_message3;</string>

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

@ -4297,7 +4297,8 @@ Tab.prototype = {
Messaging.sendRequest({
type: "Content:PageShow",
tabID: this.id,
userRequested: this.userRequested
userRequested: this.userRequested,
fromCache: Tabs.useCache
});
this.isSearch = false;
@ -7377,25 +7378,6 @@ var Tabs = {
// Clear the domain cache whenever a page is loaded into any browser.
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;
case "TabOpen":
// 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.cancel = Cancel
#Network Offline
networkOffline.message2 = Showing offline version
#Tab sharing
tabshare.title = "Choose a tab to stream"
#Tabs in context menus

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

@ -492,6 +492,56 @@ public class TestDownloadAction {
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() {
return createFontWithSize(102400L);
}
@ -504,6 +554,18 @@ public class TestDownloadAction {
.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) {
DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
doReturn(Arrays.asList(content)).when(catalog).getScheduledDownloads();

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

@ -485,6 +485,7 @@ class JsapiTestsCommand(MachCommandBase):
return jsapi_tests_result
def autotry_parser():
print("mach try is under development, please file bugs blocking 1149670.")
from autotry import arg_parser
return arg_parser()
@ -614,8 +615,6 @@ class PushToTry(MachCommandBase):
from mozbuild.testing import TestResolver
from autotry import AutoTry
print("mach try is under development, please file bugs blocking 1149670.")
resolver_func = lambda: self._spawn(TestResolver)
at = AutoTry(self.topsrcdir, resolver_func, self._mach_context)

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

@ -6,7 +6,6 @@
// https://bugzilla.mozilla.org/show_bug.cgi?id=549539
// https://bug549539.bugzilla.mozilla.org/attachment.cgi?id=429661
// 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
var EXPORTED_SYMBOLS = ["SpecialPowersObserver", "SpecialPowersObserverFactory"];

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

@ -9,16 +9,29 @@ import re
import subprocess
import sys
import which
import difflib
from collections import defaultdict
import ConfigParser
def validate_choices(values, choices):
valid = True
for value in values:
if value not in choices:
print 'Invalid choice {v!r}. Allowed choices: {c!r}'.format(v=value, c=choices)
sys.exit(1)
corrections = difflib.get_close_matches(value, choices)
if len(corrections) == 0:
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):
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',
'asyncshutdown',
'commandlines',
'console',
'contentprefs',
'cookie',
'crashmonitor',

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

@ -204,6 +204,42 @@ this.LoginHelper = {
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
* the provided data.

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

@ -334,6 +334,15 @@ var LoginManagerParent = {
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
// password, allow the user to select from a list of applicable
// logins to update the password for.
@ -355,6 +364,10 @@ var LoginManagerParent = {
prompter.promptToChangePassword(oldLogin, formLogin);
} 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(
logins, logins.length, formLogin);
}
@ -365,8 +378,8 @@ var LoginManagerParent = {
var existingLogin = null;
// Look for an existing login that matches the form login.
for (var i = 0; i < logins.length; i++) {
var same, login = logins[i];
for (let login of logins) {
let same;
// If one login has a username but the other doesn't, ignore
// the username when comparing and only match if they have the
@ -375,14 +388,23 @@ var LoginManagerParent = {
if (!login.username && formLogin.username) {
var restoreMe = formLogin.username;
formLogin.username = "";
same = formLogin.matches(login, false);
same = LoginHelper.doLoginsMatch(formLogin, login, {
ignorePassword: false,
ignoreSchemes: LoginHelper.schemeUpgrades,
});
formLogin.username = restoreMe;
} else if (!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.
} else {
same = formLogin.matches(login, true);
same = LoginHelper.doLoginsMatch(formLogin, login, {
ignorePassword: true,
ignoreSchemes: LoginHelper.schemeUpgrades,
});
}
if (same) {

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

@ -6,6 +6,10 @@ const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
"resource://gre/modules/LoginHelper.jsm");
function nsLoginInfo() {}
nsLoginInfo.prototype = {
@ -38,22 +42,9 @@ nsLoginInfo.prototype = {
},
matches(aLogin, ignorePassword) {
if (this.hostname != aLogin.hostname ||
this.httpRealm != aLogin.httpRealm ||
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;
return LoginHelper.doLoginsMatch(this, aLogin, {
ignorePassword,
});
},
equals : function (aLogin) {

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

@ -27,9 +27,7 @@ const PROMPT_ADD_OR_UPDATE = 1;
const PROMPT_NOTNOW = 2;
const PROMPT_NEVER = 3;
/*
* LoginManagerPromptFactory
*
/**
* Implements nsIPromptFactory
*
* 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.
*
* nsIAuthPrompt: Used by SeaMonkey, Thunderbird, but not Firefox.
@ -279,9 +275,7 @@ LoginManagerPrompter.prototype = {
/* ---------- nsIAuthPrompt prompts ---------- */
/*
* prompt
*
/**
* Wrapper around the prompt service prompt. Saving random fields here
* 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
* 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
* returned straight away without displaying a dialog.
*
@ -508,14 +498,12 @@ LoginManagerPrompter.prototype = {
/*
* promptAuth
*
/**
* Implementation of nsIAuthPrompt2.
*
* nsIChannel aChannel
* int aLevel
* nsIAuthInformation aAuthInfo
* @param {nsIChannel} aChannel
* @param {int} aLevel
* @param {nsIAuthInformation} aAuthInfo
*/
promptAuth : function (aChannel, aLevel, aAuthInfo) {
var selectedLogin = null;
@ -694,11 +682,6 @@ LoginManagerPrompter.prototype = {
/*
* init
*
*/
init : function (aWindow, aFactory) {
this._window = aWindow;
this._factory = aFactory || null;
@ -715,11 +698,6 @@ LoginManagerPrompter.prototype = {
this._opener = aOpener;
},
/*
* promptToSavePassword
*
*/
promptToSavePassword : function (aLogin) {
this.log("promptToSavePassword");
var notifyObj = this._getPopupNote() || this._getNotifyBox();
@ -729,12 +707,8 @@ LoginManagerPrompter.prototype = {
this._showSaveLoginDialog(aLogin);
},
/*
* _showLoginNotification
*
/**
* Displays a notification bar.
*
*/
_showLoginNotification : function (aNotifyBox, aName, aText, aButtons) {
var oldBar = aNotifyBox.getNotificationWithValue(aName);
@ -820,9 +794,13 @@ LoginManagerPrompter.prototype = {
};
let updateButtonLabel = () => {
let foundLogins = Services.logins.findLogins({}, login.hostname,
login.formSubmitURL,
login.httpRealm);
let foundLogins = LoginHelper.searchLoginsWithObject({
formSubmitURL: login.formSubmitURL,
hostname: login.hostname,
httpRealm: login.httpRealm,
schemeUpgrades: LoginHelper.schemeUpgrades,
});
let logins = this._filterUpdatableLogins(login, foundLogins);
let msgNames = (logins.length == 0) ? saveMsgNames : changeMsgNames;
@ -886,9 +864,13 @@ LoginManagerPrompter.prototype = {
};
let persistData = () => {
let foundLogins = Services.logins.findLogins({}, login.hostname,
login.formSubmitURL,
login.httpRealm);
let foundLogins = LoginHelper.searchLoginsWithObject({
formSubmitURL: login.formSubmitURL,
hostname: login.hostname,
httpRealm: login.httpRealm,
schemeUpgrades: LoginHelper.schemeUpgrades,
});
let logins = this._filterUpdatableLogins(login, foundLogins);
if (logins.length == 0) {
@ -1002,15 +984,15 @@ LoginManagerPrompter.prototype = {
);
},
/*
* _showSaveLoginNotification
*
/**
* 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
* their login, and only save a login which they know worked.
*
* @param aNotifyObj
* A notification box or a popup notification.
* @param aLogin
* The login captured from the form.
*/
_showSaveLoginNotification : function (aNotifyObj, aLogin) {
// Ugh. We can't use the strings from the popup window, because they
@ -1079,11 +1061,6 @@ LoginManagerPrompter.prototype = {
}
},
/*
* _removeLoginNotifications
*
*/
_removeLoginNotifications : function () {
var popupNote = this._getPopupNote();
if (popupNote)
@ -1108,12 +1085,9 @@ LoginManagerPrompter.prototype = {
},
/*
* _showSaveLoginDialog
*
/**
* Called when we detect a new login in a form submission,
* asks the user what to do.
*
*/
_showSaveLoginDialog : function (aLogin) {
const buttonFlags = Ci.nsIPrompt.BUTTON_POS_1_DEFAULT +
@ -1189,9 +1163,7 @@ LoginManagerPrompter.prototype = {
}
},
/*
* _showChangeLoginNotification
*
/**
* Shows the Change Password notification bar or popup notification.
*
* @param aNotifyObj
@ -1202,7 +1174,6 @@ LoginManagerPrompter.prototype = {
*
* @param aNewLogin
* The login object with the changes we want to make.
*
*/
_showChangeLoginNotification(aNotifyObj, aOldLogin, aNewLogin) {
var changeButtonText =
@ -1223,6 +1194,8 @@ LoginManagerPrompter.prototype = {
// Notification is a PopupNotification
if (aNotifyObj == this._getPopupNote()) {
aOldLogin.hostname = aNewLogin.hostname;
aOldLogin.formSubmitURL = aNewLogin.formSubmitURL;
aOldLogin.password = aNewLogin.password;
aOldLogin.username = aNewLogin.username;
this._showLoginCaptureDoorhanger(aOldLogin, "password-change");
@ -1259,11 +1232,8 @@ LoginManagerPrompter.prototype = {
},
/*
* _showChangeLoginDialog
*
/**
* Shows the Change Password dialog.
*
*/
_showChangeLoginDialog(aOldLogin, aNewLogin) {
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
* don't know which existing login (username) it's for. Asks the user
* to select a username and confirm the password change.
@ -1347,6 +1315,8 @@ LoginManagerPrompter.prototype = {
var propBag = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag);
if (aNewLogin) {
propBag.setProperty("formSubmitURL", aNewLogin.formSubmitURL);
propBag.setProperty("hostname", aNewLogin.hostname);
propBag.setProperty("password", aNewLogin.password);
propBag.setProperty("username", aNewLogin.username);
// 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.
*/
_getChromeWindow: function (aWindow) {
@ -1377,9 +1345,6 @@ LoginManagerPrompter.prototype = {
},
/*
* _getNotifyWindow
*/
_getNotifyWindow: function () {
try {
@ -1445,9 +1410,7 @@ LoginManagerPrompter.prototype = {
},
/*
* _getPopupNote
*
/**
* Returns the popup notification to this prompter,
* 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
* 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
* is the same as some other existing login. So, pick a login with a
* matching username, or return null.
@ -1510,9 +1469,7 @@ LoginManagerPrompter.prototype = {
},
/*
* _getLocalizedString
*
/**
* Can be called as:
* _getLocalizedString("key1");
* _getLocalizedString("key2", ["arg1"]);
@ -1532,9 +1489,7 @@ LoginManagerPrompter.prototype = {
},
/*
* _sanitizeUsername
*
/**
* Sanitizes the specified username, by stripping quotes and truncating if
* it's too long. This helps prevent an evil site from messing with the
* "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
* prompting purposes. Eg, "http://foo.com" --> "foo.com", or
* "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
* 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];
},

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

@ -7,13 +7,15 @@ const BRAND_SHORT_NAME = BRAND_BUNDLE.GetStringFromName("brandShortName");
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
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");
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");
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");
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");
requestLongerTimeout(2);
@ -46,7 +48,7 @@ add_task(function* test_clickNever() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = getCaptureDoorhanger("password-save");
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");
clickDoorhangerButton(notif, NEVER_BUTTON);
});
@ -59,9 +61,9 @@ add_task(function* test_clickNever() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = getCaptureDoorhanger("password-save");
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");
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");
@ -80,7 +82,7 @@ add_task(function* test_clickRemember() {
let logins = Services.logins.getAllLogins();
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.password, "notifyp1", "Check the password used on the 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();
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.password, "notifyp1", "Check the password used");
is(login.timesUsed, 2, "Check times used incremented");
@ -209,7 +211,7 @@ add_task(function* test_pwOnlyLoginMatchesForm() {
let logins = Services.logins.getAllLogins();
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.password, "notifyp1", "Check the password");
is(login.timesUsed, 1, "Check times used");
@ -230,7 +232,7 @@ add_task(function* test_pwOnlyFormMatchesLogin() {
let logins = Services.logins.getAllLogins();
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.password, "notifyp1", "Check the password");
is(login.timesUsed, 2, "Check times used");
@ -252,7 +254,7 @@ add_task(function* test_pwOnlyFormDoesntMatchExisting() {
let logins = Services.logins.getAllLogins();
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.password, "notifyp1B", "Check the password unchanged");
is(login.timesUsed, 1, "Check times used");
@ -274,7 +276,7 @@ add_task(function* test_changeUPLoginOnUPForm_dont() {
let logins = Services.logins.getAllLogins();
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.password, "notifyp1", "Check the password unchanged");
is(login.timesUsed, 1, "Check times used");
@ -297,7 +299,7 @@ add_task(function* test_changeUPLoginOnUPForm_change() {
let logins = Services.logins.getAllLogins();
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.password, "pass2", "Check the password changed");
is(login.timesUsed, 2, "Check times used");
@ -325,7 +327,7 @@ add_task(function* test_changePLoginOnUPForm() {
let logins = Services.logins.getAllLogins();
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.password, "pass2", "Check the password changed");
is(login.timesUsed, 2, "Check times used");
@ -347,7 +349,7 @@ add_task(function* test_changePLoginOnPForm() {
let logins = Services.logins.getAllLogins();
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.password, "notifyp1", "Check the password changed");
is(login.timesUsed, 3, "Check times used");
@ -422,7 +424,7 @@ add_task(function* test_change2pw0unExistingDifferentUP() {
let logins = Services.logins.getAllLogins();
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.password, "notifyp1B", "Check the password unchanged");
is(login.timesUsed, 1, "Check times used");
@ -446,7 +448,7 @@ add_task(function* test_change2pw0unExistingDifferentP() {
let logins = Services.logins.getAllLogins();
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.password, "notifyp1B", "Check the password unchanged");
is(login.timesUsed, 1, "Check times used");
@ -469,7 +471,7 @@ add_task(function* test_change2pw0unExistingWithSameP() {
let logins = Services.logins.getAllLogins();
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.password, "notifyp1", "Check the password unchanged");
is(login.timesUsed, 2, "Check times used incremented");
@ -494,7 +496,7 @@ add_task(function* test_changeUPLoginOnPUpdateForm() {
let logins = Services.logins.getAllLogins();
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.password, "pass2", "Check the password changed");
is(login.timesUsed, 2, "Check times used");
@ -525,7 +527,7 @@ add_task(function* test_recipeCaptureFields_NewLogin() {
let logins = Services.logins.getAllLogins();
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.password, "notifyp1", "Check the password unchanged");
is(login.timesUsed, 1, "Check times used");
@ -545,7 +547,7 @@ add_task(function* test_recipeCaptureFields_ExistingLogin() {
checkOnlyLoginWasUsedTwice({ justChanged: false });
let logins = Services.logins.getAllLogins();
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.password, "notifyp1", "Check the password unchanged");
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:
// * 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",
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");
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");
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 {Function} aTaskFn - task which can be run before the tab closes.
* @param {String} [aOrigin="http://mochi.test:8888"] - origin of the server to
* use to load `aPageFile`.
* @param {String} [aOrigin="http://example.com"] - origin of the server to use
* to load `aPageFile`.
*/
function testSubmittingLoginForm(aPageFile, aTaskFn, aOrigin = "http://mochi.test:8888") {
function testSubmittingLoginForm(aPageFile, aTaskFn, aOrigin = "http://example.com") {
return BrowserTestUtils.withNewTab({
gBrowser,
url: aOrigin + DIRECTORY_PATH + aPageFile,

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

@ -1222,14 +1222,17 @@
event.preventDefault(); // Prevent page scrolling
},
get videoSubtitles() {
return Array.prototype.filter.call(this.video.textTracks, function (tt) {
return tt.kind === "subtitles";
});
isSupportedTextTrack : function(textTrack) {
return textTrack.kind == "subtitles" ||
textTrack.kind == "captions";
},
get overlayableTextTracks() {
return Array.prototype.filter.call(this.video.textTracks, this.isSupportedTextTrack);
},
isClosedCaptionOn : function () {
for (let tt of this.videoSubtitles) {
for (let tt of this.overlayableTextTracks) {
if (tt.mode === "showing") {
return true;
}
@ -1239,7 +1242,7 @@
},
setClosedCaptionButtonState : function () {
if (!this.videoSubtitles.length || this.videocontrols.isTouchControl) {
if (!this.overlayableTextTracks.length || this.videocontrols.isTouchControl) {
this.closedCaptionButton.setAttribute("hidden", "true");
return;
}
@ -1266,7 +1269,7 @@
},
addNewTextTrack : function (tt) {
if (tt.kind !== "subtitles") {
if (!this.isSupportedTextTrack(tt)) {
return;
}
@ -1299,7 +1302,7 @@
},
changeTextTrack : function (index) {
for (let tt of this.videoSubtitles) {
for (let tt of this.overlayableTextTracks) {
if (tt.index === index) {
tt.mode = "showing";
@ -1323,8 +1326,8 @@
},
toggleClosedCaption : function () {
if (this.videoSubtitles.length === 1) {
const lastTTIdx = this.videoSubtitles[0].index;
if (this.overlayableTextTracks.length === 1) {
const lastTTIdx = this.overlayableTextTracks[0].index;
return this.changeTextTrack(this.isClosedCaptionOn() ? 0 : lastTTIdx);
}
@ -1391,7 +1394,7 @@
kind: "subtitles"
});
for (let tt of this.videoSubtitles) {
for (let tt of this.overlayableTextTracks) {
this.addNewTextTrack(tt);
}

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

@ -32,8 +32,6 @@
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.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)
#ifndef MOZ_FENNEC
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/tree.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/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/checkbox/cbox-check.gif (checkbox/cbox-check.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/folder.png (dirListing/folder.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-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/folder.png (../../windows/global/dirListing/folder.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/tree.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/icons/autocomplete-search.svg (icons/autocomplete-search.svg)
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/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/local-XP.png (dirListing/local-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
[extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
#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/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