MozReview-Commit-ID: BMjcETZ38gw
This commit is contained in:
Kartikaya Gupta 2017-02-23 09:51:44 -05:00
Родитель 33d74b040e e6b82618f4
Коммит 92d49690c6
871 изменённых файлов: 28023 добавлений и 80583 удалений

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

@ -2163,12 +2163,13 @@ Accessible::RemoveChild(Accessible* aChild)
void
Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
{
MOZ_ASSERT(aChild, "No child was given");
MOZ_ASSERT(aChild->mParent == this, "A child from different subtree was given");
MOZ_ASSERT(aChild->mIndexInParent != -1, "Unbound child was given");
MOZ_ASSERT(static_cast<uint32_t>(aChild->mIndexInParent) != aNewIndex,
MOZ_DIAGNOSTIC_ASSERT(aChild, "No child was given");
MOZ_DIAGNOSTIC_ASSERT(aChild->mParent == this, "A child from different subtree was given");
MOZ_DIAGNOSTIC_ASSERT(aChild->mIndexInParent != -1, "Unbound child was given");
MOZ_DIAGNOSTIC_ASSERT(aChild->mParent->GetChildAt(aChild->mIndexInParent) == aChild, "Wrong index in parent");
MOZ_DIAGNOSTIC_ASSERT(static_cast<uint32_t>(aChild->mIndexInParent) != aNewIndex,
"No move, same index");
MOZ_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given");
MOZ_DIAGNOSTIC_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given");
RefPtr<AccHideEvent> hideEvent = new AccHideEvent(aChild, false);
if (mDoc->Controller()->QueueMutationEvent(hideEvent)) {

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

@ -217,6 +217,8 @@ static int do_main(int argc, char* argv[], char* envp[])
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
sandbox::BrokerServices* brokerServices =
sandboxing::GetInitializedBrokerServices();
sandboxing::PermissionsService* permissionsService =
sandboxing::GetPermissionsService();
#if defined(MOZ_CONTENT_SANDBOX)
if (!brokerServices) {
Output("Couldn't initialize the broker services.\n");
@ -224,6 +226,7 @@ static int do_main(int argc, char* argv[], char* envp[])
}
#endif
config.sandboxBrokerServices = brokerServices;
config.sandboxPermissionsService = permissionsService;
#endif
#ifdef LIBFUZZER

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

@ -460,6 +460,7 @@ const gStoragePressureObserver = {
let buttons = [];
let usage = parseInt(data);
let prefStrBundle = document.getElementById("bundle_preferences");
let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
let notificationBox = document.getElementById("high-priority-global-notificationbox");
buttons.push({
label: prefStrBundle.getString("spaceAlert.learnMoreButton.label"),
@ -474,7 +475,7 @@ const gStoragePressureObserver = {
// This is because this usage is small and not the main cause for space issue.
// In order to avoid the bad and wrong impression among users that
// firefox eats disk space a lot, indicate users to clean up other disk space.
msg = prefStrBundle.getString("spaceAlert.under5GB.description");
msg = prefStrBundle.getFormattedString("spaceAlert.under5GB.message", [brandShortName]);
buttons.push({
label: prefStrBundle.getString("spaceAlert.under5GB.okButton.label"),
accessKey: prefStrBundle.getString("spaceAlert.under5GB.okButton.accesskey"),
@ -483,15 +484,15 @@ const gStoragePressureObserver = {
} else {
// The firefox-used space >= 5GB, then guide users to about:preferences
// to clear some data stored on firefox by websites.
let descriptionStringID = "spaceAlert.over5GB.description";
let descriptionStringID = "spaceAlert.over5GB.message";
let prefButtonLabelStringID = "spaceAlert.over5GB.prefButton.label";
let prefButtonAccesskeyStringID = "spaceAlert.over5GB.prefButton.accesskey";
if (AppConstants.platform == "win") {
descriptionStringID = "spaceAlert.over5GB.descriptionWin";
descriptionStringID = "spaceAlert.over5GB.messageWin";
prefButtonLabelStringID = "spaceAlert.over5GB.prefButtonWin.label";
prefButtonAccesskeyStringID = "spaceAlert.over5GB.prefButtonWin.accesskey";
}
msg = prefStrBundle.getString(descriptionStringID);
msg = prefStrBundle.getFormattedString(descriptionStringID, [brandShortName]);
buttons.push({
label: prefStrBundle.getString(prefButtonLabelStringID),
accessKey: prefStrBundle.getString(prefButtonAccesskeyStringID),
@ -3435,7 +3436,7 @@ var PrintPreviewListener = {
let browser = gBrowser.selectedBrowser;
let preferredRemoteType = browser.remoteType;
this._tabBeforePrintPreview = gBrowser.selectedTab;
this._printPreviewTab = gBrowser.loadOneTab("about:blank", {
this._printPreviewTab = gBrowser.loadOneTab("about:printpreview", {
inBackground: false,
preferredRemoteType,
sameProcessAsFrameLoader: browser.frameLoader
@ -3446,7 +3447,7 @@ var PrintPreviewListener = {
},
createSimplifiedBrowser() {
let browser = this._tabBeforePrintPreview.linkedBrowser;
this._simplifyPageTab = gBrowser.loadOneTab("about:blank", {
this._simplifyPageTab = gBrowser.loadOneTab("about:printpreview", {
inBackground: true,
sameProcessAsFrameLoader: browser.frameLoader
});

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

@ -17,6 +17,15 @@ function* wait_for_tab_playing_event(tab, expectPlaying) {
});
}
function* is_audio_playing(tab) {
let browser = tab.linkedBrowser;
let isPlaying = yield ContentTask.spawn(browser, {}, function* () {
let audio = content.document.querySelector("audio");
return !audio.paused;
});
return isPlaying;
}
function* play(tab) {
let browser = tab.linkedBrowser;
yield ContentTask.spawn(browser, {}, function* () {
@ -24,12 +33,16 @@ function* play(tab) {
audio.play();
});
// If the tab has already be muted, it means the tab won't get soundplaying,
// so we don't need to check this attribute.
if (browser.audioMuted) {
return;
}
yield wait_for_tab_playing_event(tab, true);
}
function* pause(tab, options) {
ok(tab.hasAttribute("soundplaying"), "The tab should have the soundplaying attribute when pause() is called");
let extendedDelay = options && options.extendedDelay;
if (extendedDelay) {
// Use 10s to remove possibility of race condition with attr removal.
@ -47,6 +60,12 @@ function* pause(tab, options) {
audio.pause();
});
// If the tab has already be muted, it means the tab won't have soundplaying,
// so we don't need to check this attribute.
if (browser.audioMuted) {
return;
}
if (extendedDelay) {
ok(tab.hasAttribute("soundplaying"), "The tab should still have the soundplaying attribute immediately after pausing");
@ -141,6 +160,13 @@ function* test_mute_tab(tab, icon, expectMuted) {
is(gBrowser.selectedTab, activeTab, "Clicking on mute should not change the currently selected tab");
// If the audio is playing, we should check whether clicking on icon affects
// the media element's playing state.
let isAudioPlaying = yield is_audio_playing(tab);
if (isAudioPlaying) {
yield wait_for_tab_playing_event(tab, !expectMuted);
}
return mutedPromise;
}
@ -169,7 +195,7 @@ function* test_muting_using_menu(tab, expectMuted) {
yield play(tab);
is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute");
ok(toggleMute.hasAttribute("soundplaying"), "Should have the soundplaying attribute");
is(!toggleMute.hasAttribute("soundplaying"), expectMuted, "The value of soundplaying attribute is incorrect");
yield pause(tab);
@ -234,14 +260,14 @@ function* test_playing_icon_on_tab(tab, browser, isPinned) {
}
function* test_swapped_browser_while_playing(oldTab, newBrowser) {
// The tab was muted so it won't have soundplaying attribute even it's playing.
ok(oldTab.hasAttribute("muted"), "Expected the correct muted attribute on the old tab");
is(oldTab.muteReason, null, "Expected the correct muteReason attribute on the old tab");
ok(oldTab.hasAttribute("soundplaying"), "Expected the correct soundplaying attribute on the old tab");
ok(!oldTab.hasAttribute("soundplaying"), "Expected the correct soundplaying attribute on the old tab");
let newTab = gBrowser.getTabForBrowser(newBrowser);
let AttrChangePromise = BrowserTestUtils.waitForEvent(newTab, "TabAttrModified", false, event => {
return event.detail.changed.includes("soundplaying") &&
event.detail.changed.includes("muted");
return event.detail.changed.includes("muted");
});
gBrowser.swapBrowsersAndCloseOther(newTab, oldTab);
@ -249,7 +275,7 @@ function* test_swapped_browser_while_playing(oldTab, newBrowser) {
ok(newTab.hasAttribute("muted"), "Expected the correct muted attribute on the new tab");
is(newTab.muteReason, null, "Expected the correct muteReason property on the new tab");
ok(newTab.hasAttribute("soundplaying"), "Expected the correct soundplaying attribute on the new tab");
ok(!newTab.hasAttribute("soundplaying"), "Expected the correct soundplaying attribute on the new tab");
let icon = document.getAnonymousElementByAttribute(newTab, "anonid",
"soundplaying-icon");

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

@ -142,7 +142,7 @@ function delayedStartupFinished(aWindow) {
function someTabLoaded(aWindow) {
return new Promise(function(resolve) {
aWindow.gBrowser.addEventListener("load", function onLoad(aEvent) {
if (aWindow.location === "about:blank") {
if (aWindow.location.href === "about:blank") {
return;
}
let tab = aWindow.gBrowser._getTabForContentWindow(

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

@ -309,6 +309,8 @@ const CustomizableWidgets = [
DECKINDEX_FETCHING: 2,
DECKINDEX_NOCLIENTS: 3,
},
TABS_PER_PAGE: 25,
NEXT_PAGE_MIN_TABS: 5, // Minimum number of tabs displayed when we click "Show All"
onCreated(aNode) {
// Add an observer to the button so we get the animation during sync.
// (Note the observer sets many attributes, including label and
@ -401,13 +403,13 @@ const CustomizableWidgets = [
_showTabsPromise: Promise.resolve(),
// Update the tab list after any existing in-flight updates are complete.
_showTabs() {
_showTabs(paginationInfo) {
this._showTabsPromise = this._showTabsPromise.then(() => {
return this.__showTabs();
return this.__showTabs(paginationInfo);
});
},
// Return a new promise to update the tab list.
__showTabs() {
__showTabs(paginationInfo) {
let doc = this._tabsList.ownerDocument;
return SyncedTabs.getTabClients().then(clients => {
// The view may have been hidden while the promise was resolving.
@ -427,7 +429,7 @@ const CustomizableWidgets = [
this.setDeckIndex(this.deckIndices.DECKINDEX_TABS);
this._clearTabList();
SyncedTabs.sortTabClientsByLastUsed(clients, 50 /* maxTabs */);
SyncedTabs.sortTabClientsByLastUsed(clients);
let fragment = doc.createDocumentFragment();
for (let client of clients) {
@ -436,7 +438,11 @@ const CustomizableWidgets = [
let separator = doc.createElementNS(kNSXUL, "menuseparator");
fragment.appendChild(separator);
}
this._appendClient(client, fragment);
if (paginationInfo && paginationInfo.clientId == client.id) {
this._appendClient(client, fragment, paginationInfo.maxTabs);
} else {
this._appendClient(client, fragment);
}
}
this._tabsList.appendChild(fragment);
}).catch(err => {
@ -466,7 +472,7 @@ const CustomizableWidgets = [
appendTo.appendChild(messageLabel);
return messageLabel;
},
_appendClient(client, attachFragment) {
_appendClient(client, attachFragment, maxTabs = this.TABS_PER_PAGE) {
let doc = attachFragment.ownerDocument;
// Create the element for the remote client.
let clientItem = doc.createElementNS(kNSXUL, "label");
@ -482,10 +488,30 @@ const CustomizableWidgets = [
let label = this._appendMessageLabel("notabsforclientlabel", attachFragment);
label.setAttribute("class", "PanelUI-remotetabs-notabsforclient-label");
} else {
// If this page will display all tabs, show no additional buttons.
// If the next page will display all the remaining tabs, show a "Show All" button
// Otherwise, show a "Shore More" button
let hasNextPage = client.tabs.length > maxTabs;
let nextPageIsLastPage = hasNextPage && maxTabs + this.TABS_PER_PAGE >= client.tabs.length;
if (nextPageIsLastPage) {
// When the user clicks "Show All", try to have at least NEXT_PAGE_MIN_TABS more tabs
// to display in order to avoid user frustration
maxTabs = Math.min(client.tabs.length - this.NEXT_PAGE_MIN_TABS, maxTabs);
}
if (hasNextPage) {
client.tabs = client.tabs.slice(0, maxTabs);
}
for (let tab of client.tabs) {
let tabEnt = this._createTabElement(doc, tab);
attachFragment.appendChild(tabEnt);
}
if (hasNextPage) {
let showAllEnt = this._createShowMoreElement(doc, client.id,
nextPageIsLastPage ?
Infinity :
maxTabs + this.TABS_PER_PAGE);
attachFragment.appendChild(showAllEnt);
}
}
},
_createTabElement(doc, tabInfo) {
@ -506,6 +532,29 @@ const CustomizableWidgets = [
});
return item;
},
_createShowMoreElement(doc, clientId, showCount) {
let labelAttr, tooltipAttr;
if (showCount === Infinity) {
labelAttr = "showAllLabel";
tooltipAttr = "showAllTooltipText";
} else {
labelAttr = "showMoreLabel";
tooltipAttr = "showMoreTooltipText";
}
let showAllItem = doc.createElementNS(kNSXUL, "toolbarbutton");
showAllItem.setAttribute("itemtype", "showmorebutton");
showAllItem.setAttribute("class", "subviewbutton");
let label = this._tabsList.getAttribute(labelAttr);
showAllItem.setAttribute("label", label);
let tooltipText = this._tabsList.getAttribute(tooltipAttr);
showAllItem.setAttribute("tooltiptext", tooltipText);
showAllItem.addEventListener("click", e => {
e.preventDefault();
e.stopPropagation();
this._showTabs({ clientId, maxTabs: showCount });
});
return showAllItem;
}
}, {
id: "privatebrowsing-button",
shortcutId: "key_privatebrowsing",

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

@ -125,6 +125,10 @@
<!-- Sync is ready to Sync and the "tabs" engine is enabled -->
<vbox id="PanelUI-remotetabs-tabspane">
<vbox id="PanelUI-remotetabs-tabslist"
showAllLabel="&appMenuRemoteTabs.showAll.label;"
showAllTooltipText="&appMenuRemoteTabs.showAll.tooltip;"
showMoreLabel="&appMenuRemoteTabs.showMore.label;"
showMoreTooltipText="&appMenuRemoteTabs.showMore.tooltip;"
notabsforclientlabel="&appMenuRemoteTabs.notabs.label;"
/>
</vbox>

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

@ -100,7 +100,6 @@ skip-if = os == "linux" # Intermittent failures
[browser_963639_customizing_attribute_non_customizable_toolbar.js]
[browser_967000_button_charEncoding.js]
[browser_967000_button_feeds.js]
[browser_967000_button_sync.js]
[browser_968447_bookmarks_toolbar_items_in_panel.js]
skip-if = os == "linux" # Intemittent failures - bug 979207
[browser_968565_insert_before_hidden_items.js]
@ -151,4 +150,5 @@ skip-if = os == "mac"
[browser_customizemode_contextmenu_menubuttonstate.js]
[browser_panel_toggle.js]
[browser_switch_to_customize_mode.js]
[browser_synced_tabs_menu.js]
[browser_check_tooltips_in_navbar.js]

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

@ -32,8 +32,8 @@ function updateTabsPanel() {
// functions.
let mockedInternal = {
get isConfiguredToSyncTabs() { return true; },
getTabClients() { return []; },
syncTabs() {},
getTabClients() { return Promise.resolve([]); },
syncTabs() { return Promise.resolve(); },
hasSyncedThisSession: false,
};
@ -73,7 +73,9 @@ function* openPrefsFromMenuPanel(expectedPanelId, entryPoint) {
let syncButton = document.getElementById("sync-button");
ok(syncButton, "The Sync button was added to the Panel Menu");
let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
syncButton.click();
yield tabsUpdatedPromise;
let syncPanel = document.getElementById("PanelUI-remotetabs");
ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
@ -105,12 +107,16 @@ function* openPrefsFromMenuPanel(expectedPanelId, entryPoint) {
ok(!isPanelUIOpen(), "The panel closed");
if (isPanelUIOpen()) {
let panelHidePromise = promisePanelHidden(window);
PanelUI.hide();
yield panelHidePromise;
yield panelUIHide();
}
}
function panelUIHide() {
let panelHidePromise = promisePanelHidden(window);
PanelUI.hide();
return panelHidePromise;
}
function* asyncCleanup() {
Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
// reset the panel UI to the default state
@ -124,7 +130,12 @@ function* asyncCleanup() {
}
// When Sync is not setup.
add_task(() => openPrefsFromMenuPanel("PanelUI-remotetabs-setupsync", "synced-tabs"));
add_task(function* () {
document.getElementById("sync-reauth-state").hidden = true;
document.getElementById("sync-setup-state").hidden = false;
document.getElementById("sync-syncnow-state").hidden = true;
yield openPrefsFromMenuPanel("PanelUI-remotetabs-setupsync", "synced-tabs")
});
add_task(asyncCleanup);
// When Sync is configured in a "needs reauthentication" state.
@ -142,9 +153,6 @@ add_task(function* () {
Services.prefs.setCharPref("identity.mobilepromo.android", "http://example.com/?os=android&tail=");
Services.prefs.setCharPref("identity.mobilepromo.ios", "http://example.com/?os=ios&tail=");
mockedInternal.getTabClients = () => [];
mockedInternal.syncTabs = () => Promise.resolve();
document.getElementById("sync-reauth-state").hidden = true;
document.getElementById("sync-setup-state").hidden = true;
document.getElementById("sync-syncnow-state").hidden = false;
@ -190,7 +198,7 @@ add_task(function* () {
ok(isPanelUIOpen(), "panel remains open after right-click");
is(gBrowser.tabs.length, 1, "no new tab was opened");
}
PanelUI.hide();
yield panelUIHide();
Services.prefs.clearUserPref("identity.mobilepromo.android");
Services.prefs.clearUserPref("identity.mobilepromo.ios");
@ -198,18 +206,15 @@ add_task(function* () {
// Test the "Sync Now" button
add_task(function* () {
mockedInternal.getTabClients = () => [];
mockedInternal.syncTabs = () => {
return Promise.resolve();
}
// configure our broadcasters so we are in the right state.
document.getElementById("sync-reauth-state").hidden = true;
document.getElementById("sync-setup-state").hidden = true;
document.getElementById("sync-syncnow-state").hidden = false;
yield PanelUI.show();
let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
document.getElementById("sync-button").click();
yield tabsUpdatedPromise;
let syncPanel = document.getElementById("PanelUI-remotetabs");
ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
@ -334,4 +339,89 @@ add_task(function* () {
node = node.nextSibling;
is(node, null, "no more entries");
yield panelUIHide();
});
// Test the pagination capabilities (Show More/All tabs)
add_task(function* () {
mockedInternal.getTabClients = () => {
return Promise.resolve([
{
id: "guid_desktop",
type: "client",
name: "My Desktop",
tabs: function() {
let allTabsDesktop = [];
// We choose 77 tabs, because TABS_PER_PAGE is 25, which means
// on the second to last page we should have 22 items shown
// (because we have to show at least NEXT_PAGE_MIN_TABS=5 tabs on the last page)
for (let i = 1; i <= 77; i++) {
allTabsDesktop.push({ title: "Tab #" + i });
}
return allTabsDesktop;
}(),
}
]);
};
// configure our broadcasters so we are in the right state.
document.getElementById("sync-reauth-state").hidden = true;
document.getElementById("sync-setup-state").hidden = true;
document.getElementById("sync-syncnow-state").hidden = false;
yield PanelUI.show();
let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
document.getElementById("sync-button").click();
yield tabsUpdatedPromise;
// Check pre-conditions
let syncPanel = document.getElementById("PanelUI-remotetabs");
ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
let subpanel = document.getElementById("PanelUI-remotetabs-main")
ok(!subpanel.hidden, "main pane is visible");
let deck = document.getElementById("PanelUI-remotetabs-deck");
is(deck.selectedIndex, DECKINDEX_TABS, "we should be showing tabs");
function checkTabsPage(tabsShownCount, showMoreLabel) {
let tabList = document.getElementById("PanelUI-remotetabs-tabslist");
let node = tabList.firstChild;
is(node.getAttribute("itemtype"), "client", "node is a client entry");
is(node.textContent, "My Desktop", "correct client");
for (let i = 0; i < tabsShownCount; i++) {
node = node.nextSibling;
is(node.getAttribute("itemtype"), "tab", "node is a tab");
is(node.getAttribute("label"), "Tab #" + (i + 1), "the tab is the correct one");
}
let showMoreButton;
if (showMoreLabel) {
node = showMoreButton = node.nextSibling;
is(node.getAttribute("itemtype"), "showmorebutton", "node is a show more button");
is(node.getAttribute("label"), showMoreLabel);
}
node = node.nextSibling;
is(node, null, "no more entries");
return showMoreButton;
}
let showMoreButton;
function clickShowMoreButton() {
let promise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
showMoreButton.click();
return promise;
}
showMoreButton = checkTabsPage(25, "Show More");
yield clickShowMoreButton();
showMoreButton = checkTabsPage(50, "Show More");
yield clickShowMoreButton();
showMoreButton = checkTabsPage(72, "Show All");
yield clickShowMoreButton();
checkTabsPage(77, null);
yield panelUIHide();
});

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

@ -38,7 +38,14 @@ function isAncestorOrSelf(target, node) {
}
// WeakMap[Extension -> BrowserAction]
var browserActionMap = new WeakMap();
const browserActionMap = new WeakMap();
const browserAreas = {
"navbar": CustomizableUI.AREA_NAVBAR,
"menupanel": CustomizableUI.AREA_PANEL,
"tabstrip": CustomizableUI.AREA_TABSTRIP,
"personaltoolbar": CustomizableUI.AREA_BOOKMARKS,
};
// Responsible for the browser_action section of the manifest as well
// as the associated popup.
@ -62,6 +69,7 @@ function BrowserAction(options, extension) {
badgeBackgroundColor: null,
icon: IconDetails.normalize({path: options.default_icon}, extension),
popup: options.default_popup || "",
area: browserAreas[options.default_area || "navbar"],
};
this.browserStyle = options.browser_style || false;
@ -85,7 +93,7 @@ BrowserAction.prototype = {
removable: true,
label: this.defaults.title || this.extension.name,
tooltiptext: this.defaults.title || "",
defaultArea: CustomizableUI.AREA_NAVBAR,
defaultArea: this.defaults.area,
onBeforeCreated: document => {
let view = document.createElementNS(XUL_NS, "panelview");

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

@ -31,6 +31,12 @@
"browser_style": {
"type": "boolean",
"optional": true
},
"default_area": {
"description": "Defines the location the browserAction will appear by default. The default location is navbar.",
"type": "string",
"enum": ["navbar", "menupanel", "tabstrip", "personaltoolbar"],
"optional": true
}
},
"optional": true

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

@ -24,6 +24,7 @@ support-files =
searchSuggestionEngine.sjs
../../../../../toolkit/components/extensions/test/mochitest/head_webrequest.js
[browser_ext_browserAction_area.js]
[browser_ext_browserAction_context.js]
[browser_ext_browserAction_disabled.js]
[browser_ext_browserAction_pageAction_icon.js]

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

@ -0,0 +1,49 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
var browserAreas = {
"navbar": CustomizableUI.AREA_NAVBAR,
"menupanel": CustomizableUI.AREA_PANEL,
"tabstrip": CustomizableUI.AREA_TABSTRIP,
"personaltoolbar": CustomizableUI.AREA_BOOKMARKS,
};
function* testInArea(area) {
let manifest = {
"browser_action": {
"browser_style": true,
},
};
if (area) {
manifest.browser_action.default_area = area;
}
let extension = ExtensionTestUtils.loadExtension({
manifest,
});
yield extension.startup();
let widget = getBrowserActionWidget(extension);
let placement = CustomizableUI.getPlacementOfWidget(widget.id);
is(placement && placement.area, browserAreas[area || "navbar"], `widget located in correct area`);
yield extension.unload();
}
add_task(function* testBrowserActionDefaultArea() {
yield testInArea();
});
add_task(function* testBrowserActionInToolbar() {
yield testInArea("navbar");
});
add_task(function* testBrowserActionInMenuPanel() {
yield testInArea("menupanel");
});
add_task(function* testBrowserActionInTabStrip() {
yield testInArea("tabstrip");
});
add_task(function* testBrowserActionInPersonalToolbar() {
yield testInArea("personaltoolbar");
});

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

@ -388,7 +388,7 @@ this.SessionStore = {
let win = aState.windows[i];
for (let j = win.tabs.length - 1; j >= 0; j--) {
let tab = win.tabs[j];
if (!SessionStoreInternal._shouldSaveTabState(tab)) {
if (!SessionStoreInternal._shouldSaveTab(tab)) {
win.tabs.splice(j, 1);
if (win.selected > j) {
win.selected--;
@ -4198,10 +4198,29 @@ var SessionStoreInternal = {
!(aTabState.entries.length == 1 &&
(aTabState.entries[0].url == "about:blank" ||
aTabState.entries[0].url == "about:newtab" ||
aTabState.entries[0].url == "about:printpreview" ||
aTabState.entries[0].url == "about:privatebrowsing") &&
!aTabState.userTypedValue);
},
/**
* Determine if the tab state we're passed is something we should keep to be
* reopened at session restore. This is used when we are saving the current
* session state to disk. This method is very similar to _shouldSaveTabState,
* however, "about:blank" and "about:newtab" tabs will still be saved to disk.
*
* @param aTabState
* The current tab state
* @returns boolean
*/
_shouldSaveTab: function ssi_shouldSaveTab(aTabState) {
// If the tab has one of the following transient about: history entry,
// then we don't actually want to write this tab's data to disk.
return aTabState.entries.length &&
!(aTabState.entries[0].url == "about:printpreview" ||
aTabState.entries[0].url == "about:privatebrowsing");
},
/**
* This is going to take a state as provided at startup (via
* nsISessionStartup.state) and split it into 2 parts. The first part

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

@ -55,7 +55,6 @@ ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
# The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
MAR_CHANNEL_ID=firefox-mozilla-central
MOZ_PROFILE_MIGRATOR=1
MOZ_WEBGL_CONFORMANT=1
MOZ_JSDOWNLOADS=1
MOZ_RUST_MP4PARSE=1

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

@ -1,99 +0,0 @@
This system add-on is a follow-up to the MITM prevalence experiment. The purpose
is to facilitate rolling out the disabling of SHA-1 in signatures on
certificates issued by publicly-trusted roots. When installed, this add-on will
perform a number of checks to determine if it should change the preference that
controls the SHA-1 policy. First, this should only apply to users on the beta
update channel. It should also only apply to users who have not otherwise
changed the policy to always allow or always forbid SHA-1. Additionally, it
must double-check that the user is not affected by a TLS intercepting proxy
using a publicly-trusted root. If these checks pass, the add-on will divide the
population into a test group and a control group (starting on a 10%/90% split).
The test group will have the policy changed. After doing this, a telemetry
payload is reported with the following values:
* cohortName -- the name of the group this user is in:
1. "notSafeToDisableSHA1" if the user is behind a MITM proxy using a
publicly-trusted root
2. "optedOut" if the user already set the SHA-1 policy to always allow or
always forbid
3. "optedIn" if the user already set the SHA-1 policy to only allow for
non-built-in roots
4. "test" if the user is in the test cohort (and SHA-1 will be disabled)
5. "control" if the user is not in the test cohort
* errorCode -- 0 for successful connections, some PR error code otherwise
* error -- a short description of one of four error conditions encountered, if
applicable, and an empty string otherwise:
1. "timeout" if the connection to telemetry.mozilla.org timed out
2. "user override" if the user has stored a permanent certificate exception
override for telemetry.mozilla.org (due to technical limitations, we can't
gather much information in this situation)
3. "certificate reverification" if re-building the certificate chain after
connecting failed for some reason (unfortunately this step is necessary
due to technical limitations)
4. "connection error" if the connection to telemetry.mozilla.org failed for
another reason
* chain -- a list of dictionaries each corresponding to a certificate in the
verified certificate chain, if it was successfully constructed. The first
entry is the end-entity certificate. The last entry is the root certificate.
This will be empty if the connection failed or if reverification failed. Each
element in the list contains the following values:
* sha256Fingerprint -- a hex string representing the SHA-256 hash of the
certificate
* isBuiltInRoot -- true if the certificate is a trust anchor in the web PKI,
false otherwise
* signatureAlgorithm -- a description of the algorithm used to sign the
certificate. Will be one of "md2WithRSAEncryption", "md5WithRSAEncryption",
"sha1WithRSAEncryption", "sha256WithRSAEncryption",
"sha384WithRSAEncryption", "sha512WithRSAEncryption", "ecdsaWithSHA1",
"ecdsaWithSHA224", "ecdsaWithSHA256", "ecdsaWithSHA384", "ecdsaWithSHA512",
or "unknown".
* disabledSHA1 -- true if SHA-1 was disabled, false otherwise
* didNotDisableSHA1Because -- a short string describing why SHA-1 could not be
disabled, if applicable. Reasons are limited to:
1. "MITM" if the user is behind a TLS intercepting proxy using a
publicly-trusted root
2. "connection error" if there was an error connecting to
telemetry.mozilla.org
3. "code error" if some inconsistent state was detected, and it was
determined that the experiment should not attempt to change the
preference
4. "preference:userReset" if the user reset the SHA-1 policy after it had
been changed by this add-on
5. "preference:allow" if the user had already configured Firefox to always
accept SHA-1 signatures
6. "preference:forbid" if the user had already configured Firefox to always
forbid SHA-1 signatures
For a connection not intercepted by a TLS proxy and where the user is in the
test cohort, the expected result will be:
{ "cohortName": "test",
"errorCode": 0,
"error": "",
"chain": [
{ "sha256Fingerprint": "197feaf3faa0f0ad637a89c97cb91336bfc114b6b3018203cbd9c3d10c7fa86c",
"isBuiltInRoot": false,
"signatureAlgorithm": "sha256WithRSAEncryption"
},
{ "sha256Fingerprint": "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
"isBuiltInRoot": false,
"signatureAlgorithm": "sha256WithRSAEncryption"
},
{ "sha256Fingerprint": "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161",
"isBuiltInRoot": true,
"signatureAlgorithm": "sha1WithRSAEncryption"
}
],
"disabledSHA1": true,
"didNotDisableSHA1Because": ""
}
When this result is encountered, the user's preferences are updated to disable
SHA-1 in signatures on certificates issued by publicly-trusted roots.
Similarly, if the user is behind a TLS intercepting proxy but the root
certificate is not part of the public web PKI, we can also disable SHA-1 in
signatures on certificates issued by publicly-trusted roots.
If the user has already indicated in their preferences that they will always
accept SHA-1 in signatures or that they will never accept SHA-1 in signatures,
then the preference is not changed.

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

@ -1,306 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/UpdateUtils.jsm");
Cu.import("resource://gre/modules/TelemetryController.jsm");
// Percentage of the population to attempt to disable SHA-1 for, by channel.
const TEST_THRESHOLD = {
beta: 0.1, // 10%
};
const PREF_COHORT_SAMPLE = "disableSHA1.rollout.cohortSample";
const PREF_COHORT_NAME = "disableSHA1.rollout.cohort";
const PREF_SHA1_POLICY = "security.pki.sha1_enforcement_level";
const PREF_SHA1_POLICY_SET_BY_ADDON = "disableSHA1.rollout.policySetByAddOn";
const PREF_SHA1_POLICY_RESET_BY_USER = "disableSHA1.rollout.userResetPref";
const SHA1_MODE_ALLOW = 0;
const SHA1_MODE_FORBID = 1;
const SHA1_MODE_IMPORTED_ROOTS_ONLY = 3;
const SHA1_MODE_CURRENT_DEFAULT = 4;
function startup() {
Preferences.observe(PREF_SHA1_POLICY, policyPreferenceChanged);
}
function install() {
let updateChannel = UpdateUtils.getUpdateChannel(false);
if (updateChannel in TEST_THRESHOLD) {
makeRequest().then(defineCohort).catch((e) => console.error(e));
}
}
function policyPreferenceChanged() {
let currentPrefValue = Preferences.get(PREF_SHA1_POLICY,
SHA1_MODE_CURRENT_DEFAULT);
Preferences.reset(PREF_SHA1_POLICY_RESET_BY_USER);
if (currentPrefValue == SHA1_MODE_CURRENT_DEFAULT) {
Preferences.set(PREF_SHA1_POLICY_RESET_BY_USER, true);
}
}
function defineCohort(result) {
let userOptedOut = optedOut();
let userOptedIn = optedIn();
let shouldNotDisableSHA1Because = reasonToNotDisableSHA1(result);
let safeToDisableSHA1 = shouldNotDisableSHA1Because.length == 0;
let updateChannel = UpdateUtils.getUpdateChannel(false);
let testGroup = getUserSample() < TEST_THRESHOLD[updateChannel];
let cohortName;
if (!safeToDisableSHA1) {
cohortName = "notSafeToDisableSHA1";
} else if (userOptedOut) {
cohortName = "optedOut";
} else if (userOptedIn) {
cohortName = "optedIn";
} else if (testGroup) {
cohortName = "test";
Preferences.ignore(PREF_SHA1_POLICY, policyPreferenceChanged);
Preferences.set(PREF_SHA1_POLICY, SHA1_MODE_IMPORTED_ROOTS_ONLY);
Preferences.observe(PREF_SHA1_POLICY, policyPreferenceChanged);
Preferences.set(PREF_SHA1_POLICY_SET_BY_ADDON, true);
} else {
cohortName = "control";
}
Preferences.set(PREF_COHORT_NAME, cohortName);
reportTelemetry(result, cohortName, shouldNotDisableSHA1Because,
cohortName == "test");
}
function shutdown(data, reason) {
Preferences.ignore(PREF_SHA1_POLICY, policyPreferenceChanged);
}
function uninstall() {
}
function getUserSample() {
let prefValue = Preferences.get(PREF_COHORT_SAMPLE, undefined);
let value = 0.0;
if (typeof(prefValue) == "string") {
value = parseFloat(prefValue, 10);
return value;
}
value = Math.random();
Preferences.set(PREF_COHORT_SAMPLE, value.toString().substr(0, 8));
return value;
}
function reportTelemetry(result, cohortName, didNotDisableSHA1Because,
disabledSHA1) {
result.cohortName = cohortName;
result.disabledSHA1 = disabledSHA1;
if (cohortName == "optedOut") {
let userResetPref = Preferences.get(PREF_SHA1_POLICY_RESET_BY_USER, false);
let currentPrefValue = Preferences.get(PREF_SHA1_POLICY,
SHA1_MODE_CURRENT_DEFAULT);
if (userResetPref) {
didNotDisableSHA1Because = "preference:userReset";
} else if (currentPrefValue == SHA1_MODE_ALLOW) {
didNotDisableSHA1Because = "preference:allow";
} else {
didNotDisableSHA1Because = "preference:forbid";
}
}
result.didNotDisableSHA1Because = didNotDisableSHA1Because;
return TelemetryController.submitExternalPing("disableSHA1rollout", result,
{});
}
function optedIn() {
let policySetByAddOn = Preferences.get(PREF_SHA1_POLICY_SET_BY_ADDON, false);
let currentPrefValue = Preferences.get(PREF_SHA1_POLICY,
SHA1_MODE_CURRENT_DEFAULT);
return currentPrefValue == SHA1_MODE_IMPORTED_ROOTS_ONLY && !policySetByAddOn;
}
function optedOut() {
// Users can also opt-out by setting the policy to always allow or always
// forbid SHA-1, or by resetting the preference after this add-on has changed
// it (in that case, this will be reported the next time this add-on is
// updated).
let currentPrefValue = Preferences.get(PREF_SHA1_POLICY,
SHA1_MODE_CURRENT_DEFAULT);
let userResetPref = Preferences.get(PREF_SHA1_POLICY_RESET_BY_USER, false);
return currentPrefValue == SHA1_MODE_ALLOW ||
currentPrefValue == SHA1_MODE_FORBID ||
userResetPref;
}
function delocalizeAlgorithm(localizedString) {
let bundle = Services.strings.createBundle(
"chrome://pipnss/locale/pipnss.properties");
let algorithmStringIdsToOIDDescriptionMap = {
"CertDumpMD2WithRSA": "md2WithRSAEncryption",
"CertDumpMD5WithRSA": "md5WithRSAEncryption",
"CertDumpSHA1WithRSA": "sha1WithRSAEncryption",
"CertDumpSHA256WithRSA": "sha256WithRSAEncryption",
"CertDumpSHA384WithRSA": "sha384WithRSAEncryption",
"CertDumpSHA512WithRSA": "sha512WithRSAEncryption",
"CertDumpAnsiX962ECDsaSignatureWithSha1": "ecdsaWithSHA1",
"CertDumpAnsiX962ECDsaSignatureWithSha224": "ecdsaWithSHA224",
"CertDumpAnsiX962ECDsaSignatureWithSha256": "ecdsaWithSHA256",
"CertDumpAnsiX962ECDsaSignatureWithSha384": "ecdsaWithSHA384",
"CertDumpAnsiX962ECDsaSignatureWithSha512": "ecdsaWithSHA512",
};
let description;
Object.keys(algorithmStringIdsToOIDDescriptionMap).forEach((l10nID) => {
let candidateLocalizedString = bundle.GetStringFromName(l10nID);
if (localizedString == candidateLocalizedString) {
description = algorithmStringIdsToOIDDescriptionMap[l10nID];
}
});
if (!description) {
return "unknown";
}
return description;
}
function getSignatureAlgorithm(cert) {
// Certificate ::= SEQUENCE {
// tbsCertificate TBSCertificate,
// signatureAlgorithm AlgorithmIdentifier,
// signatureValue BIT STRING }
let certificate = cert.ASN1Structure.QueryInterface(Ci.nsIASN1Sequence);
let signatureAlgorithm = certificate.ASN1Objects
.queryElementAt(1, Ci.nsIASN1Sequence);
// AlgorithmIdentifier ::= SEQUENCE {
// algorithm OBJECT IDENTIFIER,
// parameters ANY DEFINED BY algorithm OPTIONAL }
// If parameters is NULL (or empty), signatureAlgorithm won't be a container
// under this implementation. Just get its displayValue.
if (!signatureAlgorithm.isValidContainer) {
return signatureAlgorithm.displayValue;
}
let oid = signatureAlgorithm.ASN1Objects.queryElementAt(0, Ci.nsIASN1Object);
return oid.displayValue;
}
function processCertChain(chain) {
let output = [];
let enumerator = chain.getEnumerator();
while (enumerator.hasMoreElements()) {
let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
output.push({
sha256Fingerprint: cert.sha256Fingerprint.replace(/:/g, "").toLowerCase(),
isBuiltInRoot: cert.isBuiltInRoot,
signatureAlgorithm: delocalizeAlgorithm(getSignatureAlgorithm(cert)),
});
}
return output;
}
class CertificateVerificationResult {
constructor(resolve) {
this.resolve = resolve;
}
verifyCertFinished(aPRErrorCode, aVerifiedChain, aEVStatus) {
let result = { errorCode: aPRErrorCode, error: "", chain: [] };
if (aPRErrorCode == 0) {
result.chain = processCertChain(aVerifiedChain);
} else {
result.error = "certificate reverification";
}
this.resolve(result);
}
}
function makeRequest() {
return new Promise((resolve) => {
let hostname = "telemetry.mozilla.org";
let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
req.open("GET", "https://" + hostname);
req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
req.timeout = 30000;
req.addEventListener("error", (evt) => {
// If we can't connect to telemetry.mozilla.org, then how did we even
// download the experiment? In any case, we may still be able to get some
// information.
let result = { error: "connection error" };
if (evt.target.channel && evt.target.channel.securityInfo) {
let securityInfo = evt.target.channel.securityInfo
.QueryInterface(Ci.nsITransportSecurityInfo);
if (securityInfo) {
result.errorCode = securityInfo.errorCode;
}
if (securityInfo && securityInfo.failedCertChain) {
result.chain = processCertChain(securityInfo.failedCertChain);
}
}
resolve(result);
});
req.addEventListener("timeout", (evt) => {
resolve({ error: "timeout" });
});
req.addEventListener("load", (evt) => {
let securityInfo = evt.target.channel.securityInfo
.QueryInterface(Ci.nsITransportSecurityInfo);
if (securityInfo.securityState &
Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN) {
resolve({ error: "user override" });
return;
}
let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
.SSLStatus;
let certdb = Cc["@mozilla.org/security/x509certdb;1"]
.getService(Ci.nsIX509CertDB);
let result = new CertificateVerificationResult(resolve);
// Unfortunately, we don't have direct access to the verified certificate
// chain as built by the AuthCertificate hook, so we have to re-build it
// here. In theory we are likely to get the same result.
certdb.asyncVerifyCertAtTime(sslStatus.serverCert,
2, // certificateUsageSSLServer
0, // flags
hostname,
Date.now() / 1000,
result);
});
req.send();
});
}
// As best we know, it is safe to disable SHA1 if the connection was successful
// and either the connection was MITM'd by a root not in the public PKI or the
// chain is part of the public PKI and is the one served by the real
// telemetry.mozilla.org.
// This will return a short string description of why it might not be safe to
// disable SHA1 or an empty string if it is safe to disable SHA1.
function reasonToNotDisableSHA1(result) {
if (!("errorCode" in result) || result.errorCode != 0) {
return "connection error";
}
if (!("chain" in result)) {
return "code error";
}
if (!result.chain[result.chain.length - 1].isBuiltInRoot) {
return "";
}
if (result.chain.length != 3) {
return "MITM";
}
const kEndEntityFingerprint = "197feaf3faa0f0ad637a89c97cb91336bfc114b6b3018203cbd9c3d10c7fa86c";
const kIntermediateFingerprint = "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f";
const kRootFingerprint = "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161";
if (result.chain[0].sha256Fingerprint != kEndEntityFingerprint ||
result.chain[1].sha256Fingerprint != kIntermediateFingerprint ||
result.chain[2].sha256Fingerprint != kRootFingerprint) {
return "MITM";
}
return "";
}

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

@ -1,32 +0,0 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
#filter substitution
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>disableSHA1rollout@mozilla.org</em:id>
<em:version>1.0</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
<!-- Target Application this theme can install into,
with minimum and maximum supported versions. -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
<em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>SHA-1 deprecation staged rollout</em:name>
<em:description>Staged rollout deprecating SHA-1 in certificate signatures.</em:description>
</Description>
</RDF>

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

@ -271,9 +271,10 @@ var FormAutofillContent = {
ProfileAutocomplete.ensureUnregistered();
}
});
Services.cpmm.sendAsyncMessage("FormAutofill:getEnabledStatus");
// TODO: use initialProcessData:
// Services.cpmm.initialProcessData.autofillEnabled
if (Services.cpmm.initialProcessData.autofillEnabled) {
ProfileAutocomplete.ensureRegistered();
}
},
/**

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

@ -80,11 +80,10 @@ FormAutofillParent.prototype = {
// Observing the pref and storage changes
Services.prefs.addObserver(ENABLED_PREF, this, false);
Services.obs.addObserver(this, "formautofill-storage-changed", false);
this._enabled = this._getStatus();
// Force to trigger the onStatusChanged function for setting listeners properly
// while initizlization
this._onStatusChanged();
Services.ppmm.addMessageListener("FormAutofill:getEnabledStatus", this);
this._setStatus(this._getStatus());
},
observe(subject, topic, data) {
@ -104,8 +103,7 @@ FormAutofillParent.prototype = {
// Observe pref changes and update _enabled cache if status is changed.
let currentStatus = this._getStatus();
if (currentStatus !== this._enabled) {
this._enabled = currentStatus;
this._onStatusChanged();
this._setStatus(currentStatus);
}
break;
}
@ -118,8 +116,7 @@ FormAutofillParent.prototype = {
let currentStatus = this._getStatus();
if (currentStatus !== this._enabled) {
this._enabled = currentStatus;
this._onStatusChanged();
this._setStatus(currentStatus);
}
break;
}
@ -143,6 +140,9 @@ FormAutofillParent.prototype = {
}
Services.ppmm.broadcastAsyncMessage("FormAutofill:enabledStatus", this._enabled);
// Sync process data autofillEnabled to make sure the value up to date
// no matter when the new content process is initialized.
Services.ppmm.initialProcessData.autofillEnabled = this._enabled;
},
/**
@ -159,6 +159,16 @@ FormAutofillParent.prototype = {
return this._profileStore.getAll().length > 0;
},
/**
* Set status and trigger _onStatusChanged.
*
* @param {boolean} newStatus The latest status we want to set for _enabled
*/
_setStatus(newStatus) {
this._enabled = newStatus;
this._onStatusChanged();
},
/**
* Handles the message coming from FormAutofillContent.
*
@ -171,10 +181,6 @@ FormAutofillParent.prototype = {
case "FormAutofill:GetProfiles":
this._getProfiles(data, target);
break;
case "FormAutofill:getEnabledStatus":
Services.ppmm.broadcastAsyncMessage("FormAutofill:enabledStatus",
this._enabled);
break;
}
},

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

@ -69,11 +69,11 @@ ProfileAutoCompleteResult.prototype = {
* @returns {number} The number of results
*/
get matchCount() {
return this._matchingProfiles.length;
return this._popupLabels.length;
},
_checkIndexBounds(index) {
if (index < 0 || index >= this._matchingProfiles.length) {
if (index < 0 || index >= this._popupLabels.length) {
throw Components.Exception("Index out of range.", Cr.NS_ERROR_ILLEGAL_VALUE);
}
},
@ -123,7 +123,10 @@ ProfileAutoCompleteResult.prototype = {
},
_generateLabels(focusedFieldName, allFieldNames, profiles) {
return profiles.map(profile => {
// Skip results without a primary label.
return profiles.filter(profile => {
return !!profile[focusedFieldName];
}).map(profile => {
return {
primary: profile[focusedFieldName],
secondary: this._getSecondaryLabel(focusedFieldName,

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

@ -8,13 +8,15 @@ Cu.import("resource://formautofill/FormAutofillParent.jsm");
add_task(function* test_enabledStatus_init() {
let formAutofillParent = new FormAutofillParent();
sinon.spy(formAutofillParent, "_onStatusChanged");
sinon.spy(formAutofillParent, "_setStatus");
// Default status is false before initialization
do_check_eq(formAutofillParent._enabled, false);
do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, undefined);
formAutofillParent.init();
do_check_eq(formAutofillParent._onStatusChanged.called, true);
do_check_eq(formAutofillParent._setStatus.called, true);
do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, false);
formAutofillParent._uninit();
});
@ -22,36 +24,36 @@ add_task(function* test_enabledStatus_init() {
add_task(function* test_enabledStatus_observe() {
let formAutofillParent = new FormAutofillParent();
sinon.stub(formAutofillParent, "_getStatus");
sinon.spy(formAutofillParent, "_onStatusChanged");
sinon.spy(formAutofillParent, "_setStatus");
// _enabled = _getStatus() => No need to trigger onStatusChanged
formAutofillParent._enabled = true;
formAutofillParent._getStatus.returns(true);
formAutofillParent.observe(null, "nsPref:changed", "browser.formautofill.enabled");
do_check_eq(formAutofillParent._onStatusChanged.called, false);
do_check_eq(formAutofillParent._setStatus.called, false);
// _enabled != _getStatus() => Need to trigger onStatusChanged
formAutofillParent._getStatus.returns(false);
formAutofillParent.observe(null, "nsPref:changed", "browser.formautofill.enabled");
do_check_eq(formAutofillParent._onStatusChanged.called, true);
do_check_eq(formAutofillParent._setStatus.called, true);
// profile added => Need to trigger onStatusChanged
formAutofillParent._getStatus.returns(!formAutofillParent._enabled);
formAutofillParent._onStatusChanged.reset();
formAutofillParent._setStatus.reset();
formAutofillParent.observe(null, "formautofill-storage-changed", "add");
do_check_eq(formAutofillParent._onStatusChanged.called, true);
do_check_eq(formAutofillParent._setStatus.called, true);
// profile removed => Need to trigger onStatusChanged
formAutofillParent._getStatus.returns(!formAutofillParent._enabled);
formAutofillParent._onStatusChanged.reset();
formAutofillParent._setStatus.reset();
formAutofillParent.observe(null, "formautofill-storage-changed", "remove");
do_check_eq(formAutofillParent._onStatusChanged.called, true);
do_check_eq(formAutofillParent._setStatus.called, true);
// profile updated => no need to trigger onStatusChanged
formAutofillParent._getStatus.returns(!formAutofillParent._enabled);
formAutofillParent._onStatusChanged.reset();
formAutofillParent._setStatus.reset();
formAutofillParent.observe(null, "formautofill-storage-changed", "update");
do_check_eq(formAutofillParent._onStatusChanged.called, false);
do_check_eq(formAutofillParent._setStatus.called, false);
});
add_task(function* test_enabledStatus_getStatus() {
@ -82,3 +84,19 @@ add_task(function* test_enabledStatus_getStatus() {
Services.prefs.setBoolPref("browser.formautofill.enabled", false);
do_check_eq(formAutofillParent._getStatus(), false);
});
add_task(function* test_enabledStatus_setStatus() {
let formAutofillParent = new FormAutofillParent();
sinon.spy(formAutofillParent, "_onStatusChanged");
formAutofillParent._setStatus(true);
do_check_eq(formAutofillParent._enabled, true);
do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, true);
do_check_eq(formAutofillParent._onStatusChanged.called, true);
formAutofillParent._onStatusChanged.reset();
formAutofillParent._setStatus(false);
do_check_eq(formAutofillParent._enabled, false);
do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, false);
do_check_eq(formAutofillParent._onStatusChanged.called, true);
});

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

@ -12,11 +12,17 @@ let matchingProfiles = [{
organization: "Mozilla",
"street-address": "331 E. Evelyn Avenue",
tel: "1-650-903-0800",
}, {
guid: "test-guid-3",
organization: "",
"street-address": "321, No Name St.",
tel: "1-000-000-0000",
}];
let allFieldNames = ["street-address", "organization", "tel"];
let testCases = [{
description: "Focus on an `organization` field",
options: {},
matchingProfiles,
allFieldNames,
@ -46,6 +52,7 @@ let testCases = [{
}],
},
}, {
description: "Focus on an `tel` field",
options: {},
matchingProfiles,
allFieldNames,
@ -72,9 +79,19 @@ let testCases = [{
secondary: "331 E. Evelyn Avenue",
}),
image: "",
}, {
value: "1-000-000-0000",
style: "autofill-profile",
comment: JSON.stringify(matchingProfiles[2]),
label: JSON.stringify({
primary: "1-000-000-0000",
secondary: "321, No Name St.",
}),
image: "",
}],
},
}, {
description: "Focus on an `street-address` field",
options: {},
matchingProfiles,
allFieldNames,
@ -101,9 +118,19 @@ let testCases = [{
secondary: "Mozilla",
}),
image: "",
}, {
value: "321, No Name St.",
style: "autofill-profile",
comment: JSON.stringify(matchingProfiles[2]),
label: JSON.stringify({
primary: "321, No Name St.",
secondary: "1-000-000-0000",
}),
image: "",
}],
},
}, {
description: "No matching profiles",
options: {},
matchingProfiles: [],
allFieldNames,
@ -115,6 +142,7 @@ let testCases = [{
items: [],
},
}, {
description: "Search with failure",
options: {resultCode: Ci.nsIAutoCompleteResult.RESULT_FAILURE},
matchingProfiles: [],
allFieldNames,
@ -128,13 +156,14 @@ let testCases = [{
}];
add_task(function* test_all_patterns() {
testCases.forEach(pattern => {
let actual = new ProfileAutoCompleteResult(pattern.searchString,
pattern.fieldName,
pattern.allFieldNames,
pattern.matchingProfiles,
pattern.options);
let expectedValue = pattern.expected;
testCases.forEach(testCase => {
do_print("Starting testcase: " + testCase.description);
let actual = new ProfileAutoCompleteResult(testCase.searchString,
testCase.fieldName,
testCase.allFieldNames,
testCase.matchingProfiles,
testCase.options);
let expectedValue = testCase.expected;
equal(actual.searchResult, expectedValue.searchResult);
equal(actual.defaultIndex, expectedValue.defaultIndex);
equal(actual.matchCount, expectedValue.items.length);

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

@ -6,7 +6,6 @@
DIRS += [
'aushelper',
'disableSHA1rollout',
'e10srollout',
'pdfjs',
'pocket',

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

@ -636,6 +636,7 @@
@RESPATH@/greprefs.js
@RESPATH@/defaults/autoconfig/prefcalls.js
@RESPATH@/browser/defaults/permissions
@RESPATH@/browser/defaults/blocklists
; Warning: changing the path to channel-prefs.js can cause bugs (Bug 756325)
; Technically this is an app pref file, but we are keeping it in the original

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

@ -358,6 +358,14 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!-- LOCALIZATION NOTE (appMenuRemoteTabs.notabs.label): This is shown beneath
the name of a device when that device has no open tabs -->
<!ENTITY appMenuRemoteTabs.notabs.label "No open tabs">
<!-- LOCALIZATION NOTE (appMenuRemoteTabs.showMore.label, appMenuRemoteTabs.showMore.tooltip):
This is shown after the tabs list if we can display more tabs by clicking on the button -->
<!ENTITY appMenuRemoteTabs.showMore.label "Show More">
<!ENTITY appMenuRemoteTabs.showMore.tooltip "Show more tabs from this device">
<!-- LOCALIZATION NOTE (appMenuRemoteTabs.showAll.label, appMenuRemoteTabs.showAll.tooltip):
This is shown after the tabs list if we can all the remaining tabs by clicking on the button -->
<!ENTITY appMenuRemoteTabs.showAll.label "Show All">
<!ENTITY appMenuRemoteTabs.showAll.tooltip "Show all tabs from this device">
<!-- LOCALIZATION NOTE (appMenuRemoteTabs.tabsnotsyncing.label): This is shown
when Sync is configured but syncing tabs is disabled. -->
<!ENTITY appMenuRemoteTabs.tabsnotsyncing.label "Turn on tab syncing to view a list of tabs from your other devices.">

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

@ -207,12 +207,16 @@ spaceAlert.over5GB.prefButton.accesskey=O
# LOCALIZATION NOTE (spaceAlert.over5GB.prefButtonWin.label): On Windows Preferences is called Options
spaceAlert.over5GB.prefButtonWin.label=Open Options
spaceAlert.over5GB.prefButtonWin.accesskey=O
spaceAlert.over5GB.description=Firefox is running out of disk space. Website contents may not display properly. You can clear stored site data in Preferences > Advanced > Site Data.
# LOCALIZATION NOTE (spaceAlert.over5GB.descriptionWin): On Windows Preferences is called Options
spaceAlert.over5GB.descriptionWin=Firefox is running out of disk space. Website contents may not display properly. You can clear stored site data in Options > Advanced > Site Data.
# LOCALIZATION NOTE (spaceAlert.over5GB.message): %S = brandShortName
spaceAlert.over5GB.message=%S is running out of disk space. Website contents may not display properly. You can clear stored site data in Preferences > Advanced > Site Data.
# LOCALIZATION NOTE (spaceAlert.over5GB.messageWin):
# - On Windows Preferences is called Options
# - %S = brandShortName
spaceAlert.over5GB.messageWin=%S is running out of disk space. Website contents may not display properly. You can clear stored site data in Options > Advanced > Site Data.
spaceAlert.under5GB.okButton.label=OK, Got it
spaceAlert.under5GB.okButton.accesskey=K
spaceAlert.under5GB.description=Firefox is running out of disk space. Website contents may not display properly. Visit “Learn More” to optimize your disk usage for better browsing experience.
# LOCALIZATION NOTE (spaceAlert.under5GB.message): %S = brandShortName
spaceAlert.under5GB.message=%S is running out of disk space. Website contents may not display properly. Visit “Learn More” to optimize your disk usage for better browsing experience.
# LOCALIZATION NOTE (featureEnableRequiresRestart, featureDisableRequiresRestart, restartTitle): %S = brandShortName
featureEnableRequiresRestart=%S must restart to enable this feature.

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

@ -1243,19 +1243,19 @@ menuitem.panel-subview-footer@menuStateActive@,
color: GrayText;
}
#PanelUI-remotetabs-tabslist > toolbarbutton,
#PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"],
#PanelUI-historyItems > toolbarbutton {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
@media (min-resolution: 1.1dppx) {
#PanelUI-remotetabs-tabslist > toolbarbutton,
#PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"],
#PanelUI-historyItems > toolbarbutton {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
}
}
#PanelUI-remotetabs-tabslist > toolbarbutton > .toolbarbutton-icon,
#PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"] > .toolbarbutton-icon,
#PanelUI-recentlyClosedWindows > toolbarbutton > .toolbarbutton-icon,
#PanelUI-recentlyClosedTabs > toolbarbutton > .toolbarbutton-icon,
#PanelUI-historyItems > toolbarbutton > .toolbarbutton-icon {

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

@ -78,9 +78,6 @@ check_and_add_gcc_warning('-Werror=non-literal-null-conversion',
# catches string literals used in boolean expressions
check_and_add_gcc_warning('-Wstring-conversion')
# catches inconsistent use of mutexes
check_and_add_gcc_warning('-Wthread-safety')
# we inline 'new' and 'delete' in mozalloc
check_and_add_gcc_warning('-Wno-inline-new-delete', cxx_compiler)

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

@ -41,6 +41,7 @@ SOURCES += [
UNIFIED_SOURCES += [
'DomainPolicy.cpp',
'nsExpandedPrincipal.cpp',
'nsJSPrincipals.cpp',
'nsNullPrincipal.cpp',
'nsNullPrincipalURI.cpp',

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

@ -0,0 +1,208 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et tw=80: */
/* 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/. */
#include "nsExpandedPrincipal.h"
#include "nsIClassInfoImpl.h"
using namespace mozilla;
NS_IMPL_CLASSINFO(nsExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
NS_EXPANDEDPRINCIPAL_CID)
NS_IMPL_QUERY_INTERFACE_CI(nsExpandedPrincipal,
nsIPrincipal,
nsIExpandedPrincipal)
NS_IMPL_CI_INTERFACE_GETTER(nsExpandedPrincipal,
nsIPrincipal,
nsIExpandedPrincipal)
struct OriginComparator
{
bool LessThan(nsIPrincipal* a, nsIPrincipal* b) const
{
nsAutoCString originA;
nsresult rv = a->GetOrigin(originA);
NS_ENSURE_SUCCESS(rv, false);
nsAutoCString originB;
rv = b->GetOrigin(originB);
NS_ENSURE_SUCCESS(rv, false);
return originA < originB;
}
bool Equals(nsIPrincipal* a, nsIPrincipal* b) const
{
nsAutoCString originA;
nsresult rv = a->GetOrigin(originA);
NS_ENSURE_SUCCESS(rv, false);
nsAutoCString originB;
rv = b->GetOrigin(originB);
NS_ENSURE_SUCCESS(rv, false);
return a == b;
}
};
nsExpandedPrincipal::nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList,
const OriginAttributes& aAttrs)
{
// We force the principals to be sorted by origin so that nsExpandedPrincipal
// origins can have a canonical form.
OriginComparator c;
for (size_t i = 0; i < aWhiteList.Length(); ++i) {
mPrincipals.InsertElementSorted(aWhiteList[i], c);
}
mOriginAttributes = aAttrs;
}
nsExpandedPrincipal::~nsExpandedPrincipal()
{ }
NS_IMETHODIMP
nsExpandedPrincipal::GetDomain(nsIURI** aDomain)
{
*aDomain = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsExpandedPrincipal::SetDomain(nsIURI* aDomain)
{
return NS_OK;
}
nsresult
nsExpandedPrincipal::GetOriginInternal(nsACString& aOrigin)
{
aOrigin.AssignLiteral("[Expanded Principal [");
for (size_t i = 0; i < mPrincipals.Length(); ++i) {
if (i != 0) {
aOrigin.AppendLiteral(", ");
}
nsAutoCString subOrigin;
nsresult rv = mPrincipals.ElementAt(i)->GetOrigin(subOrigin);
NS_ENSURE_SUCCESS(rv, rv);
aOrigin.Append(subOrigin);
}
aOrigin.Append("]]");
return NS_OK;
}
bool
nsExpandedPrincipal::SubsumesInternal(nsIPrincipal* aOther,
BasePrincipal::DocumentDomainConsideration aConsideration)
{
// If aOther is an ExpandedPrincipal too, we break it down into its component
// nsIPrincipals, and check subsumes on each one.
nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aOther);
if (expanded) {
nsTArray< nsCOMPtr<nsIPrincipal> >* otherList;
expanded->GetWhiteList(&otherList);
for (uint32_t i = 0; i < otherList->Length(); ++i){
// Use SubsumesInternal rather than Subsumes here, since OriginAttribute
// checks are only done between non-expanded sub-principals, and we don't
// need to incur the extra virtual call overhead.
if (!SubsumesInternal((*otherList)[i], aConsideration)) {
return false;
}
}
return true;
}
// We're dealing with a regular principal. One of our principals must subsume
// it.
for (uint32_t i = 0; i < mPrincipals.Length(); ++i) {
if (Cast(mPrincipals[i])->Subsumes(aOther, aConsideration)) {
return true;
}
}
return false;
}
bool
nsExpandedPrincipal::MayLoadInternal(nsIURI* uri)
{
for (uint32_t i = 0; i < mPrincipals.Length(); ++i){
if (BasePrincipal::Cast(mPrincipals[i])->MayLoadInternal(uri)) {
return true;
}
}
return false;
}
NS_IMETHODIMP
nsExpandedPrincipal::GetHashValue(uint32_t* result)
{
MOZ_CRASH("extended principal should never be used as key in a hash map");
}
NS_IMETHODIMP
nsExpandedPrincipal::GetURI(nsIURI** aURI)
{
*aURI = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsExpandedPrincipal::GetWhiteList(nsTArray<nsCOMPtr<nsIPrincipal> >** aWhiteList)
{
*aWhiteList = &mPrincipals;
return NS_OK;
}
NS_IMETHODIMP
nsExpandedPrincipal::GetBaseDomain(nsACString& aBaseDomain)
{
return NS_ERROR_NOT_AVAILABLE;
}
bool
nsExpandedPrincipal::AddonHasPermission(const nsAString& aPerm)
{
for (size_t i = 0; i < mPrincipals.Length(); ++i) {
if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) {
return true;
}
}
return false;
}
nsresult
nsExpandedPrincipal::GetScriptLocation(nsACString& aStr)
{
aStr.Assign("[Expanded Principal [");
for (size_t i = 0; i < mPrincipals.Length(); ++i) {
if (i != 0) {
aStr.AppendLiteral(", ");
}
nsAutoCString spec;
nsresult rv =
nsJSPrincipals::get(mPrincipals.ElementAt(i))->GetScriptLocation(spec);
NS_ENSURE_SUCCESS(rv, rv);
aStr.Append(spec);
}
aStr.Append("]]");
return NS_OK;
}
//////////////////////////////////////////
// Methods implementing nsISerializable //
//////////////////////////////////////////
NS_IMETHODIMP
nsExpandedPrincipal::Read(nsIObjectInputStream* aStream)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsExpandedPrincipal::Write(nsIObjectOutputStream* aStream)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

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

@ -0,0 +1,53 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsExpandedPrincipal_h
#define nsExpandedPrincipal_h
#include "nsCOMPtr.h"
#include "nsJSPrincipals.h"
#include "nsTArray.h"
#include "nsNetUtil.h"
#include "mozilla/BasePrincipal.h"
class nsExpandedPrincipal : public nsIExpandedPrincipal
, public mozilla::BasePrincipal
{
public:
nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList,
const mozilla::OriginAttributes& aAttrs);
NS_DECL_NSIEXPANDEDPRINCIPAL
NS_DECL_NSISERIALIZABLE
NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return nsJSPrincipals::AddRef(); };
NS_IMETHOD_(MozExternalRefCountType) Release() override { return nsJSPrincipals::Release(); };
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
NS_IMETHOD GetURI(nsIURI** aURI) override;
NS_IMETHOD GetDomain(nsIURI** aDomain) override;
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
virtual bool AddonHasPermission(const nsAString& aPerm) override;
virtual nsresult GetScriptLocation(nsACString &aStr) override;
nsresult GetOriginInternal(nsACString& aOrigin) override;
PrincipalKind Kind() override { return eExpandedPrincipal; }
protected:
virtual ~nsExpandedPrincipal();
bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
bool MayLoadInternal(nsIURI* aURI) override;
private:
nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals;
};
#define NS_EXPANDEDPRINCIPAL_CONTRACTID "@mozilla.org/expandedprincipal;1"
#define NS_EXPANDEDPRINCIPAL_CID \
{ 0xe8ee88b0, 0x5571, 0x4086, \
{ 0xa4, 0x5b, 0x39, 0xa7, 0x16, 0x90, 0x6b, 0xdb } }
#endif // nsExpandedPrincipal_h

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

@ -11,9 +11,29 @@
struct JSPrincipals;
#include "nsCOMPtr.h"
#include "nsTArray.h"
#include "mozilla/DebugOnly.h"
namespace mozilla {
class OriginAttributes;
}
/**
* Some methods have a fast path for the case when we're comparing a principal
* to itself. The situation may happen for example with about:blank documents.
*/
#define DECL_FAST_INLINE_HELPER(method_) \
inline bool method_(nsIPrincipal* aOther) \
{ \
mozilla::DebugOnly<bool> val = false; \
MOZ_ASSERT_IF(this == aOther, \
NS_SUCCEEDED(method_(aOther, &val)) && val); \
\
bool retVal = false; \
return \
this == aOther || \
(NS_SUCCEEDED(method_(aOther, &retVal)) && retVal); \
}
%}
interface nsIURI;
@ -41,15 +61,8 @@ interface nsIPrincipal : nsISerializable
boolean equalsConsideringDomain(in nsIPrincipal other);
%{C++
inline bool Equals(nsIPrincipal* aOther) {
bool equal = false;
return NS_SUCCEEDED(Equals(aOther, &equal)) && equal;
}
inline bool EqualsConsideringDomain(nsIPrincipal* aOther) {
bool equal = false;
return NS_SUCCEEDED(EqualsConsideringDomain(aOther, &equal)) && equal;
}
DECL_FAST_INLINE_HELPER(Equals)
DECL_FAST_INLINE_HELPER(EqualsConsideringDomain)
%}
/**
@ -101,20 +114,10 @@ interface nsIPrincipal : nsISerializable
boolean subsumesConsideringDomainIgnoringFPD(in nsIPrincipal other);
%{C++
inline bool Subsumes(nsIPrincipal* aOther) {
bool subsumes = false;
return NS_SUCCEEDED(Subsumes(aOther, &subsumes)) && subsumes;
}
inline bool SubsumesConsideringDomain(nsIPrincipal* aOther) {
bool subsumes = false;
return NS_SUCCEEDED(SubsumesConsideringDomain(aOther, &subsumes)) && subsumes;
}
inline bool SubsumesConsideringDomainIgnoringFPD(nsIPrincipal* aOther) {
bool subsumes = false;
return NS_SUCCEEDED(SubsumesConsideringDomainIgnoringFPD(aOther, &subsumes)) && subsumes;
}
DECL_FAST_INLINE_HELPER(Subsumes)
DECL_FAST_INLINE_HELPER(SubsumesConsideringDomain)
DECL_FAST_INLINE_HELPER(SubsumesConsideringDomainIgnoringFPD)
#undef DECL_FAST_INLINE_HELPER
%}
/**

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

@ -458,203 +458,3 @@ nsPrincipal::Write(nsIObjectOutputStream* aStream)
return NS_OK;
}
/************************************************************************************************************************/
NS_IMPL_CLASSINFO(nsExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
NS_EXPANDEDPRINCIPAL_CID)
NS_IMPL_QUERY_INTERFACE_CI(nsExpandedPrincipal,
nsIPrincipal,
nsIExpandedPrincipal)
NS_IMPL_CI_INTERFACE_GETTER(nsExpandedPrincipal,
nsIPrincipal,
nsIExpandedPrincipal)
struct OriginComparator
{
bool LessThan(nsIPrincipal* a, nsIPrincipal* b) const
{
nsAutoCString originA;
nsresult rv = a->GetOrigin(originA);
NS_ENSURE_SUCCESS(rv, false);
nsAutoCString originB;
rv = b->GetOrigin(originB);
NS_ENSURE_SUCCESS(rv, false);
return originA < originB;
}
bool Equals(nsIPrincipal* a, nsIPrincipal* b) const
{
nsAutoCString originA;
nsresult rv = a->GetOrigin(originA);
NS_ENSURE_SUCCESS(rv, false);
nsAutoCString originB;
rv = b->GetOrigin(originB);
NS_ENSURE_SUCCESS(rv, false);
return a == b;
}
};
nsExpandedPrincipal::nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList,
const OriginAttributes& aAttrs)
{
// We force the principals to be sorted by origin so that nsExpandedPrincipal
// origins can have a canonical form.
OriginComparator c;
for (size_t i = 0; i < aWhiteList.Length(); ++i) {
mPrincipals.InsertElementSorted(aWhiteList[i], c);
}
mOriginAttributes = aAttrs;
}
nsExpandedPrincipal::~nsExpandedPrincipal()
{ }
NS_IMETHODIMP
nsExpandedPrincipal::GetDomain(nsIURI** aDomain)
{
*aDomain = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsExpandedPrincipal::SetDomain(nsIURI* aDomain)
{
return NS_OK;
}
nsresult
nsExpandedPrincipal::GetOriginInternal(nsACString& aOrigin)
{
aOrigin.AssignLiteral("[Expanded Principal [");
for (size_t i = 0; i < mPrincipals.Length(); ++i) {
if (i != 0) {
aOrigin.AppendLiteral(", ");
}
nsAutoCString subOrigin;
nsresult rv = mPrincipals.ElementAt(i)->GetOrigin(subOrigin);
NS_ENSURE_SUCCESS(rv, rv);
aOrigin.Append(subOrigin);
}
aOrigin.Append("]]");
return NS_OK;
}
bool
nsExpandedPrincipal::SubsumesInternal(nsIPrincipal* aOther,
BasePrincipal::DocumentDomainConsideration aConsideration)
{
// If aOther is an ExpandedPrincipal too, we break it down into its component
// nsIPrincipals, and check subsumes on each one.
nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aOther);
if (expanded) {
nsTArray< nsCOMPtr<nsIPrincipal> >* otherList;
expanded->GetWhiteList(&otherList);
for (uint32_t i = 0; i < otherList->Length(); ++i){
// Use SubsumesInternal rather than Subsumes here, since OriginAttribute
// checks are only done between non-expanded sub-principals, and we don't
// need to incur the extra virtual call overhead.
if (!SubsumesInternal((*otherList)[i], aConsideration)) {
return false;
}
}
return true;
}
// We're dealing with a regular principal. One of our principals must subsume
// it.
for (uint32_t i = 0; i < mPrincipals.Length(); ++i) {
if (Cast(mPrincipals[i])->Subsumes(aOther, aConsideration)) {
return true;
}
}
return false;
}
bool
nsExpandedPrincipal::MayLoadInternal(nsIURI* uri)
{
for (uint32_t i = 0; i < mPrincipals.Length(); ++i){
if (BasePrincipal::Cast(mPrincipals[i])->MayLoadInternal(uri)) {
return true;
}
}
return false;
}
NS_IMETHODIMP
nsExpandedPrincipal::GetHashValue(uint32_t* result)
{
MOZ_CRASH("extended principal should never be used as key in a hash map");
}
NS_IMETHODIMP
nsExpandedPrincipal::GetURI(nsIURI** aURI)
{
*aURI = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsExpandedPrincipal::GetWhiteList(nsTArray<nsCOMPtr<nsIPrincipal> >** aWhiteList)
{
*aWhiteList = &mPrincipals;
return NS_OK;
}
NS_IMETHODIMP
nsExpandedPrincipal::GetBaseDomain(nsACString& aBaseDomain)
{
return NS_ERROR_NOT_AVAILABLE;
}
bool
nsExpandedPrincipal::AddonHasPermission(const nsAString& aPerm)
{
for (size_t i = 0; i < mPrincipals.Length(); ++i) {
if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) {
return true;
}
}
return false;
}
nsresult
nsExpandedPrincipal::GetScriptLocation(nsACString& aStr)
{
aStr.Assign("[Expanded Principal [");
for (size_t i = 0; i < mPrincipals.Length(); ++i) {
if (i != 0) {
aStr.AppendLiteral(", ");
}
nsAutoCString spec;
nsresult rv =
nsJSPrincipals::get(mPrincipals.ElementAt(i))->GetScriptLocation(spec);
NS_ENSURE_SUCCESS(rv, rv);
aStr.Append(spec);
}
aStr.Append("]]");
return NS_OK;
}
//////////////////////////////////////////
// Methods implementing nsISerializable //
//////////////////////////////////////////
NS_IMETHODIMP
nsExpandedPrincipal::Read(nsIObjectInputStream* aStream)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsExpandedPrincipal::Write(nsIObjectOutputStream* aStream)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

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

@ -58,46 +58,9 @@ protected:
bool MayLoadInternal(nsIURI* aURI) override;
};
class nsExpandedPrincipal : public nsIExpandedPrincipal, public mozilla::BasePrincipal
{
public:
nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList,
const mozilla::OriginAttributes& aAttrs);
NS_DECL_NSIEXPANDEDPRINCIPAL
NS_DECL_NSISERIALIZABLE
NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return nsJSPrincipals::AddRef(); };
NS_IMETHOD_(MozExternalRefCountType) Release() override { return nsJSPrincipals::Release(); };
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
NS_IMETHOD GetURI(nsIURI** aURI) override;
NS_IMETHOD GetDomain(nsIURI** aDomain) override;
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
virtual bool AddonHasPermission(const nsAString& aPerm) override;
virtual nsresult GetScriptLocation(nsACString &aStr) override;
nsresult GetOriginInternal(nsACString& aOrigin) override;
PrincipalKind Kind() override { return eExpandedPrincipal; }
protected:
virtual ~nsExpandedPrincipal();
bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
bool MayLoadInternal(nsIURI* aURI) override;
private:
nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals;
};
#define NS_PRINCIPAL_CONTRACTID "@mozilla.org/principal;1"
#define NS_PRINCIPAL_CID \
{ 0x653e0e4d, 0x3ee4, 0x45fa, \
{ 0xb2, 0x72, 0x97, 0xc2, 0x0b, 0xc0, 0x1e, 0xb8 } }
#define NS_EXPANDEDPRINCIPAL_CONTRACTID "@mozilla.org/expandedprincipal;1"
#define NS_EXPANDEDPRINCIPAL_CID \
{ 0xe8ee88b0, 0x5571, 0x4086, \
{ 0xa4, 0x5b, 0x39, 0xa7, 0x16, 0x90, 0x6b, 0xdb } }
#endif // nsPrincipal_h__

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

@ -4,8 +4,6 @@
"use strict";
const { Cu, Ci } = require("chrome");
const { TargetFactory } = require("devtools/client/framework/target");
const { DebuggerServer } = require("devtools/server/main");
const { DebuggerClient } = require("devtools/shared/client/main");
@ -21,20 +19,25 @@ const { Task } = require("devtools/shared/task");
* ws:
* {Boolean} If true, connect via websocket instread of regular TCP connection.
*
* type: tab, process
* {String} The type of target to connect to. Currently tabs and processes are supported types.
* type: tab, process, window
* {String} The type of target to connect to.
*
* If type="tab":
* If type == "tab":
* id:
* {Number} the tab outerWindowID
* chrome: Optional
* {Boolean} Force the creation of a chrome target. Gives more privileges to the tab
* actor. Allows chrome execution in the webconsole and see chrome files in
* the debugger. (handy when contributing to firefox)
* {Boolean} Force the creation of a chrome target. Gives more privileges to
* the tab actor. Allows chrome execution in the webconsole and see chrome
* files in the debugger. (handy when contributing to firefox)
*
* If type="process":
* If type == "process":
* id:
* {Number} the process id to debug. Default to 0, which is the parent process.
* {Number} the process id to debug. Default to 0, which is the parent
* process.
*
* If type == "window":
* id:
* {Number} the window outerWindowID
*
* @param {URL} url
* The url to fetch query params from.
@ -93,6 +96,26 @@ exports.targetFromURL = Task.async(function* (url) {
}
throw ex;
}
} else if (type == "window") {
// Fetch target for a remote window actor
DebuggerServer.allowChromeProcess = true;
try {
id = parseInt(id, 10);
if (isNaN(id)) {
throw new Error("targetFromURL, window requires id parameter");
}
let response = yield client.mainRoot.getWindow({
outerWindowID: id,
});
form = response.window;
chrome = true;
} catch (ex) {
if (ex.error == "notFound") {
throw new Error(`targetFromURL, window with id:'${id}' ` +
"doesn't exist");
}
throw ex;
}
} else {
throw new Error("targetFromURL, unsupported type='" + type + "' parameter");
}

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

@ -36,8 +36,19 @@ add_task(function* () {
is(e.message, "targetFromURL, unsupported type='x' parameter");
}
info("Test browser window");
let windowId = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.outerWindowID;
target = yield targetFromURL(new URL("http://foo?type=window&id=" + windowId));
is(target.url, window.location.href);
is(target.isLocalTab, false);
is(target.chrome, true);
is(target.isTabActor, true);
is(target.isRemote, true);
info("Test tab");
let windowId = browser.outerWindowID;
windowId = browser.outerWindowID;
target = yield targetFromURL(new URL("http://foo?type=tab&id=" + windowId));
assertIsTabTarget(target, TEST_URI);

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

@ -112,6 +112,7 @@ registerCleanupFunction(function* cleanup() {
* - {Boolean} background If true, open the tab in background
* - {ChromeWindow} window Firefox top level window we should use to open the tab
* - {Number} userContextId The userContextId of the tab.
* - {String} preferredRemoteType
* @return a promise that resolves to the tab object when the url is loaded
*/
var addTab = Task.async(function* (url, options = { background: false, window: window }) {
@ -121,7 +122,8 @@ var addTab = Task.async(function* (url, options = { background: false, window: w
let { gBrowser } = options.window ? options.window : window;
let { userContextId } = options;
let tab = gBrowser.addTab(url, {userContextId});
let tab = gBrowser.addTab(url,
{userContextId, preferredRemoteType: options.preferredRemoteType});
if (!background) {
gBrowser.selectedTab = tab;
}

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

@ -2,6 +2,8 @@
* 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/. */
/* eslint-env browser */
"use strict";
// URL constructor doesn't support about: scheme
@ -26,6 +28,23 @@ if (url.search.length > 1) {
.getInterface(Ci.nsIDOMWindowUtils)
.containerElement;
// If there's no containerElement (which happens when loading about:devtools-toolbox as
// a top level document), use the current window.
if (!host) {
host = {
contentWindow: window,
contentDocument: document,
// toolbox-host-manager.js wants to set attributes on the frame that contains it,
// but that is fine to skip and doesn't make sense when using the current window.
setAttribute() {},
ownerDocument: document,
// toolbox-host-manager.js wants to listen for unload events from outside the frame,
// but this is fine to skip since the toolbox code listens inside the frame as well,
// and there is no outer document in this case.
addEventListener() {},
};
}
// Specify the default tool to open
let tool = url.searchParams.get("tool");

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

@ -1290,7 +1290,7 @@ Inspector.prototype = {
attributesSubmenu.append(new MenuItem({
id: "node-menu-copy-attribute",
label: INSPECTOR_L10N.getFormatStr("inspectorCopyAttributeValue.label",
isAttributeClicked ? `"${nodeInfo.value}"` : ""),
isAttributeClicked ? `${nodeInfo.value}` : ""),
accesskey: INSPECTOR_L10N.getStr("inspectorCopyAttributeValue.accesskey"),
disabled: !isAttributeClicked,
click: () => this.onCopyAttributeValue(),
@ -1298,7 +1298,7 @@ Inspector.prototype = {
attributesSubmenu.append(new MenuItem({
id: "node-menu-edit-attribute",
label: INSPECTOR_L10N.getFormatStr("inspectorEditAttribute.label",
isAttributeClicked ? `"${nodeInfo.name}"` : ""),
isAttributeClicked ? `${nodeInfo.name}` : ""),
accesskey: INSPECTOR_L10N.getStr("inspectorEditAttribute.accesskey"),
disabled: !isAttributeClicked,
click: () => this.onEditAttribute(),
@ -1306,7 +1306,7 @@ Inspector.prototype = {
attributesSubmenu.append(new MenuItem({
id: "node-menu-remove-attribute",
label: INSPECTOR_L10N.getFormatStr("inspectorRemoveAttribute.label",
isAttributeClicked ? `"${nodeInfo.name}"` : ""),
isAttributeClicked ? `${nodeInfo.name}` : ""),
accesskey: INSPECTOR_L10N.getStr("inspectorRemoveAttribute.accesskey"),
disabled: !isAttributeClicked,
click: () => this.onRemoveAttribute(),

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

@ -5,12 +5,29 @@
"use strict";
const {
UPDATE_GRID_COLOR,
UPDATE_GRID_HIGHLIGHTED,
UPDATE_GRIDS,
} = require("./index");
module.exports = {
/**
* Update the color used for the grid's highlighter.
*
* @param {NodeFront} nodeFront
* The NodeFront of the DOM node to toggle the grid highlighter.
* @param {String} color
* The color to use for thie nodeFront's grid highlighter.
*/
updateGridColor(nodeFront, color) {
return {
type: UPDATE_GRID_COLOR,
color,
nodeFront,
};
},
/**
* Update the grid highlighted state.
*
@ -22,8 +39,8 @@ module.exports = {
updateGridHighlighted(nodeFront, highlighted) {
return {
type: UPDATE_GRID_HIGHLIGHTED,
nodeFront,
highlighted,
nodeFront,
};
},

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

@ -8,6 +8,9 @@ const { createEnum } = require("devtools/client/shared/enum");
createEnum([
// Update the color used for the overlay of a grid.
"UPDATE_GRID_COLOR",
// Update the grid highlighted state.
"UPDATE_GRID_HIGHLIGHTED",

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

@ -24,7 +24,7 @@
}
.accordion ._header:hover {
background-color: var(--theme-selection-color);
background-color: var(--theme-toolbar-hover);
}
.accordion ._header:hover svg {

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

@ -26,11 +26,13 @@ const App = createClass({
propTypes: {
boxModel: PropTypes.shape(Types.boxModel).isRequired,
getSwatchColorPickerTooltip: PropTypes.func.isRequired,
grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
showBoxModelProperties: PropTypes.bool.isRequired,
onShowBoxModelEditor: PropTypes.func.isRequired,
onHideBoxModelHighlighter: PropTypes.func.isRequired,
onSetGridOverlayColor: PropTypes.func.isRequired,
onShowBoxModelEditor: PropTypes.func.isRequired,
onShowBoxModelHighlighter: PropTypes.func.isRequired,
onToggleGridHighlighter: PropTypes.func.isRequired,
onToggleShowGridLineNumbers: PropTypes.func.isRequired,

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

@ -18,8 +18,10 @@ module.exports = createClass({
displayName: "Grid",
propTypes: {
getSwatchColorPickerTooltip: PropTypes.func.isRequired,
grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
onSetGridOverlayColor: PropTypes.func.isRequired,
onToggleGridHighlighter: PropTypes.func.isRequired,
onToggleShowGridLineNumbers: PropTypes.func.isRequired,
onToggleShowInfiniteLines: PropTypes.func.isRequired,
@ -29,8 +31,10 @@ module.exports = createClass({
render() {
let {
getSwatchColorPickerTooltip,
grids,
highlighterSettings,
onSetGridOverlayColor,
onToggleGridHighlighter,
onToggleShowGridLineNumbers,
onToggleShowInfiniteLines,
@ -42,7 +46,9 @@ module.exports = createClass({
id: "layout-grid-container",
},
GridList({
getSwatchColorPickerTooltip,
grids,
onSetGridOverlayColor,
onToggleGridHighlighter,
}),
GridDisplaySettings({

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

@ -0,0 +1,118 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { addons, createClass, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react");
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
const Types = require("../types");
module.exports = createClass({
displayName: "GridItem",
propTypes: {
getSwatchColorPickerTooltip: PropTypes.func.isRequired,
grid: PropTypes.shape(Types.grid).isRequired,
onSetGridOverlayColor: PropTypes.func.isRequired,
onToggleGridHighlighter: PropTypes.func.isRequired,
},
mixins: [ addons.PureRenderMixin ],
componentDidMount() {
let tooltip = this.props.getSwatchColorPickerTooltip();
let swatchEl = findDOMNode(this).querySelector(".grid-color-swatch");
let previousColor;
tooltip.addSwatch(swatchEl, {
onCommit: this.setGridColor,
onPreview: this.setGridColor,
onRevert: () => {
this.props.onSetGridOverlayColor(this.props.grid.nodeFront, previousColor);
},
onShow: () => {
previousColor = this.props.grid.color;
},
});
},
componentWillUnmount() {
let tooltip = this.props.getSwatchColorPickerTooltip();
let swatchEl = findDOMNode(this).querySelector(".grid-color-swatch");
tooltip.removeSwatch(swatchEl);
},
setGridColor() {
let color = findDOMNode(this).querySelector(".grid-color-value").textContent;
this.props.onSetGridOverlayColor(this.props.grid.nodeFront, color);
},
onGridCheckboxClick() {
let {
grid,
onToggleGridHighlighter,
} = this.props;
onToggleGridHighlighter(grid.nodeFront);
},
render() {
let { grid } = this.props;
let { nodeFront } = grid;
let { displayName, attributes } = nodeFront;
let gridName = displayName;
let idIndex = attributes.findIndex(({ name }) => name === "id");
if (idIndex > -1 && attributes[idIndex].value) {
gridName += "#" + attributes[idIndex].value;
}
let classIndex = attributes.findIndex(({name}) => name === "class");
if (classIndex > -1 && attributes[classIndex].value) {
gridName += "." + attributes[classIndex].value.split(" ").join(".");
}
return dom.li(
{
key: grid.id,
className: "grid-item",
},
dom.label(
{},
dom.input(
{
type: "checkbox",
value: grid.id,
checked: grid.highlighted,
onChange: this.onGridCheckboxClick,
}
),
gridName
),
dom.div(
{
className: "grid-color-swatch",
style: {
backgroundColor: grid.color,
},
title: grid.color,
}
),
// The SwatchColorPicker relies on the nextSibling of the swatch element to apply
// the selected color. This is why we use a span in display: none for now.
// Ideally we should modify the SwatchColorPickerTooltip to bypass this requirement.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1341578
dom.span(
{
className: "grid-color-value"
},
grid.color
)
);
},
});

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

@ -4,9 +4,11 @@
"use strict";
const { addons, createClass, DOM: dom, PropTypes } =
const { addons, createClass, createFactory, DOM: dom, PropTypes } =
require("devtools/client/shared/vendor/react");
const GridItem = createFactory(require("./GridItem"));
const Types = require("../types");
const { getStr } = require("../utils/l10n");
@ -15,24 +17,20 @@ module.exports = createClass({
displayName: "GridList",
propTypes: {
getSwatchColorPickerTooltip: PropTypes.func.isRequired,
grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
onSetGridOverlayColor: PropTypes.func.isRequired,
onToggleGridHighlighter: PropTypes.func.isRequired,
},
mixins: [ addons.PureRenderMixin ],
onGridCheckboxClick({ target }) {
let {
grids,
onToggleGridHighlighter,
} = this.props;
onToggleGridHighlighter(grids[target.value].nodeFront);
},
render() {
let {
getSwatchColorPickerTooltip,
grids,
onSetGridOverlayColor,
onToggleGridHighlighter,
} = this.props;
return dom.div(
@ -45,43 +43,14 @@ module.exports = createClass({
),
dom.ul(
{},
grids.map(grid => {
let { nodeFront } = grid;
let { displayName, attributes } = nodeFront;
let gridName = displayName;
let idIndex = attributes.findIndex(({ name }) => name === "id");
if (idIndex > -1 && attributes[idIndex].value) {
gridName += "#" + attributes[idIndex].value;
}
let classIndex = attributes.findIndex(({name}) => name === "class");
if (classIndex > -1 && attributes[classIndex].value) {
gridName += "." + attributes[classIndex].value.split(" ").join(".");
}
return dom.li(
{
key: grid.id,
},
dom.label(
{},
dom.input(
{
type: "checkbox",
value: grid.id,
checked: grid.highlighted,
onChange: this.onGridCheckboxClick,
}
),
gridName
)
);
})
grids.map(grid => GridItem({
getSwatchColorPickerTooltip,
grid,
onSetGridOverlayColor,
onToggleGridHighlighter,
}))
)
);
},
});

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

@ -16,5 +16,6 @@ DevToolsModules(
'ComputedProperty.js',
'Grid.js',
'GridDisplaySettings.js',
'GridItem.js',
'GridList.js',
)

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

@ -13,10 +13,13 @@ const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
const { Provider } = require("devtools/client/shared/vendor/react-redux");
const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
const {
updateLayout,
} = require("./actions/box-model");
const {
updateGridColor,
updateGridHighlighted,
updateGrids,
} = require("./actions/grids");
@ -37,6 +40,18 @@ const NUMERIC = /^-?[\d\.]+$/;
const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";
// Default grid colors.
const GRID_COLORS = [
"#05E4EE",
"#BB9DFF",
"#FFB53B",
"#71F362",
"#FF90FF",
"#FF90FF",
"#1B80FF",
"#FF2647"
];
function LayoutView(inspector, window) {
this.document = window.document;
this.highlighters = inspector.highlighters;
@ -52,11 +67,6 @@ function LayoutView(inspector, window) {
this.onSidebarSelect = this.onSidebarSelect.bind(this);
this.init();
this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
this.inspector.selection.on("new-node-front", this.onNewSelection);
this.inspector.sidebar.on("select", this.onSidebarSelect);
}
LayoutView.prototype = {
@ -74,7 +84,28 @@ LayoutView.prototype = {
this.loadHighlighterSettings();
this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
this.inspector.selection.on("new-node-front", this.onNewSelection);
this.inspector.sidebar.on("select", this.onSidebarSelect);
// Create a shared SwatchColorPicker instance to be reused by all GridItem components.
this.swatchColorPickerTooltip = new SwatchColorPickerTooltip(
this.inspector.toolbox.doc,
this.inspector,
{
supportsCssColor4ColorFunction: () => false
}
);
let app = App({
/**
* Retrieve the shared SwatchColorPicker instance.
*/
getSwatchColorPickerTooltip: () => {
return this.swatchColorPickerTooltip;
},
/**
* Shows the box model properties under the box model if true, otherwise, hidden by
* default.
@ -89,6 +120,29 @@ LayoutView.prototype = {
toolbox.highlighterUtils.unhighlight();
},
/**
* Handler for a change in the grid overlay color picker for a grid container.
*
* @param {NodeFront} node
* The NodeFront of the grid container element for which the grid color is
* being updated.
* @param {String} color
* A hex string representing the color to use.
*/
onSetGridOverlayColor: (node, color) => {
this.store.dispatch(updateGridColor(node, color));
let { grids } = this.store.getState();
// If the grid for which the color was updated currently has a highlighter, update
// the color.
for (let grid of grids) {
if (grid.nodeFront === node && grid.highlighted) {
let highlighterSettings = this.getGridHighlighterSettings(node);
this.highlighters.showGridHighlighter(node, highlighterSettings);
}
}
},
/**
* Shows the inplace editor when a box model editable value is clicked on the
* box model panel.
@ -180,13 +234,13 @@ LayoutView.prototype = {
* highlighter is toggled on/off for.
*/
onToggleGridHighlighter: node => {
let { highlighterSettings } = this.store.getState();
let highlighterSettings = this.getGridHighlighterSettings(node);
this.highlighters.toggleGridHighlighter(node, highlighterSettings);
},
/**
* Handler for a change in the show grid line numbers checkbox in the
* GridDisplaySettings component. TOggles on/off the option to show the grid line
* GridDisplaySettings component. Toggles on/off the option to show the grid line
* numbers in the grid highlighter. Refreshes the shown grid highlighter for the
* grids currently highlighted.
*
@ -197,10 +251,11 @@ LayoutView.prototype = {
this.store.dispatch(updateShowGridLineNumbers(enabled));
Services.prefs.setBoolPref(SHOW_GRID_LINE_NUMBERS, enabled);
let { grids, highlighterSettings } = this.store.getState();
let { grids } = this.store.getState();
for (let grid of grids) {
if (grid.highlighted) {
let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
}
}
@ -219,14 +274,15 @@ LayoutView.prototype = {
this.store.dispatch(updateShowInfiniteLines(enabled));
Services.prefs.setBoolPref(SHOW_INFINITE_LINES_PREF, enabled);
let { grids, highlighterSettings } = this.store.getState();
let { grids } = this.store.getState();
for (let grid of grids) {
if (grid.highlighted) {
let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
}
}
},
}
});
let provider = createElement(Provider, {
@ -270,6 +326,43 @@ LayoutView.prototype = {
this.walker = null;
},
/**
* Returns the color set for the grid highlighter associated with the provided
* nodeFront.
*
* @param {NodeFront} nodeFront
* The NodeFront for which we need the color.
*/
getGridColorForNodeFront(nodeFront) {
let { grids } = this.store.getState();
for (let grid of grids) {
if (grid.nodeFront === nodeFront) {
return grid.color;
}
}
return null;
},
/**
* Create a highlighter settings object for the provided nodeFront.
*
* @param {NodeFront} nodeFront
* The NodeFront for which we need highlighter settings.
*/
getGridHighlighterSettings(nodeFront) {
let { highlighterSettings } = this.store.getState();
// Get the grid color for the provided nodeFront.
let color = this.getGridColorForNodeFront(nodeFront);
// Merge the grid color to the generic highlighter settings.
return Object.assign({}, highlighterSettings, {
color
});
},
/**
* Returns true if the layout panel is visible, and false otherwise.
*/
@ -391,8 +484,12 @@ LayoutView.prototype = {
let grid = gridFronts[i];
let nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
let fallbackColor = GRID_COLORS[i % GRID_COLORS.length];
let color = this.getGridColorForNodeFront(nodeFront) || fallbackColor;
grids.push({
id: i,
color,
gridFragments: grid.gridFragments,
highlighted: nodeFront == this.highlighters.gridHighlighterShown,
nodeFront,

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

@ -5,6 +5,7 @@
"use strict";
const {
UPDATE_GRID_COLOR,
UPDATE_GRID_HIGHLIGHTED,
UPDATE_GRIDS,
} = require("../actions/index");
@ -13,12 +14,10 @@ const INITIAL_GRIDS = [];
let reducers = {
[UPDATE_GRID_HIGHLIGHTED](grids, { nodeFront, highlighted }) {
[UPDATE_GRID_COLOR](grids, { nodeFront, color }) {
let newGrids = grids.map(g => {
if (g.nodeFront == nodeFront) {
g.highlighted = highlighted;
} else {
g.highlighted = false;
g.color = color;
}
return g;
@ -27,6 +26,14 @@ let reducers = {
return newGrids;
},
[UPDATE_GRID_HIGHLIGHTED](grids, { nodeFront, highlighted }) {
return grids.map(g => {
return Object.assign({}, g, {
highlighted: g.nodeFront === nodeFront ? highlighted : false
});
});
},
[UPDATE_GRIDS](_, { grids }) {
return grids;
},

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

@ -24,6 +24,9 @@ exports.grid = {
// The id of the grid
id: PropTypes.number,
// The color for the grid overlay highlighter
color: PropTypes.string,
// The grid fragment object of the grid container
gridFragments: PropTypes.array,

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

@ -107,21 +107,21 @@ inspector.menu.selectElement.label=Select Element #%S
# sub-menu "Attribute" in the inspector contextual-menu that appears
# when the user right-clicks on the node in the inspector, and that allows
# to edit an attribute on this node.
inspectorEditAttribute.label=Edit Attribute %S
inspectorEditAttribute.label=Edit Attribute %S
inspectorEditAttribute.accesskey=E
# LOCALIZATION NOTE (inspectorRemoveAttribute.label): This is the label of a
# sub-menu "Attribute" in the inspector contextual-menu that appears
# when the user right-clicks on the attribute of a node in the inspector,
# and that allows to remove this attribute.
inspectorRemoveAttribute.label=Remove Attribute %S
inspectorRemoveAttribute.label=Remove Attribute %S
inspectorRemoveAttribute.accesskey=R
# LOCALIZATION NOTE (inspectorCopyAttributeValue.label): This is the label of a
# sub-menu "Attribute" in the inspector contextual-menu that appears
# when the user right-clicks on the attribute of a node in the inspector,
# and that allows to copy the attribute value to clipboard.
inspectorCopyAttributeValue.label=Copy Attribute Value %S
inspectorCopyAttributeValue.label=Copy Attribute Value %S
inspectorCopyAttributeValue.accesskey=V
# LOCALIZATION NOTE (inspector.nodePreview.selectNodeLabel):

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

@ -21,7 +21,7 @@
.theme-dark {
--rdm-box-shadow: 0 4px 4px 0 rgba(105, 105, 105, 0.26);
--submit-button-active-background-color: var(--toolbar-tab-hover-active);
--submit-button-active-background-color: var(--theme-toolbar-hover-active);
--submit-button-active-color: var(--theme-selection-color);
--viewport-color: #c6ccd0;
--viewport-hover-color: #dde1e4;
@ -536,7 +536,7 @@ select > option.divider {
}
#device-submit-button:hover {
background-color: var(--toolbar-tab-hover);
background-color: var(--theme-toolbar-hover);
}
#device-submit-button:hover:active {

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

@ -95,12 +95,12 @@
.theme-dark .tabs .tabs-menu-item:hover:not(.is-active),
.theme-light .tabs .tabs-menu-item:hover:not(.is-active) {
background-color: var(--toolbar-tab-hover);
background-color: var(--theme-toolbar-hover);
}
.theme-dark .tabs .tabs-menu-item:hover:active:not(.is-active),
.theme-light .tabs .tabs-menu-item:hover:active:not(.is-active) {
background-color: var(--toolbar-tab-hover-active);
background-color: var(--theme-toolbar-hover-active);
}
.theme-dark .tabs .tabs-menu-item.is-active,
@ -165,7 +165,7 @@
}
.theme-firebug .tabs .tabs-menu-item:hover:active a {
background-color: var(--toolbar-tab-hover-active);
background-color: var(--theme-toolbar-hover-active);
}
.theme-firebug .tabs .tabs-menu-item.is-active:hover:active a {

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

@ -52,3 +52,29 @@
text-align: center;
padding: 0.5em;
}
/**
* Grid Item
*/
.grid-item {
display: flex;
align-items: center;
}
.grid-item input {
margin: 0 5px;
}
.grid-color-swatch {
width: 12px;
height: 12px;
margin-left: 5px;
border: 1px solid var(--theme-highlight-gray);
border-radius: 50%;
cursor: pointer;
}
.grid-color-value {
display: none;
}

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

@ -5,8 +5,6 @@
/* CSS Variables specific to the devtools toolbar that aren't defined by the themes */
.theme-light {
--toolbar-tab-hover: rgba(170, 170, 170, .2);
--toolbar-tab-hover-active: rgba(170, 170, 170, .4);
--searchbox-background-color: #ffee99;
--searchbox-border-color: #ffbf00;
--searcbox-no-match-background-color: #ffe5e5;
@ -19,8 +17,6 @@
}
.theme-dark {
--toolbar-tab-hover: rgba(110,120,130,0.1);
--toolbar-tab-hover-active: rgba(110,120,130,0.2);
--searchbox-background-color: #4d4222;
--searchbox-border-color: #d99f2b;
--searcbox-no-match-background-color: #402325;

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

@ -134,7 +134,7 @@
}
.devtools-tab:hover {
background-color: var(--toolbar-tab-hover);
background-color: var(--theme-toolbar-hover);
}
.theme-dark .devtools-tab:hover:active {
@ -142,7 +142,7 @@
}
.devtools-tab:hover:active {
background-color: var(--toolbar-tab-hover-active);
background-color: var(--theme-toolbar-hover-active);
}
.theme-dark .devtools-tab:not(.selected).highlighted {

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

@ -21,6 +21,8 @@
--theme-tab-toolbar-background: #fcfcfc;
--theme-toolbar-background: #fcfcfc;
--theme-toolbar-hover: rgba(170, 170, 170, .2);
--theme-toolbar-hover-active: rgba(170, 170, 170, .4);
--theme-selection-background: #4c9ed9;
--theme-selection-background-semitransparent: rgba(76, 158, 217, 0.15);
--theme-selection-color: #f5f7fa;
@ -81,6 +83,8 @@
--theme-tab-toolbar-background: #272b35;
--theme-toolbar-background: #272b35;
--theme-toolbar-hover: rgba(110, 120, 130, 0.1);
--theme-toolbar-hover-active: rgba(110, 120, 130, 0.2);
--theme-selection-background: #5675B9;
--theme-selection-background-semitransparent: rgba(86, 117, 185, 0.5);
--theme-selection-color: #f5f7fa;

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

@ -47,16 +47,9 @@ const DOCS_GA_PARAMS = "?utm_source=mozilla" +
flags.testing = true;
function loadTab(url, preferredRemoteType) {
let deferred = promise.defer();
let tab = gBrowser.selectedTab = gBrowser.addTab(url, { preferredRemoteType });
let browser = gBrowser.getBrowserForTab(tab);
browser.addEventListener("load", function () {
deferred.resolve({tab: tab, browser: browser});
}, {capture: true, once: true});
return deferred.promise;
return addTab(url, { preferredRemoteType }).then( tab => {
return { tab, browser: tab.linkedBrowser };
});
}
function loadBrowser(browser) {
@ -64,17 +57,7 @@ function loadBrowser(browser) {
}
function closeTab(tab) {
let deferred = promise.defer();
let container = gBrowser.tabContainer;
container.addEventListener("TabClose", function () {
deferred.resolve(null);
}, {capture: true, once: true});
gBrowser.removeTab(tab);
return deferred.promise;
return removeTab(tab);
}
/**

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

@ -21,20 +21,22 @@ const {
const { stringifyGridFragments } = require("devtools/server/actors/utils/css-grid-utils");
const CSS_GRID_ENABLED_PREF = "layout.css.grid.enabled";
const DEFAULT_GRID_COLOR = "#4B0082";
const ROWS = "rows";
const COLUMNS = "cols";
const GRID_LINES_PROPERTIES = {
"edge": {
lineDash: [0, 0],
strokeStyle: "#4B0082"
alpha: 1,
},
"explicit": {
lineDash: [5, 3],
strokeStyle: "#8A2BE2"
alpha: 0.75,
},
"implicit": {
lineDash: [2, 2],
strokeStyle: "#9370DB"
alpha: 0.5,
}
};
@ -42,7 +44,7 @@ const GRID_LINES_PROPERTIES = {
const GRID_GAP_PATTERN_WIDTH = 14;
const GRID_GAP_PATTERN_HEIGHT = 14;
const GRID_GAP_PATTERN_LINE_DASH = [5, 3];
const GRID_GAP_PATTERN_STROKE_STYLE = "#9370DB";
const GRID_GAP_ALPHA = 0.5;
/**
* Cached used by `CssGridHighlighter.getGridGapPattern`.
@ -64,6 +66,9 @@ const COLUMN_KEY = {};
* h.destroy();
*
* Available Options:
* - color(colorValue)
* @param {String} colorValue
* The color that should be used to draw the highlighter for this grid.
* - showGridArea(areaName)
* @param {String} areaName
* Shows the grid area highlight for the given area name.
@ -251,6 +256,10 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
return this.getElement("canvas");
},
get color() {
return this.options.color || DEFAULT_GRID_COLOR;
},
/**
* Gets the grid gap pattern used to render the gap regions.
*
@ -270,6 +279,7 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
canvas.height = GRID_GAP_PATTERN_HEIGHT;
let ctx = canvas.getContext("2d");
ctx.save();
ctx.setLineDash(GRID_GAP_PATTERN_LINE_DASH);
ctx.beginPath();
ctx.translate(.5, .5);
@ -282,8 +292,10 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
ctx.lineTo(0, GRID_GAP_PATTERN_HEIGHT);
}
ctx.strokeStyle = GRID_GAP_PATTERN_STROKE_STYLE;
ctx.strokeStyle = this.color;
ctx.globalAlpha = GRID_GAP_ALPHA;
ctx.stroke();
ctx.restore();
let pattern = ctx.createPattern(canvas, "repeat");
gCachedGridPattern.set(dimension, pattern);
@ -295,8 +307,7 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
* using DeadWrapper objects as gap patterns the next time.
*/
onNavigate() {
gCachedGridPattern.delete(ROW_KEY);
gCachedGridPattern.delete(COLUMN_KEY);
this._clearCache();
},
onWillNavigate({ isTopLevel }) {
@ -311,9 +322,17 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
return false;
}
// The grid pattern cache should be cleared in case the color changed.
this._clearCache();
return this._update();
},
_clearCache() {
gCachedGridPattern.delete(ROW_KEY);
gCachedGridPattern.delete(COLUMN_KEY);
},
/**
* Shows the grid area highlight for the given area name.
*
@ -610,7 +629,9 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
this.ctx.lineTo(endPos, linePos);
}
this.ctx.strokeStyle = GRID_LINES_PROPERTIES[lineType].strokeStyle;
this.ctx.strokeStyle = this.color;
this.ctx.globalAlpha = GRID_LINES_PROPERTIES[lineType].alpha;
this.ctx.stroke();
this.ctx.restore();
},
@ -631,11 +652,16 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
renderGridLineNumber(lineNumber, linePos, startPos, dimensionType) {
this.ctx.save();
let textWidth = this.ctx.measureText(lineNumber).width;
// Guess the font height based on the measured width
let textHeight = textWidth * 2;
if (dimensionType === COLUMNS) {
this.ctx.fillText(lineNumber, linePos, startPos);
let yPos = Math.max(startPos, textHeight);
this.ctx.fillText(lineNumber, linePos, yPos);
} else {
let textWidth = this.ctx.measureText(lineNumber).width;
this.ctx.fillText(lineNumber, startPos - textWidth, linePos);
let xPos = Math.max(startPos, textWidth);
this.ctx.fillText(lineNumber, xPos - textWidth, linePos);
}
this.ctx.restore();

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

@ -66,6 +66,7 @@ DevToolsModules(
'webextension-inspected-window.js',
'webextension.js',
'webgl.js',
'window.js',
'worker-list.js',
'worker.js',
)

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

@ -7,6 +7,7 @@
"use strict";
const { Cc, Ci, Cu } = require("chrome");
const Services = require("Services");
const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common");
const { DebuggerServer } = require("devtools/server/main");
@ -14,6 +15,8 @@ loader.lazyGetter(this, "ppmm", () => {
return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(
Ci.nsIMessageBroadcaster);
});
loader.lazyRequireGetter(this, "WindowActor",
"devtools/server/actors/window", true);
/* Root actor for the remote debugging protocol. */
@ -167,12 +170,15 @@ RootActor.prototype = {
// Added in Firefox 40. Indicates that the backend supports registering custom
// commands through the WebConsoleCommands API.
webConsoleCommands: true,
// Whether root actor exposes tab actors
// if allowChromeProcess is true, you can fetch a ChromeActor instance
// to debug chrome and any non-content ressource via getProcess request
// if allocChromeProcess is defined, but not true, it means that root actor
// no longer expose tab actors, but also that getProcess forbids
// exposing actors for security reasons
// Whether root actor exposes tab actors and access to any window.
// If allowChromeProcess is true, you can:
// * get a ChromeActor instance to debug chrome and any non-content
// resource via getProcess requests
// * get a WindowActor instance to debug windows which could be chrome,
// like browser windows via getWindow requests
// If allowChromeProcess is defined, but not true, it means that root actor
// no longer expose tab actors, but also that the above requests are
// forbidden for security reasons.
get allowChromeProcess() {
return DebuggerServer.allowChromeProcess;
},
@ -232,6 +238,7 @@ RootActor.prototype = {
this.conn = null;
this._tabActorPool = null;
this._globalActorPool = null;
this._windowActorPool = null;
this._parameters = null;
this._chromeActor = null;
this._processActors.clear();
@ -338,6 +345,38 @@ RootActor.prototype = {
});
},
onGetWindow: function ({ outerWindowID }) {
if (!DebuggerServer.allowChromeProcess) {
return {
from: this.actorID,
error: "forbidden",
message: "You are not allowed to debug windows."
};
}
let window = Services.wm.getOuterWindowWithId(outerWindowID);
if (!window) {
return {
from: this.actorID,
error: "notFound",
message: `No window found with outerWindowID ${outerWindowID}`,
};
}
if (!this._windowActorPool) {
this._windowActorPool = new ActorPool(this.conn);
this.conn.addActorPool(this._windowActorPool);
}
let actor = new WindowActor(this.conn, window);
actor.parentID = this.actorID;
this._windowActorPool.addActor(actor);
return {
from: this.actorID,
window: actor.form(),
};
},
onTabListChanged: function () {
this.conn.send({ from: this.actorID, type: "tabListChanged" });
/* It's a one-shot notification; no need to watch any more. */
@ -539,6 +578,7 @@ RootActor.prototype = {
RootActor.prototype.requestTypes = {
"listTabs": RootActor.prototype.onListTabs,
"getTab": RootActor.prototype.onGetTab,
"getWindow": RootActor.prototype.onGetWindow,
"listAddons": RootActor.prototype.onListAddons,
"listWorkers": RootActor.prototype.onListWorkers,
"listServiceWorkerRegistrations": RootActor.prototype.onListServiceWorkerRegistrations,

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

@ -0,0 +1,83 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Ci } = require("chrome");
const Services = require("Services");
const { TabActor } = require("./tab");
/**
* Creates a WindowActor for debugging a single window, like a browser window in Firefox,
* but it can be used to reach any window in the process. (Currently this is parent
* process only because the root actor's `onGetWindow` doesn't try to cross process
* boundaries.) Both chrome and content windows are supported.
*
* Most of the implementation is inherited from TabActor. WindowActor exposes all tab
* actors via its form() request, like TabActor.
*
* You can request a specific window's actor via RootActor.getWindow().
*
* @param connection DebuggerServerConnection
* The connection to the client.
* @param window DOMWindow
* The window.
*/
function WindowActor(connection, window) {
TabActor.call(this, connection);
let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
Object.defineProperty(this, "docShell", {
value: docShell,
configurable: true
});
}
WindowActor.prototype = Object.create(TabActor.prototype);
// Bug 1266561: This setting is mysteriously named, we should split up the
// functionality that is triggered by it.
WindowActor.prototype.isRootActor = true;
WindowActor.prototype.observe = function (subject, topic, data) {
TabActor.prototype.observe.call(this, subject, topic, data);
if (!this.attached) {
return;
}
if (topic == "chrome-webnavigation-destroy") {
this._onDocShellDestroy(subject);
}
};
WindowActor.prototype._attach = function () {
if (this.attached) {
return false;
}
TabActor.prototype._attach.call(this);
// Listen for chrome docshells in addition to content docshells
if (this.docShell.itemType == Ci.nsIDocShellTreeItem.typeChrome) {
Services.obs.addObserver(this, "chrome-webnavigation-destroy", false);
}
return true;
};
WindowActor.prototype._detach = function () {
if (!this.attached) {
return false;
}
if (this.docShell.itemType == Ci.nsIDocShellTreeItem.typeChrome) {
Services.obs.removeObserver(this, "chrome-webnavigation-destroy");
}
TabActor.prototype._detach.call(this);
return true;
};
exports.WindowActor = WindowActor;

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

@ -7,7 +7,8 @@ once a parent is removed from the pool, its children are removed as well.
The overall hierarchy of actors looks like this:
RootActor: First one, automatically instantiated when we start connecting.
```
RootActor: First one, automatically instantiated when we start connecting.
| Mostly meant to instantiate new actors.
|
|--> Global-scoped actors:
@ -27,6 +28,7 @@ The overall hierarchy of actors looks like this:
worker). Examples include the console and inspector actors.
These actors may extend this hierarchy by having their
own children, like LongStringActor, WalkerActor, etc.
```
## RootActor
@ -36,7 +38,8 @@ All other actors have an `actorID` which is computed dynamically,
so that you need to ask an existing actor to create an Actor
and returns its `actorID`. That's the main role of RootActor.
RootActor (root.js)
```
RootActor (root.js)
|
|-- BrowserTabActor (webbrowser.js)
| Targets tabs living in the parent or child process. Note that this is
@ -60,6 +63,11 @@ and returns its `actorID`. That's the main role of RootActor.
| Returned by "listWorkers" request to a ChildProcessActor to get workers
| for the chrome of the child process.
|
|-- WindowActor (window.js)
| Targets a single window, such as a browser window in Firefox, but it can
| be used to reach any window in the parent process.
| Returned by "getWindow" request to the root actor.
|
|-- ChromeActor (chrome.js)
| Targets all resources in the parent process of firefox
| (chrome documents, JSM, JS XPCOM, etc.).
@ -73,6 +81,7 @@ and returns its `actorID`. That's the main role of RootActor.
\-- BrowserAddonActor (addon.js)
Targets the javascript of add-ons.
Returned by "listAddons" request.
```
## "TabActor"

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

@ -12,8 +12,8 @@
.simple-animation {
display: inline-block;
width: 50px;
height: 50px;
width: 64px;
height: 64px;
border-radius: 50%;
background: red;

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

@ -1741,6 +1741,27 @@ RootClient.prototype = {
return this.request(packet);
},
/**
* Fetch the WindowActor for a specific window, like a browser window in
* Firefox, but it can be used to reach any window in the process.
*
* @param number outerWindowID
* The outerWindowID of the top level window you are looking for.
*/
getWindow: function ({ outerWindowID }) {
if (!outerWindowID) {
throw new Error("Must specify outerWindowID");
}
let packet = {
to: this.actor,
type: "getWindow",
outerWindowID,
};
return this.request(packet);
},
/**
* Description of protocol's actors and methods.
*

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

@ -46,6 +46,7 @@ LoadContext::LoadContext(nsIPrincipal* aPrincipal,
, mNestedFrameId(0)
, mIsContent(true)
, mUseRemoteTabs(false)
, mUseTrackingProtection(false)
#ifdef DEBUG
, mIsNotNull(true)
#endif
@ -57,6 +58,7 @@ LoadContext::LoadContext(nsIPrincipal* aPrincipal,
MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetIsContent(&mIsContent));
MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetUseRemoteTabs(&mUseRemoteTabs));
MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetUseTrackingProtection(&mUseTrackingProtection));
}
//-----------------------------------------------------------------------------
@ -180,22 +182,24 @@ LoadContext::GetOriginAttributes(JS::MutableHandleValue aAttrs)
}
NS_IMETHODIMP
LoadContext::IsTrackingProtectionOn(bool* aIsTrackingProtectionOn)
LoadContext::GetUseTrackingProtection(bool* aUseTrackingProtection)
{
MOZ_ASSERT(mIsNotNull);
if (Preferences::GetBool("privacy.trackingprotection.enabled", false)) {
*aIsTrackingProtectionOn = true;
} else if ((mOriginAttributes.mPrivateBrowsingId > 0) &&
Preferences::GetBool("privacy.trackingprotection.pbmode.enabled", false)) {
*aIsTrackingProtectionOn = true;
} else {
*aIsTrackingProtectionOn = false;
}
NS_ENSURE_ARG_POINTER(aUseTrackingProtection);
*aUseTrackingProtection = mUseTrackingProtection;
return NS_OK;
}
NS_IMETHODIMP
LoadContext::SetUseTrackingProtection(bool aUseTrackingProtection)
{
MOZ_ASSERT_UNREACHABLE("Should only be set through nsDocShell");
return NS_ERROR_UNEXPECTED;
}
//-----------------------------------------------------------------------------
// LoadContext::nsIInterfaceRequestor
//-----------------------------------------------------------------------------

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

@ -45,6 +45,7 @@ public:
, mNestedFrameId(0)
, mIsContent(aToCopy.mIsContent)
, mUseRemoteTabs(aToCopy.mUseRemoteTabs)
, mUseTrackingProtection(aToCopy.mUseTrackingProtection)
, mOriginAttributes(aAttrs)
#ifdef DEBUG
, mIsNotNull(aToCopy.mIsNotNull)
@ -61,6 +62,7 @@ public:
, mNestedFrameId(aNestedFrameId)
, mIsContent(aToCopy.mIsContent)
, mUseRemoteTabs(aToCopy.mUseRemoteTabs)
, mUseTrackingProtection(aToCopy.mUseTrackingProtection)
, mOriginAttributes(aAttrs)
#ifdef DEBUG
, mIsNotNull(aToCopy.mIsNotNull)
@ -72,11 +74,13 @@ public:
bool aIsContent,
bool aUsePrivateBrowsing,
bool aUseRemoteTabs,
bool aUseTrackingProtection,
const OriginAttributes& aAttrs)
: mTopFrameElement(do_GetWeakReference(aTopFrameElement))
, mNestedFrameId(0)
, mIsContent(aIsContent)
, mUseRemoteTabs(aUseRemoteTabs)
, mUseTrackingProtection(aUseTrackingProtection)
, mOriginAttributes(aAttrs)
#ifdef DEBUG
, mIsNotNull(true)
@ -91,6 +95,7 @@ public:
, mNestedFrameId(0)
, mIsContent(false)
, mUseRemoteTabs(false)
, mUseTrackingProtection(false)
, mOriginAttributes(aAttrs)
#ifdef DEBUG
, mIsNotNull(true)
@ -110,6 +115,7 @@ private:
uint64_t mNestedFrameId;
bool mIsContent;
bool mUseRemoteTabs;
bool mUseTrackingProtection;
OriginAttributes mOriginAttributes;
#ifdef DEBUG
bool mIsNotNull;

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

@ -61,6 +61,7 @@ SerializedLoadContext::Init(nsILoadContext* aLoadContext)
mIsPrivateBitValid = true;
aLoadContext->GetIsContent(&mIsContent);
aLoadContext->GetUseRemoteTabs(&mUseRemoteTabs);
aLoadContext->GetUseTrackingProtection(&mUseTrackingProtection);
if (!aLoadContext->GetOriginAttributes(mOriginAttributes)) {
NS_WARNING("GetOriginAttributes failed");
}
@ -71,6 +72,7 @@ SerializedLoadContext::Init(nsILoadContext* aLoadContext)
// we won't be GetInterfaced to nsILoadContext
mIsContent = true;
mUseRemoteTabs = false;
mUseTrackingProtection = false;
}
}

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

@ -32,6 +32,7 @@ public:
, mIsPrivateBitValid(false)
, mIsContent(false)
, mUseRemoteTabs(false)
, mUseTrackingProtection(false)
{
Init(nullptr);
}
@ -52,6 +53,7 @@ public:
bool mIsPrivateBitValid;
bool mIsContent;
bool mUseRemoteTabs;
bool mUseTrackingProtection;
mozilla::OriginAttributes mOriginAttributes;
};
@ -70,6 +72,7 @@ struct ParamTraits<SerializedLoadContext>
WriteParam(aMsg, aParam.mIsContent);
WriteParam(aMsg, aParam.mIsPrivateBitValid);
WriteParam(aMsg, aParam.mUseRemoteTabs);
WriteParam(aMsg, aParam.mUseTrackingProtection);
WriteParam(aMsg, suffix);
}
@ -80,6 +83,7 @@ struct ParamTraits<SerializedLoadContext>
!ReadParam(aMsg, aIter, &aResult->mIsContent) ||
!ReadParam(aMsg, aIter, &aResult->mIsPrivateBitValid) ||
!ReadParam(aMsg, aIter, &aResult->mUseRemoteTabs) ||
!ReadParam(aMsg, aIter, &aResult->mUseTrackingProtection) ||
!ReadParam(aMsg, aIter, &suffix)) {
return false;
}

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

@ -138,6 +138,12 @@ static const RedirEntry kRedirMap[] = {
{
"webrtc", "chrome://global/content/aboutwebrtc/aboutWebrtc.html",
nsIAboutModule::ALLOW_SCRIPT
},
{
"printpreview", "about:blank",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT |
nsIAboutModule::URI_CAN_LOAD_IN_CHILD
}
};
static const int kRedirTotal = mozilla::ArrayLength(kRedirMap);
@ -173,9 +179,11 @@ nsAboutRedirector::NewChannel(nsIURI* aURI,
&isUIResource);
NS_ENSURE_SUCCESS(rv, rv);
nsLoadFlags loadFlags =
isUIResource ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
: static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
bool isAboutBlank = NS_IsAboutBlank(tempURI);
nsLoadFlags loadFlags = isUIResource || isAboutBlank
? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
: static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
tempURI,

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

@ -795,6 +795,7 @@ nsDocShell::nsDocShell()
, mIsAppTab(false)
, mUseGlobalHistory(false)
, mUseRemoteTabs(false)
, mUseTrackingProtection(false)
, mDeviceSizeIsPageSize(false)
, mWindowDraggingAllowed(false)
, mInFrameSwap(false)
@ -8852,14 +8853,7 @@ nsDocShell::RestoreFromHistory()
nsCOMPtr<nsIDocument> d = parent->GetDocument();
if (d) {
if (d->EventHandlingSuppressed()) {
document->SuppressEventHandling(nsIDocument::eEvents,
d->EventHandlingSuppressed());
}
// Ick, it'd be nicer to not rewalk all of the subdocs here.
if (d->AnimationsPaused()) {
document->SuppressEventHandling(nsIDocument::eAnimationsOnly,
d->AnimationsPaused());
document->SuppressEventHandling(d->EventHandlingSuppressed());
}
}
}
@ -13686,17 +13680,40 @@ nsDocShell::GetNestedFrameId(uint64_t* aId)
}
NS_IMETHODIMP
nsDocShell::IsTrackingProtectionOn(bool* aIsTrackingProtectionOn)
nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection)
{
if (Preferences::GetBool("privacy.trackingprotection.enabled", false)) {
*aIsTrackingProtectionOn = true;
} else if (UsePrivateBrowsing() &&
Preferences::GetBool("privacy.trackingprotection.pbmode.enabled", false)) {
*aIsTrackingProtectionOn = true;
} else {
*aIsTrackingProtectionOn = false;
*aUseTrackingProtection = false;
static bool sTPEnabled = false;
static bool sTPInPBEnabled = false;
static bool sPrefsInit = false;
if (!sPrefsInit) {
sPrefsInit = true;
Preferences::AddBoolVarCache(&sTPEnabled,
"privacy.trackingprotection.enabled", false);
Preferences::AddBoolVarCache(&sTPInPBEnabled,
"privacy.trackingprotection.pbmode.enabled", false);
}
if (mUseTrackingProtection || sTPEnabled ||
(UsePrivateBrowsing() && sTPInPBEnabled)) {
*aUseTrackingProtection = true;
return NS_OK;
}
RefPtr<nsDocShell> parent = GetParentDocshell();
if (parent) {
return parent->GetUseTrackingProtection(aUseTrackingProtection);
}
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection)
{
mUseTrackingProtection = aUseTrackingProtection;
return NS_OK;
}
@ -14347,10 +14364,19 @@ nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview)
#if NS_PRINT_PREVIEW
nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
if (!print || !print->IsInitializedForPrintPreview()) {
// XXX: Creating a brand new content viewer to host preview every
// time we enter here seems overwork. We could skip ahead to where
// we QI the mContentViewer if the current URI is either about:blank
// or about:printpreview.
Stop(nsIWebNavigation::STOP_ALL);
nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::CreateWithInheritedAttributes(this);
nsresult rv = CreateAboutBlankContentViewer(principal, nullptr);
nsCOMPtr<nsIURI> uri;
NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:printpreview"));
nsresult rv = CreateAboutBlankContentViewer(principal, uri);
NS_ENSURE_SUCCESS(rv, rv);
// Here we manually set current URI since we have just created a
// brand new content viewer (about:blank) to host preview.
SetCurrentURI(uri, nullptr, true, 0);
print = do_QueryInterface(mContentViewer);
NS_ENSURE_STATE(print);
print->InitializeForPrintPreview();

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

@ -234,7 +234,6 @@ public:
NS_IMETHOD GetUseRemoteTabs(bool*) override;
NS_IMETHOD SetRemoteTabs(bool) override;
NS_IMETHOD GetOriginAttributes(JS::MutableHandle<JS::Value>) override;
NS_IMETHOD IsTrackingProtectionOn(bool*) override;
// Restores a cached presentation from history (mLSHE).
// This method swaps out the content viewer and simulates loads for
@ -961,6 +960,7 @@ protected:
bool mIsAppTab : 1;
bool mUseGlobalHistory : 1;
bool mUseRemoteTabs : 1;
bool mUseTrackingProtection : 1;
bool mDeviceSizeIsPageSize : 1;
bool mWindowDraggingAllowed : 1;
bool mInFrameSwap : 1;

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

@ -1131,4 +1131,9 @@ interface nsIDocShell : nsIDocShellTreeItem
* header, and has not seen the initiating load yet.
*/
[infallible] readonly attribute boolean awaitingLargeAlloc;
/**
* Attribute that determines whether tracking protection is enabled.
*/
attribute boolean useTrackingProtection;
};

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

@ -78,21 +78,36 @@ interface nsILoadContext : nsISupports
*/
readonly attribute boolean useRemoteTabs;
/*
* Attribute that determines if tracking protection should be used. May not be
* changed after a document has been loaded in this context.
*/
attribute boolean useTrackingProtection;
%{C++
/**
* De-XPCOMed getter to make call-sites cleaner.
*/
bool UsePrivateBrowsing() {
bool usingPB;
bool UsePrivateBrowsing()
{
bool usingPB = false;
GetUsePrivateBrowsing(&usingPB);
return usingPB;
}
bool UseRemoteTabs() {
bool usingRT;
bool UseRemoteTabs()
{
bool usingRT = false;
GetUseRemoteTabs(&usingRT);
return usingRT;
}
bool UseTrackingProtection()
{
bool usingTP = false;
GetUseTrackingProtection(&usingTP);
return usingTP;
}
%}
/**
@ -129,21 +144,5 @@ interface nsILoadContext : nsISupports
*/
bool GetOriginAttributes(mozilla::OriginAttributes& aAttrs);
#endif
%}
/**
* Returns true if tracking protection is enabled for the load context.
*/
boolean IsTrackingProtectionOn();
%{C++
/**
* De-XPCOMed getter to make call-sites cleaner.
*/
bool UseTrackingProtection() {
bool usingTP;
IsTrackingProtectionOn(&usingTP);
return usingTP;
}
%}
};

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

@ -189,6 +189,7 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "telemetry", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "webrtc", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "printpreview", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_URI_LOADER_CONTRACTID, &kNS_URI_LOADER_CID },
{ NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &kNS_DOCUMENTLOADER_SERVICE_CID },
{ NS_HANDLERSERVICE_CONTRACTID, &kNS_CONTENTHANDLERSERVICE_CID, mozilla::Module::CONTENT_PROCESS_ONLY },

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

@ -33,13 +33,6 @@ AnimationPerformanceWarning::ToLocalizedString(
const char* key = nullptr;
switch (mType) {
case Type::ContentTooSmall:
MOZ_ASSERT(mParams && mParams->Length() == 2,
"Parameter's length should be 2 for ContentTooSmall");
return NS_SUCCEEDED(
ToLocalizedStringWithIntParams<2>(
"CompositorAnimationWarningContentTooSmall", aLocalizedString));
case Type::ContentTooLarge:
MOZ_ASSERT(mParams && mParams->Length() == 6,
"Parameter's length should be 6 for ContentTooLarge");

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

@ -20,7 +20,6 @@ namespace mozilla {
struct AnimationPerformanceWarning
{
enum class Type : uint8_t {
ContentTooSmall,
ContentTooLarge,
TransformBackfaceVisibilityHidden,
TransformPreserve3D,

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

@ -840,7 +840,7 @@ function testMultipleAnimationsWithGeometricAnimations() {
function testSmallElements() {
[
{
desc: 'opacity on too small element',
desc: 'opacity on small element',
frames: {
opacity: [0, 1]
},
@ -855,13 +855,12 @@ function testSmallElements() {
expected: [
{
property: 'opacity',
runningOnCompositor: false,
warning: /Animation cannot be run on the compositor because frame size \(8, 8\) is smaller than \(16, 16\)/
runningOnCompositor: true
}
]
},
{
desc: 'transform on too small element',
desc: 'transform on small element',
frames: {
transform: ['translate(0px)', 'translate(100px)']
},
@ -869,8 +868,7 @@ function testSmallElements() {
expected: [
{
property: 'transform',
runningOnCompositor: false,
warning: /Animation cannot be run on the compositor because frame size \(8, 8\) is smaller than \(16, 16\)/
runningOnCompositor: true
}
]
},

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

@ -5,6 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GroupedSHistory.h"
#include "mozilla/dom/Promise.h"
#include "TabParent.h"
#include "PartialSHistory.h"

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

@ -11,6 +11,9 @@
#include "nsIGroupedSHistory.h"
#include "nsIPartialSHistory.h"
#include "nsTArray.h"
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWeakReference.h"
namespace mozilla {

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

@ -3444,12 +3444,12 @@ nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
// static
nsresult
nsContentUtils::NameChanged(mozilla::dom::NodeInfo* aNodeInfo, nsIAtom* aName,
mozilla::dom::NodeInfo** aResult)
nsContentUtils::QNameChanged(mozilla::dom::NodeInfo* aNodeInfo, nsIAtom* aName,
mozilla::dom::NodeInfo** aResult)
{
nsNodeInfoManager *niMgr = aNodeInfo->NodeInfoManager();
*aResult = niMgr->GetNodeInfo(aName, aNodeInfo->GetPrefixAtom(),
*aResult = niMgr->GetNodeInfo(aName, nullptr,
aNodeInfo->NamespaceID(),
aNodeInfo->NodeType(),
aNodeInfo->GetExtraName()).take();

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

@ -796,11 +796,13 @@ public:
static bool IsDraggableLink(const nsIContent* aContent);
/**
* Convenience method to create a new nodeinfo that differs only by name
* from aNodeInfo.
* Convenience method to create a new nodeinfo that differs only by prefix and
* name from aNodeInfo. The new nodeinfo's name is set to aName, and prefix is
* set to null.
*/
static nsresult NameChanged(mozilla::dom::NodeInfo* aNodeInfo, nsIAtom* aName,
mozilla::dom::NodeInfo** aResult);
static nsresult QNameChanged(mozilla::dom::NodeInfo* aNodeInfo,
nsIAtom* aName,
mozilla::dom::NodeInfo** aResult);
/**
* Returns the appropriate event argument names for the specified

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

@ -755,8 +755,13 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
*aActionTaken = false;
}
NS_ASSERTION(aEventMessage == eCut || aEventMessage == eCopy ||
aEventMessage == ePaste,
EventMessage originalEventMessage = aEventMessage;
if (originalEventMessage == ePasteNoFormatting) {
originalEventMessage = ePaste;
}
NS_ASSERTION(originalEventMessage == eCut || originalEventMessage == eCopy ||
originalEventMessage == ePaste,
"Invalid clipboard event type");
nsCOMPtr<nsIPresShell> presShell = aPresShell;
@ -813,10 +818,10 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
if (chromeShell || Preferences::GetBool("dom.event.clipboardevents.enabled", true)) {
clipboardData =
new DataTransfer(doc->GetScopeObject(), aEventMessage,
aEventMessage == ePaste, aClipboardType);
originalEventMessage == ePaste, aClipboardType);
nsEventStatus status = nsEventStatus_eIgnore;
InternalClipboardEvent evt(true, aEventMessage);
InternalClipboardEvent evt(true, originalEventMessage);
evt.mClipboardData = clipboardData;
EventDispatcher::Dispatch(content, presShell->GetPresContext(), &evt,
nullptr, &status);
@ -827,7 +832,7 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
// No need to do anything special during a paste. Either an event listener
// took care of it and cancelled the event, or the caller will handle it.
// Return true to indicate that the event wasn't cancelled.
if (aEventMessage == ePaste) {
if (originalEventMessage == ePaste) {
// Clear and mark the clipboardData as readonly. This prevents someone
// from reading the clipboard contents after the paste event has fired.
if (clipboardData) {
@ -867,7 +872,7 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
// when cutting non-editable content, do nothing
// XXX this is probably the wrong editable flag to check
if (aEventMessage != eCut || content->IsEditable()) {
if (originalEventMessage != eCut || content->IsEditable()) {
// get the data from the selection if any
bool isCollapsed;
sel->GetIsCollapsed(&isCollapsed);

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

@ -1650,9 +1650,9 @@ nsDOMWindowUtils::SuppressEventHandling(bool aSuppress)
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
if (aSuppress) {
doc->SuppressEventHandling(nsIDocument::eEvents);
doc->SuppressEventHandling();
} else {
doc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, true);
doc->UnsuppressEventHandlingAndFireEvents(true);
}
return NS_OK;

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

@ -1358,7 +1358,6 @@ nsIDocument::nsIDocument()
mPresShell(nullptr),
mSubtreeModifiedDepth(0),
mEventsSuppressed(0),
mAnimationsPaused(0),
mExternalScriptsBeingEvaluated(0),
mFrameRequestCallbackCounter(0),
mStaticCloneCount(0),
@ -3811,7 +3810,7 @@ nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
void
nsDocument::MaybeRescheduleAnimationFrameNotifications()
{
if (!mPresShell || !IsEventHandlingEnabled() || AnimationsPaused()) {
if (!mPresShell || !IsEventHandlingEnabled()) {
// bail out for now, until one of those conditions changes
return;
}
@ -3874,7 +3873,7 @@ void
nsDocument::DeleteShell()
{
mExternalResourceMap.HideViewers();
if (IsEventHandlingEnabled() && !AnimationsPaused()) {
if (IsEventHandlingEnabled()) {
RevokeAnimationFrameNotifications();
}
if (nsPresContext* presContext = mPresShell->GetPresContext()) {
@ -4656,7 +4655,7 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
// our layout history state now.
mLayoutHistoryState = GetLayoutHistoryState();
if (mPresShell && !EventHandlingSuppressed() && !AnimationsPaused()) {
if (mPresShell && !EventHandlingSuppressed()) {
RevokeAnimationFrameNotifications();
}
@ -9325,44 +9324,28 @@ nsIDocument::GetReadyState(nsAString& aReadyState) const
}
}
namespace {
struct SuppressArgs
{
nsIDocument::SuppressionType mWhat;
uint32_t mIncrease;
};
} // namespace
static bool
SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData)
{
SuppressArgs* args = static_cast<SuppressArgs*>(aData);
aDocument->SuppressEventHandling(args->mWhat, args->mIncrease);
aDocument->SuppressEventHandling(*static_cast<uint32_t*>(aData));
return true;
}
void
nsDocument::SuppressEventHandling(nsIDocument::SuppressionType aWhat,
uint32_t aIncrease)
nsDocument::SuppressEventHandling(uint32_t aIncrease)
{
if (mEventsSuppressed == 0 && mAnimationsPaused == 0 &&
aIncrease != 0 && mPresShell && mScriptGlobalObject) {
if (mEventsSuppressed == 0 && aIncrease != 0 && mPresShell &&
mScriptGlobalObject) {
RevokeAnimationFrameNotifications();
}
if (aWhat == eAnimationsOnly) {
mAnimationsPaused += aIncrease;
} else {
mEventsSuppressed += aIncrease;
for (uint32_t i = 0; i < aIncrease; ++i) {
ScriptLoader()->AddExecuteBlocker();
}
mEventsSuppressed += aIncrease;
for (uint32_t i = 0; i < aIncrease; ++i) {
ScriptLoader()->AddExecuteBlocker();
}
SuppressArgs args = { aWhat, aIncrease };
EnumerateSubDocuments(SuppressEventHandlingInDocument, &args);
EnumerateSubDocuments(SuppressEventHandlingInDocument, &aIncrease);
}
static void
@ -9664,62 +9647,35 @@ private:
nsTArray<nsCOMPtr<nsIDocument> > mDocuments;
};
namespace {
struct UnsuppressArgs
{
explicit UnsuppressArgs(nsIDocument::SuppressionType aWhat)
: mWhat(aWhat)
{
}
nsIDocument::SuppressionType mWhat;
nsTArray<nsCOMPtr<nsIDocument>> mDocs;
};
} // namespace
static bool
GetAndUnsuppressSubDocuments(nsIDocument* aDocument,
void* aData)
{
UnsuppressArgs* args = static_cast<UnsuppressArgs*>(aData);
if (args->mWhat != nsIDocument::eAnimationsOnly &&
aDocument->EventHandlingSuppressed() > 0) {
if (aDocument->EventHandlingSuppressed() > 0) {
static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression();
aDocument->ScriptLoader()->RemoveExecuteBlocker();
} else if (args->mWhat == nsIDocument::eAnimationsOnly &&
aDocument->AnimationsPaused()) {
static_cast<nsDocument*>(aDocument)->ResumeAnimations();
}
if (args->mWhat != nsIDocument::eAnimationsOnly) {
// No need to remember documents if we only care about animation frames.
args->mDocs.AppendElement(aDocument);
}
nsTArray<nsCOMPtr<nsIDocument> >* docs =
static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData);
docs->AppendElement(aDocument);
aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData);
return true;
}
void
nsDocument::UnsuppressEventHandlingAndFireEvents(nsIDocument::SuppressionType aWhat,
bool aFireEvents)
nsDocument::UnsuppressEventHandlingAndFireEvents(bool aFireEvents)
{
UnsuppressArgs args(aWhat);
GetAndUnsuppressSubDocuments(this, &args);
if (aWhat == nsIDocument::eAnimationsOnly) {
// No need to fire events if we only care about animations here.
return;
}
nsTArray<nsCOMPtr<nsIDocument>> documents;
GetAndUnsuppressSubDocuments(this, &documents);
if (aFireEvents) {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(args.mDocs);
nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(documents);
Dispatch("nsDelayedEventDispatcher", TaskCategory::Other, ded.forget());
} else {
FireOrClearDelayedEvents(args.mDocs, false);
FireOrClearDelayedEvents(documents, false);
}
}
@ -10044,8 +10000,7 @@ nsIDocument::ScheduleFrameRequestCallback(FrameRequestCallback& aCallback,
DebugOnly<FrameRequest*> request =
mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle));
NS_ASSERTION(request, "This is supposed to be infallible!");
if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled() &&
!AnimationsPaused()) {
if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled()) {
mPresShell->GetPresContext()->RefreshDriver()->
ScheduleFrameRequestCallbacks(this);
}
@ -10060,7 +10015,7 @@ nsIDocument::CancelFrameRequestCallback(int32_t aHandle)
// mFrameRequestCallbacks is stored sorted by handle
if (mFrameRequestCallbacks.RemoveElementSorted(aHandle) &&
mFrameRequestCallbacks.IsEmpty() &&
mPresShell && IsEventHandlingEnabled() && !AnimationsPaused()) {
mPresShell && IsEventHandlingEnabled()) {
mPresShell->GetPresContext()->RefreshDriver()->
RevokeFrameRequestCallbacks(this);
}

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

@ -907,11 +907,9 @@ public:
virtual mozilla::PendingAnimationTracker*
GetOrCreatePendingAnimationTracker() override;
virtual void SuppressEventHandling(SuppressionType aWhat,
uint32_t aIncrease) override;
virtual void SuppressEventHandling(uint32_t aIncrease) override;
virtual void UnsuppressEventHandlingAndFireEvents(SuppressionType aWhat,
bool aFireEvents) override;
virtual void UnsuppressEventHandlingAndFireEvents(bool aFireEvents) override;
void DecreaseEventSuppression() {
MOZ_ASSERT(mEventsSuppressed);
@ -919,12 +917,6 @@ public:
MaybeRescheduleAnimationFrameNotifications();
}
void ResumeAnimations() {
MOZ_ASSERT(mAnimationsPaused);
--mAnimationsPaused;
MaybeRescheduleAnimationFrameNotifications();
}
virtual nsIDocument* GetTemplateContentsOwner() override;
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument,

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

@ -2004,11 +2004,8 @@ nsGlobalWindow::FreeInnerObjects()
mDocBaseURI = mDoc->GetDocBaseURI();
while (mDoc->EventHandlingSuppressed()) {
mDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, false);
mDoc->UnsuppressEventHandlingAndFireEvents(false);
}
// Note: we don't have to worry about eAnimationsOnly suppressions because
// they won't leak.
}
// Remove our reference to the document and the document principal.
@ -9266,7 +9263,7 @@ nsGlobalWindow::EnterModalState()
topWin->mSuspendedDoc = topDoc;
if (topDoc) {
topDoc->SuppressEventHandling(nsIDocument::eEvents);
topDoc->SuppressEventHandling();
}
nsGlobalWindow* inner = topWin->GetCurrentInnerWindowInternal();
@ -9303,8 +9300,7 @@ nsGlobalWindow::LeaveModalState()
if (topWin->mSuspendedDoc) {
nsCOMPtr<nsIDocument> currentDoc = topWin->GetExtantDoc();
topWin->mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents,
currentDoc == topWin->mSuspendedDoc);
topWin->mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(currentDoc == topWin->mSuspendedDoc);
topWin->mSuspendedDoc = nullptr;
}
}

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

@ -2158,31 +2158,20 @@ public:
virtual mozilla::PendingAnimationTracker*
GetOrCreatePendingAnimationTracker() = 0;
enum SuppressionType {
eAnimationsOnly = 0x1,
// Note that suppressing events also suppresses animation frames, so
// there's no need to split out events in its own bitmask.
eEvents = 0x3,
};
/**
* Prevents user initiated events from being dispatched to the document and
* subdocuments.
*/
virtual void SuppressEventHandling(SuppressionType aWhat,
uint32_t aIncrease = 1) = 0;
virtual void SuppressEventHandling(uint32_t aIncrease = 1) = 0;
/**
* Unsuppress event handling.
* @param aFireEvents If true, delayed events (focus/blur) will be fired
* asynchronously.
*/
virtual void UnsuppressEventHandlingAndFireEvents(SuppressionType aWhat,
bool aFireEvents) = 0;
virtual void UnsuppressEventHandlingAndFireEvents(bool aFireEvents) = 0;
uint32_t EventHandlingSuppressed() const { return mEventsSuppressed; }
uint32_t AnimationsPaused() const { return mAnimationsPaused; }
bool IsEventHandlingEnabled() {
return !EventHandlingSuppressed() && mScriptGlobalObject;
@ -3282,8 +3271,6 @@ protected:
uint32_t mEventsSuppressed;
uint32_t mAnimationsPaused;
/**
* The number number of external scripts (ones with the src attribute) that
* have this document as their owner and that are being evaluated right now.

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

@ -2787,7 +2787,7 @@ HandlePrerenderingViolation(nsPIDOMWindowInner* aWindow)
// Suspend event handling on the document
nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
if (doc) {
doc->SuppressEventHandling(nsIDocument::eEvents);
doc->SuppressEventHandling();
}
}

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

@ -21,7 +21,14 @@ class UnionMember
{
AlignedStorage2<T> mStorage;
// Copy construction can't be supported because C++ requires that any enclosed
// T be initialized in a way C++ knows about -- that is, by |new| or similar.
UnionMember(const UnionMember&) = delete;
public:
UnionMember() = default;
~UnionMember() = default;
T& SetValue()
{
new (mStorage.addr()) T();

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

@ -36,22 +36,13 @@ GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_typ
return true;
}
if (str.EqualsLiteral("experimental-webgl")) {
if (str.EqualsLiteral("webgl") ||
str.EqualsLiteral("experimental-webgl"))
{
*out_type = dom::CanvasContextType::WebGL1;
return true;
}
#ifdef MOZ_WEBGL_CONFORMANT
if (str.EqualsLiteral("webgl")) {
/* WebGL 1.0, $2.1 "Context Creation":
* If the user agent supports both the webgl and experimental-webgl
* canvas context types, they shall be treated as aliases.
*/
*out_type = dom::CanvasContextType::WebGL1;
return true;
}
#endif
if (WebGL2Context::IsSupported()) {
if (str.EqualsLiteral("webgl2")) {
*out_type = dom::CanvasContextType::WebGL2;

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

@ -2,7 +2,7 @@
default-preferences pref(canvas.filters.enabled,true)
fails asserts-if(stylo,2) == default-color.html default-color.html # bug 1324700
# == drop-shadow.html drop-shadow.html
fails asserts-if(stylo,2) == drop-shadow.html drop-shadow.html # bug 1324700
fails asserts-if(stylo,2) == drop-shadow-transformed.html drop-shadow-transformed.html # bug 1324700
fails asserts-if(stylo,2) == global-alpha.html global-alpha.html # bug 1324700
fails asserts-if(stylo,2) == global-composite-operation.html global-composite-operation.html # bug 1324700

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

@ -59,7 +59,7 @@ skip-if(Android) == webgl-color-test.html?frame=1&aa&________&premult&alpha webg
skip-if(Android) == webgl-color-test.html?frame=1&__&preserve&premult&alpha webgl-color-test.html?frame=1&__&preserve&premult&alpha
skip-if(Android) == webgl-color-test.html?frame=1&aa&preserve&premult&alpha webgl-color-test.html?frame=1&aa&preserve&premult&alpha
# == webgl-color-test.html?frame=6&__&________&_______&_____ webgl-color-test.html?frame=6&__&________&_______&_____
== webgl-color-test.html?frame=6&__&________&_______&_____ webgl-color-test.html?frame=6&__&________&_______&_____
skip-if(Android) == webgl-color-test.html?frame=6&aa&________&_______&_____ webgl-color-test.html?frame=6&aa&________&_______&_____
skip-if(Android) == webgl-color-test.html?frame=6&__&preserve&_______&_____ webgl-color-test.html?frame=6&__&preserve&_______&_____
skip-if(Android) == webgl-color-test.html?frame=6&aa&preserve&_______&_____ webgl-color-test.html?frame=6&aa&preserve&_______&_____

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

@ -1,6 +1,6 @@
# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
# == bug863728-1.html bug863728-1.html
== bug863728-1.html bug863728-1.html
== bug863728-2.html bug863728-2.html
== bug863728-3.html bug863728-3.html
# == bug945215-1.html bug945215-1.html
== bug945215-1.html bug945215-1.html
== bug945215-2.html bug945215-2.html

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

@ -105,8 +105,11 @@ DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
aEventMessage == eDragStart) {
mReadOnly = false;
} else if (mIsExternal) {
if (aEventMessage == ePaste) {
CacheExternalClipboardFormats();
if (aEventMessage == ePasteNoFormatting) {
mEventMessage = ePaste;
CacheExternalClipboardFormats(true);
} else if (aEventMessage == ePaste) {
CacheExternalClipboardFormats(false);
} else if (aEventMessage >= eDragDropEventFirst &&
aEventMessage <= eDragDropEventLast) {
CacheExternalDragFormats();
@ -1356,7 +1359,7 @@ DataTransfer::CacheExternalDragFormats()
}
void
DataTransfer::CacheExternalClipboardFormats()
DataTransfer::CacheExternalClipboardFormats(bool aPlainTextOnly)
{
NS_ASSERTION(mEventMessage == ePaste,
"caching clipboard data for invalid event");
@ -1375,6 +1378,17 @@ DataTransfer::CacheExternalClipboardFormats()
nsCOMPtr<nsIPrincipal> sysPrincipal;
ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
if (aPlainTextOnly) {
bool supported;
const char* unicodeMime[] = { kUnicodeMime };
clipboard->HasDataMatchingFlavors(unicodeMime, 1, mClipboardType,
&supported);
if (supported) {
CacheExternalData(kUnicodeMime, 0, sysPrincipal, false);
}
return;
}
// Check if the clipboard has any files
bool hasFileData = false;
const char *fileMime[] = { kFileMime };

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

@ -307,7 +307,7 @@ protected:
void CacheExternalDragFormats();
// caches the formats that exist in the clipboard
void CacheExternalClipboardFormats();
void CacheExternalClipboardFormats(bool aPlainTextOnly);
FileList* GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal);
nsresult GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,

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

@ -139,6 +139,8 @@ support-files = bug1017086_inner.html
[test_bug1248459.html]
[test_bug1264380.html]
run-if = (e10s && os != "win") # Bug 1270043, crash at windows platforms; Bug1264380 comment 20, nsDragService::InvokeDragSessionImpl behaves differently among platform implementations in non-e10s mode which prevents us to check the validity of nsIDragService::getCurrentSession() consistently via synthesize mouse clicks in non-e10s mode.
[test_bug1327798.html]
subsuite = clipboard
[test_clickevent_on_input.html]
skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
[test_continuous_wheel_events.html]

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

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>Test for bug 1327798</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?=id=1327798">Mozilla Bug 1327798</a>
<p id="display"></p>
<div id="content" style="display: none;"></div>
<div contenteditable="true" id="editable1"><b>Formatted Text</b><br></div>
<pre>
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(() => {
var editable = document.getElementById("editable1");
editable.focus();
window.getSelection().selectAllChildren(editable1);
SpecialPowers.doCommand(window, "cmd_copy");
//--------- now check the content of the clipboard
var clipboard = SpecialPowers.Cc["@mozilla.org/widget/clipboard;1"]
.getService(SpecialPowers.Ci.nsIClipboard);
// does the clipboard contain text/unicode data ?
ok(clipboard.hasDataMatchingFlavors(["text/unicode"], 1, clipboard.kGlobalClipboard),
"clipboard contains unicode text");
// does the clipboard contain text/html data ?
ok(clipboard.hasDataMatchingFlavors(["text/html"], 1, clipboard.kGlobalClipboard),
"clipboard contains html text");
window.addEventListener("paste", (e) => {
ok(e.clipboardData.types.indexOf('text/html'), -1, "clipboardData shouldn't have text/html");
ok(e.clipboardData.getData('text/plain'), "Formatted Text", "getData(text/plain) should return plain text");
SimpleTest.finish();
});
SpecialPowers.doCommand(window, "cmd_pasteNoFormatting");
});
</script>
</pre>
</body>
</html>

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

@ -13,6 +13,7 @@
#include "mozilla/dom/FileCreatorHelper.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/Promise.h"
#include "nsXULAppAPI.h"
namespace mozilla {
namespace dom {
@ -60,6 +61,7 @@ File::CreateMemoryFile(nsISupports* aParent, void* aMemoryBuffer,
/* static */ already_AddRefed<File>
File::CreateFromFile(nsISupports* aParent, nsIFile* aFile)
{
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
RefPtr<File> file = new File(aParent, new FileBlobImpl(aFile));
return file.forget();
}
@ -68,6 +70,7 @@ File::CreateFromFile(nsISupports* aParent, nsIFile* aFile)
File::CreateFromFile(nsISupports* aParent, nsIFile* aFile,
const nsAString& aName, const nsAString& aContentType)
{
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
RefPtr<File> file = new File(aParent,
new FileBlobImpl(aFile, aName, aContentType));
return file.forget();
@ -166,8 +169,6 @@ File::CreateFromNsIFile(const GlobalObject& aGlobal,
SystemCallerGuarantee aGuarantee,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
RefPtr<Promise> promise =

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