Merge autoland to central, a=merge

MozReview-Commit-ID: BoJFR48izPq
This commit is contained in:
Wes Kocher 2017-01-30 15:21:59 -08:00
Родитель 1bd01b630b 591e9ae130
Коммит a5176322a6
102 изменённых файлов: 1877 добавлений и 606 удалений

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

@ -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;
}

0
browser/components/preferences/in-content/sync.xul Normal file → Executable file
Просмотреть файл

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

@ -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 = {

0
browser/themes/shared/fxa/sync-illustration.svg Normal file → Executable file
Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 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);
}
},
@ -497,4 +506,4 @@ WebConsoleConnectionProxy.prototype = {
},
};
exports.WebConsoleConnectionProxy = WebConsoleConnectionProxy;
exports.WebConsoleConnectionProxy = WebConsoleConnectionProxy;

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

@ -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;
@ -21,10 +21,10 @@ import android.os.AsyncTask;
/**
* An async loader to load user config in background thread based on internal generated UUID.
*
* Call <code>AsyncConfigLoader.execute()</code> to load SwitchBoard.loadConfig() with own ID.
*
* Call <code>AsyncConfigLoader.execute()</code> to load SwitchBoard.loadConfig() with own ID.
* To use your custom UUID call <code>AsyncConfigLoader.execute(uuid)</code> with uuid being your unique user id
* as a String
* as a String
*
* @author Philipp Berner
*

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

@ -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;
@ -22,7 +22,7 @@ import android.content.SharedPreferences;
/**
* Generates a UUID and stores is persistent as in the apps shared preferences.
*
*
* @author Philipp Berner
*/
public class DeviceUuidFactory {

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

@ -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;
@ -21,7 +21,7 @@ import android.content.Context;
/**
* Single instance of an existing experiment for easier and cleaner code.
*
*
* @author Philipp Berner
*
*/
@ -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;
@ -43,18 +42,18 @@ import android.util.Log;
/**
* SwitchBoard is the core class of the KeepSafe Switchboard mobile A/B testing framework.
* This class provides a bunch of static methods that can be used in your app to run A/B tests.
*
* The SwitchBoard supports production and staging environment.
*
* This class provides a bunch of static methods that can be used in your app to run A/B tests.
*
* The SwitchBoard supports production and staging environment.
*
* For usage <code>initDefaultServerUrls</code> for first time usage. Server URLs can be updates from
* a remote location with <code>initConfigServerUrl</code>.
*
* To run a experiment use <code>isInExperiment()</code>. The experiment name has to match the one you
*
* To run a experiment use <code>isInExperiment()</code>. The experiment name has to match the one you
* setup on the server.
* All functions are design to be safe for programming mistakes and network connection issues. If the
* All functions are design to be safe for programming mistakes and network connection issues. If the
* experiment does not exists it will return false and pretend the user is not part of it.
*
*
* @author Philipp Berner
*
*/
@ -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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше