зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to central, a=merge
MozReview-Commit-ID: BoJFR48izPq
This commit is contained in:
Коммит
a5176322a6
|
@ -1015,6 +1015,8 @@ pref("security.sandbox.content.level", 1);
|
|||
// This setting may not be required anymore once we decide to permanently
|
||||
// enable the content sandbox.
|
||||
pref("security.sandbox.content.level", 2);
|
||||
pref("security.sandbox.content.write_path_whitelist", "");
|
||||
pref("security.sandbox.content.syscall_whitelist", "");
|
||||
#endif
|
||||
|
||||
#if defined(XP_MACOSX) || defined(XP_WIN)
|
||||
|
|
|
@ -8,6 +8,8 @@ Cu.import("resource://gre/modules/NetUtil.jsm");
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OfflineAppCacheHelper",
|
||||
"resource:///modules/offlineAppCache.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
|
||||
"resource://gre/modules/ContextualIdentityService.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"SiteDataManager"
|
||||
|
@ -29,6 +31,7 @@ this.SiteDataManager = {
|
|||
// - quotaUsage: the usage of indexedDB and localStorage.
|
||||
// - appCacheList: an array of app cache; instances of nsIApplicationCache
|
||||
// - diskCacheList: an array. Each element is object holding metadata of http cache:
|
||||
// - uri: the uri of that http cache
|
||||
// - dataSize: that http cache size
|
||||
// - idEnhance: the id extension of that http cache
|
||||
_sites: new Map(),
|
||||
|
@ -53,7 +56,7 @@ this.SiteDataManager = {
|
|||
status = Services.perms.testExactPermissionFromPrincipal(perm.principal, "persistent-storage");
|
||||
if (status === Ci.nsIPermissionManager.ALLOW_ACTION ||
|
||||
status === Ci.nsIPermissionManager.DENY_ACTION) {
|
||||
this._sites.set(perm.principal.origin, {
|
||||
this._sites.set(perm.principal.URI.spec, {
|
||||
perm,
|
||||
status,
|
||||
quotaUsage: 0,
|
||||
|
@ -125,6 +128,7 @@ this.SiteDataManager = {
|
|||
for (let site of sites.values()) {
|
||||
if (site.perm.matchesURI(uri, true)) {
|
||||
site.diskCacheList.push({
|
||||
uri,
|
||||
dataSize,
|
||||
idEnhance
|
||||
});
|
||||
|
@ -161,25 +165,6 @@ this.SiteDataManager = {
|
|||
});
|
||||
},
|
||||
|
||||
_removePermission(site) {
|
||||
Services.perms.removePermission(site.perm);
|
||||
},
|
||||
|
||||
_removeQuotaUsage(site) {
|
||||
this._qms.clearStoragesForPrincipal(site.perm.principal, null, true);
|
||||
},
|
||||
|
||||
removeAll() {
|
||||
for (let site of this._sites.values()) {
|
||||
this._removePermission(site);
|
||||
this._removeQuotaUsage(site);
|
||||
}
|
||||
Services.cache2.clear();
|
||||
Services.cookies.removeAll();
|
||||
OfflineAppCacheHelper.clear();
|
||||
this.updateSites();
|
||||
},
|
||||
|
||||
getSites() {
|
||||
return Promise.all([this._updateQuotaPromise, this._updateDiskCachePromise])
|
||||
.then(() => {
|
||||
|
@ -201,5 +186,70 @@ this.SiteDataManager = {
|
|||
}
|
||||
return list;
|
||||
});
|
||||
},
|
||||
|
||||
_removePermission(site) {
|
||||
Services.perms.removePermission(site.perm);
|
||||
},
|
||||
|
||||
_removeQuotaUsage(site) {
|
||||
this._qms.clearStoragesForPrincipal(site.perm.principal, null, true);
|
||||
},
|
||||
|
||||
_removeDiskCache(site) {
|
||||
for (let cache of site.diskCacheList) {
|
||||
this._diskCache.asyncDoomURI(cache.uri, cache.idEnhance, null);
|
||||
}
|
||||
},
|
||||
|
||||
_removeAppCache(site) {
|
||||
for (let cache of site.appCacheList) {
|
||||
cache.discard();
|
||||
}
|
||||
},
|
||||
|
||||
_removeCookie(site) {
|
||||
let host = site.perm.principal.URI.host;
|
||||
let e = Services.cookies.getCookiesFromHost(host, {});
|
||||
while (e.hasMoreElements()) {
|
||||
let cookie = e.getNext();
|
||||
if (cookie instanceof Components.interfaces.nsICookie) {
|
||||
if (this.isPrivateCookie(cookie)) {
|
||||
continue;
|
||||
}
|
||||
Services.cookies.remove(
|
||||
cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
remove(uris) {
|
||||
for (let uri of uris) {
|
||||
let site = this._sites.get(uri.spec);
|
||||
if (site) {
|
||||
this._removePermission(site);
|
||||
this._removeQuotaUsage(site);
|
||||
this._removeDiskCache(site);
|
||||
this._removeAppCache(site);
|
||||
this._removeCookie(site);
|
||||
}
|
||||
}
|
||||
this.updateSites();
|
||||
},
|
||||
|
||||
removeAll() {
|
||||
for (let site of this._sites.values()) {
|
||||
this._removePermission(site);
|
||||
this._removeQuotaUsage(site);
|
||||
}
|
||||
Services.cache2.clear();
|
||||
Services.cookies.removeAll();
|
||||
OfflineAppCacheHelper.clear();
|
||||
this.updateSites();
|
||||
},
|
||||
|
||||
isPrivateCookie(cookie) {
|
||||
let { userContextId } = cookie.originAttributes;
|
||||
return userContextId && !ContextualIdentityService.getIdentityFromId(userContextId).public;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,6 +10,8 @@ Components.utils.import("resource://gre/modules/PluralForm.jsm");
|
|||
Components.utils.import("resource://gre/modules/Services.jsm")
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SiteDataManager",
|
||||
"resource:///modules/SiteDataManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
|
||||
"resource://gre/modules/ContextualIdentityService.jsm");
|
||||
|
||||
|
@ -76,21 +78,12 @@ var gCookiesWindow = {
|
|||
aCookieB.originAttributes);
|
||||
},
|
||||
|
||||
_isPrivateCookie(aCookie) {
|
||||
let { userContextId } = aCookie.originAttributes;
|
||||
if (!userContextId) {
|
||||
// Default identity is public.
|
||||
return false;
|
||||
}
|
||||
return !ContextualIdentityService.getIdentityFromId(userContextId).public;
|
||||
},
|
||||
|
||||
observe(aCookie, aTopic, aData) {
|
||||
if (aTopic != "cookie-changed")
|
||||
return;
|
||||
|
||||
if (aCookie instanceof Components.interfaces.nsICookie) {
|
||||
if (this._isPrivateCookie(aCookie)) {
|
||||
if (SiteDataManager.isPrivateCookie(aCookie)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -484,7 +477,7 @@ var gCookiesWindow = {
|
|||
while (e.hasMoreElements()) {
|
||||
var cookie = e.getNext();
|
||||
if (cookie && cookie instanceof Components.interfaces.nsICookie) {
|
||||
if (this._isPrivateCookie(cookie)) {
|
||||
if (SiteDataManager.isPrivateCookie(cookie)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -115,6 +115,12 @@ function addPersistentStoragePerm(origin) {
|
|||
Services.perms.addFromPrincipal(principal, "persistent-storage", Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
}
|
||||
|
||||
function removePersistentStoragePerm(origin) {
|
||||
let uri = NetUtil.newURI(origin);
|
||||
let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
|
||||
Services.perms.removeFromPrincipal(principal, "persistent-storage");
|
||||
}
|
||||
|
||||
function getPersistentStoragePermStatus(origin) {
|
||||
let uri = NetUtil.newURI(origin);
|
||||
let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
|
||||
|
@ -144,6 +150,33 @@ function getCacheUsage() {
|
|||
});
|
||||
}
|
||||
|
||||
function openSettingsDialog() {
|
||||
let doc = gBrowser.selectedBrowser.contentDocument;
|
||||
let settingsBtn = doc.getElementById("siteDataSettings");
|
||||
let dialogOverlay = doc.getElementById("dialogOverlay");
|
||||
let dialogLoadPromise = promiseLoadSubDialog("chrome://browser/content/preferences/siteDataSettings.xul");
|
||||
let dialogInitPromise = TestUtils.topicObserved("sitedata-settings-init", () => true);
|
||||
let fullyLoadPromise = Promise.all([ dialogLoadPromise, dialogInitPromise ]).then(() => {
|
||||
is(dialogOverlay.style.visibility, "visible", "The Settings dialog should be visible");
|
||||
});
|
||||
settingsBtn.doCommand();
|
||||
return fullyLoadPromise;
|
||||
}
|
||||
|
||||
function promiseSettingsDialogClose() {
|
||||
return new Promise(resolve => {
|
||||
let doc = gBrowser.selectedBrowser.contentDocument;
|
||||
let dialogOverlay = doc.getElementById("dialogOverlay");
|
||||
let win = content.gSubDialog._frame.contentWindow;
|
||||
win.addEventListener("unload", function unload() {
|
||||
if (win.document.documentURI === "chrome://browser/content/preferences/siteDataSettings.xul") {
|
||||
isnot(dialogOverlay.style.visibility, "visible", "The Settings dialog should be hidden");
|
||||
resolve();
|
||||
}
|
||||
}, { once: true });
|
||||
});
|
||||
}
|
||||
|
||||
function promiseSitesUpdated() {
|
||||
return TestUtils.topicObserved("sitedatamanager:sites-updated", () => true);
|
||||
}
|
||||
|
@ -237,16 +270,9 @@ add_task(function* () {
|
|||
let updatePromise = promiseSitesUpdated();
|
||||
yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
|
||||
yield updatePromise;
|
||||
yield openSettingsDialog();
|
||||
|
||||
// Open the siteDataSettings subdialog
|
||||
let doc = gBrowser.selectedBrowser.contentDocument;
|
||||
let settingsBtn = doc.getElementById("siteDataSettings");
|
||||
let dialogOverlay = doc.getElementById("dialogOverlay");
|
||||
let dialogPromise = promiseLoadSubDialog("chrome://browser/content/preferences/siteDataSettings.xul");
|
||||
settingsBtn.doCommand();
|
||||
yield dialogPromise;
|
||||
is(dialogOverlay.style.visibility, "visible", "The dialog should be visible");
|
||||
|
||||
let dialogFrame = doc.getElementById("dialogFrame");
|
||||
let frameDoc = dialogFrame.contentDocument;
|
||||
let hostCol = frameDoc.getElementById("hostCol");
|
||||
|
@ -335,16 +361,9 @@ add_task(function* () {
|
|||
let updatePromise = promiseSitesUpdated();
|
||||
yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
|
||||
yield updatePromise;
|
||||
yield openSettingsDialog();
|
||||
|
||||
// Open the siteDataSettings subdialog
|
||||
let doc = gBrowser.selectedBrowser.contentDocument;
|
||||
let settingsBtn = doc.getElementById("siteDataSettings");
|
||||
let dialogOverlay = doc.getElementById("dialogOverlay");
|
||||
let dialogPromise = promiseLoadSubDialog("chrome://browser/content/preferences/siteDataSettings.xul");
|
||||
settingsBtn.doCommand();
|
||||
yield dialogPromise;
|
||||
is(dialogOverlay.style.visibility, "visible", "The dialog should be visible");
|
||||
|
||||
let frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
let searchBox = frameDoc.getElementById("searchBox");
|
||||
let mockOrigins = Array.from(mockSiteDataManager.sites.keys());
|
||||
|
@ -374,3 +393,202 @@ add_task(function* () {
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Test selecting and removing all sites one by one
|
||||
add_task(function* () {
|
||||
yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
|
||||
let fakeOrigins = [
|
||||
"https://news.foo.com/",
|
||||
"https://mails.bar.com/",
|
||||
"https://videos.xyz.com/",
|
||||
"https://books.foo.com/",
|
||||
"https://account.bar.com/",
|
||||
"https://shopping.xyz.com/"
|
||||
];
|
||||
fakeOrigins.forEach(origin => addPersistentStoragePerm(origin));
|
||||
|
||||
let updatePromise = promiseSitesUpdated();
|
||||
yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
|
||||
yield updatePromise;
|
||||
yield openSettingsDialog();
|
||||
|
||||
let doc = gBrowser.selectedBrowser.contentDocument;
|
||||
let frameDoc = null;
|
||||
let saveBtn = null;
|
||||
let cancelBtn = null;
|
||||
let settingsDialogClosePromise = null;
|
||||
|
||||
// Test the initial state
|
||||
assertAllSitesListed();
|
||||
|
||||
// Test the "Cancel" button
|
||||
settingsDialogClosePromise = promiseSettingsDialogClose();
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
cancelBtn = frameDoc.getElementById("cancel");
|
||||
removeAllSitesOneByOne();
|
||||
assertAllSitesNotListed();
|
||||
cancelBtn.doCommand();
|
||||
yield settingsDialogClosePromise;
|
||||
yield openSettingsDialog();
|
||||
assertAllSitesListed();
|
||||
|
||||
// Test the "Save Changes" button but cancelling save
|
||||
let cancelPromise = promiseAlertDialogOpen("cancel");
|
||||
settingsDialogClosePromise = promiseSettingsDialogClose();
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
saveBtn = frameDoc.getElementById("save");
|
||||
removeAllSitesOneByOne();
|
||||
assertAllSitesNotListed();
|
||||
saveBtn.doCommand();
|
||||
yield cancelPromise;
|
||||
yield settingsDialogClosePromise;
|
||||
yield openSettingsDialog();
|
||||
assertAllSitesListed();
|
||||
|
||||
// Test the "Save Changes" button and accepting save
|
||||
let acceptPromise = promiseAlertDialogOpen("accept");
|
||||
settingsDialogClosePromise = promiseSettingsDialogClose();
|
||||
updatePromise = promiseSitesUpdated();
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
saveBtn = frameDoc.getElementById("save");
|
||||
removeAllSitesOneByOne();
|
||||
assertAllSitesNotListed();
|
||||
saveBtn.doCommand();
|
||||
yield acceptPromise;
|
||||
yield settingsDialogClosePromise;
|
||||
yield updatePromise;
|
||||
yield openSettingsDialog();
|
||||
assertAllSitesNotListed();
|
||||
|
||||
// Always clean up the fake origins
|
||||
fakeOrigins.forEach(origin => removePersistentStoragePerm(origin));
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
|
||||
function removeAllSitesOneByOne() {
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
let removeBtn = frameDoc.getElementById("removeSelected");
|
||||
let sitesList = frameDoc.getElementById("sitesList");
|
||||
let sites = sitesList.getElementsByTagName("richlistitem");
|
||||
for (let i = sites.length - 1; i >= 0; --i) {
|
||||
sites[i].click();
|
||||
removeBtn.doCommand();
|
||||
}
|
||||
}
|
||||
|
||||
function assertAllSitesListed() {
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
let removeBtn = frameDoc.getElementById("removeSelected");
|
||||
let sitesList = frameDoc.getElementById("sitesList");
|
||||
let sites = sitesList.getElementsByTagName("richlistitem");
|
||||
is(sites.length, fakeOrigins.length, "Should list all sites");
|
||||
is(removeBtn.disabled, false, "Should enable the removeSelected button");
|
||||
}
|
||||
|
||||
function assertAllSitesNotListed() {
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
let removeBtn = frameDoc.getElementById("removeSelected");
|
||||
let sitesList = frameDoc.getElementById("sitesList");
|
||||
let sites = sitesList.getElementsByTagName("richlistitem");
|
||||
is(sites.length, 0, "Should not list all sites");
|
||||
is(removeBtn.disabled, true, "Should disable the removeSelected button");
|
||||
}
|
||||
});
|
||||
|
||||
// Test selecting and removing partial sites
|
||||
add_task(function* () {
|
||||
yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
|
||||
let fakeOrigins = [
|
||||
"https://news.foo.com/",
|
||||
"https://mails.bar.com/",
|
||||
"https://videos.xyz.com/",
|
||||
"https://books.foo.com/",
|
||||
"https://account.bar.com/",
|
||||
"https://shopping.xyz.com/"
|
||||
];
|
||||
fakeOrigins.forEach(origin => addPersistentStoragePerm(origin));
|
||||
|
||||
let updatePromise = promiseSitesUpdated();
|
||||
yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
|
||||
yield updatePromise;
|
||||
yield openSettingsDialog();
|
||||
|
||||
const removeDialogURL = "chrome://browser/content/preferences/siteDataRemoveSelected.xul";
|
||||
let doc = gBrowser.selectedBrowser.contentDocument;
|
||||
let frameDoc = null;
|
||||
let saveBtn = null;
|
||||
let cancelBtn = null;
|
||||
let removeDialogOpenPromise = null;
|
||||
let settingsDialogClosePromise = null;
|
||||
|
||||
// Test the initial state
|
||||
assertSitesListed(fakeOrigins);
|
||||
|
||||
// Test the "Cancel" button
|
||||
settingsDialogClosePromise = promiseSettingsDialogClose();
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
cancelBtn = frameDoc.getElementById("cancel");
|
||||
removeSelectedSite(fakeOrigins.slice(0, 4));
|
||||
assertSitesListed(fakeOrigins.slice(4));
|
||||
cancelBtn.doCommand();
|
||||
yield settingsDialogClosePromise;
|
||||
yield openSettingsDialog();
|
||||
assertSitesListed(fakeOrigins);
|
||||
|
||||
// Test the "Save Changes" button but canceling save
|
||||
removeDialogOpenPromise = promiseWindowDialogOpen("cancel", removeDialogURL);
|
||||
settingsDialogClosePromise = promiseSettingsDialogClose();
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
saveBtn = frameDoc.getElementById("save");
|
||||
removeSelectedSite(fakeOrigins.slice(0, 4));
|
||||
assertSitesListed(fakeOrigins.slice(4));
|
||||
saveBtn.doCommand();
|
||||
yield removeDialogOpenPromise;
|
||||
yield settingsDialogClosePromise;
|
||||
yield openSettingsDialog();
|
||||
assertSitesListed(fakeOrigins);
|
||||
|
||||
// Test the "Save Changes" button and accepting save
|
||||
removeDialogOpenPromise = promiseWindowDialogOpen("accept", removeDialogURL);
|
||||
settingsDialogClosePromise = promiseSettingsDialogClose();
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
saveBtn = frameDoc.getElementById("save");
|
||||
removeSelectedSite(fakeOrigins.slice(0, 4));
|
||||
assertSitesListed(fakeOrigins.slice(4));
|
||||
saveBtn.doCommand();
|
||||
yield removeDialogOpenPromise;
|
||||
yield settingsDialogClosePromise;
|
||||
yield openSettingsDialog();
|
||||
assertSitesListed(fakeOrigins.slice(4));
|
||||
|
||||
// Always clean up the fake origins
|
||||
fakeOrigins.forEach(origin => removePersistentStoragePerm(origin));
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
|
||||
function removeSelectedSite(origins) {
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
let removeBtn = frameDoc.getElementById("removeSelected");
|
||||
let sitesList = frameDoc.getElementById("sitesList");
|
||||
origins.forEach(origin => {
|
||||
let site = sitesList.querySelector(`richlistitem[data-origin="${origin}"]`);
|
||||
if (site) {
|
||||
site.click();
|
||||
removeBtn.doCommand();
|
||||
} else {
|
||||
ok(false, `Should not select and remove inexisted site of ${origin}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function assertSitesListed(origins) {
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
let removeBtn = frameDoc.getElementById("removeSelected");
|
||||
let sitesList = frameDoc.getElementById("sitesList");
|
||||
let totalSitesNumber = sitesList.getElementsByTagName("richlistitem").length;
|
||||
is(totalSitesNumber, origins.length, "Should list the right sites number");
|
||||
origins.forEach(origin => {
|
||||
let site = sitesList.querySelector(`richlistitem[data-origin="${origin}"]`);
|
||||
ok(!!site, `Should list the site of ${origin}`);
|
||||
});
|
||||
is(removeBtn.disabled, false, "Should enable the removeSelected button");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -161,12 +161,12 @@ function waitForCondition(aConditionFn, aMaxTries = 50, aCheckInterval = 100) {
|
|||
});
|
||||
}
|
||||
|
||||
function promiseAlertDialogOpen(buttonAction) {
|
||||
function promiseWindowDialogOpen(buttonAction, url) {
|
||||
return new Promise(resolve => {
|
||||
Services.ww.registerNotification(function onOpen(subj, topic, data) {
|
||||
if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
|
||||
subj.addEventListener("load", function() {
|
||||
if (subj.document.documentURI == "chrome://global/content/commonDialog.xul") {
|
||||
subj.addEventListener("load", function onLoad() {
|
||||
if (subj.document.documentURI == url) {
|
||||
Services.ww.unregisterNotification(onOpen);
|
||||
let doc = subj.document.documentElement;
|
||||
doc.getButton(buttonAction).click();
|
||||
|
@ -177,3 +177,7 @@ function promiseAlertDialogOpen(buttonAction) {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
function promiseAlertDialogOpen(buttonAction) {
|
||||
return promiseWindowDialogOpen(buttonAction, "chrome://global/content/commonDialog.xul");
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ browser.jar:
|
|||
content/browser/preferences/siteDataSettings.xul
|
||||
content/browser/preferences/siteDataSettings.js
|
||||
content/browser/preferences/siteDataSettings.css
|
||||
* content/browser/preferences/siteDataRemoveSelected.xul
|
||||
content/browser/preferences/siteDataRemoveSelected.js
|
||||
content/browser/preferences/siteListItem.xml
|
||||
content/browser/preferences/translation.xul
|
||||
content/browser/preferences/translation.js
|
||||
|
|
|
@ -19,7 +19,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3', 'cocoa'):
|
|||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'SiteDataManager.jsm',
|
||||
'SiteDataManager.jsm'
|
||||
]
|
||||
|
||||
with Files('**'):
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
|
||||
/* 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 { utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
"use strict";
|
||||
|
||||
let gSiteDataRemoveSelected = {
|
||||
|
||||
_tree: null,
|
||||
|
||||
init() {
|
||||
// Organize items for the tree from the argument
|
||||
let hostsTable = window.arguments[0].hostsTable;
|
||||
let visibleItems = [];
|
||||
let itemsTable = new Map();
|
||||
for (let [ baseDomain, hosts ] of hostsTable) {
|
||||
// In the beginning, only display base domains in the topmost level.
|
||||
visibleItems.push({
|
||||
level: 0,
|
||||
opened: false,
|
||||
host: baseDomain
|
||||
});
|
||||
// Other hosts are in the second level.
|
||||
let items = hosts.map(host => {
|
||||
return { host, level: 1 };
|
||||
});
|
||||
items.sort(sortByHost);
|
||||
itemsTable.set(baseDomain, items);
|
||||
}
|
||||
visibleItems.sort(sortByHost);
|
||||
this._view.itemsTable = itemsTable;
|
||||
this._view.visibleItems = visibleItems;
|
||||
this._tree = document.getElementById("sitesTree");
|
||||
this._tree.view = this._view;
|
||||
|
||||
function sortByHost(a, b) {
|
||||
let aHost = a.host.toLowerCase();
|
||||
let bHost = b.host.toLowerCase();
|
||||
return aHost.localeCompare(bHost);
|
||||
}
|
||||
},
|
||||
|
||||
ondialogaccept() {
|
||||
window.arguments[0].allowed = true;
|
||||
},
|
||||
|
||||
ondialogcancel() {
|
||||
window.arguments[0].allowed = false;
|
||||
},
|
||||
|
||||
_view: {
|
||||
_selection: null,
|
||||
|
||||
itemsTable: null,
|
||||
|
||||
visibleItems: null,
|
||||
|
||||
get rowCount() {
|
||||
return this.visibleItems.length;
|
||||
},
|
||||
|
||||
getCellText(index, column) {
|
||||
let item = this.visibleItems[index];
|
||||
return item ? item.host : "";
|
||||
},
|
||||
|
||||
isContainer(index) {
|
||||
let item = this.visibleItems[index];
|
||||
if (item && item.level === 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
isContainerEmpty() {
|
||||
return false;
|
||||
},
|
||||
|
||||
isContainerOpen(index) {
|
||||
let item = this.visibleItems[index];
|
||||
if (item && item.level === 0) {
|
||||
return item.opened;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
getLevel(index) {
|
||||
let item = this.visibleItems[index];
|
||||
return item ? item.level : 0;
|
||||
},
|
||||
|
||||
hasNextSibling(index, afterIndex) {
|
||||
let item = this.visibleItems[index];
|
||||
if (item) {
|
||||
let thisLV = this.getLevel(index);
|
||||
for (let i = afterIndex + 1; i < this.rowCount; ++i) {
|
||||
let nextLV = this.getLevel(i);
|
||||
if (nextLV == thisLV) {
|
||||
return true;
|
||||
}
|
||||
if (nextLV < thisLV) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
getParentIndex(index) {
|
||||
if (!this.isContainer(index)) {
|
||||
for (let i = index - 1; i >= 0; --i) {
|
||||
if (this.isContainer(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
toggleOpenState(index) {
|
||||
let item = this.visibleItems[index];
|
||||
if (!this.isContainer(index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.opened) {
|
||||
item.opened = false;
|
||||
|
||||
let deleteCount = 0;
|
||||
for (let i = index + 1; i < this.visibleItems.length; ++i) {
|
||||
if (!this.isContainer(i)) {
|
||||
++deleteCount;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteCount) {
|
||||
this.visibleItems.splice(index + 1, deleteCount);
|
||||
this.treeBox.rowCountChanged(index + 1, -deleteCount);
|
||||
}
|
||||
} else {
|
||||
item.opened = true;
|
||||
|
||||
let childItems = this.itemsTable.get(item.host);
|
||||
for (let i = 0; i < childItems.length; ++i) {
|
||||
this.visibleItems.splice(index + i + 1, 0, childItems[i]);
|
||||
}
|
||||
this.treeBox.rowCountChanged(index + 1, childItems.length);
|
||||
}
|
||||
this.treeBox.invalidateRow(index);
|
||||
},
|
||||
|
||||
get selection() {
|
||||
return this._selection;
|
||||
},
|
||||
set selection(v) {
|
||||
this._selection = v;
|
||||
return v;
|
||||
},
|
||||
setTree(treeBox) {
|
||||
this.treeBox = treeBox;
|
||||
},
|
||||
isSeparator(index) {
|
||||
return false;
|
||||
},
|
||||
isSorted(index) {
|
||||
return false;
|
||||
},
|
||||
canDrop() {
|
||||
return false;
|
||||
},
|
||||
drop() {},
|
||||
getRowProperties() {},
|
||||
getCellProperties() {},
|
||||
getColumnProperties() {},
|
||||
hasPreviousSibling(index) {},
|
||||
getImageSrc() {},
|
||||
getProgressMode() {},
|
||||
getCellValue() {},
|
||||
cycleHeader() {},
|
||||
selectionChanged() {},
|
||||
cycleCell() {},
|
||||
isEditable() {},
|
||||
isSelectable() {},
|
||||
setCellValue() {},
|
||||
setCellText() {},
|
||||
performAction() {},
|
||||
performActionOnRow() {},
|
||||
performActionOnCell() {}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
<?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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/preferences/siteDataSettings.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/siteDataSettings.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/siteDataSettings.dtd" >
|
||||
|
||||
<dialog id="SiteDataRemoveSelectedDialog"
|
||||
windowtype="Browser:SiteDataRemoveSelected"
|
||||
width="500"
|
||||
title="&removingDialog.title;"
|
||||
onload="gSiteDataRemoveSelected.init();"
|
||||
ondialogaccept="gSiteDataRemoveSelected.ondialogaccept(); return true;"
|
||||
ondialogcancel="gSiteDataRemoveSelected.ondialogcancel(); return true;"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script src="chrome://browser/content/preferences/siteDataRemoveSelected.js"/>
|
||||
|
||||
<stringbundle id="bundlePreferences"
|
||||
src="chrome://browser/locale/preferences/preferences.properties"/>
|
||||
|
||||
<vbox id="contentContainer">
|
||||
<hbox flex="1">
|
||||
<vbox>
|
||||
<image class="question-icon"/>
|
||||
</vbox>
|
||||
<vbox flex="1">
|
||||
<!-- Only show this label on OS X because of no dialog title -->
|
||||
<label id="removing-label"
|
||||
#ifndef XP_MACOSX
|
||||
hidden="true"
|
||||
#endif
|
||||
>&removingDialog.title;</label>
|
||||
<separator class="thin"/>
|
||||
<description id="removing-description">&removingDialog.description;</description>
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
||||
<separator />
|
||||
|
||||
<vbox flex="1">
|
||||
<label>&siteTree.label;</label>
|
||||
<separator class="thin"/>
|
||||
<tree id="sitesTree" flex="1" seltype="single" hidecolumnpicker="true">
|
||||
<treecols>
|
||||
<treecol primary="true" flex="1" hideheader="true"/>
|
||||
</treecols>
|
||||
<treechildren />
|
||||
</tree>
|
||||
</vbox>
|
||||
</vbox>
|
||||
|
||||
</dialog>
|
|
@ -2,18 +2,10 @@
|
|||
* 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/. */
|
||||
|
||||
#searchBoxContainer {
|
||||
-moz-box-align: center;
|
||||
}
|
||||
|
||||
#sitesList {
|
||||
min-height: 20em;
|
||||
}
|
||||
|
||||
#sitesList > richlistitem {
|
||||
-moz-binding: url("chrome://browser/content/preferences/siteListItem.xml#siteListItem");
|
||||
}
|
||||
|
||||
.item-box {
|
||||
padding: 5px 8px;
|
||||
#SiteDataRemoveSelectedDialog {
|
||||
-moz-binding: url("chrome://global/content/bindings/dialog.xml#dialog");
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ let gSiteDataSettings = {
|
|||
// - uri: uri of site; instance of nsIURI
|
||||
// - status: persistent-storage permission status
|
||||
// - usage: disk usage which site uses
|
||||
// - userAction: "remove" or "update-permission"; the action user wants to take.
|
||||
// If not specified, means no action to take
|
||||
_sites: null,
|
||||
|
||||
_list: null,
|
||||
|
@ -38,12 +40,23 @@ let gSiteDataSettings = {
|
|||
let sortCol = document.getElementById("hostCol");
|
||||
this._sortSites(this._sites, sortCol);
|
||||
this._buildSitesList(this._sites);
|
||||
this._updateButtonsState();
|
||||
Services.obs.notifyObservers(null, "sitedata-settings-init", null);
|
||||
});
|
||||
|
||||
setEventListener("hostCol", "click", this.onClickTreeCol);
|
||||
setEventListener("usageCol", "click", this.onClickTreeCol);
|
||||
setEventListener("statusCol", "click", this.onClickTreeCol);
|
||||
setEventListener("searchBox", "command", this.onCommandSearch);
|
||||
setEventListener("cancel", "command", this.close);
|
||||
setEventListener("save", "command", this.saveChanges);
|
||||
setEventListener("removeSelected", "command", this.removeSelected);
|
||||
},
|
||||
|
||||
_updateButtonsState() {
|
||||
let items = this._list.getElementsByTagName("richlistitem");
|
||||
let removeBtn = document.getElementById("removeSelected");
|
||||
removeBtn.disabled = !(items.length > 0);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -110,6 +123,10 @@ let gSiteDataSettings = {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (data.userAction === "remove") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let statusStrId = data.status === Ci.nsIPermissionManager.ALLOW_ACTION ? "important" : "default";
|
||||
let size = DownloadUtils.convertByteUnits(data.usage);
|
||||
let item = document.createElement("richlistitem");
|
||||
|
@ -128,5 +145,95 @@ let gSiteDataSettings = {
|
|||
|
||||
onCommandSearch() {
|
||||
this._buildSitesList(this._sites);
|
||||
},
|
||||
|
||||
removeSelected() {
|
||||
let selected = this._list.selectedItem;
|
||||
if (selected) {
|
||||
let origin = selected.getAttribute("data-origin");
|
||||
for (let site of this._sites) {
|
||||
if (site.uri.spec === origin) {
|
||||
site.userAction = "remove";
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._list.removeChild(selected);
|
||||
this._updateButtonsState();
|
||||
}
|
||||
},
|
||||
|
||||
saveChanges() {
|
||||
let allowed = true;
|
||||
|
||||
// Confirm user really wants to remove site data starts
|
||||
let removals = [];
|
||||
this._sites = this._sites.filter(site => {
|
||||
if (site.userAction === "remove") {
|
||||
removals.push(site.uri);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (removals.length > 0) {
|
||||
if (this._sites.length == 0) {
|
||||
// User selects all sites so equivalent to clearing all data
|
||||
let flags =
|
||||
Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 +
|
||||
Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1 +
|
||||
Services.prompt.BUTTON_POS_0_DEFAULT;
|
||||
let prefStrBundle = document.getElementById("bundlePreferences");
|
||||
let title = prefStrBundle.getString("clearSiteDataPromptTitle");
|
||||
let text = prefStrBundle.getString("clearSiteDataPromptText");
|
||||
let btn0Label = prefStrBundle.getString("clearSiteDataNow");
|
||||
let result = Services.prompt.confirmEx(window, title, text, flags, btn0Label, null, null, null, {});
|
||||
allowed = result == 0;
|
||||
if (allowed) {
|
||||
SiteDataManager.removeAll();
|
||||
}
|
||||
} else {
|
||||
// User only removes partial sites.
|
||||
// We will remove cookies based on base domain, say, user selects "news.foo.com" to remove.
|
||||
// The cookies under "music.foo.com" will be removed together.
|
||||
// We have to prmopt user about this action.
|
||||
let hostsTable = new Map();
|
||||
// Group removed sites by base domain
|
||||
for (let uri of removals) {
|
||||
let baseDomain = Services.eTLD.getBaseDomain(uri);
|
||||
let hosts = hostsTable.get(baseDomain);
|
||||
if (!hosts) {
|
||||
hosts = [];
|
||||
hostsTable.set(baseDomain, hosts);
|
||||
}
|
||||
hosts.push(uri.host);
|
||||
}
|
||||
// Pick out sites with the same base domain as removed sites
|
||||
for (let site of this._sites) {
|
||||
let baseDomain = Services.eTLD.getBaseDomain(site.uri);
|
||||
let hosts = hostsTable.get(baseDomain);
|
||||
if (hosts) {
|
||||
hosts.push(site.uri.host);
|
||||
}
|
||||
}
|
||||
|
||||
let args = {
|
||||
hostsTable,
|
||||
allowed: false
|
||||
};
|
||||
let features = "centerscreen,chrome,modal,resizable=no";
|
||||
window.openDialog("chrome://browser/content/preferences/siteDataRemoveSelected.xul", "", features, args);
|
||||
allowed = args.allowed;
|
||||
if (allowed) {
|
||||
SiteDataManager.remove(removals);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Confirm user really wants to remove site data ends
|
||||
|
||||
this.close();
|
||||
},
|
||||
|
||||
close() {
|
||||
window.close();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/preferences/siteDataSettings.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/siteDataSettings.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/siteDataSettings.dtd" >
|
||||
|
||||
|
@ -41,4 +42,15 @@
|
|||
</richlistbox>
|
||||
</vbox>
|
||||
|
||||
<hbox align="start">
|
||||
<button id="removeSelected" label="&removeSelected.label;" accesskey="&removeSelected.accesskey;"/>
|
||||
</hbox>
|
||||
|
||||
<vbox align="end">
|
||||
<hbox>
|
||||
<button id="cancel" label="&cancel.label;" accesskey="&cancel.accesskey;"/>
|
||||
<button id="save" label="&save.label;" accesskey="&save.accesskey;"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
</window>
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
# LOCALIZATION NOTE(wc-reporter.label): This string will be used in the
|
||||
# Firefox menu panel below its button. Localized length should be considered.
|
||||
wc-reporter.label=Report Site Issue
|
||||
# \u00ad is included at the beginning of the string to disable auto-hyphens.
|
||||
wc-reporter.label=\u00adReport Site Issue
|
||||
# LOCALIZATION NOTE(wc-reporter.tooltip): A site compatibility issue is
|
||||
# a website bug that exists in one browser (Firefox), but not another.
|
||||
wc-reporter.tooltip=Report a site compatibility issue
|
||||
|
|
|
@ -125,12 +125,11 @@ webextPerms.hostDescription.tooManySites=Access your data on #1 other site;Acces
|
|||
# %2$S is replaced with the localized name of the application.
|
||||
addonPostInstall.message1=%1$S has been added to %2$S.
|
||||
|
||||
# LOCALIZATION NOTE (addonPostInstall.message2)
|
||||
# %1$S is replaced with the localized name of the extension.
|
||||
# %2$S is replaced with the icon for the add-ons menu.
|
||||
# %3$S is replaced with the icon for the toolbar menu.
|
||||
# LOCALIZATION NOTE (addonPostInstall.messageDetail)
|
||||
# %1$S is replaced with the icon for the add-ons menu.
|
||||
# %2$S is replaced with the icon for the toolbar menu.
|
||||
# Note, this string will be used as raw markup. Avoid characters like <, >, &
|
||||
addonPostInstall.message2=Manage %1$S by clicking %2$S in the %3$S menu.
|
||||
addonPostInstall.messageDetail=Manage your add-ons by clicking %1$S in the %2$S menu.
|
||||
addonPostInstall.okay.label=OK
|
||||
addonPostInstall.okay.key=O
|
||||
|
||||
|
|
|
@ -9,3 +9,12 @@
|
|||
<!ENTITY usageCol.label "Storage">
|
||||
<!ENTITY search.label "Search:">
|
||||
<!ENTITY search.accesskey "S">
|
||||
<!ENTITY removeSelected.label "Remove Selected">
|
||||
<!ENTITY removeSelected.accesskey "r">
|
||||
<!ENTITY save.label "Save Changes">
|
||||
<!ENTITY save.accesskey "a">
|
||||
<!ENTITY cancel.label "Cancel">
|
||||
<!ENTITY cancel.accesskey "C">
|
||||
<!ENTITY removingDialog.title "Removing Site Data">
|
||||
<!ENTITY removingDialog.description "Removing site data will also remove cookies. This may log you out of websites and remove offline web content. Are you sure you want to make the changes?">
|
||||
<!ENTITY siteTree.label "The following website cookies will be removed:">
|
||||
|
|
|
@ -331,8 +331,8 @@ this.ExtensionsUI = {
|
|||
let bundle = win.gNavigatorBundle;
|
||||
let msg1 = bundle.getFormattedString("addonPostInstall.message1",
|
||||
[addonLabel, appName]);
|
||||
let msg2 = bundle.getFormattedString("addonPostInstall.message2",
|
||||
[addonLabel, addonIcon, toolbarIcon]);
|
||||
let msg2 = bundle.getFormattedString("addonPostInstall.messageDetail",
|
||||
[addonIcon, toolbarIcon]);
|
||||
|
||||
return new Promise(resolve => {
|
||||
let action = {
|
||||
|
|
До Ширина: | Высота: | Размер: 4.5 KiB После Ширина: | Высота: | Размер: 4.5 KiB |
|
@ -0,0 +1,38 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Site Data - Settings dialog
|
||||
*/
|
||||
#sitesList {
|
||||
min-height: 20em;
|
||||
}
|
||||
|
||||
.item-box {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog of removing sites selected
|
||||
*/
|
||||
#SiteDataRemoveSelectedDialog {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
#contentContainer {
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.question-icon {
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
#removing-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#sitesTree {
|
||||
height: 15em;
|
||||
}
|
|
@ -75,6 +75,7 @@
|
|||
skin/classic/browser/preferences/in-content/favicon.ico (../shared/incontentprefs/favicon.ico)
|
||||
skin/classic/browser/preferences/in-content/icons.svg (../shared/incontentprefs/icons.svg)
|
||||
skin/classic/browser/preferences/in-content/search.css (../shared/incontentprefs/search.css)
|
||||
skin/classic/browser/preferences/in-content/siteDataSettings.css (../shared/incontentprefs/siteDataSettings.css)
|
||||
* skin/classic/browser/preferences/in-content/containers.css (../shared/incontentprefs/containers.css)
|
||||
* skin/classic/browser/preferences/containers.css (../shared/preferences/containers.css)
|
||||
skin/classic/browser/fxa/default-avatar.svg (../shared/fxa/default-avatar.svg)
|
||||
|
|
|
@ -24,6 +24,11 @@ loader.lazyGetter(this, "StoragePanel", () => require("devtools/client/storage/p
|
|||
loader.lazyGetter(this, "ScratchpadPanel", () => require("devtools/client/scratchpad/scratchpad-panel").ScratchpadPanel);
|
||||
loader.lazyGetter(this, "DomPanel", () => require("devtools/client/dom/dom-panel").DomPanel);
|
||||
|
||||
// Other dependencies
|
||||
loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true);
|
||||
loader.lazyImporter(this, "ResponsiveUIManager", "resource://devtools/client/responsivedesign/responsivedesign.jsm");
|
||||
loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
|
||||
|
||||
const {LocalizationHelper} = require("devtools/shared/l10n");
|
||||
const L10N = new LocalizationHelper("devtools/client/locales/startup.properties");
|
||||
|
||||
|
@ -470,26 +475,93 @@ exports.defaultThemes = [
|
|||
// addons that have manually inserted toolbarbuttons into DOM.
|
||||
// (By default, supported target is only local tab)
|
||||
exports.ToolboxButtons = [
|
||||
{ id: "command-button-pick",
|
||||
isTargetSupported: target => {
|
||||
return target.activeTab && target.activeTab.traits.frames;
|
||||
}
|
||||
},
|
||||
{ id: "command-button-frames",
|
||||
isTargetSupported: target => {
|
||||
return target.activeTab && target.activeTab.traits.frames;
|
||||
}
|
||||
},
|
||||
{ id: "command-button-splitconsole",
|
||||
isTargetSupported: target => !target.isAddon },
|
||||
{ id: "command-button-responsive" },
|
||||
{ id: "command-button-paintflashing" },
|
||||
{ id: "command-button-scratchpad" },
|
||||
{ id: "command-button-screenshot" },
|
||||
{ id: "command-button-rulers" },
|
||||
{ id: "command-button-measure" },
|
||||
{ id: "command-button-noautohide",
|
||||
isTargetSupported: target => target.chrome },
|
||||
description: l10n("toolbox.buttons.splitconsole"),
|
||||
isTargetSupported: target => !target.isAddon,
|
||||
onClick(event, toolbox) {
|
||||
toolbox.toggleSplitConsole();
|
||||
},
|
||||
isChecked(toolbox) {
|
||||
return toolbox.splitConsole;
|
||||
},
|
||||
setup(toolbox, onChange) {
|
||||
toolbox.on("split-console", onChange);
|
||||
},
|
||||
teardown(toolbox, onChange) {
|
||||
toolbox.off("split-console", onChange);
|
||||
}
|
||||
},
|
||||
{ id: "command-button-paintflashing",
|
||||
description: l10n("toolbox.buttons.paintflashing"),
|
||||
isTargetSupported: target => target.isLocalTab,
|
||||
onClick(event, toolbox) {
|
||||
CommandUtils.executeOnTarget(toolbox.target, "paintflashing toggle");
|
||||
},
|
||||
autoToggle: true
|
||||
},
|
||||
{ id: "command-button-scratchpad",
|
||||
description: l10n("toolbox.buttons.scratchpad"),
|
||||
isTargetSupported: target => target.isLocalTab,
|
||||
onClick(event, toolbox) {
|
||||
ScratchpadManager.openScratchpad();
|
||||
}
|
||||
},
|
||||
{ id: "command-button-responsive",
|
||||
description: l10n("toolbox.buttons.responsive",
|
||||
osString == "Darwin" ? "Cmd+Opt+M" : "Ctrl+Shift+M"),
|
||||
isTargetSupported: target => target.isLocalTab,
|
||||
onClick(event, toolbox) {
|
||||
let browserWindow = toolbox.win.top;
|
||||
ResponsiveUIManager.handleGcliCommand(browserWindow,
|
||||
browserWindow.gBrowser.selectedTab,
|
||||
"resize toggle",
|
||||
null);
|
||||
},
|
||||
isChecked(toolbox) {
|
||||
if (!toolbox.target.tab) {
|
||||
return false;
|
||||
}
|
||||
return ResponsiveUIManager.isActiveForTab(toolbox.target.tab);
|
||||
},
|
||||
setup(toolbox, onChange) {
|
||||
ResponsiveUIManager.on("on", onChange);
|
||||
ResponsiveUIManager.on("off", onChange);
|
||||
},
|
||||
teardown(toolbox, onChange) {
|
||||
ResponsiveUIManager.off("on", onChange);
|
||||
ResponsiveUIManager.off("off", onChange);
|
||||
}
|
||||
},
|
||||
{ id: "command-button-screenshot",
|
||||
description: l10n("toolbox.buttons.screenshot"),
|
||||
isTargetSupported: target => target.isLocalTab,
|
||||
onClick(event, toolbox) {
|
||||
// Special case for screenshot button to check for clipboard preference
|
||||
const clipboardEnabled = Services.prefs
|
||||
.getBoolPref("devtools.screenshot.clipboard.enabled");
|
||||
let args = "--fullpage --file";
|
||||
if (clipboardEnabled) {
|
||||
args += " --clipboard";
|
||||
}
|
||||
CommandUtils.executeOnTarget(toolbox.target, "screenshot " + args);
|
||||
}
|
||||
},
|
||||
{ id: "command-button-rulers",
|
||||
description: l10n("toolbox.buttons.rulers"),
|
||||
isTargetSupported: target => target.isLocalTab,
|
||||
onClick(event, toolbox) {
|
||||
CommandUtils.executeOnTarget(toolbox.target, "rulers");
|
||||
},
|
||||
autoToggle: true
|
||||
},
|
||||
{ id: "command-button-measure",
|
||||
description: l10n("toolbox.buttons.measure"),
|
||||
isTargetSupported: target => target.isLocalTab,
|
||||
onClick(event, toolbox) {
|
||||
CommandUtils.executeOnTarget(toolbox.target, "measure");
|
||||
},
|
||||
autoToggle: true
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -558,23 +558,54 @@ Toolbox.prototype = {
|
|||
* @property {String} description - The value that will display as a tooltip and in
|
||||
* the options panel for enabling/disabling.
|
||||
* @property {Function} onClick - The function to run when the button is activated by
|
||||
* click or keyboard shortcut.
|
||||
* click or keyboard shortcut. First argument will be the 'click'
|
||||
* event, and second argument is the toolbox instance.
|
||||
* @property {Boolean} isInStartContainer - Buttons can either be placed at the start
|
||||
* of the toolbar, or at the end.
|
||||
* @property {Function} setup - Function run immediately to listen for events changing
|
||||
* whenever the button is checked or unchecked. The toolbox object
|
||||
* is passed as first argument and a callback is passed as second
|
||||
* argument, to be called whenever the checked state changes.
|
||||
* @property {Function} teardown - Function run on toolbox close to let a chance to
|
||||
* unregister listeners set when `setup` was called and avoid
|
||||
* memory leaks. The same arguments than `setup` function are
|
||||
* passed to `teardown`.
|
||||
* @property {Function} isTargetSupported - Function to automatically enable/disable
|
||||
* the button based on the target. If the target don't support
|
||||
* the button feature, this method should return false.
|
||||
* @property {Function} isChecked - Optional function called to known if the button
|
||||
* is toggled or not. The function should return true when
|
||||
* the button should be displayed as toggled on.
|
||||
* @property {Boolean} autoToggle - If true, the checked state is going to be
|
||||
* automatically toggled on click.
|
||||
*/
|
||||
_createButtonState: function (options) {
|
||||
let isChecked = false;
|
||||
const {id, className, description, onClick, isInStartContainer} = options;
|
||||
let isCheckedValue = false;
|
||||
const { id, className, description, onClick, isInStartContainer, setup, teardown,
|
||||
isTargetSupported, isChecked, autoToggle } = options;
|
||||
const toolbox = this;
|
||||
const button = {
|
||||
id,
|
||||
className,
|
||||
description,
|
||||
onClick,
|
||||
onClick(event) {
|
||||
if (typeof onClick == "function") {
|
||||
onClick(event, toolbox);
|
||||
}
|
||||
if (autoToggle) {
|
||||
button.isChecked = !button.isChecked;
|
||||
}
|
||||
},
|
||||
isTargetSupported,
|
||||
get isChecked() {
|
||||
return isChecked;
|
||||
if (typeof isChecked == "function") {
|
||||
return isChecked(toolbox);
|
||||
}
|
||||
return isCheckedValue;
|
||||
},
|
||||
set isChecked(value) {
|
||||
isChecked = value;
|
||||
// Note that if options.isChecked is given, this is ignored
|
||||
isCheckedValue = value;
|
||||
this.emit("updatechecked");
|
||||
},
|
||||
// The preference for having this button visible.
|
||||
|
@ -583,6 +614,17 @@ Toolbox.prototype = {
|
|||
// holding buttons. By default the buttons are placed in the end container.
|
||||
isInStartContainer: !!isInStartContainer
|
||||
};
|
||||
if (typeof setup == "function") {
|
||||
let onChange = () => {
|
||||
button.emit("updatechecked");
|
||||
};
|
||||
setup(this, onChange);
|
||||
// Save a reference to the cleanup method that will unregister the onChange
|
||||
// callback. Immediately bind the function argument so that we don't have to
|
||||
// also save a reference to them.
|
||||
button.teardown = teardown.bind(options, this, onChange);
|
||||
}
|
||||
button.isVisible = this._commandIsVisible(button);
|
||||
|
||||
EventEmitter.decorate(button);
|
||||
|
||||
|
@ -1030,7 +1072,7 @@ Toolbox.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Add buttons to the UI as specified in the devtools.toolbox.toolbarSpec pref
|
||||
* Add buttons to the UI as specified in devtools/client/definitions.js
|
||||
*/
|
||||
_buildButtons: Task.async(function* () {
|
||||
// Beyond the normal preference filtering
|
||||
|
@ -1040,27 +1082,9 @@ Toolbox.prototype = {
|
|||
yield this._buildNoAutoHideButton()
|
||||
];
|
||||
|
||||
// Build buttons from the GCLI commands only if the GCLI actor is supported and the
|
||||
// target isn't chrome.
|
||||
if (this.target.hasActor("gcli") && !this.target.chrome) {
|
||||
const options = {
|
||||
environment: CommandUtils.createEnvironment(this, "_target")
|
||||
};
|
||||
|
||||
this._requisition = yield CommandUtils.createRequisition(this.target, options);
|
||||
const spec = this.getToolbarSpec();
|
||||
const commandButtons = yield CommandUtils.createCommandButtons(
|
||||
spec, this.target, this.doc, this._requisition, this._createButtonState);
|
||||
this.toolbarButtons = [...this.toolbarButtons, ...commandButtons];
|
||||
}
|
||||
|
||||
// Mutate the objects here with their visibility.
|
||||
this.toolbarButtons.forEach(command => {
|
||||
const definition = ToolboxButtons.find(t => t.id === command.id);
|
||||
command.isTargetSupported = definition.isTargetSupported
|
||||
? definition.isTargetSupported
|
||||
: target => target.isLocalTab;
|
||||
command.isVisible = this._commandIsVisible(command.id);
|
||||
ToolboxButtons.forEach(definition => {
|
||||
let button = this._createButtonState(definition);
|
||||
this.toolbarButtons.push(button);
|
||||
});
|
||||
|
||||
this.component.setToolboxButtons(this.toolbarButtons);
|
||||
|
@ -1073,7 +1097,10 @@ Toolbox.prototype = {
|
|||
this.frameButton = this._createButtonState({
|
||||
id: "command-button-frames",
|
||||
description: L10N.getStr("toolbox.frames.tooltip"),
|
||||
onClick: this.showFramesMenu
|
||||
onClick: this.showFramesMenu,
|
||||
isTargetSupported: target => {
|
||||
return target.activeTab && target.activeTab.traits.frames;
|
||||
}
|
||||
});
|
||||
|
||||
return this.frameButton;
|
||||
|
@ -1087,7 +1114,8 @@ Toolbox.prototype = {
|
|||
this.autohideButton = this._createButtonState({
|
||||
id: "command-button-noautohide",
|
||||
description: L10N.getStr("toolbox.noautohide.tooltip"),
|
||||
onClick: this._toggleNoAutohide
|
||||
onClick: this._toggleNoAutohide,
|
||||
isTargetSupported: target => target.chrome
|
||||
});
|
||||
|
||||
this._isDisableAutohideEnabled().then(enabled => {
|
||||
|
@ -1138,7 +1166,10 @@ Toolbox.prototype = {
|
|||
id: "command-button-pick",
|
||||
description: L10N.getStr("pickButton.tooltip"),
|
||||
onClick: this._onPickerClick,
|
||||
isInStartContainer: true
|
||||
isInStartContainer: true,
|
||||
isTargetSupported: target => {
|
||||
return target.activeTab && target.activeTab.traits.frames;
|
||||
}
|
||||
});
|
||||
|
||||
return this.pickerButton;
|
||||
|
@ -1178,16 +1209,7 @@ Toolbox.prototype = {
|
|||
*/
|
||||
getToolbarSpec: function () {
|
||||
let spec = CommandUtils.getCommandbarSpec("devtools.toolbox.toolbarSpec");
|
||||
// Special case for screenshot command button to check for clipboard preference
|
||||
const clipboardEnabled = Services.prefs
|
||||
.getBoolPref("devtools.screenshot.clipboard.enabled");
|
||||
if (clipboardEnabled) {
|
||||
for (let i = 0; i < spec.length; i++) {
|
||||
if (spec[i] == "screenshot --fullpage --file") {
|
||||
spec[i] += " --clipboard";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spec;
|
||||
},
|
||||
|
||||
|
@ -1199,8 +1221,8 @@ Toolbox.prototype = {
|
|||
* Update the visibility of the buttons.
|
||||
*/
|
||||
updateToolboxButtonsVisibility() {
|
||||
this.toolbarButtons.forEach(command => {
|
||||
command.isVisible = this._commandIsVisible(command.id);
|
||||
this.toolbarButtons.forEach(button => {
|
||||
button.isVisible = this._commandIsVisible(button);
|
||||
});
|
||||
this.component.setToolboxButtons(this.toolbarButtons);
|
||||
},
|
||||
|
@ -1208,11 +1230,11 @@ Toolbox.prototype = {
|
|||
/**
|
||||
* Ensure the visibility of each toolbox button matches the preference value.
|
||||
*/
|
||||
_commandIsVisible: function (id) {
|
||||
_commandIsVisible: function (button) {
|
||||
const {
|
||||
isTargetSupported,
|
||||
visibilityswitch
|
||||
} = this.toolbarButtons.find(btn => btn.id === id);
|
||||
} = button;
|
||||
|
||||
let visible = true;
|
||||
try {
|
||||
|
@ -2318,12 +2340,17 @@ Toolbox.prototype = {
|
|||
detachThread(this._threadClient);
|
||||
this._threadClient = null;
|
||||
|
||||
// Unregister buttons listeners
|
||||
this.toolbarButtons.forEach(button => {
|
||||
if (typeof button.teardown == "function") {
|
||||
// teardown arguments have already been bound in _createButtonState
|
||||
button.teardown();
|
||||
}
|
||||
});
|
||||
|
||||
// We need to grab a reference to win before this._host is destroyed.
|
||||
let win = this.win;
|
||||
|
||||
if (this._requisition) {
|
||||
CommandUtils.destroyRequisition(this._requisition, this.target);
|
||||
}
|
||||
this._telemetry.toolClosed("toolbox");
|
||||
this._telemetry.destroy();
|
||||
|
||||
|
|
|
@ -1755,14 +1755,11 @@ Inspector.prototype = {
|
|||
const command = Services.prefs.getBoolPref("devtools.screenshot.clipboard.enabled") ?
|
||||
"screenshot --file --clipboard --selector" :
|
||||
"screenshot --file --selector";
|
||||
CommandUtils.createRequisition(this._target, {
|
||||
environment: CommandUtils.createEnvironment(this, "_target")
|
||||
}).then(requisition => {
|
||||
// Bug 1180314 - CssSelector might contain white space so need to make sure it is
|
||||
// passed to screenshot as a single parameter. More work *might* be needed if
|
||||
// CssSelector could contain escaped single- or double-quotes, backslashes, etc.
|
||||
requisition.updateExec(`${command} '${this.selectionCssSelector}'`);
|
||||
});
|
||||
// Bug 1180314 - CssSelector might contain white space so need to make sure it is
|
||||
// passed to screenshot as a single parameter. More work *might* be needed if
|
||||
// CssSelector could contain escaped single- or double-quotes, backslashes, etc.
|
||||
CommandUtils.executeOnTarget(this._target,
|
||||
`${command} '${this.selectionCssSelector}'`);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -260,3 +260,40 @@ dom.accesskey=D
|
|||
# displayed inside the developer tools window.
|
||||
# Keyboard shortcut for DOM panel will be shown inside the brackets.
|
||||
dom.tooltip=DOM (%S)
|
||||
|
||||
# LOCALIZATION NOTE (toolbox.buttons.splitconsole):
|
||||
# This is the tooltip of the button in the toolbox toolbar used to toggle
|
||||
# the split console.
|
||||
# Keyboard shortcut will be shown inside brackets.
|
||||
toolbox.buttons.splitconsole = Toggle split console (%S)
|
||||
|
||||
# LOCALIZATION NOTE (toolbox.buttons.responsive):
|
||||
# This is the tooltip of the button in the toolbox toolbar that toggles
|
||||
# the Responsive mode.
|
||||
# Keyboard shortcut will be shown inside brackets.
|
||||
toolbox.buttons.responsive = Responsive Design Mode (%S)
|
||||
|
||||
# LOCALIZATION NOTE (toolbox.buttons.paintflashing):
|
||||
# This is the tooltip of the paintflashing button in the toolbox toolbar
|
||||
# that toggles paintflashing.
|
||||
toolbox.buttons.paintflashing = Toggle paint flashing
|
||||
|
||||
# LOCALIZATION NOTE (toolbox.buttons.scratchpad):
|
||||
# This is the tooltip of the button in the toolbox toolbar that opens
|
||||
# the scratchpad window
|
||||
toolbox.buttons.scratchpad = Scratchpad
|
||||
|
||||
# LOCALIZATION NOTE (toolbox.buttons.screenshot):
|
||||
# This is the tooltip of the button in the toolbox toolbar that allows you to
|
||||
# take a screenshot of the entire page
|
||||
toolbox.buttons.screenshot = Take a screenshot of the entire page
|
||||
|
||||
# LOCALIZATION NOTE (toolbox.buttons.rulers):
|
||||
# This is the tooltip of the button in the toolbox toolbar that toggles the
|
||||
# rulers in the page
|
||||
toolbox.buttons.rulers = Toggle rulers for the page
|
||||
|
||||
# LOCALIZATION NOTE (toolbox.buttons.measure):
|
||||
# This is the tooltip of the button in the toolbox toolbar that toggles the
|
||||
# measuring tools
|
||||
toolbox.buttons.measure = Measure a portion of the page
|
||||
|
|
|
@ -148,11 +148,7 @@ exports.menuitems = [
|
|||
let window = event.target.ownerDocument.defaultView;
|
||||
let target = TargetFactory.forTab(window.gBrowser.selectedTab);
|
||||
|
||||
CommandUtils.createRequisition(target, {
|
||||
environment: CommandUtils.createEnvironment({target})
|
||||
}).then(requisition => {
|
||||
requisition.updateExec("eyedropper --frommenu");
|
||||
}, e => console.error(e));
|
||||
CommandUtils.executeOnTarget(target, "eyedropper --frommenu");
|
||||
},
|
||||
checkbox: true
|
||||
},
|
||||
|
|
|
@ -30,7 +30,6 @@ pref("devtools.toolbox.sidebar.width", 500);
|
|||
pref("devtools.toolbox.host", "bottom");
|
||||
pref("devtools.toolbox.previousHost", "side");
|
||||
pref("devtools.toolbox.selectedTool", "webconsole");
|
||||
pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","scratchpad","resize toggle","screenshot --fullpage --file", "rulers", "measure"]');
|
||||
pref("devtools.toolbox.sideEnabled", true);
|
||||
pref("devtools.toolbox.zoomValue", "1");
|
||||
pref("devtools.toolbox.splitconsoleEnabled", false);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { Ci } = require("chrome");
|
||||
const promise = require("promise");
|
||||
const { Task } = require("devtools/shared/task");
|
||||
const { tunnelToInnerBrowser } = require("./tunnel");
|
||||
|
@ -58,11 +59,16 @@ function swapToInnerBrowser({ tab, containerURL, getInnerBrowser }) {
|
|||
freezeNavigationState(tab);
|
||||
|
||||
// 1. Create a temporary, hidden tab to load the tool UI.
|
||||
let containerTab = gBrowser.addTab(containerURL, {
|
||||
let containerTab = gBrowser.addTab("about:blank", {
|
||||
skipAnimation: true,
|
||||
forceNotRemote: true,
|
||||
});
|
||||
gBrowser.hideTab(containerTab);
|
||||
let containerBrowser = containerTab.linkedBrowser;
|
||||
// Prevent the `containerURL` from ending up in the tab's history.
|
||||
containerBrowser.loadURIWithFlags(containerURL, {
|
||||
flags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
|
||||
});
|
||||
|
||||
// Copy tab listener state flags to container tab. Each tab gets its own tab
|
||||
// listener and state flags which cache document loading progress. The state flags
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
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");
|
||||
const { getStack } = require("devtools/shared/platform/stack");
|
||||
|
||||
|
@ -127,7 +126,8 @@ function tunnelToInnerBrowser(outer, inner) {
|
|||
// 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);
|
||||
debug("Copy inner permanentKey to outer browser");
|
||||
outer.permanentKey = inner.permanentKey;
|
||||
|
||||
// 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.
|
||||
|
@ -304,33 +304,6 @@ function tunnelToInnerBrowser(outer, inner) {
|
|||
|
||||
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[FRAME_LOADER].messageManager;
|
||||
let onHistoryEntry = message => {
|
||||
let data = message.data.data;
|
||||
let history = data.history || data.historychange;
|
||||
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
|
||||
|
|
|
@ -41,4 +41,5 @@ add_task(function* () {
|
|||
is(ui.destroyed, true, "RDM closed synchronously");
|
||||
|
||||
yield clientClosed;
|
||||
yield removeTab(tab);
|
||||
});
|
||||
|
|
|
@ -365,8 +365,11 @@ function addDeviceForTest(device) {
|
|||
|
||||
function waitForClientClose(ui) {
|
||||
return new Promise(resolve => {
|
||||
info("RDM's debugger client is now closed");
|
||||
ui.client.addOneTimeListener("closed", resolve);
|
||||
info("Waiting for RDM debugger client to close");
|
||||
ui.client.addOneTimeListener("closed", () => {
|
||||
info("RDM's debugger client is now closed");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,28 @@ loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
|
|||
* A collection of utilities to help working with commands
|
||||
*/
|
||||
var CommandUtils = {
|
||||
/**
|
||||
* Caches requisitions created when calling executeOnTarget:
|
||||
* Target => Requisition Promise
|
||||
*/
|
||||
_requisitions: new WeakMap(),
|
||||
|
||||
/**
|
||||
* Utility to execute a command string on a given target
|
||||
*/
|
||||
executeOnTarget: Task.async(function* (target, command) {
|
||||
let requisitionPromise = this._requisitions.get(target);
|
||||
if (!requisitionPromise) {
|
||||
requisitionPromise = this.createRequisition(target, {
|
||||
environment: CommandUtils.createEnvironment({ target }, "target")
|
||||
});
|
||||
// Store the promise to avoid races by storing the promise immediately
|
||||
this._requisitions.set(target, requisitionPromise);
|
||||
}
|
||||
let requisition = yield requisitionPromise;
|
||||
requisition.updateExec(command);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Utility to ensure that things are loaded in the correct order
|
||||
*/
|
||||
|
|
|
@ -44,16 +44,13 @@ function* delayedClicks(toolbox, node, clicks) {
|
|||
setTimeout(() => resolve(), TOOL_DELAY);
|
||||
});
|
||||
|
||||
// this event will fire once the command execution starts and
|
||||
// the output object is created
|
||||
let clicked = toolbox._requisition.commandOutputManager.onOutput.once();
|
||||
let PaintFlashingCmd = require("devtools/shared/gcli/commands/paintflashing");
|
||||
let clicked = PaintFlashingCmd.eventEmitter.once("changed");
|
||||
|
||||
info("Clicking button " + node.id);
|
||||
node.click();
|
||||
|
||||
let outputEvent = yield clicked;
|
||||
// promise gets resolved once execution finishes and output is ready
|
||||
yield outputEvent.output.promise;
|
||||
yield clicked;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -744,6 +744,10 @@ a.learn-more-link.webconsole-learn-more-link {
|
|||
margin-inline-end: 5px;
|
||||
}
|
||||
|
||||
.network .message-flex-body > .message-body {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.webconsole-output-wrapper .message .indent {
|
||||
display: inline-block;
|
||||
border-inline-end: solid 1px var(--theme-splitter-color);
|
||||
|
@ -757,7 +761,7 @@ a.learn-more-link.webconsole-learn-more-link {
|
|||
|
||||
.message.startGroup .icon,
|
||||
.message.startGroupCollapsed .icon {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* console.table() */
|
||||
|
|
|
@ -13,6 +13,7 @@ const { IdGenerator } = require("devtools/client/webconsole/new-console-output/u
|
|||
const { batchActions } = require("devtools/client/webconsole/new-console-output/actions/enhancers");
|
||||
const {
|
||||
MESSAGE_ADD,
|
||||
NETWORK_MESSAGE_UPDATE,
|
||||
MESSAGES_CLEAR,
|
||||
MESSAGE_OPEN,
|
||||
MESSAGE_CLOSE,
|
||||
|
@ -90,10 +91,25 @@ function messageTableDataReceive(id, data) {
|
|||
};
|
||||
}
|
||||
|
||||
function networkMessageUpdate(packet, idGenerator = null) {
|
||||
if (idGenerator == null) {
|
||||
idGenerator = defaultIdGenerator;
|
||||
}
|
||||
|
||||
let message = prepareMessage(packet, idGenerator);
|
||||
|
||||
return {
|
||||
type: NETWORK_MESSAGE_UPDATE,
|
||||
message,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
messageAdd,
|
||||
messagesClear,
|
||||
messageOpen,
|
||||
messageClose,
|
||||
messageTableDataGet,
|
||||
networkMessageUpdate,
|
||||
};
|
||||
|
||||
|
|
|
@ -50,7 +50,10 @@ const MessageContainer = createClass({
|
|||
const repeatChanged = this.props.message.repeat !== nextProps.message.repeat;
|
||||
const openChanged = this.props.open !== nextProps.open;
|
||||
const tableDataChanged = this.props.tableData !== nextProps.tableData;
|
||||
return repeatChanged || openChanged || tableDataChanged;
|
||||
const responseChanged = this.props.message.response !== nextProps.message.response;
|
||||
const totalTimeChanged = this.props.message.totalTime !== nextProps.message.totalTime;
|
||||
return repeatChanged || openChanged || tableDataChanged || responseChanged ||
|
||||
totalTimeChanged;
|
||||
},
|
||||
|
||||
render() {
|
||||
|
|
|
@ -29,13 +29,35 @@ NetworkEventMessage.defaultProps = {
|
|||
indent: 0,
|
||||
};
|
||||
|
||||
function NetworkEventMessage(props) {
|
||||
const { message, serviceContainer, indent } = props;
|
||||
const { actor, source, type, level, request, isXHR, timeStamp } = message;
|
||||
function NetworkEventMessage({
|
||||
indent,
|
||||
message = {},
|
||||
serviceContainer,
|
||||
}) {
|
||||
const {
|
||||
actor,
|
||||
source,
|
||||
type,
|
||||
level,
|
||||
request,
|
||||
response: {
|
||||
httpVersion,
|
||||
status,
|
||||
statusText,
|
||||
},
|
||||
isXHR,
|
||||
timeStamp,
|
||||
totalTime,
|
||||
} = message;
|
||||
|
||||
const topLevelClasses = [ "cm-s-mozilla" ];
|
||||
let statusInfo;
|
||||
|
||||
function onUrlClick() {
|
||||
if (httpVersion && status && statusText && totalTime !== undefined) {
|
||||
statusInfo = `[${httpVersion} ${status} ${statusText} ${totalTime}ms]`;
|
||||
}
|
||||
|
||||
function openNetworkMonitor() {
|
||||
serviceContainer.openNetworkPanel(actor);
|
||||
}
|
||||
|
||||
|
@ -43,10 +65,13 @@ function NetworkEventMessage(props) {
|
|||
const xhr = isXHR
|
||||
? dom.span({ className: "xhr" }, l10n.getStr("webConsoleXhrIndicator"))
|
||||
: null;
|
||||
const url = dom.a({ className: "url", title: request.url, onClick: onUrlClick },
|
||||
request.url.replace(/\?.+/, ""));
|
||||
const url = dom.a({ className: "url", title: request.url, onClick: openNetworkMonitor },
|
||||
request.url.replace(/\?.+/, ""));
|
||||
const statusBody = statusInfo
|
||||
? dom.a({ className: "status", onClick: openNetworkMonitor }, statusInfo)
|
||||
: null;
|
||||
|
||||
const messageBody = dom.span({}, method, xhr, url);
|
||||
const messageBody = [method, xhr, url, statusBody];
|
||||
|
||||
const childProps = {
|
||||
source,
|
||||
|
|
|
@ -11,6 +11,7 @@ const actionTypes = {
|
|||
MESSAGES_CLEAR: "MESSAGES_CLEAR",
|
||||
MESSAGE_OPEN: "MESSAGE_OPEN",
|
||||
MESSAGE_CLOSE: "MESSAGE_CLOSE",
|
||||
NETWORK_MESSAGE_UPDATE: "NETWORK_MESSAGE_UPDATE",
|
||||
MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE",
|
||||
TIMESTAMPS_TOGGLE: "TIMESTAMPS_TOGGLE",
|
||||
FILTER_TOGGLE: "FILTER_TOGGLE",
|
||||
|
|
|
@ -134,7 +134,7 @@ NewConsoleOutputWrapper.prototype = {
|
|||
let jsterm = this.jsterm;
|
||||
jsterm.hud.on("new-messages", function onThisMessage(e, messages) {
|
||||
for (let m of messages) {
|
||||
if (m.messageId == messageId) {
|
||||
if (m.messageId === messageId) {
|
||||
resolve(m.node);
|
||||
jsterm.hud.off("new-messages", onThisMessage);
|
||||
return;
|
||||
|
@ -160,6 +160,16 @@ NewConsoleOutputWrapper.prototype = {
|
|||
store.dispatch(actions.timestampsToggle(enabled));
|
||||
},
|
||||
|
||||
dispatchMessageUpdate: function (message, res) {
|
||||
batchedMessageAdd(actions.networkMessageUpdate(message));
|
||||
|
||||
// network-message-updated will emit when eventTimings message arrives
|
||||
// which is the last one of 8 updates happening on network message update.
|
||||
if (res.packet.updateType === "eventTimings") {
|
||||
this.jsterm.hud.emit("network-message-updated", res);
|
||||
}
|
||||
},
|
||||
|
||||
// Should be used for test purpose only.
|
||||
getStore: function () {
|
||||
return store;
|
||||
|
|
|
@ -98,6 +98,11 @@ function messages(state = new MessageState(), action) {
|
|||
case constants.MESSAGE_TABLE_RECEIVE:
|
||||
const {id, data} = action;
|
||||
return state.set("messagesTableDataById", messagesTableDataById.set(id, data));
|
||||
case constants.NETWORK_MESSAGE_UPDATE:
|
||||
let updateMessage = action.message;
|
||||
return state.set("messagesById", messagesById.map((message) =>
|
||||
(message.id === updateMessage.id) ? updateMessage : message
|
||||
));
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
|
@ -18,11 +18,12 @@ const { stubPreparedMessages } = require("devtools/client/webconsole/new-console
|
|||
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
|
||||
|
||||
const EXPECTED_URL = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html";
|
||||
const EXPECTED_STATUS = /\[HTTP\/\d\.\d \d+ [A-Za-z ]+ \d+ms\]/;
|
||||
|
||||
describe("NetworkEventMessage component:", () => {
|
||||
describe("GET request", () => {
|
||||
it("renders as expected", () => {
|
||||
const message = stubPreparedMessages.get("GET request");
|
||||
const message = stubPreparedMessages.get("GET request eventTimings");
|
||||
const wrapper = render(NetworkEventMessage({ message, serviceContainer }));
|
||||
const L10n = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
|
||||
const { timestampString } = new L10n();
|
||||
|
@ -32,9 +33,8 @@ describe("NetworkEventMessage component:", () => {
|
|||
expect(wrapper.find(".message-body .xhr").length).toBe(0);
|
||||
expect(wrapper.find(".message-body .url").length).toBe(1);
|
||||
expect(wrapper.find(".message-body .url").text()).toBe(EXPECTED_URL);
|
||||
expect(wrapper
|
||||
.find("div.message.cm-s-mozilla span.message-body.devtools-monospace").length
|
||||
).toBe(1);
|
||||
expect(wrapper.find(".message-body .status").length).toBe(1);
|
||||
expect(wrapper.find(".message-body .status").text()).toMatch(EXPECTED_STATUS);
|
||||
});
|
||||
|
||||
it("has the expected indent", () => {
|
||||
|
@ -52,21 +52,20 @@ describe("NetworkEventMessage component:", () => {
|
|||
|
||||
describe("XHR GET request", () => {
|
||||
it("renders as expected", () => {
|
||||
const message = stubPreparedMessages.get("XHR GET request");
|
||||
const message = stubPreparedMessages.get("XHR GET request eventTimings");
|
||||
const wrapper = render(NetworkEventMessage({ message, serviceContainer }));
|
||||
|
||||
expect(wrapper.find(".message-body .method").text()).toBe("GET");
|
||||
expect(wrapper.find(".message-body .xhr").length).toBe(1);
|
||||
expect(wrapper.find(".message-body .xhr").text()).toBe("XHR");
|
||||
expect(wrapper.find(".message-body .url").text()).toBe(EXPECTED_URL);
|
||||
let selector = "div.message.cm-s-mozilla span.message-body.devtools-monospace";
|
||||
expect(wrapper.find(selector).length).toBe(1);
|
||||
expect(wrapper.find(".message-body .status").text()).toMatch(EXPECTED_STATUS);
|
||||
});
|
||||
});
|
||||
|
||||
describe("XHR POST request", () => {
|
||||
it("renders as expected", () => {
|
||||
const message = stubPreparedMessages.get("XHR POST request");
|
||||
const message = stubPreparedMessages.get("XHR POST request eventTimings");
|
||||
const wrapper = render(NetworkEventMessage({ message, serviceContainer }));
|
||||
|
||||
expect(wrapper.find(".message-body .method").text()).toBe("POST");
|
||||
|
@ -74,8 +73,8 @@ describe("NetworkEventMessage component:", () => {
|
|||
expect(wrapper.find(".message-body .xhr").text()).toBe("XHR");
|
||||
expect(wrapper.find(".message-body .url").length).toBe(1);
|
||||
expect(wrapper.find(".message-body .url").text()).toBe(EXPECTED_URL);
|
||||
let selector = "div.message.cm-s-mozilla span.message-body.devtools-monospace";
|
||||
expect(wrapper.find(selector).length);
|
||||
expect(wrapper.find(".message-body .status").length).toBe(1);
|
||||
expect(wrapper.find(".message-body .status").text()).toMatch(EXPECTED_STATUS);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,22 +24,34 @@ add_task(function* () {
|
|||
ok(ui.jsterm, "jsterm exists");
|
||||
ok(ui.newConsoleOutput, "newConsoleOutput exists");
|
||||
|
||||
let received = new Promise(resolve => {
|
||||
let networkEvent = new Promise(resolve => {
|
||||
let i = 0;
|
||||
toolbox.target.client.addListener(TARGET, (type, res) => {
|
||||
toolbox.target.activeConsole.on(TARGET, (type, res) => {
|
||||
stubs.packets.push(formatPacket(keys[i], res));
|
||||
stubs.preparedMessages.push(formatNetworkStub(keys[i], res));
|
||||
stubs.preparedMessages.push(formatNetworkEventStub(keys[i], res));
|
||||
if (++i === keys.length) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let networkEventUpdate = new Promise(resolve => {
|
||||
let i = 0;
|
||||
ui.jsterm.hud.on("network-message-updated", function onNetworkUpdated(event, res) {
|
||||
ui.jsterm.hud.off("network-message-updated", onNetworkUpdated);
|
||||
stubs.preparedMessages.push(
|
||||
formatNetworkEventStub(`${keys[i++]} ${res.packet.updateType}`, res));
|
||||
if (i === keys.length) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
|
||||
content.wrappedJSObject.triggerPacket();
|
||||
});
|
||||
|
||||
yield received;
|
||||
yield Promise.all([networkEvent, networkEventUpdate]);
|
||||
}
|
||||
let filePath = OS.Path.join(`${BASE_PATH}/stubs/${TARGET}.js`);
|
||||
OS.File.writeAtomic(filePath, formatFile(stubs, "NetworkEventMessage"));
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* import-globals-from ../../../../../framework/test/shared-head.js */
|
||||
/* exported TEMP_FILE_PATH, TEMP_CSS_FILE_PATH, formatPacket, formatStub,
|
||||
formatNetworkStub, formatFile */
|
||||
formatNetworkEventStub, formatFile */
|
||||
"use strict";
|
||||
|
||||
// shared-head.js handles imports, constants, and utility functions
|
||||
|
@ -133,31 +133,9 @@ function formatStub(key, packet) {
|
|||
return `stubPreparedMessages.set("${key}", new ConsoleMessage(${stringifiedMessage}));`;
|
||||
}
|
||||
|
||||
function formatNetworkStub(key, packet) {
|
||||
let actor = packet.eventActor;
|
||||
let networkInfo = {
|
||||
_type: "NetworkEvent",
|
||||
timeStamp: actor.timeStamp,
|
||||
node: null,
|
||||
actor: actor.actor,
|
||||
discardRequestBody: true,
|
||||
discardResponseBody: true,
|
||||
startedDateTime: actor.startedDateTime,
|
||||
request: {
|
||||
url: actor.url,
|
||||
method: actor.method,
|
||||
},
|
||||
isXHR: actor.isXHR,
|
||||
cause: actor.cause,
|
||||
response: {},
|
||||
timings: {},
|
||||
// track the list of network event updates
|
||||
updates: [],
|
||||
private: actor.private,
|
||||
fromCache: actor.fromCache,
|
||||
fromServiceWorker: actor.fromServiceWorker
|
||||
};
|
||||
let prepared = prepareMessage(networkInfo, {getNextId: () => "1"});
|
||||
function formatNetworkEventStub(key, packet) {
|
||||
let networkInfo = packet.actor ? packet : packet.networkInfo;
|
||||
let prepared = prepareMessage(networkInfo, {getNextId: () => networkInfo.actor});
|
||||
let stringifiedMessage = JSON.stringify(prepared, null, 2);
|
||||
return `stubPreparedMessages.set("${key}", ` +
|
||||
`new NetworkEventMessage(${stringifiedMessage}));`;
|
||||
|
|
|
@ -14,8 +14,8 @@ const { NetworkEventMessage } =
|
|||
let stubPreparedMessages = new Map();
|
||||
let stubPackets = new Map();
|
||||
stubPreparedMessages.set("GET request", new NetworkEventMessage({
|
||||
"id": "1",
|
||||
"actor": "server1.conn0.child1/netEvent29",
|
||||
"id": "server1.conn0.child1/netEvent30",
|
||||
"actor": "server1.conn0.child1/netEvent30",
|
||||
"level": "log",
|
||||
"isXHR": false,
|
||||
"request": {
|
||||
|
@ -25,13 +25,38 @@ stubPreparedMessages.set("GET request", new NetworkEventMessage({
|
|||
"response": {},
|
||||
"source": "network",
|
||||
"type": "log",
|
||||
"timeStamp": 1479159937660,
|
||||
"groupId": null
|
||||
"groupId": null,
|
||||
"timeStamp": 1485777989897
|
||||
}));
|
||||
|
||||
stubPreparedMessages.set("GET request eventTimings", new NetworkEventMessage({
|
||||
"id": "server1.conn0.child1/netEvent30",
|
||||
"actor": "server1.conn0.child1/netEvent30",
|
||||
"level": "log",
|
||||
"isXHR": false,
|
||||
"request": {
|
||||
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
|
||||
"method": "GET",
|
||||
"headersSize": 489
|
||||
},
|
||||
"response": {
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"status": "404",
|
||||
"statusText": "Not Found",
|
||||
"headersSize": 160,
|
||||
"remoteAddress": "127.0.0.1",
|
||||
"remotePort": 8888
|
||||
},
|
||||
"source": "network",
|
||||
"type": "log",
|
||||
"groupId": null,
|
||||
"timeStamp": 1485777989897,
|
||||
"totalTime": 7
|
||||
}));
|
||||
|
||||
stubPreparedMessages.set("XHR GET request", new NetworkEventMessage({
|
||||
"id": "1",
|
||||
"actor": "server1.conn1.child1/netEvent29",
|
||||
"id": "server1.conn1.child1/netEvent30",
|
||||
"actor": "server1.conn1.child1/netEvent30",
|
||||
"level": "log",
|
||||
"isXHR": true,
|
||||
"request": {
|
||||
|
@ -41,13 +66,38 @@ stubPreparedMessages.set("XHR GET request", new NetworkEventMessage({
|
|||
"response": {},
|
||||
"source": "network",
|
||||
"type": "log",
|
||||
"timeStamp": 1479159938522,
|
||||
"groupId": null
|
||||
"groupId": null,
|
||||
"timeStamp": 1485777990639
|
||||
}));
|
||||
|
||||
stubPreparedMessages.set("XHR GET request eventTimings", new NetworkEventMessage({
|
||||
"id": "server1.conn1.child1/netEvent30",
|
||||
"actor": "server1.conn1.child1/netEvent30",
|
||||
"level": "log",
|
||||
"isXHR": true,
|
||||
"request": {
|
||||
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
|
||||
"method": "GET",
|
||||
"headersSize": 489
|
||||
},
|
||||
"response": {
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"status": "404",
|
||||
"statusText": "Not Found",
|
||||
"headersSize": 160,
|
||||
"remoteAddress": "127.0.0.1",
|
||||
"remotePort": 8888
|
||||
},
|
||||
"source": "network",
|
||||
"type": "log",
|
||||
"groupId": null,
|
||||
"timeStamp": 1485777990639,
|
||||
"totalTime": 10
|
||||
}));
|
||||
|
||||
stubPreparedMessages.set("XHR POST request", new NetworkEventMessage({
|
||||
"id": "1",
|
||||
"actor": "server1.conn2.child1/netEvent29",
|
||||
"id": "server1.conn2.child1/netEvent30",
|
||||
"actor": "server1.conn2.child1/netEvent30",
|
||||
"level": "log",
|
||||
"isXHR": true,
|
||||
"request": {
|
||||
|
@ -57,131 +107,174 @@ stubPreparedMessages.set("XHR POST request", new NetworkEventMessage({
|
|||
"response": {},
|
||||
"source": "network",
|
||||
"type": "log",
|
||||
"timeStamp": 1479159939328,
|
||||
"groupId": null
|
||||
"groupId": null,
|
||||
"timeStamp": 1485777991739
|
||||
}));
|
||||
|
||||
stubPreparedMessages.set("XHR POST request eventTimings", new NetworkEventMessage({
|
||||
"id": "server1.conn2.child1/netEvent30",
|
||||
"actor": "server1.conn2.child1/netEvent30",
|
||||
"level": "log",
|
||||
"isXHR": true,
|
||||
"request": {
|
||||
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
|
||||
"method": "POST",
|
||||
"headersSize": 509
|
||||
},
|
||||
"response": {
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"status": "404",
|
||||
"statusText": "Not Found",
|
||||
"headersSize": 160,
|
||||
"remoteAddress": "127.0.0.1",
|
||||
"remotePort": 8888
|
||||
},
|
||||
"source": "network",
|
||||
"type": "log",
|
||||
"groupId": null,
|
||||
"timeStamp": 1485777991739,
|
||||
"totalTime": 9
|
||||
}));
|
||||
|
||||
stubPackets.set("GET request", {
|
||||
"from": "server1.conn0.child1/consoleActor2",
|
||||
"type": "networkEvent",
|
||||
"eventActor": {
|
||||
"actor": "server1.conn0.child1/netEvent29",
|
||||
"startedDateTime": "2016-10-15T23:12:04.196Z",
|
||||
"timeStamp": 1479159937660,
|
||||
"_type": "NetworkEvent",
|
||||
"timeStamp": 1485777989897,
|
||||
"node": null,
|
||||
"actor": "server1.conn0.child1/netEvent30",
|
||||
"discardRequestBody": true,
|
||||
"discardResponseBody": true,
|
||||
"startedDateTime": "2017-01-30T12:06:29.897Z",
|
||||
"request": {
|
||||
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
|
||||
"method": "GET",
|
||||
"isXHR": false,
|
||||
"cause": {
|
||||
"type": 3,
|
||||
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
|
||||
"stacktrace": [
|
||||
{
|
||||
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
|
||||
"lineNumber": 3,
|
||||
"columnNumber": 1,
|
||||
"functionName": "triggerPacket",
|
||||
"asyncCause": null
|
||||
},
|
||||
{
|
||||
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
|
||||
"lineNumber": 4,
|
||||
"columnNumber": 7,
|
||||
"functionName": null,
|
||||
"asyncCause": null
|
||||
},
|
||||
{
|
||||
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
|
||||
"lineNumber": 53,
|
||||
"columnNumber": 20,
|
||||
"functionName": null,
|
||||
"asyncCause": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"private": false
|
||||
}
|
||||
"method": "GET"
|
||||
},
|
||||
"isXHR": false,
|
||||
"cause": {
|
||||
"type": "img",
|
||||
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
|
||||
"stacktrace": [
|
||||
{
|
||||
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
|
||||
"lineNumber": 3,
|
||||
"columnNumber": 1,
|
||||
"functionName": "triggerPacket",
|
||||
"asyncCause": null
|
||||
},
|
||||
{
|
||||
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
|
||||
"lineNumber": 4,
|
||||
"columnNumber": 7,
|
||||
"functionName": null,
|
||||
"asyncCause": null
|
||||
},
|
||||
{
|
||||
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
|
||||
"lineNumber": 53,
|
||||
"columnNumber": 20,
|
||||
"functionName": null,
|
||||
"asyncCause": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"response": {},
|
||||
"timings": {},
|
||||
"updates": [],
|
||||
"private": false,
|
||||
"from": "server1.conn0.child1/consoleActor2"
|
||||
});
|
||||
|
||||
stubPackets.set("XHR GET request", {
|
||||
"from": "server1.conn1.child1/consoleActor2",
|
||||
"type": "networkEvent",
|
||||
"eventActor": {
|
||||
"actor": "server1.conn1.child1/netEvent29",
|
||||
"startedDateTime": "2016-10-15T23:12:05.690Z",
|
||||
"timeStamp": 1479159938522,
|
||||
"_type": "NetworkEvent",
|
||||
"timeStamp": 1485777990639,
|
||||
"node": null,
|
||||
"actor": "server1.conn1.child1/netEvent30",
|
||||
"discardRequestBody": true,
|
||||
"discardResponseBody": true,
|
||||
"startedDateTime": "2017-01-30T12:06:30.639Z",
|
||||
"request": {
|
||||
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
|
||||
"method": "GET",
|
||||
"isXHR": true,
|
||||
"cause": {
|
||||
"type": 11,
|
||||
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
|
||||
"stacktrace": [
|
||||
{
|
||||
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
|
||||
"lineNumber": 4,
|
||||
"columnNumber": 1,
|
||||
"functionName": "triggerPacket",
|
||||
"asyncCause": null
|
||||
},
|
||||
{
|
||||
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
|
||||
"lineNumber": 4,
|
||||
"columnNumber": 7,
|
||||
"functionName": null,
|
||||
"asyncCause": null
|
||||
},
|
||||
{
|
||||
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
|
||||
"lineNumber": 53,
|
||||
"columnNumber": 20,
|
||||
"functionName": null,
|
||||
"asyncCause": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"private": false
|
||||
}
|
||||
"method": "GET"
|
||||
},
|
||||
"isXHR": true,
|
||||
"cause": {
|
||||
"type": "xhr",
|
||||
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
|
||||
"stacktrace": [
|
||||
{
|
||||
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
|
||||
"lineNumber": 4,
|
||||
"columnNumber": 1,
|
||||
"functionName": "triggerPacket",
|
||||
"asyncCause": null
|
||||
},
|
||||
{
|
||||
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
|
||||
"lineNumber": 4,
|
||||
"columnNumber": 7,
|
||||
"functionName": null,
|
||||
"asyncCause": null
|
||||
},
|
||||
{
|
||||
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
|
||||
"lineNumber": 53,
|
||||
"columnNumber": 20,
|
||||
"functionName": null,
|
||||
"asyncCause": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"response": {},
|
||||
"timings": {},
|
||||
"updates": [],
|
||||
"private": false,
|
||||
"from": "server1.conn1.child1/consoleActor2"
|
||||
});
|
||||
|
||||
stubPackets.set("XHR POST request", {
|
||||
"from": "server1.conn2.child1/consoleActor2",
|
||||
"type": "networkEvent",
|
||||
"eventActor": {
|
||||
"actor": "server1.conn2.child1/netEvent29",
|
||||
"startedDateTime": "2016-10-15T23:12:07.158Z",
|
||||
"timeStamp": 1479159939328,
|
||||
"_type": "NetworkEvent",
|
||||
"timeStamp": 1485777991739,
|
||||
"node": null,
|
||||
"actor": "server1.conn2.child1/netEvent30",
|
||||
"discardRequestBody": true,
|
||||
"discardResponseBody": true,
|
||||
"startedDateTime": "2017-01-30T12:06:31.739Z",
|
||||
"request": {
|
||||
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
|
||||
"method": "POST",
|
||||
"isXHR": true,
|
||||
"cause": {
|
||||
"type": 11,
|
||||
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
|
||||
"stacktrace": [
|
||||
{
|
||||
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
|
||||
"lineNumber": 4,
|
||||
"columnNumber": 1,
|
||||
"functionName": "triggerPacket",
|
||||
"asyncCause": null
|
||||
},
|
||||
{
|
||||
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
|
||||
"lineNumber": 4,
|
||||
"columnNumber": 7,
|
||||
"functionName": null,
|
||||
"asyncCause": null
|
||||
},
|
||||
{
|
||||
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
|
||||
"lineNumber": 53,
|
||||
"columnNumber": 20,
|
||||
"functionName": null,
|
||||
"asyncCause": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"private": false
|
||||
}
|
||||
"method": "POST"
|
||||
},
|
||||
"isXHR": true,
|
||||
"cause": {
|
||||
"type": "xhr",
|
||||
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
|
||||
"stacktrace": [
|
||||
{
|
||||
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
|
||||
"lineNumber": 4,
|
||||
"columnNumber": 1,
|
||||
"functionName": "triggerPacket",
|
||||
"asyncCause": null
|
||||
},
|
||||
{
|
||||
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
|
||||
"lineNumber": 4,
|
||||
"columnNumber": 7,
|
||||
"functionName": null,
|
||||
"asyncCause": null
|
||||
},
|
||||
{
|
||||
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
|
||||
"lineNumber": 53,
|
||||
"columnNumber": 20,
|
||||
"functionName": null,
|
||||
"asyncCause": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"response": {},
|
||||
"timings": {},
|
||||
"updates": [],
|
||||
"private": false,
|
||||
"from": "server1.conn2.child1/consoleActor2"
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -50,6 +50,7 @@ exports.NetworkEventMessage = Immutable.Record({
|
|||
response: null,
|
||||
source: MESSAGE_SOURCE.NETWORK,
|
||||
type: MESSAGE_TYPE.LOG,
|
||||
timeStamp: null,
|
||||
groupId: null,
|
||||
timeStamp: null,
|
||||
totalTime: null,
|
||||
});
|
||||
|
|
|
@ -11,12 +11,7 @@ exports.IdGenerator = class IdGenerator {
|
|||
this.messageId = 1;
|
||||
}
|
||||
|
||||
getNextId() {
|
||||
// Return the next message id, as a string.
|
||||
return "" + this.messageId++;
|
||||
}
|
||||
|
||||
getCurrentId() {
|
||||
return this.messageId;
|
||||
getNextId(packet) {
|
||||
return (packet && packet.actor) ? packet.actor : "" + this.messageId++;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -29,7 +29,7 @@ function prepareMessage(packet, idGenerator) {
|
|||
if (packet.allowRepeating) {
|
||||
packet = packet.set("repeatId", getRepeatId(packet));
|
||||
}
|
||||
return packet.set("id", idGenerator.getNextId());
|
||||
return packet.set("id", idGenerator.getNextId(packet));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -182,7 +182,8 @@ function transformPacket(packet) {
|
|||
isXHR: networkEvent.isXHR,
|
||||
request: networkEvent.request,
|
||||
response: networkEvent.response,
|
||||
timeStamp: networkEvent.timeStamp
|
||||
timeStamp: networkEvent.timeStamp,
|
||||
totalTime: networkEvent.totalTime,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -228,17 +228,24 @@ WebConsoleConnectionProxy.prototype = {
|
|||
/**
|
||||
* Dispatch a message add on the new frontend and emit an event for tests.
|
||||
*/
|
||||
dispatchMessageAdd: function(packet) {
|
||||
dispatchMessageAdd: function (packet) {
|
||||
this.webConsoleFrame.newConsoleOutput.dispatchMessageAdd(packet);
|
||||
},
|
||||
|
||||
/**
|
||||
* Batched dispatch of messages.
|
||||
*/
|
||||
dispatchMessagesAdd: function(packets) {
|
||||
dispatchMessagesAdd: function (packets) {
|
||||
this.webConsoleFrame.newConsoleOutput.dispatchMessagesAdd(packets);
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatch a message event on the new frontend and emit an event for tests.
|
||||
*/
|
||||
dispatchMessageUpdate: function (networkInfo, response) {
|
||||
this.webConsoleFrame.newConsoleOutput.dispatchMessageUpdate(networkInfo, response);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "cachedMessages" response handler.
|
||||
*
|
||||
|
@ -360,13 +367,15 @@ WebConsoleConnectionProxy.prototype = {
|
|||
* @private
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
* @param object networkInfo
|
||||
* The network request information.
|
||||
* @param object response
|
||||
* The update response received from the server.
|
||||
*/
|
||||
_onNetworkEventUpdate: function (type, { packet, networkInfo }) {
|
||||
_onNetworkEventUpdate: function (type, response) {
|
||||
let { packet, networkInfo } = response;
|
||||
if (this.webConsoleFrame) {
|
||||
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
|
||||
this.dispatchMessageUpdate(networkInfo, response);
|
||||
}
|
||||
this.webConsoleFrame.handleNetworkEventUpdate(networkInfo, packet);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -235,9 +235,9 @@ ActorPool.prototype = {
|
|||
addActor: function APAddActor(actor) {
|
||||
actor.conn = this.conn;
|
||||
if (!actor.actorID) {
|
||||
let prefix = actor.actorPrefix;
|
||||
// Older style actors use actorPrefix, while protocol.js-based actors use typeName
|
||||
let prefix = actor.actorPrefix || actor.typeName;
|
||||
if (!prefix && typeof actor == "function") {
|
||||
// typeName is a convention used with protocol.js-based actors
|
||||
prefix = actor.prototype.actorPrefix || actor.prototype.typeName;
|
||||
}
|
||||
actor.actorID = this.conn.allocID(prefix || undefined);
|
||||
|
|
|
@ -991,11 +991,7 @@ var InspectorFront = FrontClassWithSpec(inspectorSpec, {
|
|||
if (toolbox) {
|
||||
// If the eyedropper was already started using the gcli command, hide it so we don't
|
||||
// end up with 2 instances of the eyedropper on the page.
|
||||
let {target} = toolbox;
|
||||
let requisition = yield CommandUtils.createRequisition(target, {
|
||||
environment: CommandUtils.createEnvironment({target})
|
||||
});
|
||||
yield requisition.updateExec("eyedropper --hide");
|
||||
CommandUtils.executeOnTarget(toolbox.target, "eyedropper --hide");
|
||||
}
|
||||
|
||||
yield this._pickColorFromPage(options);
|
||||
|
|
|
@ -19,6 +19,9 @@ try {
|
|||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const eventEmitter = new EventEmitter();
|
||||
|
||||
// exports the event emitter to help test know when this command is toggled
|
||||
exports.eventEmitter = eventEmitter;
|
||||
|
||||
const gcli = require("gcli/index");
|
||||
const l10n = require("gcli/l10n");
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ DestroyPrivateKeyWithoutDestroyingPKCS11Object(SECKEYPrivateKey* key)
|
|||
// generates a random ID for each key. The given template must contain an
|
||||
// attribute slot for a key ID, but it must consist of a null pointer and have a
|
||||
// length of 0.
|
||||
SECKEYPrivateKey*
|
||||
UniqueSECKEYPrivateKey
|
||||
PrivateKeyFromPrivateKeyTemplate(CK_ATTRIBUTE* aTemplate,
|
||||
CK_ULONG aTemplateSize)
|
||||
{
|
||||
|
@ -147,7 +147,8 @@ PrivateKeyFromPrivateKeyTemplate(CK_ATTRIBUTE* aTemplate,
|
|||
}
|
||||
|
||||
// Have NSS translate the object to a private key.
|
||||
return PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr);
|
||||
return UniqueSECKEYPrivateKey(
|
||||
PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr));
|
||||
}
|
||||
|
||||
CryptoKey::CryptoKey(nsIGlobalObject* aGlobal)
|
||||
|
@ -364,8 +365,8 @@ CryptoKey::AddPublicKeyData(SECKEYPublicKey* aPublicKey)
|
|||
{ CKA_VALUE, value.data, value.len },
|
||||
};
|
||||
|
||||
mPrivateKey = UniqueSECKEYPrivateKey(
|
||||
PrivateKeyFromPrivateKeyTemplate(keyTemplate, ArrayLength(keyTemplate)));
|
||||
mPrivateKey = PrivateKeyFromPrivateKeyTemplate(keyTemplate,
|
||||
ArrayLength(keyTemplate));
|
||||
NS_ENSURE_TRUE(mPrivateKey, NS_ERROR_DOM_OPERATION_ERR);
|
||||
|
||||
return NS_OK;
|
||||
|
@ -485,24 +486,24 @@ CryptoKey::GetSymKey() const
|
|||
return mSymKey;
|
||||
}
|
||||
|
||||
SECKEYPrivateKey*
|
||||
UniqueSECKEYPrivateKey
|
||||
CryptoKey::GetPrivateKey() const
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (!mPrivateKey || isAlreadyShutDown()) {
|
||||
return nullptr;
|
||||
}
|
||||
return SECKEY_CopyPrivateKey(mPrivateKey.get());
|
||||
return UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(mPrivateKey.get()));
|
||||
}
|
||||
|
||||
SECKEYPublicKey*
|
||||
UniqueSECKEYPublicKey
|
||||
CryptoKey::GetPublicKey() const
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (!mPublicKey || isAlreadyShutDown()) {
|
||||
return nullptr;
|
||||
}
|
||||
return SECKEY_CopyPublicKey(mPublicKey.get());
|
||||
return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(mPublicKey.get()));
|
||||
}
|
||||
|
||||
void CryptoKey::virtualDestroyNSSReference()
|
||||
|
@ -519,11 +520,10 @@ void CryptoKey::destructorSafeDestroyNSSReference()
|
|||
|
||||
// Serialization and deserialization convenience methods
|
||||
|
||||
SECKEYPrivateKey*
|
||||
UniqueSECKEYPrivateKey
|
||||
CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
{
|
||||
SECKEYPrivateKey* privKey;
|
||||
UniquePK11SlotInfo slot(PK11_GetInternalSlot());
|
||||
if (!slot) {
|
||||
return nullptr;
|
||||
|
@ -542,6 +542,7 @@ CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData,
|
|||
// Allow everything, we enforce usage ourselves
|
||||
unsigned int usage = KU_ALL;
|
||||
|
||||
SECKEYPrivateKey* privKey;
|
||||
SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
|
||||
slot.get(), &pkcs8Item, nullptr, nullptr, false, false,
|
||||
usage, &privKey, nullptr);
|
||||
|
@ -549,10 +550,11 @@ CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData,
|
|||
if (rv == SECFailure) {
|
||||
return nullptr;
|
||||
}
|
||||
return privKey;
|
||||
|
||||
return UniqueSECKEYPrivateKey(privKey);
|
||||
}
|
||||
|
||||
SECKEYPublicKey*
|
||||
UniqueSECKEYPublicKey
|
||||
CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
{
|
||||
|
@ -607,7 +609,7 @@ CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return SECKEY_CopyPublicKey(tmp.get());
|
||||
return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(tmp.get()));
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -748,7 +750,7 @@ CreateECPointForCoordinates(const CryptoBuffer& aX,
|
|||
return point;
|
||||
}
|
||||
|
||||
SECKEYPrivateKey*
|
||||
UniqueSECKEYPrivateKey
|
||||
CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
{
|
||||
|
@ -1000,7 +1002,7 @@ CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey,
|
|||
}
|
||||
}
|
||||
|
||||
SECKEYPublicKey*
|
||||
UniqueSECKEYPublicKey
|
||||
CreateECPublicKey(const SECItem* aKeyData, const nsString& aNamedCurve)
|
||||
{
|
||||
UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
|
||||
|
@ -1037,10 +1039,10 @@ CreateECPublicKey(const SECItem* aKeyData, const nsString& aNamedCurve)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return SECKEY_CopyPublicKey(key.get());
|
||||
return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(key.get()));
|
||||
}
|
||||
|
||||
SECKEYPublicKey*
|
||||
UniqueSECKEYPublicKey
|
||||
CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
{
|
||||
|
@ -1074,7 +1076,7 @@ CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA);
|
||||
return UniqueSECKEYPublicKey(SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA));
|
||||
}
|
||||
|
||||
if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) {
|
||||
|
@ -1140,7 +1142,7 @@ CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
|
|||
}
|
||||
}
|
||||
|
||||
SECKEYPublicKey*
|
||||
UniqueSECKEYPublicKey
|
||||
CryptoKey::PublicDhKeyFromRaw(CryptoBuffer& aKeyData,
|
||||
const CryptoBuffer& aPrime,
|
||||
const CryptoBuffer& aGenerator,
|
||||
|
@ -1171,7 +1173,7 @@ CryptoKey::PublicDhKeyFromRaw(CryptoBuffer& aKeyData,
|
|||
key->u.dh.base.type = siUnsignedInteger;
|
||||
key->u.dh.publicValue.type = siUnsignedInteger;
|
||||
|
||||
return SECKEY_CopyPublicKey(key);
|
||||
return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(key));
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -1185,7 +1187,7 @@ CryptoKey::PublicDhKeyToRaw(SECKEYPublicKey* aPubKey,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
SECKEYPublicKey*
|
||||
UniqueSECKEYPublicKey
|
||||
CryptoKey::PublicECKeyFromRaw(CryptoBuffer& aKeyData,
|
||||
const nsString& aNamedCurve,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
|
||||
|
@ -1322,12 +1324,10 @@ CryptoKey::ReadStructuredClone(JSStructuredCloneReader* aReader)
|
|||
return false;
|
||||
}
|
||||
if (priv.Length() > 0) {
|
||||
mPrivateKey = UniqueSECKEYPrivateKey(
|
||||
CryptoKey::PrivateKeyFromPkcs8(priv, locker));
|
||||
mPrivateKey = CryptoKey::PrivateKeyFromPkcs8(priv, locker);
|
||||
}
|
||||
if (pub.Length() > 0) {
|
||||
mPublicKey = UniqueSECKEYPublicKey(
|
||||
CryptoKey::PublicKeyFromSpki(pub, locker));
|
||||
mPublicKey = CryptoKey::PublicKeyFromSpki(pub, locker);
|
||||
}
|
||||
|
||||
// Ensure that what we've read is consistent
|
||||
|
|
|
@ -132,12 +132,9 @@ public:
|
|||
nsresult SetPublicKey(SECKEYPublicKey* aPublicKey);
|
||||
|
||||
// Accessors for the keys themselves
|
||||
// Note: GetPrivateKey and GetPublicKey return copies of the internal
|
||||
// key handles, which the caller must free with SECKEY_DestroyPrivateKey
|
||||
// or SECKEY_DestroyPublicKey.
|
||||
const CryptoBuffer& GetSymKey() const;
|
||||
SECKEYPrivateKey* GetPrivateKey() const;
|
||||
SECKEYPublicKey* GetPublicKey() const;
|
||||
UniqueSECKEYPrivateKey GetPrivateKey() const;
|
||||
UniqueSECKEYPublicKey GetPublicKey() const;
|
||||
|
||||
// For nsNSSShutDownObject
|
||||
virtual void virtualDestroyNSSReference() override;
|
||||
|
@ -148,41 +145,47 @@ public:
|
|||
// 1. The inputs aKeyData are non-const only because the NSS import
|
||||
// functions lack the const modifier. They should not be modified.
|
||||
// 2. All of the NSS key objects returned need to be freed by the caller.
|
||||
static SECKEYPrivateKey* PrivateKeyFromPkcs8(CryptoBuffer& aKeyData,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static UniqueSECKEYPrivateKey PrivateKeyFromPkcs8(
|
||||
CryptoBuffer& aKeyData,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static nsresult PrivateKeyToPkcs8(SECKEYPrivateKey* aPrivKey,
|
||||
CryptoBuffer& aRetVal,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
|
||||
static SECKEYPublicKey* PublicKeyFromSpki(CryptoBuffer& aKeyData,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static UniqueSECKEYPublicKey PublicKeyFromSpki(
|
||||
CryptoBuffer& aKeyData,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static nsresult PublicKeyToSpki(SECKEYPublicKey* aPubKey,
|
||||
CryptoBuffer& aRetVal,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
|
||||
static SECKEYPrivateKey* PrivateKeyFromJwk(const JsonWebKey& aJwk,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static UniqueSECKEYPrivateKey PrivateKeyFromJwk(
|
||||
const JsonWebKey& aJwk,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static nsresult PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey,
|
||||
JsonWebKey& aRetVal,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
|
||||
static SECKEYPublicKey* PublicKeyFromJwk(const JsonWebKey& aKeyData,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static UniqueSECKEYPublicKey PublicKeyFromJwk(
|
||||
const JsonWebKey& aKeyData,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static nsresult PublicKeyToJwk(SECKEYPublicKey* aPubKey,
|
||||
JsonWebKey& aRetVal,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
|
||||
static SECKEYPublicKey* PublicDhKeyFromRaw(CryptoBuffer& aKeyData,
|
||||
const CryptoBuffer& aPrime,
|
||||
const CryptoBuffer& aGenerator,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static UniqueSECKEYPublicKey PublicDhKeyFromRaw(
|
||||
CryptoBuffer& aKeyData,
|
||||
const CryptoBuffer& aPrime,
|
||||
const CryptoBuffer& aGenerator,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static nsresult PublicDhKeyToRaw(SECKEYPublicKey* aPubKey,
|
||||
CryptoBuffer& aRetVal,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
|
||||
static SECKEYPublicKey* PublicECKeyFromRaw(CryptoBuffer& aKeyData,
|
||||
const nsString& aNamedCurve,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static UniqueSECKEYPublicKey PublicECKeyFromRaw(
|
||||
CryptoBuffer& aKeyData,
|
||||
const nsString& aNamedCurve,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
static nsresult PublicECKeyToRaw(SECKEYPublicKey* aPubKey,
|
||||
CryptoBuffer& aRetVal,
|
||||
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
|
||||
|
|
|
@ -1780,11 +1780,9 @@ private:
|
|||
!mJwk.mD.WasPassed())) {
|
||||
// Public key import
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
|
||||
pubKey = UniqueSECKEYPublicKey(
|
||||
CryptoKey::PublicKeyFromSpki(mKeyData, locker));
|
||||
pubKey = CryptoKey::PublicKeyFromSpki(mKeyData, locker);
|
||||
} else {
|
||||
pubKey = UniqueSECKEYPublicKey(
|
||||
CryptoKey::PublicKeyFromJwk(mJwk, locker));
|
||||
pubKey = CryptoKey::PublicKeyFromJwk(mJwk, locker);
|
||||
}
|
||||
|
||||
if (!pubKey) {
|
||||
|
@ -1801,11 +1799,9 @@ private:
|
|||
mJwk.mD.WasPassed())) {
|
||||
// Private key import
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
|
||||
privKey = UniqueSECKEYPrivateKey(
|
||||
CryptoKey::PrivateKeyFromPkcs8(mKeyData, locker));
|
||||
privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData, locker);
|
||||
} else {
|
||||
privKey = UniqueSECKEYPrivateKey(
|
||||
CryptoKey::PrivateKeyFromJwk(mJwk, locker));
|
||||
privKey = CryptoKey::PrivateKeyFromJwk(mJwk, locker);
|
||||
}
|
||||
|
||||
if (!privKey) {
|
||||
|
@ -1929,8 +1925,7 @@ private:
|
|||
nsNSSShutDownPreventionLock locker;
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) && mJwk.mD.WasPassed()) {
|
||||
// Private key import
|
||||
privKey = UniqueSECKEYPrivateKey(
|
||||
CryptoKey::PrivateKeyFromJwk(mJwk, locker));
|
||||
privKey = CryptoKey::PrivateKeyFromJwk(mJwk, locker);
|
||||
if (!privKey) {
|
||||
return NS_ERROR_DOM_DATA_ERR;
|
||||
}
|
||||
|
@ -1946,14 +1941,11 @@ private:
|
|||
!mJwk.mD.WasPassed())) {
|
||||
// Public key import
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
|
||||
pubKey = UniqueSECKEYPublicKey(
|
||||
CryptoKey::PublicECKeyFromRaw(mKeyData, mNamedCurve, locker));
|
||||
pubKey = CryptoKey::PublicECKeyFromRaw(mKeyData, mNamedCurve, locker);
|
||||
} else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
|
||||
pubKey = UniqueSECKEYPublicKey(
|
||||
CryptoKey::PublicKeyFromSpki(mKeyData, locker));
|
||||
pubKey = CryptoKey::PublicKeyFromSpki(mKeyData, locker);
|
||||
} else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
|
||||
pubKey = UniqueSECKEYPublicKey(
|
||||
CryptoKey::PublicKeyFromJwk(mJwk, locker));
|
||||
pubKey = CryptoKey::PublicKeyFromJwk(mJwk, locker);
|
||||
} else {
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
@ -2087,11 +2079,10 @@ private:
|
|||
mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
|
||||
// Public key import
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
|
||||
pubKey = UniqueSECKEYPublicKey(
|
||||
CryptoKey::PublicDhKeyFromRaw(mKeyData, mPrime, mGenerator, locker));
|
||||
pubKey = CryptoKey::PublicDhKeyFromRaw(mKeyData, mPrime, mGenerator,
|
||||
locker);
|
||||
} else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
|
||||
pubKey = UniqueSECKEYPublicKey(
|
||||
CryptoKey::PublicKeyFromSpki(mKeyData, locker));
|
||||
pubKey = CryptoKey::PublicKeyFromSpki(mKeyData, locker);
|
||||
} else {
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
@ -3025,7 +3016,7 @@ public:
|
|||
}
|
||||
|
||||
CryptoKey* publicKey = params.mPublic;
|
||||
mPubKey = UniqueSECKEYPublicKey(publicKey->GetPublicKey());
|
||||
mPubKey = publicKey->GetPublicKey();
|
||||
if (!mPubKey) {
|
||||
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
return;
|
||||
|
@ -3125,7 +3116,7 @@ public:
|
|||
}
|
||||
|
||||
CryptoKey* publicKey = params.mPublic;
|
||||
mPubKey = UniqueSECKEYPublicKey(publicKey->GetPublicKey());
|
||||
mPubKey = publicKey->GetPublicKey();
|
||||
if (!mPubKey) {
|
||||
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
return;
|
||||
|
|
|
@ -223,6 +223,7 @@ using namespace mozilla::system;
|
|||
using namespace mozilla::widget;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
||||
// IPC sender for remote GC/CC logging.
|
||||
|
@ -1353,7 +1354,20 @@ ContentChild::RecvSetProcessSandbox(const MaybeFileDesc& aBroker)
|
|||
// didn't intend it.
|
||||
MOZ_RELEASE_ASSERT(brokerFd >= 0);
|
||||
}
|
||||
sandboxEnabled = SetContentProcessSandbox(brokerFd);
|
||||
// Allow user overrides of seccomp-bpf syscall filtering
|
||||
std::vector<int> syscallWhitelist;
|
||||
nsAdoptingCString extraSyscalls =
|
||||
Preferences::GetCString("security.sandbox.content.syscall_whitelist");
|
||||
if (extraSyscalls) {
|
||||
for (const nsCSubstring& callNrString : extraSyscalls.Split(',')) {
|
||||
nsresult rv;
|
||||
int callNr = PromiseFlatCString(callNrString).ToInteger(&rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
syscallWhitelist.push_back(callNr);
|
||||
}
|
||||
}
|
||||
}
|
||||
sandboxEnabled = SetContentProcessSandbox(brokerFd, syscallWhitelist);
|
||||
}
|
||||
#elif defined(XP_WIN)
|
||||
mozilla::SandboxTarget::Instance()->StartSandbox();
|
||||
|
|
|
@ -220,11 +220,11 @@ private:
|
|||
{
|
||||
// Make copies of the private key and certificate, otherwise, when this
|
||||
// object is deleted, the structures they reference will be deleted too.
|
||||
SECKEYPrivateKey* key = mKeyPair->mPrivateKey.get()->GetPrivateKey();
|
||||
UniqueSECKEYPrivateKey key = mKeyPair->mPrivateKey.get()->GetPrivateKey();
|
||||
CERTCertificate* cert = CERT_DupCertificate(mCertificate.get());
|
||||
RefPtr<RTCCertificate> result =
|
||||
new RTCCertificate(mResultPromise->GetParentObject(),
|
||||
key, cert, mAuthType, mExpires);
|
||||
key.release(), cert, mAuthType, mExpires);
|
||||
mResultPromise->MaybeResolve(result);
|
||||
}
|
||||
};
|
||||
|
@ -416,7 +416,7 @@ RTCCertificate::ReadPrivateKey(JSStructuredCloneReader* aReader,
|
|||
if (!jwk.Init(json)) {
|
||||
return false;
|
||||
}
|
||||
mPrivateKey.reset(CryptoKey::PrivateKeyFromJwk(jwk, aLockProof));
|
||||
mPrivateKey = CryptoKey::PrivateKeyFromJwk(jwk, aLockProof);
|
||||
return !!mPrivateKey;
|
||||
}
|
||||
|
||||
|
|
|
@ -667,6 +667,13 @@ EditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
|
|||
return rv;
|
||||
}
|
||||
|
||||
// IMEStateManager::OnClickInEditor() may cause anything because it may
|
||||
// set input context. For example, it may cause opening VKB, changing focus
|
||||
// or reflow. So, mEditorBase here might have been gone.
|
||||
if (!mEditorBase) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we got a mouse down inside the editing area, we should force the
|
||||
// IME to commit before we change the cursor position
|
||||
mEditorBase->ForceCompositionEnd();
|
||||
|
@ -765,7 +772,9 @@ EditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
|
|||
{
|
||||
// FYI: This may be called by HTMLEditorEventListener::MouseDown() even
|
||||
// when the event is not acceptable for committing composition.
|
||||
mEditorBase->ForceCompositionEnd();
|
||||
if (mEditorBase) {
|
||||
mEditorBase->ForceCompositionEnd();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from collections import OrderedDict
|
|||
import ipdl.ast
|
||||
import ipdl.builtin
|
||||
from ipdl.cxx.ast import *
|
||||
from ipdl.type import Actor, ActorType, ProcessGraph, TypeVisitor, builtinHeaderIncludes
|
||||
from ipdl.type import ActorType, ProcessGraph, TypeVisitor, builtinHeaderIncludes
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
## "Public" interface to lowering
|
||||
|
@ -419,10 +419,6 @@ def _killProcess(pid):
|
|||
ExprVar('base::PROCESS_END_KILLED_BY_USER'),
|
||||
ExprLiteral.FALSE ])
|
||||
|
||||
def _badTransition():
|
||||
# FIXME: make this a FatalError()
|
||||
return [ _printWarningMessage('bad state transition!') ]
|
||||
|
||||
# Results that IPDL-generated code returns back to *Channel code.
|
||||
# Users never see these
|
||||
class _Result:
|
||||
|
@ -578,10 +574,10 @@ def _cxxConstPtrToType(ipdltype, side):
|
|||
return t
|
||||
|
||||
def _allocMethod(ptype, side):
|
||||
return ExprVar('Alloc'+ str(Actor(ptype, side)))
|
||||
return ExprVar('Alloc' + ptype.name() + side.title())
|
||||
|
||||
def _deallocMethod(ptype, side):
|
||||
return ExprVar('Dealloc'+ str(Actor(ptype, side)))
|
||||
return ExprVar('Dealloc' + ptype.name() + side.title())
|
||||
|
||||
##
|
||||
## A _HybridDecl straddles IPDL and C++ decls. It knows which C++
|
||||
|
|
|
@ -439,11 +439,6 @@ def iteractortypes(t, visited=None):
|
|||
for actor in iteractortypes(c, visited):
|
||||
yield actor
|
||||
|
||||
def hasactor(type):
|
||||
"""Return true iff |type| is an actor or has one buried within."""
|
||||
for _ in iteractortypes(type): return True
|
||||
return False
|
||||
|
||||
def hasshmem(type):
|
||||
"""Return true iff |type| is shmem or has it buried within."""
|
||||
class found: pass
|
||||
|
@ -455,17 +450,6 @@ def hasshmem(type):
|
|||
return True
|
||||
return False
|
||||
|
||||
def hasfd(type):
|
||||
"""Return true iff |type| is fd or has it buried within."""
|
||||
class found: pass
|
||||
class findFD(TypeVisitor):
|
||||
def visitFDType(self, s): raise found()
|
||||
try:
|
||||
type.accept(findFD())
|
||||
except found:
|
||||
return True
|
||||
return False
|
||||
|
||||
##--------------------
|
||||
_builtinloc = Loc('<builtin>', 0)
|
||||
def makeBuiltinUsing(tname):
|
||||
|
|
|
@ -56,8 +56,6 @@ import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
|
|||
import org.mozilla.gecko.home.HomePanelsManager;
|
||||
import org.mozilla.gecko.home.HomeScreen;
|
||||
import org.mozilla.gecko.home.SearchEngine;
|
||||
import org.mozilla.gecko.icons.IconCallback;
|
||||
import org.mozilla.gecko.icons.IconResponse;
|
||||
import org.mozilla.gecko.icons.Icons;
|
||||
import org.mozilla.gecko.javaaddons.JavaAddonManager;
|
||||
import org.mozilla.gecko.media.VideoPlayer;
|
||||
|
@ -77,7 +75,6 @@ import org.mozilla.gecko.reader.SavedReaderViewHelper;
|
|||
import org.mozilla.gecko.reader.ReaderModeUtils;
|
||||
import org.mozilla.gecko.reader.ReadingListHelper;
|
||||
import org.mozilla.gecko.restrictions.Restrictable;
|
||||
import org.mozilla.gecko.restrictions.RestrictedProfileConfiguration;
|
||||
import org.mozilla.gecko.restrictions.Restrictions;
|
||||
import org.mozilla.gecko.search.SearchEngineManager;
|
||||
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
|
||||
|
@ -101,7 +98,6 @@ import org.mozilla.gecko.updater.UpdateServiceHelper;
|
|||
import org.mozilla.gecko.util.ActivityUtils;
|
||||
import org.mozilla.gecko.util.Clipboard;
|
||||
import org.mozilla.gecko.util.ContextUtils;
|
||||
import org.mozilla.gecko.util.BundleEventListener;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
import org.mozilla.gecko.util.FloatUtils;
|
||||
import org.mozilla.gecko.util.GamepadUtils;
|
||||
|
@ -146,8 +142,6 @@ import android.support.v4.app.NotificationCompat;
|
|||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Base64;
|
||||
import android.util.Base64OutputStream;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
|
@ -168,14 +162,13 @@ import android.widget.Button;
|
|||
import android.widget.ListView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.ViewFlipper;
|
||||
import com.keepsafe.switchboard.AsyncConfigLoader;
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.switchboard.AsyncConfigLoader;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
import android.animation.Animator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
@ -184,7 +177,6 @@ import java.net.URLEncoder;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
@ -656,9 +648,20 @@ public class BrowserApp extends GeckoApp
|
|||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (BrowserApp.this.isFinishing()) {
|
||||
// TabHistoryController is rather slow - and involves calling into Gecko
|
||||
// to retrieve tab history. That means there can be a significant
|
||||
// delay between the back-button long-press, and onShowHistory()
|
||||
// being called. Hence we need to guard against the Activity being
|
||||
// shut down (in which case trying to perform UI changes, such as showing
|
||||
// fragments below, will crash).
|
||||
return;
|
||||
}
|
||||
|
||||
final TabHistoryFragment fragment = TabHistoryFragment.newInstance(historyPageList, toIndex);
|
||||
final FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
GeckoAppShell.vibrateOnHapticFeedbackEnabled(getResources().getIntArray(R.array.long_press_vibrate_msec));
|
||||
if (BrowserApp.this.isForegrounded())
|
||||
fragment.show(R.id.tab_history_panel, fragmentManager.beginTransaction(), TAB_HISTORY_FRAGMENT_TAG);
|
||||
}
|
||||
});
|
||||
|
@ -1069,6 +1072,7 @@ public class BrowserApp extends GeckoApp
|
|||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (mIsAbortingAppLaunch) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ import android.content.Context;
|
|||
import android.util.Log;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.keepsafe.switchboard.Preferences;
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.switchboard.Preferences;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
|
|
@ -8,14 +8,10 @@ package org.mozilla.gecko.activitystream;
|
|||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.Experiments;
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
|
@ -24,7 +20,6 @@ import org.mozilla.gecko.util.StringUtils;
|
|||
import org.mozilla.gecko.util.publicsuffix.PublicSuffix;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ActivityStream {
|
||||
|
|
|
@ -9,7 +9,7 @@ import android.content.Context;
|
|||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
|
|
@ -14,10 +14,9 @@ import android.support.annotation.Nullable;
|
|||
import android.support.v4.net.ConnectivityManagerCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.feeds.action.FeedAction;
|
||||
|
|
|
@ -11,7 +11,7 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.feeds.FeedAlarmReceiver;
|
||||
|
|
|
@ -15,7 +15,7 @@ import android.net.Uri;
|
|||
import android.support.v4.app.NotificationCompat;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
import org.mozilla.gecko.Locales;
|
||||
|
|
|
@ -9,7 +9,6 @@ import org.json.JSONArray;
|
|||
import org.mozilla.gecko.AboutPages;
|
||||
import org.mozilla.gecko.AdjustConstants;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.BrowserLocaleManager;
|
||||
import org.mozilla.gecko.DataReportingNotification;
|
||||
|
@ -17,7 +16,6 @@ import org.mozilla.gecko.DynamicToolbar;
|
|||
import org.mozilla.gecko.EventDispatcher;
|
||||
import org.mozilla.gecko.Experiments;
|
||||
import org.mozilla.gecko.GeckoActivityStatus;
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoApplication;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
|
@ -31,7 +29,6 @@ import org.mozilla.gecko.Telemetry;
|
|||
import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.gecko.TelemetryContract.Method;
|
||||
import org.mozilla.gecko.activitystream.ActivityStream;
|
||||
import org.mozilla.gecko.background.common.GlobalConstants;
|
||||
import org.mozilla.gecko.db.BrowserContract.SuggestedSites;
|
||||
import org.mozilla.gecko.feeds.FeedService;
|
||||
import org.mozilla.gecko.feeds.action.CheckForUpdatesAction;
|
||||
|
@ -48,7 +45,6 @@ import org.mozilla.gecko.util.EventCallback;
|
|||
import org.mozilla.gecko.util.GeckoBundle;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
import org.mozilla.gecko.util.InputOptionsUtils;
|
||||
import org.mozilla.gecko.util.NativeJSObject;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.util.ViewUtil;
|
||||
|
||||
|
@ -81,16 +77,12 @@ import android.preference.TwoStatePreference;
|
|||
import android.support.design.widget.Snackbar;
|
||||
import android.support.design.widget.TextInputLayout;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v4.content.res.ResourcesCompat;
|
||||
import android.support.v4.text.TextUtilsCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
|
@ -99,7 +91,7 @@ import android.widget.LinearLayout;
|
|||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
|
|
@ -12,7 +12,7 @@ import android.os.Bundle;
|
|||
import android.support.annotation.CallSuper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
|
|
@ -8,7 +8,7 @@ package org.mozilla.gecko.promotion;
|
|||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
|
||||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package com.keepsafe.switchboard;
|
||||
package org.mozilla.gecko.switchboard;
|
||||
|
||||
|
||||
import android.content.Context;
|
|
@ -13,7 +13,7 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package com.keepsafe.switchboard;
|
||||
package org.mozilla.gecko.switchboard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package com.keepsafe.switchboard;
|
||||
package org.mozilla.gecko.switchboard;
|
||||
|
||||
|
||||
import android.content.Context;
|
|
@ -13,7 +13,7 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package com.keepsafe.switchboard;
|
||||
package org.mozilla.gecko.switchboard;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
@ -64,7 +64,7 @@ public class Switch {
|
|||
* @return Values in JSONObject or null if non
|
||||
*/
|
||||
public JSONObject getValues() {
|
||||
if(hasValues())
|
||||
if (hasValues())
|
||||
return SwitchBoard.getExperimentValuesFromJson(context, experimentName);
|
||||
else
|
||||
return null;
|
|
@ -13,7 +13,7 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package com.keepsafe.switchboard;
|
||||
package org.mozilla.gecko.switchboard;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
|
@ -23,7 +23,6 @@ import java.net.HttpURLConnection;
|
|||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
|
@ -223,7 +222,7 @@ public class SwitchBoard {
|
|||
if (!country.matches(matchKeys.getString(KEY_COUNTRY))) {
|
||||
return false;
|
||||
}
|
||||
} catch (MissingResourceException|JSONException e) {
|
||||
} catch (MissingResourceException | JSONException e) {
|
||||
Log.e(TAG, "Exception matching country", e);
|
||||
}
|
||||
}
|
||||
|
@ -245,7 +244,7 @@ public class SwitchBoard {
|
|||
if (!lang.matches(matchKeys.getString(KEY_LANG))) {
|
||||
return false;
|
||||
}
|
||||
} catch (MissingResourceException|JSONException e) {
|
||||
} catch (MissingResourceException | JSONException e) {
|
||||
Log.e(TAG, "Exception matching lang", e);
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +265,7 @@ public class SwitchBoard {
|
|||
if (!version.matches(matchKeys.getString(KEY_VERSION))) {
|
||||
return false;
|
||||
}
|
||||
} catch (NameNotFoundException|JSONException e) {
|
||||
} catch (NameNotFoundException | JSONException e) {
|
||||
Log.e(TAG, "Exception matching version", e);
|
||||
}
|
||||
}
|
|
@ -124,7 +124,10 @@ public class TabHistoryFragment extends Fragment implements OnItemClickListener,
|
|||
dismissed = false;
|
||||
transaction.add(containerViewId, this, tag);
|
||||
transaction.addToBackStack(tag);
|
||||
backStackId = transaction.commit();
|
||||
// Populating the tab history requires a gecko call (which can be slow) - therefore the app
|
||||
// state by the time we try to show this fragment is unknown, and we could be in the
|
||||
// middle of shutting down:
|
||||
backStackId = transaction.commitAllowingStateLoss();
|
||||
}
|
||||
|
||||
// Pop the fragment from backstack if it exists.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
package org.mozilla.gecko.tabs;
|
||||
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
|
||||
import org.mozilla.gecko.Experiments;
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoApplication;
|
||||
|
@ -44,7 +44,7 @@ import android.widget.ImageButton;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
|
||||
import org.mozilla.gecko.widget.themed.ThemedImageButton;
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ import android.view.View;
|
|||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
|
||||
/**
|
||||
* {@code ToolbarDisplayLayout} is the UI for when the toolbar is in
|
||||
|
|
|
@ -5,13 +5,10 @@
|
|||
|
||||
package org.mozilla.gecko.updater;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.delegates.BrowserAppDelegateWithReference;
|
||||
|
@ -21,14 +18,10 @@ import org.mozilla.gecko.util.IOUtils;
|
|||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Perform tasks in the background after the app has been installed/updated.
|
||||
|
|
|
@ -702,6 +702,11 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
|
|||
'SiteIdentity.java',
|
||||
'SnackbarBuilder.java',
|
||||
'SuggestClient.java',
|
||||
'switchboard/AsyncConfigLoader.java',
|
||||
'switchboard/DeviceUuidFactory.java',
|
||||
'switchboard/Preferences.java',
|
||||
'switchboard/Switch.java',
|
||||
'switchboard/SwitchBoard.java',
|
||||
'Tab.java',
|
||||
'tabqueue/TabQueueHelper.java',
|
||||
'tabqueue/TabQueuePrompt.java',
|
||||
|
@ -988,11 +993,6 @@ gtjar.sources += [ thirdparty_source_dir + f for f in [
|
|||
'com/jakewharton/disklrucache/DiskLruCache.java',
|
||||
'com/jakewharton/disklrucache/StrictLineReader.java',
|
||||
'com/jakewharton/disklrucache/Util.java',
|
||||
'com/keepsafe/switchboard/AsyncConfigLoader.java',
|
||||
'com/keepsafe/switchboard/DeviceUuidFactory.java',
|
||||
'com/keepsafe/switchboard/Preferences.java',
|
||||
'com/keepsafe/switchboard/Switch.java',
|
||||
'com/keepsafe/switchboard/SwitchBoard.java',
|
||||
'com/squareup/leakcanary/LeakCanary.java',
|
||||
'com/squareup/leakcanary/RefWatcher.java',
|
||||
'com/squareup/picasso/Action.java',
|
||||
|
|
|
@ -1388,7 +1388,7 @@ var BrowserApp = {
|
|||
quit: function quit(aClear = { sanitize: {}, dontSaveSession: false }) {
|
||||
// Notify all windows that an application quit has been requested.
|
||||
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
|
||||
|
||||
// Quit aborted.
|
||||
if (cancelQuit.data) {
|
||||
|
@ -5536,6 +5536,7 @@ var XPInstallObserver = {
|
|||
|
||||
// If nothing aborted, quit the app
|
||||
if (cancelQuit.data == false) {
|
||||
Services.obs.notifyObservers(null, "quit-application-proceeding", null);
|
||||
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
|
||||
appStartup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
|
||||
}
|
||||
|
|
|
@ -1002,10 +1002,12 @@ SessionStore.prototype = {
|
|||
// If we have private data, send it to Java; otherwise, send null to
|
||||
// indicate that there is no private data
|
||||
let window = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
window.WindowEventDispatcher.sendRequest({
|
||||
type: "PrivateBrowsing:Data",
|
||||
session: (privateData.windows.length > 0 && privateData.windows[0].tabs.length > 0) ? JSON.stringify(privateData) : null
|
||||
});
|
||||
if (window) { // can be null if we're restarting
|
||||
window.WindowEventDispatcher.sendRequest({
|
||||
type: "PrivateBrowsing:Data",
|
||||
session: (privateData.windows.length > 0 && privateData.windows[0].tabs.length > 0) ? JSON.stringify(privateData) : null
|
||||
});
|
||||
}
|
||||
|
||||
this._lastSaveTime = Date.now();
|
||||
},
|
||||
|
|
|
@ -9,6 +9,9 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.mozilla.gecko.background.testhelpers.TestRunner;
|
||||
import org.mozilla.gecko.Experiments;
|
||||
import org.mozilla.gecko.switchboard.DeviceUuidFactory;
|
||||
import org.mozilla.gecko.switchboard.Preferences;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.util.IOUtils;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <vector>
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/SandboxInfo.h"
|
||||
|
@ -634,7 +635,7 @@ SandboxEarlyInit(GeckoProcessType aType)
|
|||
* Will normally make the process exit on failure.
|
||||
*/
|
||||
bool
|
||||
SetContentProcessSandbox(int aBrokerFd)
|
||||
SetContentProcessSandbox(int aBrokerFd, std::vector<int>& aSyscallWhitelist)
|
||||
{
|
||||
if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForContent)) {
|
||||
if (aBrokerFd >= 0) {
|
||||
|
@ -649,7 +650,8 @@ SetContentProcessSandbox(int aBrokerFd)
|
|||
sBroker.emplace(aBrokerFd);
|
||||
}
|
||||
|
||||
SetCurrentProcessSandbox(GetContentSandboxPolicy(sBroker.ptrOr(nullptr)));
|
||||
SetCurrentProcessSandbox(GetContentSandboxPolicy(sBroker.ptrOr(nullptr),
|
||||
aSyscallWhitelist));
|
||||
return true;
|
||||
}
|
||||
#endif // MOZ_CONTENT_SANDBOX
|
||||
|
|
|
@ -24,7 +24,8 @@ MOZ_EXPORT void SandboxEarlyInit(GeckoProcessType aType);
|
|||
// (No-op if MOZ_DISABLE_CONTENT_SANDBOX is set.)
|
||||
// aBrokerFd is the filesystem broker client file descriptor,
|
||||
// or -1 to allow direct filesystem access.
|
||||
MOZ_EXPORT bool SetContentProcessSandbox(int aBrokerFd);
|
||||
MOZ_EXPORT bool SetContentProcessSandbox(int aBrokerFd,
|
||||
std::vector<int>& aSyscallWhitelist);
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_GMP_SANDBOX
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "SandboxInfo.h"
|
||||
#include "SandboxInternal.h"
|
||||
#include "SandboxLogging.h"
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -26,6 +25,8 @@
|
|||
#include <sys/syscall.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
|
||||
#include "sandbox/linux/system_headers/linux_seccomp.h"
|
||||
|
@ -347,7 +348,9 @@ public:
|
|||
// this is the Android process permission model; on desktop,
|
||||
// namespaces and chroot() will be used.
|
||||
class ContentSandboxPolicy : public SandboxPolicyCommon {
|
||||
private:
|
||||
SandboxBrokerClient* mBroker;
|
||||
std::vector<int> mSyscallWhitelist;
|
||||
|
||||
// Trap handlers for filesystem brokering.
|
||||
// (The amount of code duplication here could be improved....)
|
||||
|
@ -497,7 +500,10 @@ class ContentSandboxPolicy : public SandboxPolicyCommon {
|
|||
}
|
||||
|
||||
public:
|
||||
explicit ContentSandboxPolicy(SandboxBrokerClient* aBroker):mBroker(aBroker) { }
|
||||
explicit ContentSandboxPolicy(SandboxBrokerClient* aBroker,
|
||||
const std::vector<int>& aSyscallWhitelist)
|
||||
: mBroker(aBroker),
|
||||
mSyscallWhitelist(aSyscallWhitelist) {}
|
||||
virtual ~ContentSandboxPolicy() { }
|
||||
virtual ResultExpr PrctlPolicy() const override {
|
||||
// Ideally this should be restricted to a whitelist, but content
|
||||
|
@ -570,6 +576,14 @@ public:
|
|||
#endif
|
||||
|
||||
virtual ResultExpr EvaluateSyscall(int sysno) const override {
|
||||
// Straight allow for anything that got overriden via prefs
|
||||
if (std::find(mSyscallWhitelist.begin(), mSyscallWhitelist.end(), sysno)
|
||||
!= mSyscallWhitelist.end()) {
|
||||
if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
|
||||
SANDBOX_LOG_ERROR("Allowing syscall nr %d via whitelist", sysno);
|
||||
}
|
||||
return Allow();
|
||||
}
|
||||
if (mBroker) {
|
||||
// Have broker; route the appropriate syscalls to it.
|
||||
switch (sysno) {
|
||||
|
@ -834,9 +848,10 @@ public:
|
|||
};
|
||||
|
||||
UniquePtr<sandbox::bpf_dsl::Policy>
|
||||
GetContentSandboxPolicy(SandboxBrokerClient* aMaybeBroker)
|
||||
GetContentSandboxPolicy(SandboxBrokerClient* aMaybeBroker,
|
||||
const std::vector<int>& aSyscallWhitelist)
|
||||
{
|
||||
return UniquePtr<sandbox::bpf_dsl::Policy>(new ContentSandboxPolicy(aMaybeBroker));
|
||||
return MakeUnique<ContentSandboxPolicy>(aMaybeBroker, aSyscallWhitelist);
|
||||
}
|
||||
#endif // MOZ_CONTENT_SANDBOX
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef mozilla_SandboxFilter_h
|
||||
#define mozilla_SandboxFilter_h
|
||||
|
||||
#include <vector>
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
|
@ -21,7 +22,8 @@ namespace mozilla {
|
|||
#ifdef MOZ_CONTENT_SANDBOX
|
||||
class SandboxBrokerClient;
|
||||
|
||||
UniquePtr<sandbox::bpf_dsl::Policy> GetContentSandboxPolicy(SandboxBrokerClient* aMaybeBroker);
|
||||
UniquePtr<sandbox::bpf_dsl::Policy> GetContentSandboxPolicy(SandboxBrokerClient* aMaybeBroker,
|
||||
const std::vector<int>& aSyscallWhitelist);
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_GMP_SANDBOX
|
||||
|
|
|
@ -211,22 +211,36 @@ SandboxBroker::Policy::AddDir(int aPerms, const char* aPath)
|
|||
if (path[path.Length() - 1] != '/') {
|
||||
path.Append('/');
|
||||
}
|
||||
|
||||
Policy::AddPrefixInternal(aPerms, path);
|
||||
}
|
||||
|
||||
void
|
||||
SandboxBroker::Policy::AddPrefix(int aPerms, const char* aPath)
|
||||
{
|
||||
Policy::AddPrefixInternal(aPerms, nsDependentCString(aPath));
|
||||
}
|
||||
|
||||
void
|
||||
SandboxBroker::Policy::AddPrefixInternal(int aPerms, const nsACString& aPath)
|
||||
{
|
||||
int origPerms;
|
||||
if (!mMap.Get(path, &origPerms)) {
|
||||
if (!mMap.Get(aPath, &origPerms)) {
|
||||
origPerms = MAY_ACCESS;
|
||||
} else {
|
||||
MOZ_ASSERT(origPerms & MAY_ACCESS);
|
||||
}
|
||||
int newPerms = origPerms | aPerms | RECURSIVE;
|
||||
if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
|
||||
SANDBOX_LOG_ERROR("policy for %s: %d -> %d", aPath, origPerms, newPerms);
|
||||
SANDBOX_LOG_ERROR("policy for %s: %d -> %d", PromiseFlatCString(aPath).get(),
|
||||
origPerms, newPerms);
|
||||
}
|
||||
mMap.Put(path, newPerms);
|
||||
mMap.Put(aPath, newPerms);
|
||||
}
|
||||
|
||||
void
|
||||
SandboxBroker::Policy::AddPrefix(int aPerms, const char* aDir,
|
||||
const char* aPrefix)
|
||||
SandboxBroker::Policy::AddFilePrefix(int aPerms, const char* aDir,
|
||||
const char* aPrefix)
|
||||
{
|
||||
size_t prefixLen = strlen(aPrefix);
|
||||
DIR* dirp = opendir(aDir);
|
||||
|
@ -246,6 +260,25 @@ SandboxBroker::Policy::AddPrefix(int aPerms, const char* aDir,
|
|||
closedir(dirp);
|
||||
}
|
||||
|
||||
void
|
||||
SandboxBroker::Policy::AddDynamic(int aPerms, const char* aPath)
|
||||
{
|
||||
struct stat statBuf;
|
||||
bool exists = (stat(aPath, &statBuf) == 0);
|
||||
|
||||
if (!exists) {
|
||||
AddPrefix(aPerms, aPath);
|
||||
} else {
|
||||
size_t len = strlen(aPath);
|
||||
if (!len) return;
|
||||
if (aPath[len - 1] == '/') {
|
||||
AddDir(aPerms, aPath);
|
||||
} else {
|
||||
AddPath(aPerms, aPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
SandboxBroker::Policy::Lookup(const nsACString& aPath) const
|
||||
{
|
||||
|
|
|
@ -80,7 +80,12 @@ class SandboxBroker final
|
|||
// added after creation (the dir itself must exist).
|
||||
void AddDir(int aPerms, const char* aPath);
|
||||
// All files in a directory with a given prefix; useful for devices.
|
||||
void AddPrefix(int aPerms, const char* aDir, const char* aPrefix);
|
||||
void AddFilePrefix(int aPerms, const char* aDir, const char* aPrefix);
|
||||
// Everything starting with the given path, even those files/dirs
|
||||
// added after creation. The file or directory may or may not exist.
|
||||
void AddPrefix(int aPerms, const char* aPath);
|
||||
// Adds a file or dir (end with /) if it exists, and a prefix otherwhise.
|
||||
void AddDynamic(int aPerms, const char* aPath);
|
||||
// Default: add file if it exists when creating policy or if we're
|
||||
// conferring permission to create it (log files, etc.).
|
||||
void AddPath(int aPerms, const char* aPath) {
|
||||
|
@ -98,6 +103,7 @@ class SandboxBroker final
|
|||
// * No trailing slash
|
||||
// * No /../ path traversal
|
||||
bool ValidatePath(const char* path) const;
|
||||
void AddPrefixInternal(int aPerms, const nsACString& aPath);
|
||||
};
|
||||
|
||||
// Constructing a broker involves creating a socketpair and a
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "SandboxBrokerPolicyFactory.h"
|
||||
#include "SandboxInfo.h"
|
||||
#include "SandboxLogging.h"
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
@ -67,7 +68,7 @@ SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory()
|
|||
// Graphics devices are a significant source of attack surface, but
|
||||
// there's not much we can do about it without proxying (which is
|
||||
// very difficult and a perforamnce hit).
|
||||
policy->AddPrefix(rdwr, "/dev", "kgsl"); // bug 995072
|
||||
policy->AddFilePrefix(rdwr, "/dev", "kgsl"); // bug 995072
|
||||
policy->AddPath(rdwr, "/dev/qemu_pipe"); // but 1198410: goldfish gralloc.
|
||||
|
||||
// Bug 1198475: mochitest logs. (This is actually passed in via URL
|
||||
|
@ -138,9 +139,9 @@ SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory()
|
|||
}
|
||||
|
||||
// Bug 1308851: NVIDIA proprietary driver when using WebGL
|
||||
policy->AddPrefix(rdwr, "/dev", "nvidia");
|
||||
policy->AddFilePrefix(rdwr, "/dev", "nvidia");
|
||||
|
||||
// Bug 1312678: radeonsi/Intel with DRI when using WebGL
|
||||
// Bug 1312678: radeonsi/Intel with DRI when using WebGL
|
||||
policy->AddDir(rdwr, "/dev/dri");
|
||||
|
||||
#ifdef MOZ_ALSA
|
||||
|
@ -190,6 +191,20 @@ SandboxBrokerPolicyFactory::GetContentPolicy(int aPid)
|
|||
#else
|
||||
UniquePtr<SandboxBroker::Policy>
|
||||
policy(new SandboxBroker::Policy(*mCommonContentPolicy));
|
||||
|
||||
// Now read any extra paths, this requires accessing user preferences
|
||||
// so we can only do it now. Our constructor is initialized before
|
||||
// user preferences are read in.
|
||||
nsAdoptingCString extraPathString =
|
||||
Preferences::GetCString("security.sandbox.content.write_path_whitelist");
|
||||
if (extraPathString) {
|
||||
for (const nsCSubstring& path : extraPathString.Split(',')) {
|
||||
nsCString trimPath(path);
|
||||
trimPath.Trim(" ", true, true);
|
||||
policy->AddDynamic(rdwr, trimPath.get());
|
||||
}
|
||||
}
|
||||
|
||||
// Return the common policy.
|
||||
return policy;
|
||||
#endif
|
||||
|
|
|
@ -327,6 +327,7 @@ linux64-ccov/opt:
|
|||
index:
|
||||
product: firefox
|
||||
job-name: linux64-ccov-opt
|
||||
needs-sccache: false
|
||||
treeherder:
|
||||
platform: linux64/ccov
|
||||
symbol: tc(B)
|
||||
|
|
|
@ -459,6 +459,8 @@ def build_docker_worker_payload(config, task, task_def):
|
|||
level=config.params['level'])
|
||||
)
|
||||
worker['env']['USE_SCCACHE'] = '1'
|
||||
else:
|
||||
worker['env']['SCCACHE_DISABLE'] = '1'
|
||||
|
||||
capabilities = {}
|
||||
|
||||
|
|
|
@ -52,10 +52,26 @@ class UpdateTestCase(PuppeteerMixin, MarionetteTestCase):
|
|||
|
||||
self.software_update = SoftwareUpdate(self.marionette)
|
||||
|
||||
# If a custom update channel has to be set, force a restart of
|
||||
# Firefox to actually get it applied as a default pref. Use the clean
|
||||
# option to force a non in_app restart, which would allow Firefox to
|
||||
# dump the logs to the console.
|
||||
if self.update_channel:
|
||||
self.software_update.update_channel = self.update_channel
|
||||
self.restart(clean=True)
|
||||
|
||||
self.assertEqual(self.software_update.update_channel, self.update_channel)
|
||||
|
||||
# If requested modify the list of allowed MAR channels
|
||||
if self.update_mar_channels:
|
||||
self.software_update.mar_channels.add_channels(self.update_mar_channels)
|
||||
|
||||
self.assertTrue(self.update_mar_channels.issubset(
|
||||
self.software_update.mar_channels.channels),
|
||||
'Allowed MAR channels have been set: expected "{}" in "{}"'.format(
|
||||
', '.join(self.update_mar_channels),
|
||||
', '.join(self.software_update.mar_channels.channels)))
|
||||
|
||||
# Ensure that there exists no already partially downloaded update
|
||||
self.remove_downloaded_update()
|
||||
|
||||
|
@ -70,12 +86,6 @@ class UpdateTestCase(PuppeteerMixin, MarionetteTestCase):
|
|||
'success': False,
|
||||
}]
|
||||
|
||||
self.assertTrue(self.update_mar_channels.issubset(
|
||||
self.software_update.mar_channels.channels),
|
||||
'Allowed MAR channels have been set: expected "{}" in "{}"'.format(
|
||||
', '.join(self.update_mar_channels),
|
||||
', '.join(self.software_update.mar_channels.channels)))
|
||||
|
||||
# Check if the user has permissions to run the update
|
||||
self.assertTrue(self.software_update.allowed,
|
||||
'Current user has permissions to update the application.')
|
||||
|
@ -349,8 +359,6 @@ class UpdateTestCase(PuppeteerMixin, MarionetteTestCase):
|
|||
|
||||
def set_preferences_defaults(self):
|
||||
"""Set the default value for specific preferences to force its usage."""
|
||||
if self.update_channel:
|
||||
self.software_update.update_channel = self.update_channel
|
||||
if self.update_url:
|
||||
self.software_update.update_url = self.update_url
|
||||
|
||||
|
|
|
@ -17,14 +17,11 @@ class TestSoftwareUpdate(PuppeteerMixin, MarionetteTestCase):
|
|||
self.software_update = SoftwareUpdate(self.marionette)
|
||||
|
||||
self.saved_mar_channels = self.software_update.mar_channels.channels
|
||||
self.saved_update_channel = self.software_update.update_channel
|
||||
|
||||
self.software_update.mar_channels.channels = set(['expected', 'channels'])
|
||||
|
||||
def tearDown(self):
|
||||
try:
|
||||
self.software_update.mar_channels.channels = self.saved_mar_channels
|
||||
self.software_update.update_channel = self.saved_update_channel
|
||||
finally:
|
||||
super(TestSoftwareUpdate, self).tearDown()
|
||||
|
||||
|
@ -71,10 +68,36 @@ class TestSoftwareUpdate(PuppeteerMixin, MarionetteTestCase):
|
|||
def test_staging_directory(self):
|
||||
self.assertTrue(self.software_update.staging_directory)
|
||||
|
||||
def test_set_update_channel(self):
|
||||
self.software_update.update_channel = 'new_channel'
|
||||
self.assertEqual(self.marionette.get_pref('app.update.channel', default_branch=True),
|
||||
'new_channel')
|
||||
|
||||
class TestUpdateChannel(PuppeteerMixin, MarionetteTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestUpdateChannel, self).setUp()
|
||||
|
||||
self.software_update = SoftwareUpdate(self.marionette)
|
||||
|
||||
self.saved_channel = self.software_update.update_channel
|
||||
self.software_update.update_channel = 'expected_channel'
|
||||
|
||||
def tearDown(self):
|
||||
try:
|
||||
self.software_update.update_channel = self.saved_channel
|
||||
finally:
|
||||
super(TestUpdateChannel, self).tearDown()
|
||||
|
||||
def test_update_channel_default_channel(self):
|
||||
# Without a restart the update channel will not change.
|
||||
self.assertEqual(self.software_update.update_channel, self.saved_channel)
|
||||
|
||||
def test_update_channel_set_channel(self):
|
||||
try:
|
||||
# Use the clean option to force a non in_app restart, which would allow
|
||||
# Firefox to dump the logs to the console.
|
||||
self.restart(clean=True)
|
||||
self.assertEqual(self.software_update.update_channel, 'expected_channel')
|
||||
finally:
|
||||
self.software_update.update_channel = self.saved_channel
|
||||
self.restart(clean=True)
|
||||
|
||||
|
||||
class TestMARChannels(PuppeteerMixin, MarionetteTestCase):
|
||||
|
|
|
@ -386,10 +386,8 @@ class DesktopInstance(GeckoInstance):
|
|||
# Do not show the EULA notification which can interfer with tests
|
||||
"browser.EULA.override": True,
|
||||
|
||||
# Bug 1145668, 1312674
|
||||
# Turn off once Marionette can correctly handle error pages, and doesn't
|
||||
# hang when about:blank gets loaded twice
|
||||
"browser.newtabpage.enabled": True,
|
||||
# Turn off about:newtab and make use of about:blank instead for new opened tabs
|
||||
"browser.newtabpage.enabled": False,
|
||||
# Assume the about:newtab page"s intro panels have been shown to not depend on
|
||||
# which test runs first and happens to open about:newtab
|
||||
"browser.newtabpage.introShown": True,
|
||||
|
|
|
@ -158,13 +158,18 @@ class TestNavigate(WindowManagerMixin, MarionetteTestCase):
|
|||
self.assertTrue(self.marionette.execute_script(
|
||||
"return window.visited", sandbox=None))
|
||||
|
||||
@skip_if_mobile("Fennec doesn't support other chrome windows")
|
||||
@skip_if_mobile("Bug 1334095 - Timeout: No new tab has been opened")
|
||||
def test_about_blank_for_new_docshell(self):
|
||||
""" Bug 1312674 - Hang when loading about:blank for a new docshell."""
|
||||
# Open a window to get a new docshell created for the first tab
|
||||
with self.marionette.using_context("chrome"):
|
||||
tab = self.open_tab(lambda: self.marionette.execute_script(" window.open() "))
|
||||
self.marionette.switch_to_window(tab)
|
||||
def open_with_link():
|
||||
link = self.marionette.find_element(By.ID, "new-blank-tab")
|
||||
link.click()
|
||||
|
||||
# Open a new tab to get a new docshell created
|
||||
self.marionette.navigate(self.marionette.absolute_url("windowHandles.html"))
|
||||
new_tab = self.open_tab(trigger=open_with_link)
|
||||
self.marionette.switch_to_window(new_tab)
|
||||
self.assertEqual(self.marionette.get_url(), "about:blank")
|
||||
|
||||
self.marionette.navigate('about:blank')
|
||||
self.marionette.close()
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
<title>Marionette New Tab Link</title>
|
||||
</head>
|
||||
<body>
|
||||
<a href="empty.html" id="new-tab" target="_blank">Click me!</a>
|
||||
<a href="" id="new-window" onClick='javascript:window.open("empty.html", null, "location=1,toolbar=1");'>Click me!</a>
|
||||
<a href="empty.html" id="new-tab" target="_blank">New Tab</a>
|
||||
<a href="about:blank" id="new-blank-tab" target="_blank">New blank Tab</a>
|
||||
|
||||
<a href="" id="new-window" onClick='javascript:window.open("empty.html", null, "location=1,toolbar=1");'>New Window</a>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import ConfigParser
|
||||
import os
|
||||
import re
|
||||
|
||||
import mozinfo
|
||||
|
||||
|
@ -324,8 +325,8 @@ class SoftwareUpdate(BaseLib):
|
|||
:param channel: New update channel to use
|
||||
|
||||
"""
|
||||
self.marionette.set_pref(self.PREF_APP_UPDATE_CHANNEL, channel,
|
||||
default_branch=True)
|
||||
writer = UpdateChannelWriter(self.marionette)
|
||||
writer.set_channel(channel)
|
||||
|
||||
@property
|
||||
def update_url(self):
|
||||
|
@ -389,3 +390,33 @@ class SoftwareUpdate(BaseLib):
|
|||
url += 'force=1'
|
||||
|
||||
return url
|
||||
|
||||
|
||||
class UpdateChannelWriter(BaseLib):
|
||||
"""Class to handle the update channel as listed in channel-prefs.js"""
|
||||
REGEX_UPDATE_CHANNEL = re.compile(r'("app\.update\.channel", ")([^"].*)(?=")')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
BaseLib.__init__(self, *args, **kwargs)
|
||||
|
||||
self.file_path = self.marionette.execute_script("""
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
let file = Services.dirsvc.get('PrfDef', Components.interfaces.nsIFile);
|
||||
file.append('channel-prefs.js');
|
||||
|
||||
return file.path;
|
||||
""")
|
||||
|
||||
def set_channel(self, channel):
|
||||
"""Set default update channel.
|
||||
|
||||
:param channel: New default update channel
|
||||
"""
|
||||
with open(self.file_path) as f:
|
||||
file_contents = f.read()
|
||||
|
||||
new_content = re.sub(
|
||||
self.REGEX_UPDATE_CHANNEL, r'\g<1>' + channel, file_contents)
|
||||
with open(self.file_path, 'w') as f:
|
||||
f.write(new_content)
|
||||
|
|
|
@ -1083,3 +1083,12 @@ nsTSubstring_CharT::SizeOfIncludingThisEvenIfShared(
|
|||
return aMallocSizeOf(this) + SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
|
||||
}
|
||||
|
||||
nsTSubstringSplitter_CharT nsTSubstring_CharT::Split(const nsTSubstring_CharT::char_type aChar)
|
||||
{
|
||||
return nsTSubstringSplitter_CharT(this, aChar);
|
||||
}
|
||||
|
||||
const nsTSubstring_CharT& nsTSubstringSplitter_CharT::nsTSubstringSplit_Iter::operator* () const
|
||||
{
|
||||
return mObj.Get(mPos);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// IWYU pragma: private, include "nsString.h"
|
||||
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#ifndef MOZILLA_INTERNAL_API
|
||||
|
@ -46,6 +47,8 @@ public:
|
|||
uint32_t, uint32_t) const override;
|
||||
};
|
||||
|
||||
class nsTSubstringSplitter_CharT;
|
||||
|
||||
/**
|
||||
* nsTSubstring is the most abstract class in the string hierarchy. It
|
||||
* represents a single contiguous array of characters, which may or may not
|
||||
|
@ -709,6 +712,7 @@ public:
|
|||
Replace(aCutStart, aCutLength, char_traits::sEmptyBuffer, 0);
|
||||
}
|
||||
|
||||
nsTSubstringSplitter_CharT Split(const char_type aChar);
|
||||
|
||||
/**
|
||||
* buffer sizing
|
||||
|
@ -907,8 +911,7 @@ protected:
|
|||
|
||||
friend class nsTObsoleteAStringThunk_CharT;
|
||||
friend class nsTSubstringTuple_CharT;
|
||||
|
||||
// XXX GCC 3.4 needs this :-(
|
||||
friend class nsTSubstringSplitter_CharT;
|
||||
friend class nsTPromiseFlatString_CharT;
|
||||
|
||||
char_type* mData;
|
||||
|
@ -1168,3 +1171,92 @@ operator>(const nsTSubstring_CharT::base_string_type& aLhs,
|
|||
{
|
||||
return Compare(aLhs, aRhs) > 0;
|
||||
}
|
||||
|
||||
// You should not need to instantiate this class directly.
|
||||
// Use nsTSubstring::Split instead.
|
||||
class nsTSubstringSplitter_CharT
|
||||
{
|
||||
class nsTSubstringSplit_Iter
|
||||
{
|
||||
public:
|
||||
nsTSubstringSplit_Iter(const nsTSubstringSplitter_CharT& aObj,
|
||||
nsTSubstring_CharT::size_type aPos)
|
||||
: mObj(aObj)
|
||||
, mPos(aPos)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator!=(const nsTSubstringSplit_Iter& other) const
|
||||
{
|
||||
return mPos != other.mPos;
|
||||
}
|
||||
|
||||
const nsTSubstring_CharT& operator*() const;
|
||||
|
||||
const nsTSubstringSplit_Iter& operator++()
|
||||
{
|
||||
++mPos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
const nsTSubstringSplitter_CharT& mObj;
|
||||
nsTSubstring_CharT::size_type mPos;
|
||||
};
|
||||
|
||||
private:
|
||||
const nsTSubstring_CharT* mStr;
|
||||
mozilla::UniquePtr<nsTSubstring_CharT[]> mArray;
|
||||
nsTSubstring_CharT::size_type mArraySize;
|
||||
const nsTSubstring_CharT::char_type mDelim;
|
||||
|
||||
public:
|
||||
nsTSubstringSplitter_CharT(const nsTSubstring_CharT* aStr,
|
||||
nsTSubstring_CharT::char_type aDelim)
|
||||
: mStr(aStr)
|
||||
, mArray(nullptr)
|
||||
, mDelim(aDelim)
|
||||
{
|
||||
if (mStr->IsEmpty()) {
|
||||
mArraySize = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
nsTSubstring_CharT::size_type delimCount = mStr->CountChar(aDelim);
|
||||
mArraySize = delimCount + 1;
|
||||
mArray.reset(new nsTSubstring_CharT[mArraySize]);
|
||||
|
||||
size_t seenParts = 0;
|
||||
nsTSubstring_CharT::size_type start = 0;
|
||||
do {
|
||||
MOZ_ASSERT(seenParts < mArraySize);
|
||||
int32_t offset = mStr->FindChar(aDelim, start);
|
||||
if (offset != -1) {
|
||||
nsTSubstring_CharT::size_type length =
|
||||
static_cast<nsTSubstring_CharT::size_type>(offset) - start;
|
||||
mArray[seenParts++].Assign(mStr->Data() + start, length);
|
||||
start = static_cast<nsTSubstring_CharT::size_type>(offset) + 1;
|
||||
} else {
|
||||
// Get the remainder
|
||||
mArray[seenParts++].Assign(mStr->Data() + start, mStr->Length() - start);
|
||||
break;
|
||||
}
|
||||
} while (start < mStr->Length());
|
||||
}
|
||||
|
||||
nsTSubstringSplit_Iter begin() const
|
||||
{
|
||||
return nsTSubstringSplit_Iter(*this, 0);
|
||||
}
|
||||
|
||||
nsTSubstringSplit_Iter end() const
|
||||
{
|
||||
return nsTSubstringSplit_Iter(*this, mArraySize);
|
||||
}
|
||||
|
||||
const nsTSubstring_CharT& Get(const nsTSubstring_CharT::size_type index) const
|
||||
{
|
||||
MOZ_ASSERT(index < mArraySize);
|
||||
return mArray[index];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -23,3 +23,4 @@
|
|||
#define nsTAdoptingString_CharT nsAdoptingCString
|
||||
#define nsTPromiseFlatString_CharT nsPromiseFlatCString
|
||||
#define TPromiseFlatString_CharT PromiseFlatCString
|
||||
#define nsTSubstringSplitter_CharT nsCSubstringSplitter
|
||||
|
|
|
@ -23,3 +23,4 @@
|
|||
#define nsTAdoptingString_CharT nsAdoptingString
|
||||
#define nsTPromiseFlatString_CharT nsPromiseFlatString
|
||||
#define TPromiseFlatString_CharT PromiseFlatString
|
||||
#define nsTSubstringSpliiter_CharT nsSubstringSplitter
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче