зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to graphics
MozReview-Commit-ID: BMjcETZ38gw
This commit is contained in:
Коммит
92d49690c6
|
@ -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 =
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче