зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to inbound. a=merge CLOSED TREE
This commit is contained in:
Коммит
d4554b4873
|
@ -11,15 +11,6 @@
|
|||
# Exclude expected objdirs.
|
||||
obj*/**
|
||||
|
||||
# We ignore all these directories by default, until we get them enabled.
|
||||
# If you are enabling a directory, please add directory specific exclusions
|
||||
# below.
|
||||
layout/**
|
||||
netwerk/cookie/test/browser/**
|
||||
netwerk/test/browser/**
|
||||
netwerk/test/mochitests/**
|
||||
netwerk/test/unit*/**
|
||||
|
||||
# We currently have no js files in these directories, so we ignore them by
|
||||
# default to aid ESLint's performance.
|
||||
build/**
|
||||
|
|
144
.eslintrc.js
144
.eslintrc.js
|
@ -193,5 +193,149 @@ module.exports = {
|
|||
"space-before-function-paren": "off",
|
||||
"space-infix-ops": "off",
|
||||
}
|
||||
}, {
|
||||
"files": [
|
||||
"netwerk/cookie/test/browser/**",
|
||||
"netwerk/test/browser/**",
|
||||
"netwerk/test/mochitests/**",
|
||||
"netwerk/test/unit*/**",
|
||||
],
|
||||
"rules": {
|
||||
"object-shorthand": "off",
|
||||
"mozilla/consistent-if-bracing": "off",
|
||||
"mozilla/reject-importGlobalProperties": "off",
|
||||
"mozilla/no-arbitrary-setTimeout": "off",
|
||||
"mozilla/no-define-cc-etc": "off",
|
||||
"mozilla/no-useless-parameters": "off",
|
||||
"mozilla/no-useless-run-test": "off",
|
||||
"mozilla/use-chromeutils-generateqi": "off",
|
||||
"mozilla/use-chromeutils-import": "off",
|
||||
"mozilla/use-default-preference-values": "off",
|
||||
"mozilla/use-services": "off",
|
||||
"consistent-return": "off",
|
||||
"no-array-constructor": "off",
|
||||
"no-extra-boolean-cast": "off",
|
||||
"no-eval": "off",
|
||||
"no-else-return": "off",
|
||||
"no-global-assign": "off",
|
||||
"no-lonely-if": "off",
|
||||
"no-nested-ternary": "off",
|
||||
"no-new-wrappers": "off",
|
||||
"no-redeclare": "off",
|
||||
"no-return-await": "off",
|
||||
"no-sequences": "off",
|
||||
"no-shadow": "off",
|
||||
"no-throw-literal": "off",
|
||||
"no-undef": "off",
|
||||
"no-unreachable": "off",
|
||||
"no-unused-vars": "off",
|
||||
"no-useless-return": "off",
|
||||
|
||||
// Not enabling the rules below for now pending prettier roll-out.
|
||||
"arrow-spacing": "off",
|
||||
"block-spacing": "off",
|
||||
"brace-style": "off",
|
||||
"comma-dangle": "off",
|
||||
"comma-spacing": "off",
|
||||
"comma-style": "off",
|
||||
"eol-last": "off",
|
||||
"func-call-spacing": "off",
|
||||
"generator-star-spacing": "off",
|
||||
"key-spacing": "off",
|
||||
"keyword-spacing": "off",
|
||||
"no-extra-semi": "off",
|
||||
"no-tabs": "off",
|
||||
"no-mixed-spaces-and-tabs": "off",
|
||||
"no-multi-spaces": "off",
|
||||
"no-trailing-spaces": "off",
|
||||
"no-whitespace-before-property": "off",
|
||||
"padded-blocks": "off",
|
||||
"quotes": "off",
|
||||
"rest-spread-spacing": "off",
|
||||
"semi": "off",
|
||||
"space-before-blocks": "off",
|
||||
"space-before-function-paren": "off",
|
||||
"space-infix-ops": "off",
|
||||
"space-unary-ops": "off",
|
||||
"spaced-comment": "off",
|
||||
}
|
||||
}, {
|
||||
"files": [
|
||||
"layout/**",
|
||||
],
|
||||
"rules": {
|
||||
"object-shorthand": "off",
|
||||
"mozilla/avoid-removeChild": "off",
|
||||
"mozilla/consistent-if-bracing": "off",
|
||||
"mozilla/reject-importGlobalProperties": "off",
|
||||
"mozilla/no-arbitrary-setTimeout": "off",
|
||||
"mozilla/no-define-cc-etc": "off",
|
||||
"mozilla/no-useless-parameters": "off",
|
||||
"mozilla/no-useless-run-test": "off",
|
||||
"mozilla/use-chromeutils-generateqi": "off",
|
||||
"mozilla/use-chromeutils-import": "off",
|
||||
"mozilla/use-default-preference-values": "off",
|
||||
"mozilla/use-includes-instead-of-indexOf": "off",
|
||||
"mozilla/use-services": "off",
|
||||
"mozilla/use-ownerGlobal": "off",
|
||||
"complexity": "off",
|
||||
"consistent-return": "off",
|
||||
"dot-notation": "off",
|
||||
"no-array-constructor": "off",
|
||||
"no-caller": "off",
|
||||
"no-cond-assign": "off",
|
||||
"no-extra-boolean-cast": "off",
|
||||
"no-eval": "off",
|
||||
"no-else-return": "off",
|
||||
"no-func-assign": "off",
|
||||
"no-global-assign": "off",
|
||||
"no-implied-eval": "off",
|
||||
"no-lonely-if": "off",
|
||||
"no-nested-ternary": "off",
|
||||
"no-new-wrappers": "off",
|
||||
"no-redeclare": "off",
|
||||
"no-restricted-globals": "off",
|
||||
"no-return-await": "off",
|
||||
"no-sequences": "off",
|
||||
"no-throw-literal": "off",
|
||||
"no-useless-concat": "off",
|
||||
"no-undef": "off",
|
||||
"no-unreachable": "off",
|
||||
"no-unsanitized/method": "off",
|
||||
"no-unsanitized/property": "off",
|
||||
"no-unsafe-negation": "off",
|
||||
"no-unused-vars": "off",
|
||||
"no-useless-return": "off",
|
||||
|
||||
// Not enabling the rules below for now pending prettier roll-out.
|
||||
"arrow-spacing": "off",
|
||||
"block-spacing": "off",
|
||||
"brace-style": "off",
|
||||
"comma-dangle": "off",
|
||||
"comma-spacing": "off",
|
||||
"comma-style": "off",
|
||||
"eol-last": "off",
|
||||
"func-call-spacing": "off",
|
||||
"generator-star-spacing": "off",
|
||||
"linebreak-style": "off",
|
||||
"key-spacing": "off",
|
||||
"keyword-spacing": "off",
|
||||
"no-extra-semi": "off",
|
||||
"no-tabs": "off",
|
||||
"no-mixed-spaces-and-tabs": "off",
|
||||
"no-multi-spaces": "off",
|
||||
"no-trailing-spaces": "off",
|
||||
"no-unexpected-multiline": "off",
|
||||
"no-whitespace-before-property": "off",
|
||||
"padded-blocks": "off",
|
||||
"quotes": "off",
|
||||
"rest-spread-spacing": "off",
|
||||
"semi": "off",
|
||||
"space-before-blocks": "off",
|
||||
"space-before-function-paren": "off",
|
||||
"space-infix-ops": "off",
|
||||
"space-unary-ops": "off",
|
||||
"spaced-comment": "off",
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
|
|
@ -742,8 +742,7 @@ void Accessible::TakeFocus() const {
|
|||
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
if (fm) {
|
||||
AutoHandlingUserInputStatePusher inputStatePusher(true, nullptr,
|
||||
focusContent->OwnerDoc());
|
||||
AutoHandlingUserInputStatePusher inputStatePusher(true);
|
||||
// XXXbz: Can we actually have a non-element content here?
|
||||
RefPtr<Element> element =
|
||||
focusContent->IsElement() ? focusContent->AsElement() : nullptr;
|
||||
|
|
|
@ -294,8 +294,7 @@ void DocAccessible::TakeFocus() const {
|
|||
// Focus the document.
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
RefPtr<dom::Element> newFocus;
|
||||
AutoHandlingUserInputStatePusher inputStatePusher(true, nullptr,
|
||||
mDocumentNode);
|
||||
AutoHandlingUserInputStatePusher inputStatePusher(true);
|
||||
fm->MoveFocus(mDocumentNode->GetWindow(), nullptr,
|
||||
nsFocusManager::MOVEFOCUS_ROOT, 0, getter_AddRefs(newFocus));
|
||||
}
|
||||
|
|
|
@ -498,9 +498,16 @@ pref("browser.tabs.showAudioPlayingIcon", true);
|
|||
// This should match Chromium's audio indicator delay.
|
||||
pref("browser.tabs.delayHidingAudioPlayingIconMS", 3000);
|
||||
|
||||
// Pref to control whether we use separate privileged content processes.
|
||||
#if defined(NIGHTLY_BUILD) && !defined(MOZ_ASAN)
|
||||
// Pref to control whether we use a separate privileged content process
|
||||
// for about: pages. This pref name did not age well: we will have multiple
|
||||
// types of privileged content processes, each with different privileges.
|
||||
// types of privleged content processes, each with different privleges.
|
||||
pref("browser.tabs.remote.separatePrivilegedContentProcess", true);
|
||||
// Pref to control whether we use a separate privileged content process
|
||||
// for certain mozilla webpages (which are listed in the pref
|
||||
// browser.tabs.remote.separatedMozillaDomains).
|
||||
pref("browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", true);
|
||||
#endif
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
|
|
|
@ -977,7 +977,7 @@ var gIdentityHandler = {
|
|||
let ctx = canvas.getContext("2d");
|
||||
ctx.font = `${14 * scale}px sans-serif`;
|
||||
ctx.fillText(`${value}`, 20 * scale, 14 * scale);
|
||||
let tabIcon = document.getAnonymousElementByAttribute(gBrowser.selectedTab, "anonid", "tab-icon-image");
|
||||
let tabIcon = gBrowser.selectedTab.iconImage;
|
||||
let image = new Image();
|
||||
image.src = tabIcon.src;
|
||||
ctx.drawImage(image, 0, 0, 16 * scale, 16 * scale);
|
||||
|
|
|
@ -158,10 +158,6 @@ panelview[mainview] > .panel-header {
|
|||
visibility: hidden; /* temporary space to keep a tab's close button under the cursor */
|
||||
}
|
||||
|
||||
.tabbrowser-tab {
|
||||
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
|
||||
}
|
||||
|
||||
.tabbrowser-tab:not([pinned]) {
|
||||
-moz-box-flex: 100;
|
||||
max-width: 225px;
|
||||
|
|
|
@ -96,6 +96,7 @@
|
|||
Services.scriptloader.loadSubScript("chrome://browser/content/browser-sidebar.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/browser-tabsintitlebar.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser-tab.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/search/autocomplete-popup.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/search/searchbar.js", this);
|
||||
|
||||
|
@ -728,7 +729,7 @@
|
|||
setfocus="false"
|
||||
tooltip="tabbrowser-tab-tooltip"
|
||||
stopwatchid="FX_TAB_CLICK_MS">
|
||||
<tab class="tabbrowser-tab" selected="true" visuallyselected="true" fadein="true"/>
|
||||
<tab is="tabbrowser-tab" class="tabbrowser-tab" selected="true" visuallyselected="true" fadein="true"/>
|
||||
</tabs>
|
||||
|
||||
<toolbarbutton id="new-tab-button"
|
||||
|
|
|
@ -63,12 +63,6 @@ addEventListener("DOMInputPasswordAdded", function(event) {
|
|||
let formLike = LoginFormFactory.createFromField(event.originalTarget);
|
||||
InsecurePasswordUtils.reportInsecurePasswords(formLike);
|
||||
});
|
||||
addEventListener("DOMAutoComplete", function(event) {
|
||||
if (shouldIgnoreLoginManagerEvent(event)) {
|
||||
return;
|
||||
}
|
||||
LoginManagerContent.onDOMAutoComplete(event);
|
||||
});
|
||||
|
||||
ContentMetaHandler.init(this);
|
||||
|
||||
|
|
|
@ -0,0 +1,574 @@
|
|||
/* 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";
|
||||
|
||||
// This is loaded into chrome windows with the subscript loader. Wrap in
|
||||
// a block to prevent accidentally leaking globals onto `window`.
|
||||
{
|
||||
class MozTabbrowserTab extends MozElements.MozTab {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.addEventListener("mouseover", (event) => {
|
||||
if (event.originalTarget.classList.contains("tab-close-button")) {
|
||||
this.mOverCloseButton = true;
|
||||
}
|
||||
this._mouseenter();
|
||||
});
|
||||
|
||||
this.addEventListener("mouseout", (event) => {
|
||||
if (event.originalTarget.classList.contains("tab-close-button")) {
|
||||
this.mOverCloseButton = false;
|
||||
}
|
||||
this._mouseleave();
|
||||
});
|
||||
|
||||
this.addEventListener("dragstart", (event) => {
|
||||
this.style.MozUserFocus = "";
|
||||
}, true);
|
||||
|
||||
this.addEventListener("dragstart", (event) => {
|
||||
if (this.mOverCloseButton) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
this.addEventListener("mousedown", (event) => {
|
||||
let tabContainer = this.parentNode;
|
||||
|
||||
if (tabContainer._closeTabByDblclick &&
|
||||
event.button == 0 &&
|
||||
event.detail == 1) {
|
||||
this._selectedOnFirstMouseDown = this.selected;
|
||||
}
|
||||
|
||||
if (this.selected) {
|
||||
this.style.MozUserFocus = "ignore";
|
||||
} else if (event.originalTarget.classList.contains("tab-close-button") ||
|
||||
event.originalTarget.classList.contains("tab-icon-sound") ||
|
||||
event.originalTarget.classList.contains("tab-icon-overlay")) {
|
||||
// Prevent tabbox.xml from selecting the tab.
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (event.button == 1) {
|
||||
gBrowser.warmupTab(gBrowser._findTabToBlurTo(this));
|
||||
}
|
||||
|
||||
if (event.button == 0 && tabContainer._multiselectEnabled) {
|
||||
let shiftKey = event.shiftKey;
|
||||
let accelKey = event.getModifierState("Accel");
|
||||
if (shiftKey) {
|
||||
const lastSelectedTab = gBrowser.lastMultiSelectedTab;
|
||||
if (!accelKey) {
|
||||
gBrowser.selectedTab = lastSelectedTab;
|
||||
|
||||
// Make sure selection is cleared when tab-switch doesn't happen.
|
||||
gBrowser.clearMultiSelectedTabs(false);
|
||||
}
|
||||
gBrowser.addRangeToMultiSelectedTabs(lastSelectedTab, this);
|
||||
|
||||
// Prevent tabbox.xml from selecting the tab.
|
||||
event.stopPropagation();
|
||||
} else if (accelKey) {
|
||||
// Ctrl (Cmd for mac) key is pressed
|
||||
if (this.multiselected) {
|
||||
gBrowser.removeFromMultiSelectedTabs(this, true);
|
||||
} else if (this != gBrowser.selectedTab) {
|
||||
gBrowser.addToMultiSelectedTabs(this, false);
|
||||
gBrowser.lastMultiSelectedTab = this;
|
||||
}
|
||||
|
||||
// Prevent tabbox.xml from selecting the tab.
|
||||
event.stopPropagation();
|
||||
} else if (!this.selected && this.multiselected) {
|
||||
gBrowser.lockClearMultiSelectionOnce();
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
||||
this.addEventListener("mouseup", (event) => {
|
||||
// Make sure that clear-selection is released.
|
||||
// Otherwise selection using Shift key may be broken.
|
||||
gBrowser.unlockClearMultiSelection();
|
||||
|
||||
this.style.MozUserFocus = "";
|
||||
});
|
||||
|
||||
this.addEventListener("click", (event) => {
|
||||
if (event.button != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getModifierState("Accel") || event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gBrowser.multiSelectedTabsCount > 0 &&
|
||||
!event.originalTarget.classList.contains("tab-close-button") &&
|
||||
!event.originalTarget.classList.contains("tab-icon-sound") &&
|
||||
!event.originalTarget.classList.contains("tab-icon-overlay")) {
|
||||
// Tabs were previously multi-selected and user clicks on a tab
|
||||
// without holding Ctrl/Cmd Key
|
||||
|
||||
// Force positional attributes to update when the
|
||||
// target (of the click) is the "active" tab.
|
||||
let updatePositionalAttr = gBrowser.selectedTab == this;
|
||||
|
||||
gBrowser.clearMultiSelectedTabs(updatePositionalAttr);
|
||||
}
|
||||
|
||||
if (event.originalTarget.classList.contains("tab-icon-sound") ||
|
||||
(event.originalTarget.classList.contains("tab-icon-overlay") &&
|
||||
(event.originalTarget.hasAttribute("soundplaying") ||
|
||||
event.originalTarget.hasAttribute("muted") ||
|
||||
event.originalTarget.hasAttribute("activemedia-blocked")))) {
|
||||
if (this.multiselected) {
|
||||
gBrowser.toggleMuteAudioOnMultiSelectedTabs(this);
|
||||
} else {
|
||||
this.toggleMuteAudio();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.originalTarget.classList.contains("tab-close-button")) {
|
||||
if (this.multiselected) {
|
||||
gBrowser.removeMultiSelectedTabs();
|
||||
} else {
|
||||
gBrowser.removeTab(this, {
|
||||
animate: true,
|
||||
byMouse: event.mozInputSource == MouseEvent.MOZ_SOURCE_MOUSE,
|
||||
});
|
||||
}
|
||||
// This enables double-click protection for the tab container
|
||||
// (see tabbrowser-tabs 'click' handler).
|
||||
gBrowser.tabContainer._blockDblClick = true;
|
||||
}
|
||||
});
|
||||
|
||||
this.addEventListener("dblclick", (event) => {
|
||||
if (event.button != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// for the one-close-button case
|
||||
if (event.originalTarget.classList.contains("tab-close-button")) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
let tabContainer = this.parentNode;
|
||||
if (tabContainer._closeTabByDblclick &&
|
||||
this._selectedOnFirstMouseDown &&
|
||||
this.selected &&
|
||||
!(event.originalTarget.classList.contains("tab-icon-sound") ||
|
||||
event.originalTarget.classList.contains("tab-icon-overlay"))) {
|
||||
gBrowser.removeTab(this, {
|
||||
animate: true,
|
||||
byMouse: event.mozInputSource == MouseEvent.MOZ_SOURCE_MOUSE,
|
||||
});
|
||||
}
|
||||
}, true);
|
||||
|
||||
this.addEventListener("animationend", (event) => {
|
||||
if (event.originalTarget.classList.contains("tab-loading-burst")) {
|
||||
this.removeAttribute("bursting");
|
||||
}
|
||||
});
|
||||
|
||||
this._selectedOnFirstMouseDown = false;
|
||||
|
||||
/**
|
||||
* Describes how the tab ended up in this mute state. May be any of:
|
||||
*
|
||||
* - undefined: The tabs mute state has never changed.
|
||||
* - null: The mute state was last changed through the UI.
|
||||
* - Any string: The ID was changed through an extension API. The string
|
||||
* must be the ID of the extension which changed it.
|
||||
*/
|
||||
this.muteReason = undefined;
|
||||
|
||||
this.mOverCloseButton = false;
|
||||
|
||||
this.mCorrespondingMenuitem = null;
|
||||
}
|
||||
|
||||
static get inheritedAttributes() {
|
||||
return {
|
||||
".tab-background": "selected=visuallyselected,fadein,multiselected",
|
||||
".tab-line": "selected=visuallyselected,multiselected,before-multiselected",
|
||||
".tab-loading-burst": "pinned,bursting,notselectedsinceload",
|
||||
".tab-content": "pinned,selected=visuallyselected,titlechanged,attention",
|
||||
".tab-throbber": "fadein,pinned,busy,progress,selected=visuallyselected",
|
||||
".tab-icon-pending": "fadein,pinned,busy,progress,selected=visuallyselected,pendingicon",
|
||||
".tab-icon-image": "src=image,triggeringprincipal=iconloadingprincipal,requestcontextid,fadein,pinned,selected=visuallyselected,busy,crashed,sharing",
|
||||
".tab-sharing-icon-overlay": "sharing,selected=visuallyselected,pinned",
|
||||
".tab-icon-overlay": "crashed,busy,soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected=visuallyselected,activemedia-blocked",
|
||||
".tab-label-container": "pinned,selected=visuallyselected,labeldirection",
|
||||
".tab-label": "text=label,accesskey,fadein,pinned,selected=visuallyselected,attention",
|
||||
".tab-icon-pip": "pictureinpicture",
|
||||
".tab-icon-sound": "soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected=visuallyselected,activemedia-blocked,pictureinpicture",
|
||||
".tab-close-button": "fadein,pinned,selected=visuallyselected",
|
||||
};
|
||||
}
|
||||
|
||||
get fragment() {
|
||||
if (!this._fragment) {
|
||||
this._fragment = MozXULElement.parseXULToFragment(`
|
||||
<stack class="tab-stack" flex="1">
|
||||
<vbox class="tab-background">
|
||||
<hbox class="tab-line"></hbox>
|
||||
<spacer flex="1" class="tab-background-inner"></spacer>
|
||||
<hbox class="tab-bottom-line"></hbox>
|
||||
</vbox>
|
||||
<hbox class="tab-loading-burst"></hbox>
|
||||
<hbox class="tab-content" align="center">
|
||||
<hbox class="tab-throbber" layer="true"></hbox>
|
||||
<hbox class="tab-icon-pending"></hbox>
|
||||
<image class="tab-icon-image" validate="never" role="presentation"></image>
|
||||
<image class="tab-sharing-icon-overlay" role="presentation"></image>
|
||||
<image class="tab-icon-overlay" role="presentation"></image>
|
||||
<hbox class="tab-label-container"
|
||||
onoverflow="this.setAttribute('textoverflow', 'true');"
|
||||
onunderflow="this.removeAttribute('textoverflow');"
|
||||
flex="1">
|
||||
<label class="tab-text tab-label" role="presentation"></label>
|
||||
</hbox>
|
||||
<image class="tab-icon-pip"
|
||||
role="presentation"></image>
|
||||
<image class="tab-icon-sound" role="presentation"></image>
|
||||
<image class="tab-close-button close-icon" role="presentation"></image>
|
||||
</hbox>
|
||||
</stack>
|
||||
`);
|
||||
}
|
||||
return this.ownerDocument.importNode(this._fragment, true);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.textContent = "";
|
||||
this.appendChild(this.fragment);
|
||||
this.initializeAttributeInheritance();
|
||||
this.setAttribute("context", "tabContextMenu");
|
||||
this._initialized = true;
|
||||
|
||||
if (!("_lastAccessed" in this)) {
|
||||
this.updateLastAccessed();
|
||||
}
|
||||
}
|
||||
|
||||
set _visuallySelected(val) {
|
||||
if (val == (this.getAttribute("visuallyselected") == "true")) {
|
||||
return val;
|
||||
}
|
||||
|
||||
if (val) {
|
||||
this.setAttribute("visuallyselected", "true");
|
||||
} else {
|
||||
this.removeAttribute("visuallyselected");
|
||||
}
|
||||
gBrowser._tabAttrModified(this, ["visuallyselected"]);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
set _selected(val) {
|
||||
// in e10s we want to only pseudo-select a tab before its rendering is done, so that
|
||||
// the rest of the system knows that the tab is selected, but we don't want to update its
|
||||
// visual status to selected until after we receive confirmation that its content has painted.
|
||||
if (val)
|
||||
this.setAttribute("selected", "true");
|
||||
else
|
||||
this.removeAttribute("selected");
|
||||
|
||||
// If we're non-e10s we should update the visual selection as well at the same time,
|
||||
// *or* if we're e10s and the visually selected tab isn't changing, in which case the
|
||||
// tab switcher code won't run and update anything else (like the before- and after-
|
||||
// selected attributes).
|
||||
if (!gMultiProcessBrowser || (val && this.hasAttribute("visuallyselected"))) {
|
||||
this._visuallySelected = val;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
get pinned() {
|
||||
return this.getAttribute("pinned") == "true";
|
||||
}
|
||||
|
||||
get hidden() {
|
||||
return this.getAttribute("hidden") == "true";
|
||||
}
|
||||
|
||||
get muted() {
|
||||
return this.getAttribute("muted") == "true";
|
||||
}
|
||||
|
||||
get multiselected() {
|
||||
return this.getAttribute("multiselected") == "true";
|
||||
}
|
||||
|
||||
get beforeMultiselected() {
|
||||
return this.getAttribute("before-multiselected") == "true";
|
||||
}
|
||||
|
||||
get userContextId() {
|
||||
return this.hasAttribute("usercontextid") ?
|
||||
parseInt(this.getAttribute("usercontextid")) :
|
||||
0;
|
||||
}
|
||||
|
||||
get soundPlaying() {
|
||||
return this.getAttribute("soundplaying") == "true";
|
||||
}
|
||||
|
||||
get pictureinpicture() {
|
||||
return this.getAttribute("pictureinpicture") == "true";
|
||||
}
|
||||
|
||||
get activeMediaBlocked() {
|
||||
return this.getAttribute("activemedia-blocked") == "true";
|
||||
}
|
||||
|
||||
get isEmpty() {
|
||||
// Determines if a tab is "empty", usually used in the context of determining
|
||||
// if it's ok to close the tab.
|
||||
if (this.hasAttribute("busy"))
|
||||
return false;
|
||||
|
||||
if (this.hasAttribute("customizemode"))
|
||||
return false;
|
||||
|
||||
let browser = this.linkedBrowser;
|
||||
if (!isBlankPageURL(browser.currentURI.spec))
|
||||
return false;
|
||||
|
||||
if (!checkEmptyPageOrigin(browser))
|
||||
return false;
|
||||
|
||||
if (browser.canGoForward || browser.canGoBack)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
get lastAccessed() {
|
||||
return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
|
||||
}
|
||||
|
||||
get _overPlayingIcon() {
|
||||
let iconVisible = this.hasAttribute("soundplaying") ||
|
||||
this.hasAttribute("muted") ||
|
||||
this.hasAttribute("activemedia-blocked");
|
||||
|
||||
let soundPlayingIcon = this.soundPlayingIcon;
|
||||
let overlayIcon = this.overlayIcon;
|
||||
return soundPlayingIcon && soundPlayingIcon.matches(":hover") ||
|
||||
(overlayIcon && overlayIcon.matches(":hover") && iconVisible);
|
||||
}
|
||||
|
||||
get soundPlayingIcon() {
|
||||
return this.querySelector(".tab-icon-sound");
|
||||
}
|
||||
|
||||
get overlayIcon() {
|
||||
return this.querySelector(".tab-icon-overlay");
|
||||
}
|
||||
|
||||
get throbber() {
|
||||
return this.querySelector(".tab-throbber");
|
||||
}
|
||||
|
||||
get iconImage() {
|
||||
return this.querySelector(".tab-icon-image");
|
||||
}
|
||||
|
||||
get sharingIcon() {
|
||||
return this.querySelector(".tab-sharing-icon-overlay");
|
||||
}
|
||||
|
||||
get textLabel() {
|
||||
return this.querySelector(".tab-label");
|
||||
}
|
||||
|
||||
get closeButton() {
|
||||
return this.querySelector(".tab-close-button");
|
||||
}
|
||||
|
||||
updateLastAccessed(aDate) {
|
||||
this._lastAccessed = this.selected ? Infinity : (aDate || Date.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* While it would make sense to track this in a field, the field will get nuked
|
||||
* once the node is gone from the DOM, which causes us to think the tab is not
|
||||
* closed, which causes us to make wrong decisions. So we use an expando instead.
|
||||
* <field name="closing">false</field>
|
||||
*/
|
||||
_mouseenter() {
|
||||
if (this.hidden || this.closing) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tabContainer = this.parentNode;
|
||||
let visibleTabs = tabContainer._getVisibleTabs();
|
||||
let tabIndex = visibleTabs.indexOf(this);
|
||||
|
||||
if (this.selected)
|
||||
tabContainer._handleTabSelect();
|
||||
|
||||
if (tabIndex == 0) {
|
||||
tabContainer._beforeHoveredTab = null;
|
||||
} else {
|
||||
let candidate = visibleTabs[tabIndex - 1];
|
||||
let separatedByScrollButton =
|
||||
tabContainer.getAttribute("overflow") == "true" &&
|
||||
candidate.pinned && !this.pinned;
|
||||
if (!candidate.selected && !separatedByScrollButton) {
|
||||
tabContainer._beforeHoveredTab = candidate;
|
||||
candidate.setAttribute("beforehovered", "true");
|
||||
}
|
||||
}
|
||||
|
||||
if (tabIndex == visibleTabs.length - 1) {
|
||||
tabContainer._afterHoveredTab = null;
|
||||
} else {
|
||||
let candidate = visibleTabs[tabIndex + 1];
|
||||
if (!candidate.selected) {
|
||||
tabContainer._afterHoveredTab = candidate;
|
||||
candidate.setAttribute("afterhovered", "true");
|
||||
}
|
||||
}
|
||||
|
||||
tabContainer._hoveredTab = this;
|
||||
if (this.linkedPanel && !this.selected) {
|
||||
this.linkedBrowser.unselectedTabHover(true);
|
||||
this.startUnselectedTabHoverTimer();
|
||||
}
|
||||
|
||||
// Prepare connection to host beforehand.
|
||||
SessionStore.speculativeConnectOnTabHover(this);
|
||||
|
||||
let tabToWarm = this;
|
||||
if (this.mOverCloseButton) {
|
||||
tabToWarm = gBrowser._findTabToBlurTo(this);
|
||||
}
|
||||
gBrowser.warmupTab(tabToWarm);
|
||||
}
|
||||
|
||||
_mouseleave() {
|
||||
let tabContainer = this.parentNode;
|
||||
if (tabContainer._beforeHoveredTab) {
|
||||
tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
|
||||
tabContainer._beforeHoveredTab = null;
|
||||
}
|
||||
if (tabContainer._afterHoveredTab) {
|
||||
tabContainer._afterHoveredTab.removeAttribute("afterhovered");
|
||||
tabContainer._afterHoveredTab = null;
|
||||
}
|
||||
|
||||
tabContainer._hoveredTab = null;
|
||||
if (this.linkedPanel && !this.selected) {
|
||||
this.linkedBrowser.unselectedTabHover(false);
|
||||
this.cancelUnselectedTabHoverTimer();
|
||||
}
|
||||
}
|
||||
|
||||
startUnselectedTabHoverTimer() {
|
||||
// Only record data when we need to.
|
||||
if (!this.linkedBrowser.shouldHandleUnselectedTabHover) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
|
||||
TelemetryStopwatch.start("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
|
||||
}
|
||||
|
||||
if (this._hoverTabTimer) {
|
||||
clearTimeout(this._hoverTabTimer);
|
||||
this._hoverTabTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
cancelUnselectedTabHoverTimer() {
|
||||
// Since we're listening "mouseout" event, instead of "mouseleave".
|
||||
// Every time the cursor is moving from the tab to its child node (icon),
|
||||
// it would dispatch "mouseout"(for tab) first and then dispatch
|
||||
// "mouseover" (for icon, eg: close button, speaker icon) soon.
|
||||
// It causes we would cancel present TelemetryStopwatch immediately
|
||||
// when cursor is moving on the icon, and then start a new one.
|
||||
// In order to avoid this situation, we could delay cancellation and
|
||||
// remove it if we get "mouseover" within very short period.
|
||||
this._hoverTabTimer = setTimeout(() => {
|
||||
if (TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
|
||||
TelemetryStopwatch.cancel("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
finishUnselectedTabHoverTimer() {
|
||||
// Stop timer when the tab is opened.
|
||||
if (TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
|
||||
TelemetryStopwatch.finish("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
|
||||
}
|
||||
}
|
||||
|
||||
toggleMuteAudio(aMuteReason) {
|
||||
let browser = this.linkedBrowser;
|
||||
let modifiedAttrs = [];
|
||||
let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
|
||||
|
||||
if (this.hasAttribute("activemedia-blocked")) {
|
||||
this.removeAttribute("activemedia-blocked");
|
||||
modifiedAttrs.push("activemedia-blocked");
|
||||
|
||||
browser.resumeMedia();
|
||||
hist.add(3 /* unblockByClickingIcon */ );
|
||||
} else {
|
||||
if (browser.audioMuted) {
|
||||
if (this.linkedPanel) {
|
||||
// "Lazy Browser" should not invoke its unmute method
|
||||
browser.unmute();
|
||||
}
|
||||
this.removeAttribute("muted");
|
||||
hist.add(1 /* unmute */ );
|
||||
} else {
|
||||
if (this.linkedPanel) {
|
||||
// "Lazy Browser" should not invoke its mute method
|
||||
browser.mute();
|
||||
}
|
||||
this.setAttribute("muted", "true");
|
||||
hist.add(0 /* mute */ );
|
||||
}
|
||||
this.muteReason = aMuteReason || null;
|
||||
modifiedAttrs.push("muted");
|
||||
}
|
||||
gBrowser._tabAttrModified(this, modifiedAttrs);
|
||||
}
|
||||
|
||||
setUserContextId(aUserContextId) {
|
||||
if (aUserContextId) {
|
||||
if (this.linkedBrowser) {
|
||||
this.linkedBrowser.setAttribute("usercontextid", aUserContextId);
|
||||
}
|
||||
this.setAttribute("usercontextid", aUserContextId);
|
||||
} else {
|
||||
if (this.linkedBrowser) {
|
||||
this.linkedBrowser.removeAttribute("usercontextid");
|
||||
}
|
||||
this.removeAttribute("usercontextid");
|
||||
}
|
||||
|
||||
ContextualIdentityService.setTabStyle(this);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("tabbrowser-tab", MozTabbrowserTab, {
|
||||
extends: "tab",
|
||||
});
|
||||
}
|
|
@ -655,8 +655,7 @@ window._gBrowser = {
|
|||
const animations =
|
||||
Array.from(aTab.parentNode.getElementsByTagName("tab"))
|
||||
.map(tab => {
|
||||
const throbber =
|
||||
document.getAnonymousElementByAttribute(tab, "anonid", "tab-throbber");
|
||||
const throbber = tab.throbber;
|
||||
return throbber ? throbber.getAnimations({ subtree: true }) : [];
|
||||
})
|
||||
.reduce((a, b) => a.concat(b))
|
||||
|
@ -2319,8 +2318,7 @@ window._gBrowser = {
|
|||
let openerTab = ((openerBrowser && this.getTabForBrowser(openerBrowser)) ||
|
||||
(relatedToCurrent && this.selectedTab));
|
||||
|
||||
var t = document.createXULElement("tab");
|
||||
|
||||
var t = document.createXULElement("tab", { is: "tabbrowser-tab" });
|
||||
t.openerTab = openerTab;
|
||||
|
||||
aURI = aURI || "about:blank";
|
||||
|
@ -4228,8 +4226,8 @@ window._gBrowser = {
|
|||
|
||||
createTooltip(event) {
|
||||
event.stopPropagation();
|
||||
var tab = document.tooltipNode;
|
||||
if (!tab || tab.localName != "tab") {
|
||||
let tab = document.tooltipNode ? document.tooltipNode.closest("tab") : null;
|
||||
if (!tab) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
@ -5410,8 +5408,9 @@ var TabContextMenu = {
|
|||
});
|
||||
},
|
||||
updateContextMenu(aPopupMenu) {
|
||||
this.contextTab = aPopupMenu.triggerNode.localName == "tab" ?
|
||||
aPopupMenu.triggerNode : gBrowser.selectedTab;
|
||||
let tab = aPopupMenu.triggerNode && aPopupMenu.triggerNode.closest("tab");
|
||||
this.contextTab = tab || gBrowser.selectedTab;
|
||||
|
||||
let disabled = gBrowser.tabs.length == 1;
|
||||
let multiselectionContext = this.contextTab.multiselected;
|
||||
|
||||
|
|
|
@ -991,7 +991,10 @@
|
|||
<parameter name="event"/>
|
||||
<parameter name="isLink"/>
|
||||
<body><![CDATA[
|
||||
let tab = event.target.localName == "tab" ? event.target : null;
|
||||
let tab = event.target;
|
||||
while (tab && tab.localName != "tab") {
|
||||
tab = tab.parentNode;
|
||||
}
|
||||
if (tab && isLink) {
|
||||
let {width} = tab.getBoundingClientRect();
|
||||
if (event.screenX < tab.screenX + width * .25 ||
|
||||
|
@ -1238,8 +1241,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
let tab = event.target;
|
||||
|
||||
let tab = event.target ? event.target.closest("tab") : null;
|
||||
if (tab.getAttribute("fadein") == "true") {
|
||||
if (tab._fullyOpen) {
|
||||
this._updateCloseButtons();
|
||||
|
@ -1330,8 +1332,9 @@
|
|||
return;
|
||||
}
|
||||
|
||||
if (event.target.localName == "tab") {
|
||||
gBrowser.removeTab(event.target, {
|
||||
let tab = event.target ? event.target.closest("tab") : null;
|
||||
if (tab) {
|
||||
gBrowser.removeTab(tab, {
|
||||
animate: true,
|
||||
byMouse: event.mozInputSource == MouseEvent.MOZ_SOURCE_MOUSE,
|
||||
});
|
||||
|
@ -1905,579 +1908,4 @@
|
|||
]]></handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
<binding id="tabbrowser-tab"
|
||||
extends="chrome://global/content/bindings/tabbox.xml#tab">
|
||||
<content context="tabContextMenu">
|
||||
<xul:stack class="tab-stack" flex="1">
|
||||
<xul:vbox xbl:inherits="selected=visuallyselected,fadein,multiselected"
|
||||
class="tab-background">
|
||||
<xul:hbox xbl:inherits="selected=visuallyselected,multiselected,before-multiselected"
|
||||
class="tab-line"/>
|
||||
<xul:spacer flex="1" class="tab-background-inner"/>
|
||||
<xul:hbox class="tab-bottom-line"/>
|
||||
</xul:vbox>
|
||||
<xul:hbox xbl:inherits="pinned,bursting,notselectedsinceload"
|
||||
anonid="tab-loading-burst"
|
||||
class="tab-loading-burst"/>
|
||||
<xul:hbox xbl:inherits="pinned,selected=visuallyselected,titlechanged,attention"
|
||||
class="tab-content" align="center">
|
||||
<xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected"
|
||||
anonid="tab-throbber"
|
||||
class="tab-throbber"
|
||||
layer="true"/>
|
||||
<xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected,pendingicon"
|
||||
anonid="tab-icon-pending"
|
||||
class="tab-icon-pending"/>
|
||||
<xul:image xbl:inherits="src=image,triggeringprincipal=iconloadingprincipal,requestcontextid,fadein,pinned,selected=visuallyselected,busy,crashed,sharing"
|
||||
anonid="tab-icon-image"
|
||||
class="tab-icon-image"
|
||||
validate="never"
|
||||
role="presentation"/>
|
||||
<xul:image xbl:inherits="sharing,selected=visuallyselected,pinned"
|
||||
anonid="sharing-icon"
|
||||
class="tab-sharing-icon-overlay"
|
||||
role="presentation"/>
|
||||
<xul:image xbl:inherits="crashed,busy,soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected=visuallyselected,activemedia-blocked"
|
||||
anonid="overlay-icon"
|
||||
class="tab-icon-overlay"
|
||||
role="presentation"/>
|
||||
<xul:hbox class="tab-label-container"
|
||||
xbl:inherits="pinned,selected=visuallyselected,labeldirection"
|
||||
onoverflow="this.setAttribute('textoverflow', 'true');"
|
||||
onunderflow="this.removeAttribute('textoverflow');"
|
||||
flex="1">
|
||||
<xul:label class="tab-text tab-label" anonid="tab-label"
|
||||
xbl:inherits="xbl:text=label,accesskey,fadein,pinned,selected=visuallyselected,attention"
|
||||
role="presentation"/>
|
||||
</xul:hbox>
|
||||
<xul:image xbl:inherits="pictureinpicture"
|
||||
class="tab-icon-pip"
|
||||
role="presentation"/>
|
||||
<xul:image xbl:inherits="soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected=visuallyselected,activemedia-blocked,pictureinpicture"
|
||||
anonid="soundplaying-icon"
|
||||
class="tab-icon-sound"
|
||||
role="presentation"/>
|
||||
<xul:image anonid="close-button"
|
||||
xbl:inherits="fadein,pinned,selected=visuallyselected"
|
||||
class="tab-close-button close-icon"
|
||||
role="presentation"/>
|
||||
</xul:hbox>
|
||||
</xul:stack>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<constructor><![CDATA[
|
||||
if (!("_lastAccessed" in this)) {
|
||||
this.updateLastAccessed();
|
||||
}
|
||||
]]></constructor>
|
||||
|
||||
<property name="_visuallySelected">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
if (val == (this.getAttribute("visuallyselected") == "true")) {
|
||||
return val;
|
||||
}
|
||||
|
||||
if (val) {
|
||||
this.setAttribute("visuallyselected", "true");
|
||||
} else {
|
||||
this.removeAttribute("visuallyselected");
|
||||
}
|
||||
gBrowser._tabAttrModified(this, ["visuallyselected"]);
|
||||
|
||||
return val;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<property name="_selected">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
// in e10s we want to only pseudo-select a tab before its rendering is done, so that
|
||||
// the rest of the system knows that the tab is selected, but we don't want to update its
|
||||
// visual status to selected until after we receive confirmation that its content has painted.
|
||||
if (val)
|
||||
this.setAttribute("selected", "true");
|
||||
else
|
||||
this.removeAttribute("selected");
|
||||
|
||||
// If we're non-e10s we should update the visual selection as well at the same time,
|
||||
// *or* if we're e10s and the visually selected tab isn't changing, in which case the
|
||||
// tab switcher code won't run and update anything else (like the before- and after-
|
||||
// selected attributes).
|
||||
if (!gMultiProcessBrowser || (val && this.hasAttribute("visuallyselected"))) {
|
||||
this._visuallySelected = val;
|
||||
}
|
||||
|
||||
return val;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
<field name="_selectedOnFirstMouseDown">false</field>
|
||||
|
||||
<property name="pinned" readonly="true">
|
||||
<getter>
|
||||
return this.getAttribute("pinned") == "true";
|
||||
</getter>
|
||||
</property>
|
||||
<property name="hidden" readonly="true">
|
||||
<getter>
|
||||
return this.getAttribute("hidden") == "true";
|
||||
</getter>
|
||||
</property>
|
||||
<property name="muted" readonly="true">
|
||||
<getter>
|
||||
return this.getAttribute("muted") == "true";
|
||||
</getter>
|
||||
</property>
|
||||
<property name="multiselected" readonly="true">
|
||||
<getter>
|
||||
return this.getAttribute("multiselected") == "true";
|
||||
</getter>
|
||||
</property>
|
||||
<property name="beforeMultiselected" readonly="true">
|
||||
<getter>
|
||||
return this.getAttribute("before-multiselected") == "true";
|
||||
</getter>
|
||||
</property>
|
||||
<!--
|
||||
Describes how the tab ended up in this mute state. May be any of:
|
||||
|
||||
- undefined: The tabs mute state has never changed.
|
||||
- null: The mute state was last changed through the UI.
|
||||
- Any string: The ID was changed through an extension API. The string
|
||||
must be the ID of the extension which changed it.
|
||||
-->
|
||||
<field name="muteReason">undefined</field>
|
||||
|
||||
<property name="userContextId" readonly="true">
|
||||
<getter>
|
||||
return this.hasAttribute("usercontextid")
|
||||
? parseInt(this.getAttribute("usercontextid"))
|
||||
: 0;
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<property name="soundPlaying" readonly="true">
|
||||
<getter>
|
||||
return this.getAttribute("soundplaying") == "true";
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<property name="pictureinpicture" readonly="true">
|
||||
<getter>
|
||||
return this.getAttribute("pictureinpicture") == "true";
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<property name="activeMediaBlocked" readonly="true">
|
||||
<getter>
|
||||
return this.getAttribute("activemedia-blocked") == "true";
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<property name="isEmpty" readonly="true">
|
||||
<getter>
|
||||
// Determines if a tab is "empty", usually used in the context of determining
|
||||
// if it's ok to close the tab.
|
||||
if (this.hasAttribute("busy"))
|
||||
return false;
|
||||
|
||||
if (this.hasAttribute("customizemode"))
|
||||
return false;
|
||||
|
||||
let browser = this.linkedBrowser;
|
||||
if (!isBlankPageURL(browser.currentURI.spec))
|
||||
return false;
|
||||
|
||||
if (!checkEmptyPageOrigin(browser))
|
||||
return false;
|
||||
|
||||
if (browser.canGoForward || browser.canGoBack)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<property name="lastAccessed">
|
||||
<getter>
|
||||
return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
|
||||
</getter>
|
||||
</property>
|
||||
<method name="updateLastAccessed">
|
||||
<parameter name="aDate"/>
|
||||
<body><![CDATA[
|
||||
this._lastAccessed = this.selected ? Infinity : (aDate || Date.now());
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<field name="mOverCloseButton">false</field>
|
||||
<property name="_overPlayingIcon" readonly="true">
|
||||
<getter><![CDATA[
|
||||
let iconVisible = this.hasAttribute("soundplaying") ||
|
||||
this.hasAttribute("muted") ||
|
||||
this.hasAttribute("activemedia-blocked");
|
||||
let soundPlayingIcon =
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "soundplaying-icon");
|
||||
let overlayIcon =
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "overlay-icon");
|
||||
|
||||
return soundPlayingIcon && soundPlayingIcon.matches(":hover") ||
|
||||
(overlayIcon && overlayIcon.matches(":hover") && iconVisible);
|
||||
]]></getter>
|
||||
</property>
|
||||
<field name="mCorrespondingMenuitem">null</field>
|
||||
|
||||
<!--
|
||||
While it would make sense to track this in a field, the field will get nuked
|
||||
once the node is gone from the DOM, which causes us to think the tab is not
|
||||
closed, which causes us to make wrong decisions. So we use an expando instead.
|
||||
<field name="closing">false</field>
|
||||
-->
|
||||
|
||||
<method name="_mouseenter">
|
||||
<body><![CDATA[
|
||||
if (this.hidden || this.closing) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tabContainer = this.parentNode;
|
||||
let visibleTabs = tabContainer._getVisibleTabs();
|
||||
let tabIndex = visibleTabs.indexOf(this);
|
||||
|
||||
if (this.selected)
|
||||
tabContainer._handleTabSelect();
|
||||
|
||||
if (tabIndex == 0) {
|
||||
tabContainer._beforeHoveredTab = null;
|
||||
} else {
|
||||
let candidate = visibleTabs[tabIndex - 1];
|
||||
let separatedByScrollButton =
|
||||
tabContainer.getAttribute("overflow") == "true" &&
|
||||
candidate.pinned && !this.pinned;
|
||||
if (!candidate.selected && !separatedByScrollButton) {
|
||||
tabContainer._beforeHoveredTab = candidate;
|
||||
candidate.setAttribute("beforehovered", "true");
|
||||
}
|
||||
}
|
||||
|
||||
if (tabIndex == visibleTabs.length - 1) {
|
||||
tabContainer._afterHoveredTab = null;
|
||||
} else {
|
||||
let candidate = visibleTabs[tabIndex + 1];
|
||||
if (!candidate.selected) {
|
||||
tabContainer._afterHoveredTab = candidate;
|
||||
candidate.setAttribute("afterhovered", "true");
|
||||
}
|
||||
}
|
||||
|
||||
tabContainer._hoveredTab = this;
|
||||
if (this.linkedPanel && !this.selected) {
|
||||
this.linkedBrowser.unselectedTabHover(true);
|
||||
this.startUnselectedTabHoverTimer();
|
||||
}
|
||||
|
||||
// Prepare connection to host beforehand.
|
||||
SessionStore.speculativeConnectOnTabHover(this);
|
||||
|
||||
let tabToWarm = this;
|
||||
if (this.mOverCloseButton) {
|
||||
tabToWarm = gBrowser._findTabToBlurTo(this);
|
||||
}
|
||||
gBrowser.warmupTab(tabToWarm);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_mouseleave">
|
||||
<body><![CDATA[
|
||||
let tabContainer = this.parentNode;
|
||||
if (tabContainer._beforeHoveredTab) {
|
||||
tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
|
||||
tabContainer._beforeHoveredTab = null;
|
||||
}
|
||||
if (tabContainer._afterHoveredTab) {
|
||||
tabContainer._afterHoveredTab.removeAttribute("afterhovered");
|
||||
tabContainer._afterHoveredTab = null;
|
||||
}
|
||||
|
||||
tabContainer._hoveredTab = null;
|
||||
if (this.linkedPanel && !this.selected) {
|
||||
this.linkedBrowser.unselectedTabHover(false);
|
||||
this.cancelUnselectedTabHoverTimer();
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="startUnselectedTabHoverTimer">
|
||||
<body><![CDATA[
|
||||
// Only record data when we need to.
|
||||
if (!this.linkedBrowser.shouldHandleUnselectedTabHover) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
|
||||
TelemetryStopwatch.start("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
|
||||
}
|
||||
|
||||
if (this._hoverTabTimer) {
|
||||
clearTimeout(this._hoverTabTimer);
|
||||
this._hoverTabTimer = null;
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="cancelUnselectedTabHoverTimer">
|
||||
<body><![CDATA[
|
||||
// Since we're listening "mouseout" event, instead of "mouseleave".
|
||||
// Every time the cursor is moving from the tab to its child node (icon),
|
||||
// it would dispatch "mouseout"(for tab) first and then dispatch
|
||||
// "mouseover" (for icon, eg: close button, speaker icon) soon.
|
||||
// It causes we would cancel present TelemetryStopwatch immediately
|
||||
// when cursor is moving on the icon, and then start a new one.
|
||||
// In order to avoid this situation, we could delay cancellation and
|
||||
// remove it if we get "mouseover" within very short period.
|
||||
this._hoverTabTimer = setTimeout(() => {
|
||||
if (TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
|
||||
TelemetryStopwatch.cancel("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
|
||||
}
|
||||
}, 100);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="finishUnselectedTabHoverTimer">
|
||||
<body><![CDATA[
|
||||
// Stop timer when the tab is opened.
|
||||
if (TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
|
||||
TelemetryStopwatch.finish("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="toggleMuteAudio">
|
||||
<parameter name="aMuteReason"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
let browser = this.linkedBrowser;
|
||||
let modifiedAttrs = [];
|
||||
let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
|
||||
|
||||
if (this.hasAttribute("activemedia-blocked")) {
|
||||
this.removeAttribute("activemedia-blocked");
|
||||
modifiedAttrs.push("activemedia-blocked");
|
||||
|
||||
browser.resumeMedia();
|
||||
hist.add(3 /* unblockByClickingIcon */);
|
||||
} else {
|
||||
if (browser.audioMuted) {
|
||||
if (this.linkedPanel) {
|
||||
// "Lazy Browser" should not invoke its unmute method
|
||||
browser.unmute();
|
||||
}
|
||||
this.removeAttribute("muted");
|
||||
hist.add(1 /* unmute */);
|
||||
} else {
|
||||
if (this.linkedPanel) {
|
||||
// "Lazy Browser" should not invoke its mute method
|
||||
browser.mute();
|
||||
}
|
||||
this.setAttribute("muted", "true");
|
||||
hist.add(0 /* mute */);
|
||||
}
|
||||
this.muteReason = aMuteReason || null;
|
||||
modifiedAttrs.push("muted");
|
||||
}
|
||||
gBrowser._tabAttrModified(this, modifiedAttrs);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="setUserContextId">
|
||||
<parameter name="aUserContextId"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (aUserContextId) {
|
||||
if (this.linkedBrowser) {
|
||||
this.linkedBrowser.setAttribute("usercontextid", aUserContextId);
|
||||
}
|
||||
this.setAttribute("usercontextid", aUserContextId);
|
||||
} else {
|
||||
if (this.linkedBrowser) {
|
||||
this.linkedBrowser.removeAttribute("usercontextid");
|
||||
}
|
||||
this.removeAttribute("usercontextid");
|
||||
}
|
||||
|
||||
ContextualIdentityService.setTabStyle(this);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
<handler event="mouseover"><![CDATA[
|
||||
if (event.originalTarget.getAttribute("anonid") == "close-button") {
|
||||
this.mOverCloseButton = true;
|
||||
}
|
||||
|
||||
this._mouseenter();
|
||||
]]></handler>
|
||||
<handler event="mouseout"><![CDATA[
|
||||
if (event.originalTarget.getAttribute("anonid") == "close-button") {
|
||||
this.mOverCloseButton = false;
|
||||
}
|
||||
|
||||
this._mouseleave();
|
||||
]]></handler>
|
||||
|
||||
<handler event="dragstart" phase="capturing">
|
||||
this.style.MozUserFocus = "";
|
||||
</handler>
|
||||
|
||||
<handler event="dragstart"><![CDATA[
|
||||
if (this.mOverCloseButton) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
]]></handler>
|
||||
|
||||
<handler event="mousedown" phase="capturing">
|
||||
<![CDATA[
|
||||
let tabContainer = this.parentNode;
|
||||
|
||||
if (tabContainer._closeTabByDblclick &&
|
||||
event.button == 0 &&
|
||||
event.detail == 1) {
|
||||
this._selectedOnFirstMouseDown = this.selected;
|
||||
}
|
||||
|
||||
if (this.selected) {
|
||||
this.style.MozUserFocus = "ignore";
|
||||
} else if (event.originalTarget.classList.contains("tab-close-button") ||
|
||||
event.originalTarget.classList.contains("tab-icon-sound") ||
|
||||
event.originalTarget.classList.contains("tab-icon-overlay")) {
|
||||
// Prevent tabbox.xml from selecting the tab.
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (event.button == 1) {
|
||||
gBrowser.warmupTab(gBrowser._findTabToBlurTo(this));
|
||||
}
|
||||
|
||||
if (event.button == 0 && tabContainer._multiselectEnabled) {
|
||||
let shiftKey = event.shiftKey;
|
||||
let accelKey = event.getModifierState("Accel");
|
||||
if (shiftKey) {
|
||||
const lastSelectedTab = gBrowser.lastMultiSelectedTab;
|
||||
if (!accelKey) {
|
||||
gBrowser.selectedTab = lastSelectedTab;
|
||||
|
||||
// Make sure selection is cleared when tab-switch doesn't happen.
|
||||
gBrowser.clearMultiSelectedTabs(false);
|
||||
}
|
||||
gBrowser.addRangeToMultiSelectedTabs(lastSelectedTab, this);
|
||||
|
||||
// Prevent tabbox.xml from selecting the tab.
|
||||
event.stopPropagation();
|
||||
} else if (accelKey) {
|
||||
// Ctrl (Cmd for mac) key is pressed
|
||||
if (this.multiselected) {
|
||||
gBrowser.removeFromMultiSelectedTabs(this, true);
|
||||
} else if (this != gBrowser.selectedTab) {
|
||||
gBrowser.addToMultiSelectedTabs(this, false);
|
||||
gBrowser.lastMultiSelectedTab = this;
|
||||
}
|
||||
|
||||
// Prevent tabbox.xml from selecting the tab.
|
||||
event.stopPropagation();
|
||||
} else if (!this.selected && this.multiselected) {
|
||||
gBrowser.lockClearMultiSelectionOnce();
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="mouseup">
|
||||
// Make sure that clear-selection is released.
|
||||
// Otherwise selection using Shift key may be broken.
|
||||
gBrowser.unlockClearMultiSelection();
|
||||
|
||||
this.style.MozUserFocus = "";
|
||||
</handler>
|
||||
|
||||
<handler event="click" button="0"><![CDATA[
|
||||
if (event.getModifierState("Accel") || event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gBrowser.multiSelectedTabsCount > 0 &&
|
||||
!event.originalTarget.classList.contains("tab-close-button") &&
|
||||
!event.originalTarget.classList.contains("tab-icon-sound") &&
|
||||
!event.originalTarget.classList.contains("tab-icon-overlay")) {
|
||||
// Tabs were previously multi-selected and user clicks on a tab
|
||||
// without holding Ctrl/Cmd Key
|
||||
|
||||
// Force positional attributes to update when the
|
||||
// target (of the click) is the "active" tab.
|
||||
let updatePositionalAttr = gBrowser.selectedTab == this;
|
||||
|
||||
gBrowser.clearMultiSelectedTabs(updatePositionalAttr);
|
||||
}
|
||||
|
||||
if (event.originalTarget.classList.contains("tab-icon-sound") ||
|
||||
(event.originalTarget.classList.contains("tab-icon-overlay") &&
|
||||
(event.originalTarget.hasAttribute("soundplaying") ||
|
||||
event.originalTarget.hasAttribute("muted") ||
|
||||
event.originalTarget.hasAttribute("activemedia-blocked")))) {
|
||||
if (this.multiselected) {
|
||||
gBrowser.toggleMuteAudioOnMultiSelectedTabs(this);
|
||||
} else {
|
||||
this.toggleMuteAudio();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.originalTarget.getAttribute("anonid") == "close-button") {
|
||||
if (this.multiselected) {
|
||||
gBrowser.removeMultiSelectedTabs();
|
||||
} else {
|
||||
gBrowser.removeTab(this, {
|
||||
animate: true,
|
||||
byMouse: event.mozInputSource == MouseEvent.MOZ_SOURCE_MOUSE,
|
||||
});
|
||||
}
|
||||
// This enables double-click protection for the tab container
|
||||
// (see tabbrowser-tabs 'click' handler).
|
||||
gBrowser.tabContainer._blockDblClick = true;
|
||||
}
|
||||
]]></handler>
|
||||
|
||||
<handler event="dblclick" button="0" phase="capturing"><![CDATA[
|
||||
// for the one-close-button case
|
||||
if (event.originalTarget.getAttribute("anonid") == "close-button") {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
let tabContainer = this.parentNode;
|
||||
if (tabContainer._closeTabByDblclick &&
|
||||
this._selectedOnFirstMouseDown &&
|
||||
this.selected &&
|
||||
!(event.originalTarget.classList.contains("tab-icon-sound") ||
|
||||
event.originalTarget.classList.contains("tab-icon-overlay"))) {
|
||||
gBrowser.removeTab(this, {
|
||||
animate: true,
|
||||
byMouse: event.mozInputSource == MouseEvent.MOZ_SOURCE_MOUSE,
|
||||
});
|
||||
}
|
||||
]]></handler>
|
||||
|
||||
<handler event="animationend">
|
||||
<![CDATA[
|
||||
if (event.originalTarget.getAttribute("anonid") == "tab-loading-burst") {
|
||||
this.removeAttribute("bursting");
|
||||
}
|
||||
]]>
|
||||
</handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
</bindings>
|
||||
|
|
|
@ -38,7 +38,7 @@ add_task(async () => {
|
|||
|
||||
await waitForAttributeChange(tab, "label");
|
||||
ok(tab.hasAttribute("busy"), "Should have seen the busy attribute");
|
||||
let label = document.getAnonymousElementByAttribute(tab, "anonid", "tab-label");
|
||||
let label = tab.textLabel;
|
||||
let bounds = label.getBoundingClientRect();
|
||||
|
||||
await waitForAttributeChange(tab, "busy");
|
||||
|
@ -60,7 +60,7 @@ add_task(async () => {
|
|||
is(icon.iconURL, "http://example.com/favicon.ico");
|
||||
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
let label = document.getAnonymousElementByAttribute(tab, "anonid", "tab-label");
|
||||
let label = tab.textLabel;
|
||||
let bounds = label.getBoundingClientRect();
|
||||
|
||||
await ContentTask.spawn(browser, null, () => {
|
||||
|
|
|
@ -794,3 +794,47 @@ add_task(async function test_blur_hides_popup() {
|
|||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
function getIsHandlingUserInput(browser, elementId, eventName) {
|
||||
return ContentTask.spawn(browser, [elementId, eventName],
|
||||
async function([contentElementId, contentEventName]) {
|
||||
let element = content.document.getElementById(contentElementId);
|
||||
let isHandlingUserInput = false;
|
||||
await ContentTaskUtils.waitForEvent(element, contentEventName, false, e => {
|
||||
isHandlingUserInput = content.window.windowUtils.isHandlingUserInput;
|
||||
return true;
|
||||
});
|
||||
|
||||
return isHandlingUserInput;
|
||||
});
|
||||
}
|
||||
|
||||
// This test checks if the change/click event is considered as user input event.
|
||||
add_task(async function test_handling_user_input() {
|
||||
const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
|
||||
|
||||
let menulist = document.getElementById("ContentSelectDropdown");
|
||||
let selectPopup = menulist.menupopup;
|
||||
|
||||
// Test onchange event when changing value via keyboard.
|
||||
await openSelectPopup(selectPopup, "click", "#one");
|
||||
let getPromise = getIsHandlingUserInput(tab.linkedBrowser, "one", "change");
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
await hideSelectPopup(selectPopup);
|
||||
is(await getPromise, true, "isHandlingUserInput should be true");
|
||||
|
||||
// Test onchange event when changing value via mouse click
|
||||
await openSelectPopup(selectPopup, "click", "#two");
|
||||
getPromise = getIsHandlingUserInput(tab.linkedBrowser, "two", "change");
|
||||
EventUtils.synthesizeMouseAtCenter(selectPopup.lastElementChild, {});
|
||||
is(await getPromise, true, "isHandlingUserInput should be true");
|
||||
|
||||
// Test onclick event fired from clicking select popup.
|
||||
await openSelectPopup(selectPopup, "click", "#three");
|
||||
getPromise = getIsHandlingUserInput(tab.linkedBrowser, "three", "click");
|
||||
EventUtils.synthesizeMouseAtCenter(selectPopup.firstElementChild, {});
|
||||
is(await getPromise, true, "isHandlingUserInput should be true");
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
|
|
@ -47,7 +47,6 @@ support-files =
|
|||
test_bug462673.html
|
||||
test_bug628179.html
|
||||
test_bug839103.html
|
||||
test_process_flags_chrome.html
|
||||
title_test.svg
|
||||
unknownContentType_file.pif
|
||||
unknownContentType_file.pif^headers^
|
||||
|
@ -414,21 +413,6 @@ skip-if = e10s || debug # Bug 1094240 - has findbar-related failures
|
|||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_addCertException.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_e10s_about_page_triggeringprincipal.js]
|
||||
skip-if = verify
|
||||
support-files =
|
||||
file_about_child.html
|
||||
file_about_parent.html
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_e10s_switchbrowser.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_e10s_about_process.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_e10s_chrome_process.js]
|
||||
skip-if = debug # Bug 1444565, Bug 1457887
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_e10s_javascript.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_blockHPKP.js]
|
||||
skip-if = verify && !debug
|
||||
uses-unsafe-cpows = true
|
||||
|
|
|
@ -34,7 +34,7 @@ add_task(async function closeLastTabInWindow() {
|
|||
let windowClosedPromise = BrowserTestUtils.domWindowClosed(newWin);
|
||||
expectingDialog = true;
|
||||
// close tab:
|
||||
document.getAnonymousElementByAttribute(firstTab, "anonid", "close-button").click();
|
||||
firstTab.closeButton.click();
|
||||
await windowClosedPromise;
|
||||
ok(!expectingDialog, "There should have been a dialog.");
|
||||
ok(newWin.closed, "Window should be closed.");
|
||||
|
@ -63,13 +63,13 @@ add_task(async function closeWindoWithSingleTabTwice() {
|
|||
expectingDialog = true;
|
||||
wantToClose = false;
|
||||
let firstDialogShownPromise = new Promise((resolve, reject) => { resolveDialogPromise = resolve; });
|
||||
document.getAnonymousElementByAttribute(firstTab, "anonid", "close-button").click();
|
||||
firstTab.closeButton.click();
|
||||
await firstDialogShownPromise;
|
||||
info("Got initial dialog, now trying again");
|
||||
expectingDialog = true;
|
||||
wantToClose = true;
|
||||
resolveDialogPromise = null;
|
||||
document.getAnonymousElementByAttribute(firstTab, "anonid", "close-button").click();
|
||||
firstTab.closeButton.click();
|
||||
await windowClosedPromise;
|
||||
ok(!expectingDialog, "There should have been a dialog.");
|
||||
ok(newWin.closed, "Window should be closed.");
|
||||
|
|
|
@ -61,10 +61,10 @@ add_task(async function() {
|
|||
doCompletion();
|
||||
});
|
||||
// Click again:
|
||||
document.getAnonymousElementByAttribute(testTab, "anonid", "close-button").click();
|
||||
testTab.closeButton.click();
|
||||
});
|
||||
// Click once:
|
||||
document.getAnonymousElementByAttribute(testTab, "anonid", "close-button").click();
|
||||
testTab.closeButton.click();
|
||||
});
|
||||
await TestUtils.waitForCondition(() => !testTab.parentNode);
|
||||
ok(!testTab.parentNode, "Tab should be closed completely");
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
const CHROME_PROCESS = E10SUtils.NOT_REMOTE;
|
||||
const WEB_CONTENT_PROCESS = E10SUtils.WEB_REMOTE_TYPE;
|
||||
const PRIVILEGED_CONTENT_PROCESS = E10SUtils.PRIVILEGED_REMOTE_TYPE;
|
||||
const EXTENSION_PROCESS = E10SUtils.EXTENSION_REMOTE_TYPE;
|
||||
|
||||
const CHROME = {
|
||||
id: "cb34538a-d9da-40f3-b61a-069f0b2cb9fb",
|
||||
path: "test-chrome",
|
||||
flags: 0,
|
||||
};
|
||||
const CANREMOTE = {
|
||||
id: "2480d3e1-9ce4-4b84-8ae3-910b9a95cbb3",
|
||||
path: "test-allowremote",
|
||||
flags: Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD,
|
||||
};
|
||||
const MUSTREMOTE = {
|
||||
id: "f849cee5-e13e-44d2-981d-0fb3884aaead",
|
||||
path: "test-mustremote",
|
||||
flags: Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD,
|
||||
};
|
||||
const CANPRIVILEGEDREMOTE = {
|
||||
id: "a04ffafe-6c63-4266-acae-0f4b093165aa",
|
||||
path: "test-canprivilegedremote",
|
||||
flags: Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD |
|
||||
Ci.nsIAboutModule.URI_CAN_LOAD_IN_PRIVILEGED_CHILD,
|
||||
};
|
||||
const MUSTEXTENSION = {
|
||||
id: "f7a1798f-965b-49e9-be83-ec6ee4d7d675",
|
||||
path: "test-mustextension",
|
||||
flags: Ci.nsIAboutModule.URI_MUST_LOAD_IN_EXTENSION_PROCESS,
|
||||
};
|
||||
|
||||
|
||||
const TEST_MODULES = [
|
||||
CHROME,
|
||||
CANREMOTE,
|
||||
MUSTREMOTE,
|
||||
CANPRIVILEGEDREMOTE,
|
||||
MUSTEXTENSION,
|
||||
];
|
||||
|
||||
function AboutModule() {
|
||||
}
|
||||
|
||||
AboutModule.prototype = {
|
||||
newChannel(aURI, aLoadInfo) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
getURIFlags(aURI) {
|
||||
for (let module of TEST_MODULES) {
|
||||
if (aURI.pathQueryRef.startsWith(module.path)) {
|
||||
return module.flags;
|
||||
}
|
||||
}
|
||||
|
||||
ok(false, "Called getURIFlags for an unknown page " + aURI.spec);
|
||||
return 0;
|
||||
},
|
||||
|
||||
getIndexedDBOriginPostfix(aURI) {
|
||||
return null;
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIAboutModule]),
|
||||
};
|
||||
|
||||
var AboutModuleFactory = {
|
||||
createInstance(aOuter, aIID) {
|
||||
if (aOuter)
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
return new AboutModule().QueryInterface(aIID);
|
||||
},
|
||||
|
||||
lockFactory(aLock) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIFactory]),
|
||||
};
|
||||
|
||||
add_task(async function init() {
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
for (let module of TEST_MODULES) {
|
||||
registrar.registerFactory(Components.ID(module.id), "",
|
||||
"@mozilla.org/network/protocol/about;1?what=" + module.path,
|
||||
AboutModuleFactory);
|
||||
}
|
||||
});
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
for (let module of TEST_MODULES) {
|
||||
registrar.unregisterFactory(Components.ID(module.id), AboutModuleFactory);
|
||||
}
|
||||
});
|
||||
|
||||
function test_url(url, chromeResult, webContentResult, privilegedContentResult, extensionProcessResult) {
|
||||
is(E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, CHROME_PROCESS),
|
||||
chromeResult, "Check URL in chrome process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, WEB_CONTENT_PROCESS),
|
||||
webContentResult, "Check URL in web content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, PRIVILEGED_CONTENT_PROCESS),
|
||||
privilegedContentResult, "Check URL in privileged content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, EXTENSION_PROCESS),
|
||||
extensionProcessResult, "Check URL in extension process.");
|
||||
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, CHROME_PROCESS),
|
||||
chromeResult, "Check URL with ref in chrome process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, WEB_CONTENT_PROCESS),
|
||||
webContentResult, "Check URL with ref in web content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, PRIVILEGED_CONTENT_PROCESS),
|
||||
privilegedContentResult, "Check URL with ref in privileged content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, EXTENSION_PROCESS),
|
||||
extensionProcessResult, "Check URL with ref in extension process.");
|
||||
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, CHROME_PROCESS),
|
||||
chromeResult, "Check URL with query in chrome process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, WEB_CONTENT_PROCESS),
|
||||
webContentResult, "Check URL with query in web content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, PRIVILEGED_CONTENT_PROCESS),
|
||||
privilegedContentResult, "Check URL with query in privileged content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, EXTENSION_PROCESS),
|
||||
extensionProcessResult, "Check URL with query in extension process.");
|
||||
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, CHROME_PROCESS),
|
||||
chromeResult, "Check URL with query and ref in chrome process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, WEB_CONTENT_PROCESS),
|
||||
webContentResult, "Check URL with query and ref in web content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, PRIVILEGED_CONTENT_PROCESS),
|
||||
privilegedContentResult, "Check URL with query and ref in privileged content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, EXTENSION_PROCESS),
|
||||
extensionProcessResult, "Check URL with query and ref in extension process.");
|
||||
}
|
||||
|
||||
add_task(async function test_chrome() {
|
||||
test_url("about:" + CHROME.path, true, false, false, false);
|
||||
});
|
||||
|
||||
add_task(async function test_any() {
|
||||
test_url("about:" + CANREMOTE.path, true, true, false, false);
|
||||
});
|
||||
|
||||
add_task(async function test_remote() {
|
||||
test_url("about:" + MUSTREMOTE.path, false, true, false, false);
|
||||
});
|
||||
|
||||
add_task(async function test_privileged_remote_true() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.tabs.remote.separatePrivilegedContentProcess", true],
|
||||
],
|
||||
});
|
||||
|
||||
// This shouldn't be taken literally. We will always use the privileged
|
||||
// content type if the URI_CAN_LOAD_IN_PRIVILEGED_CHILD flag is enabled and
|
||||
// the pref is turned on.
|
||||
test_url("about:" + CANPRIVILEGEDREMOTE.path, false, false, true, false);
|
||||
});
|
||||
|
||||
add_task(async function test_privileged_remote_false() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.tabs.remote.separatePrivilegedContentProcess", false],
|
||||
],
|
||||
});
|
||||
|
||||
// This shouldn't be taken literally. We will always use the privileged
|
||||
// content type if the URI_CAN_LOAD_IN_PRIVILEGED_CHILD flag is enabled and
|
||||
// the pref is turned on.
|
||||
test_url("about:" + CANPRIVILEGEDREMOTE.path, false, true, false, false);
|
||||
});
|
||||
|
||||
add_task(async function test_extension() {
|
||||
test_url("about:" + MUSTEXTENSION.path, false, false, false, true);
|
||||
});
|
|
@ -31,7 +31,7 @@ add_task(async function() {
|
|||
info("Waiting for the load in that tab to finish");
|
||||
await secondTabLoadedPromise;
|
||||
|
||||
let closeBtn = document.getAnonymousElementByAttribute(secondTab, "anonid", "close-button");
|
||||
let closeBtn = secondTab.closeButton;
|
||||
info("closing second tab (which will self-close in beforeunload)");
|
||||
closeBtn.click();
|
||||
ok(secondTab.closing, "Second tab should be marked as closing synchronously.");
|
||||
|
|
|
@ -46,6 +46,7 @@ add_task(async function() {
|
|||
"anonid", "moz-input-box").getBoundingClientRect();
|
||||
let menuButtonRect =
|
||||
document.getElementById("PanelUI-menu-button").getBoundingClientRect();
|
||||
let firstTabRect = gBrowser.selectedTab.getBoundingClientRect();
|
||||
let frameExpectations = {
|
||||
filter: rects => rects.filter(r => !(
|
||||
// We expect the menu button to get into the active state.
|
||||
|
@ -60,6 +61,11 @@ add_task(async function() {
|
|||
r.x1 >= textBoxRect.left && r.x2 <= textBoxRect.right &&
|
||||
r.y1 >= textBoxRect.top && r.y2 <= textBoxRect.bottom,
|
||||
},
|
||||
{name: "bug 1547341 - a first tab gets drawn early",
|
||||
condition: r =>
|
||||
r.x1 >= firstTabRect.left && r.x2 <= firstTabRect.right &&
|
||||
r.y1 >= firstTabRect.top && r.y2 <= firstTabRect.bottom,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
|
@ -134,7 +134,7 @@ add_task(async function open_10_tabs() {
|
|||
max: 20,
|
||||
},
|
||||
"browser.tabs.remote.logSwitchTiming": {
|
||||
max: 25,
|
||||
max: 35,
|
||||
},
|
||||
"network.loadinfo.skip_type_assertion": {
|
||||
// This is accessed in debug only.
|
||||
|
|
|
@ -31,11 +31,12 @@ add_task(async function() {
|
|||
|
||||
let tabStripRect = gBrowser.tabContainer.arrowScrollbox.getBoundingClientRect();
|
||||
let firstTabRect = gBrowser.selectedTab.getBoundingClientRect();
|
||||
let firstTabLabelRect =
|
||||
document.getAnonymousElementByAttribute(gBrowser.selectedTab, "anonid", "tab-label")
|
||||
.getBoundingClientRect();
|
||||
let firstTabLabelRect = gBrowser.selectedTab.textLabel.getBoundingClientRect();
|
||||
let textBoxRect = document.getAnonymousElementByAttribute(gURLBar.textbox,
|
||||
"anonid", "moz-input-box").getBoundingClientRect();
|
||||
let historyDropmarkerRect = document.getAnonymousElementByAttribute(
|
||||
gURLBar.textbox, "anonid", "historydropmarker").getBoundingClientRect();
|
||||
|
||||
let inRange = (val, min, max) => min <= val && val <= max;
|
||||
|
||||
// Add a reflow observer and open a new tab.
|
||||
|
@ -93,6 +94,12 @@ add_task(async function() {
|
|||
r.y1 >= firstTabLabelRect.y &&
|
||||
r.y2 <= firstTabLabelRect.bottom,
|
||||
},
|
||||
{name: "bug 1547341 - addressbar history dropmarker is shown",
|
||||
condition: r => r.x1 >= historyDropmarkerRect.x &&
|
||||
r.x2 <= historyDropmarkerRect.right &&
|
||||
r.y1 >= historyDropmarkerRect.y &&
|
||||
r.y2 <= historyDropmarkerRect.bottom,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
|
|
@ -191,8 +191,14 @@ function makeObserver(aObserveTopic, aObserveFunc) {
|
|||
return removeMe;
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("browser.tabs.remote.separatePrivilegedMozillaWebContentProcess");
|
||||
});
|
||||
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
Services.prefs.setBoolPref("browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", false);
|
||||
|
||||
(async function() {
|
||||
for (let testCase of gTests) {
|
||||
|
|
|
@ -4,6 +4,7 @@ support-files =
|
|||
dummy_page.html
|
||||
../general/audio.ogg
|
||||
file_mediaPlayback.html
|
||||
test_process_flags_chrome.html
|
||||
|
||||
[browser_accessibility_indicator.js]
|
||||
skip-if = (verify && debug && (os == 'linux')) || (os == 'win' && processor == 'aarch64')
|
||||
|
@ -19,6 +20,17 @@ skip-if = (verify && debug && (os == 'linux'))
|
|||
support-files =
|
||||
test_bug1358314.html
|
||||
[browser_isLocalAboutURI.js]
|
||||
[browser_e10s_about_page_triggeringprincipal.js]
|
||||
skip-if = verify
|
||||
support-files =
|
||||
file_about_child.html
|
||||
file_about_parent.html
|
||||
[browser_e10s_switchbrowser.js]
|
||||
[browser_e10s_about_process.js]
|
||||
[browser_e10s_mozillaweb_process.js]
|
||||
[browser_e10s_chrome_process.js]
|
||||
skip-if = debug # Bug 1444565, Bug 1457887
|
||||
[browser_e10s_javascript.js]
|
||||
[browser_multiselect_tabs_active_tab_selected_by_default.js]
|
||||
[browser_multiselect_tabs_bookmark.js]
|
||||
[browser_multiselect_tabs_clear_selection_when_tab_switch.js]
|
||||
|
@ -52,7 +64,9 @@ skip-if = !e10s # Test only relevant for e10s.
|
|||
[browser_new_tab_insert_position.js]
|
||||
skip-if = (debug && os == 'linux' && bits == 32) #Bug 1455882, disabled on Linux32 for almost permafailing
|
||||
support-files = file_new_tab_page.html
|
||||
[browser_new_tab_in_privileged_process_pref.js]
|
||||
[browser_new_tab_in_privilegedabout_process_pref.js]
|
||||
skip-if = !e10s # Pref and test only relevant for e10s.
|
||||
[browser_privilegedmozilla_process_pref.js]
|
||||
skip-if = !e10s # Pref and test only relevant for e10s.
|
||||
[browser_new_web_tab_in_file_process_pref.js]
|
||||
skip-if = !e10s # Pref and test only relevant for e10s.
|
||||
|
|
|
@ -111,8 +111,7 @@ async function test_muting_using_menu(tab, expectMuted) {
|
|||
}
|
||||
|
||||
async function test_playing_icon_on_tab(tab, browser, isPinned) {
|
||||
let icon = document.getAnonymousElementByAttribute(tab, "anonid",
|
||||
isPinned ? "overlay-icon" : "soundplaying-icon");
|
||||
let icon = isPinned ? tab.overlayIcon : tab.soundPlayingIcon;
|
||||
let isActiveTab = tab === gBrowser.selectedTab;
|
||||
|
||||
await play(tab);
|
||||
|
@ -241,9 +240,7 @@ async function test_swapped_browser_while_playing(oldTab, newBrowser) {
|
|||
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");
|
||||
|
||||
let icon = document.getAnonymousElementByAttribute(newTab, "anonid",
|
||||
"soundplaying-icon");
|
||||
await test_tooltip(icon, "Unmute tab", true);
|
||||
await test_tooltip(newTab.soundPlayingIcon, "Unmute tab", true);
|
||||
}
|
||||
|
||||
async function test_swapped_browser_while_not_playing(oldTab, newBrowser) {
|
||||
|
@ -281,18 +278,14 @@ async function test_swapped_browser_while_not_playing(oldTab, newBrowser) {
|
|||
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");
|
||||
|
||||
let icon = document.getAnonymousElementByAttribute(newTab, "anonid",
|
||||
"soundplaying-icon");
|
||||
await test_tooltip(icon, "Unmute tab", true);
|
||||
await test_tooltip(newTab.soundPlayingIcon, "Unmute tab", true);
|
||||
}
|
||||
|
||||
async function test_browser_swapping(tab, browser) {
|
||||
// First, test swapping with a playing but muted tab.
|
||||
await play(tab);
|
||||
|
||||
let icon = document.getAnonymousElementByAttribute(tab, "anonid",
|
||||
"soundplaying-icon");
|
||||
await test_mute_tab(tab, icon, true);
|
||||
await test_mute_tab(tab, tab.soundPlayingIcon, true);
|
||||
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
|
@ -327,7 +320,7 @@ async function test_click_on_pinned_tab_after_mute() {
|
|||
await play(tab);
|
||||
|
||||
// Mute the tab.
|
||||
let icon = document.getAnonymousElementByAttribute(tab, "anonid", "overlay-icon");
|
||||
let icon = tab.overlayIcon;
|
||||
await test_mute_tab(tab, icon, true);
|
||||
|
||||
// Pause playback and wait for it to finish.
|
||||
|
@ -337,8 +330,7 @@ async function test_click_on_pinned_tab_after_mute() {
|
|||
await test_mute_tab(tab, icon, false);
|
||||
|
||||
// Now click on the tab.
|
||||
let image = document.getAnonymousElementByAttribute(tab, "anonid", "tab-icon-image");
|
||||
EventUtils.synthesizeMouseAtCenter(image, {button: 0});
|
||||
EventUtils.synthesizeMouseAtCenter(tab.iconImage, {button: 0});
|
||||
|
||||
is(tab, gBrowser.selectedTab, "Tab switch should be successful");
|
||||
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
const CHROME = {
|
||||
id: "cb34538a-d9da-40f3-b61a-069f0b2cb9fb",
|
||||
path: "test-chrome",
|
||||
flags: 0,
|
||||
};
|
||||
const CANREMOTE = {
|
||||
id: "2480d3e1-9ce4-4b84-8ae3-910b9a95cbb3",
|
||||
path: "test-allowremote",
|
||||
flags: Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD,
|
||||
};
|
||||
const MUSTREMOTE = {
|
||||
id: "f849cee5-e13e-44d2-981d-0fb3884aaead",
|
||||
path: "test-mustremote",
|
||||
flags: Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD,
|
||||
};
|
||||
const CANPRIVILEGEDREMOTE = {
|
||||
id: "a04ffafe-6c63-4266-acae-0f4b093165aa",
|
||||
path: "test-canprivilegedremote",
|
||||
flags: Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD |
|
||||
Ci.nsIAboutModule.URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS,
|
||||
};
|
||||
const MUSTEXTENSION = {
|
||||
id: "f7a1798f-965b-49e9-be83-ec6ee4d7d675",
|
||||
path: "test-mustextension",
|
||||
flags: Ci.nsIAboutModule.URI_MUST_LOAD_IN_EXTENSION_PROCESS,
|
||||
};
|
||||
|
||||
|
||||
const TEST_MODULES = [
|
||||
CHROME,
|
||||
CANREMOTE,
|
||||
MUSTREMOTE,
|
||||
CANPRIVILEGEDREMOTE,
|
||||
MUSTEXTENSION,
|
||||
];
|
||||
|
||||
function AboutModule() {
|
||||
}
|
||||
|
||||
AboutModule.prototype = {
|
||||
newChannel(aURI, aLoadInfo) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
getURIFlags(aURI) {
|
||||
for (let module of TEST_MODULES) {
|
||||
if (aURI.pathQueryRef.startsWith(module.path)) {
|
||||
return module.flags;
|
||||
}
|
||||
}
|
||||
|
||||
ok(false, "Called getURIFlags for an unknown page " + aURI.spec);
|
||||
return 0;
|
||||
},
|
||||
|
||||
getIndexedDBOriginPostfix(aURI) {
|
||||
return null;
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIAboutModule]),
|
||||
};
|
||||
|
||||
var AboutModuleFactory = {
|
||||
createInstance(aOuter, aIID) {
|
||||
if (aOuter)
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
return new AboutModule().QueryInterface(aIID);
|
||||
},
|
||||
|
||||
lockFactory(aLock) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIFactory]),
|
||||
};
|
||||
|
||||
add_task(async function init() {
|
||||
SpecialPowers.setBoolPref("browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", true);
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
for (let module of TEST_MODULES) {
|
||||
registrar.registerFactory(Components.ID(module.id), "",
|
||||
"@mozilla.org/network/protocol/about;1?what=" + module.path,
|
||||
AboutModuleFactory);
|
||||
}
|
||||
});
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
SpecialPowers.clearUserPref("browser.tabs.remote.separatePrivilegedMozillaWebContentProcess");
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
for (let module of TEST_MODULES) {
|
||||
registrar.unregisterFactory(Components.ID(module.id), AboutModuleFactory);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_chrome() {
|
||||
test_url_for_process_types("about:" + CHROME.path, true, false, false, false, false);
|
||||
});
|
||||
|
||||
add_task(async function test_any() {
|
||||
test_url_for_process_types("about:" + CANREMOTE.path, true, true, false, false, false);
|
||||
});
|
||||
|
||||
add_task(async function test_remote() {
|
||||
test_url_for_process_types("about:" + MUSTREMOTE.path, false, true, false, false, false);
|
||||
});
|
||||
|
||||
add_task(async function test_privileged_remote_true() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.tabs.remote.separatePrivilegedContentProcess", true],
|
||||
],
|
||||
});
|
||||
|
||||
// This shouldn't be taken literally. We will always use the privleged about
|
||||
// content type if the URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS flag is enabled and
|
||||
// the pref is turned on.
|
||||
test_url_for_process_types("about:" + CANPRIVILEGEDREMOTE.path, false, false, true, false, false);
|
||||
});
|
||||
|
||||
add_task(async function test_privileged_remote_false() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.tabs.remote.separatePrivilegedContentProcess", false],
|
||||
],
|
||||
});
|
||||
|
||||
// This shouldn't be taken literally. We will always use the privleged about
|
||||
// content type if the URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS flag is enabled and
|
||||
// the pref is turned on.
|
||||
test_url_for_process_types("about:" + CANPRIVILEGEDREMOTE.path, false, true, false, false, false);
|
||||
});
|
||||
|
||||
add_task(async function test_extension() {
|
||||
test_url_for_process_types("about:" + MUSTEXTENSION.path, false, false, false, false, true);
|
||||
});
|
|
@ -32,8 +32,6 @@ function makeTest(name, startURL, startProcessIsRemote, endURL, endProcessIsRemo
|
|||
};
|
||||
}
|
||||
|
||||
const CHROME_PROCESS = E10SUtils.NOT_REMOTE;
|
||||
const WEB_CONTENT_PROCESS = E10SUtils.WEB_REMOTE_TYPE;
|
||||
const PATH = (getRootDirectory(gTestPath) + "test_process_flags_chrome.html").replace("chrome://mochitests", "");
|
||||
|
||||
const CHROME = "chrome://mochitests" + PATH;
|
||||
|
@ -48,38 +46,16 @@ registerCleanupFunction(() => {
|
|||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
function test_url(url, chromeResult, contentResult) {
|
||||
is(E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, CHROME_PROCESS),
|
||||
chromeResult, "Check URL in chrome process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, WEB_CONTENT_PROCESS),
|
||||
contentResult, "Check URL in web content process.");
|
||||
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, CHROME_PROCESS),
|
||||
chromeResult, "Check URL with ref in chrome process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, WEB_CONTENT_PROCESS),
|
||||
contentResult, "Check URL with ref in web content process.");
|
||||
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, CHROME_PROCESS),
|
||||
chromeResult, "Check URL with query in chrome process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, WEB_CONTENT_PROCESS),
|
||||
contentResult, "Check URL with query in web content process.");
|
||||
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, CHROME_PROCESS),
|
||||
chromeResult, "Check URL with query and ref in chrome process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, WEB_CONTENT_PROCESS),
|
||||
contentResult, "Check URL with query and ref in web content process.");
|
||||
}
|
||||
|
||||
add_task(async function test_chrome() {
|
||||
test_url(CHROME, true, false);
|
||||
test_url_for_process_types(CHROME, true, false, false, false, false);
|
||||
});
|
||||
|
||||
add_task(async function test_any() {
|
||||
test_url(CANREMOTE, true, true);
|
||||
test_url_for_process_types(CANREMOTE, true, true, false, false, false);
|
||||
});
|
||||
|
||||
add_task(async function test_remote() {
|
||||
test_url(MUSTREMOTE, false, true);
|
||||
test_url_for_process_types(MUSTREMOTE, false, true, false, false, false);
|
||||
});
|
||||
|
||||
// The set of page transitions
|
|
@ -0,0 +1,24 @@
|
|||
add_task(async function test_privileged_remote_true() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.tabs.remote.separatePrivilegedContentProcess", true],
|
||||
["browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", true],
|
||||
["browser.tabs.remote.separatedMozillaDomains", "example.org"],
|
||||
],
|
||||
});
|
||||
|
||||
test_url_for_process_types("https://example.com", false, true, false, false, false);
|
||||
test_url_for_process_types("https://example.org", false, false, false, true, false);
|
||||
});
|
||||
|
||||
add_task(async function test_privileged_remote_false() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.tabs.remote.separatePrivilegedContentProcess", true],
|
||||
["browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", false],
|
||||
],
|
||||
});
|
||||
|
||||
test_url_for_process_types("https://example.com", false, true, false, false, false);
|
||||
test_url_for_process_types("https://example.org", false, true, false, false, false);
|
||||
});
|
|
@ -29,7 +29,7 @@ add_task(async function usingTabCloseButton() {
|
|||
is(gBrowser.selectedTab, tab1, "Tab1 is active");
|
||||
|
||||
// Closing a tab which is not multiselected
|
||||
let tab4CloseBtn = document.getAnonymousElementByAttribute(tab4, "anonid", "close-button");
|
||||
let tab4CloseBtn = tab4.closeButton;
|
||||
let tab4Closing = BrowserTestUtils.waitForTabClosing(tab4);
|
||||
|
||||
tab4.mOverCloseButton = true;
|
||||
|
@ -48,7 +48,7 @@ add_task(async function usingTabCloseButton() {
|
|||
is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
|
||||
|
||||
// Closing a selected tab
|
||||
let tab2CloseBtn = document.getAnonymousElementByAttribute(tab2, "anonid", "close-button");
|
||||
let tab2CloseBtn = tab2.closeButton;
|
||||
tab2.mOverCloseButton = true;
|
||||
let tab1Closing = BrowserTestUtils.waitForTabClosing(tab1);
|
||||
let tab2Closing = BrowserTestUtils.waitForTabClosing(tab2);
|
||||
|
|
|
@ -55,7 +55,7 @@ add_task(async function muteTabs_usingButton() {
|
|||
}
|
||||
|
||||
// Mute tab0 which is not multiselected, thus other tabs muted state should not be affected
|
||||
let tab0MuteAudioBtn = document.getAnonymousElementByAttribute(tab0, "anonid", "soundplaying-icon");
|
||||
let tab0MuteAudioBtn = tab0.soundPlayingIcon;
|
||||
await test_mute_tab(tab0, tab0MuteAudioBtn, true);
|
||||
|
||||
ok(muted(tab0), "Tab0 is muted");
|
||||
|
@ -85,7 +85,7 @@ add_task(async function muteTabs_usingButton() {
|
|||
// b) unmuted tabs (tab1, tab3) will become muted.
|
||||
// b) media-blocked tabs (tab2) will remain media-blocked.
|
||||
// However tab4 (unmuted) which is not multiselected should not be affected.
|
||||
let tab1MuteAudioBtn = document.getAnonymousElementByAttribute(tab1, "anonid", "soundplaying-icon");
|
||||
let tab1MuteAudioBtn = tab1.soundPlayingIcon;
|
||||
await test_mute_tab(tab1, tab1MuteAudioBtn, true);
|
||||
|
||||
// Check mute state
|
||||
|
@ -141,7 +141,7 @@ add_task(async function unmuteTabs_usingButton() {
|
|||
// b) unmuted tabs (tab0) will remain unmuted.
|
||||
// b) media-blocked tabs (tab1, tab2) will get playing. (media not blocked anymore)
|
||||
// However tab4 (muted) which is not multiselected should not be affected.
|
||||
let tab3MuteAudioBtn = document.getAnonymousElementByAttribute(tab3, "anonid", "soundplaying-icon");
|
||||
let tab3MuteAudioBtn = tab3.soundPlayingIcon;
|
||||
await test_mute_tab(tab3, tab3MuteAudioBtn, false);
|
||||
|
||||
ok(!muted(tab0) && !activeMediaBlocked(tab0), "Tab0 is unmuted and not media-blocked");
|
||||
|
@ -249,7 +249,7 @@ add_task(async function playTabs_usingButton() {
|
|||
// b) unmuted tabs (tab3) will remain unmuted.
|
||||
// b) media-blocked tabs (tab1, tab2) will get playing. (media not blocked anymore)
|
||||
// However tab4 (muted) which is not multiselected should not be affected.
|
||||
let tab2MuteAudioBtn = document.getAnonymousElementByAttribute(tab2, "anonid", "soundplaying-icon");
|
||||
let tab2MuteAudioBtn = tab2.soundPlayingIcon;
|
||||
await test_mute_tab(tab2, tab2MuteAudioBtn, false);
|
||||
|
||||
ok(!muted(tab0) && !activeMediaBlocked(tab0), "Tab0 is unmuted and not activemedia-blocked");
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* Tests to ensure that Activity Stream loads in the privileged content process.
|
||||
* Normal http web pages should load in the web content process.
|
||||
* Tests to ensure that Activity Stream loads in the privileged about:
|
||||
* content process. Normal http web pages should load in the web content
|
||||
* process.
|
||||
* Ref: Bug 1469072.
|
||||
*/
|
||||
|
||||
|
@ -16,54 +17,29 @@ const ABOUT_NEWTAB = "about:newtab";
|
|||
const ABOUT_WELCOME = "about:welcome";
|
||||
const TEST_HTTP = "http://example.org/";
|
||||
|
||||
/**
|
||||
* Takes a xul:browser and makes sure that the remoteTypes for the browser in
|
||||
* both the parent and the child processes are the same.
|
||||
*
|
||||
* @param {xul:browser} browser
|
||||
* A xul:browser.
|
||||
* @param {string} expectedRemoteType
|
||||
* The expected remoteType value for the browser in both the parent
|
||||
* and child processes.
|
||||
* @param {optional string} message
|
||||
* If provided, shows this string as the message when remoteType values
|
||||
* do not match. If not present, it uses the default message defined
|
||||
* in the function parameters.
|
||||
*/
|
||||
function checkBrowserRemoteType(
|
||||
browser,
|
||||
expectedRemoteType,
|
||||
message = `Ensures that tab runs in the ${expectedRemoteType} content process.`
|
||||
) {
|
||||
// Check both parent and child to ensure that they have the correct remoteType.
|
||||
is(browser.remoteType, expectedRemoteType, message);
|
||||
is(browser.messageManager.remoteType, expectedRemoteType,
|
||||
"Parent and child process should agree on the remote type.");
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.newtab.preload", false],
|
||||
["browser.tabs.remote.separatePrivilegedContentProcess", true],
|
||||
["dom.ipc.processCount.privileged", 1],
|
||||
["dom.ipc.keepProcessesAlive.privileged", 1],
|
||||
["dom.ipc.processCount.privilegedabout", 1],
|
||||
["dom.ipc.keepProcessesAlive.privilegedabout", 1],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* Test to ensure that the Activity Stream tabs open in privileged content
|
||||
* Test to ensure that the Activity Stream tabs open in privileged about: content
|
||||
* process. We will first open an about:newtab page that acts as a reference to
|
||||
* the privileged content process. With the reference, we can then open Activity
|
||||
* Stream links in a new tab and ensure that the new tab opens in the same
|
||||
* privileged content process as our reference.
|
||||
* the privileged about: content process. With the reference, we can then open
|
||||
* Activity Stream links in a new tab and ensure that the new tab opens in the same
|
||||
* privileged about: content process as our reference.
|
||||
*/
|
||||
add_task(async function activity_stream_in_privileged_content_process() {
|
||||
Services.ppmm.releaseCachedProcesses();
|
||||
|
||||
await BrowserTestUtils.withNewTab(ABOUT_NEWTAB, async function(browser1) {
|
||||
checkBrowserRemoteType(browser1, E10SUtils.PRIVILEGED_REMOTE_TYPE);
|
||||
checkBrowserRemoteType(browser1, E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE);
|
||||
|
||||
// Note the processID for about:newtab for comparison later.
|
||||
let privilegedPid = browser1.frameLoader.remoteTab.osPid;
|
||||
|
@ -81,7 +57,7 @@ add_task(async function activity_stream_in_privileged_content_process() {
|
|||
]) {
|
||||
await BrowserTestUtils.withNewTab(url, async function(browser2) {
|
||||
is(browser2.frameLoader.remoteTab.osPid, privilegedPid,
|
||||
"Check that about:newtab tabs are in the same privileged content process.");
|
||||
"Check that about:newtab tabs are in the same privileged about: content process.");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -100,25 +76,25 @@ add_task(async function process_switching_through_loading_in_the_same_tab() {
|
|||
checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
|
||||
|
||||
for (let [url, remoteType] of [
|
||||
[ABOUT_NEWTAB, E10SUtils.PRIVILEGED_REMOTE_TYPE],
|
||||
[ABOUT_BLANK, E10SUtils.PRIVILEGED_REMOTE_TYPE],
|
||||
[ABOUT_NEWTAB, E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE],
|
||||
[ABOUT_BLANK, E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE],
|
||||
[TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[ABOUT_HOME, E10SUtils.PRIVILEGED_REMOTE_TYPE],
|
||||
[ABOUT_HOME, E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE],
|
||||
[TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[ABOUT_WELCOME, E10SUtils.PRIVILEGED_REMOTE_TYPE],
|
||||
[ABOUT_WELCOME, E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE],
|
||||
[TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[ABOUT_BLANK, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[`${ABOUT_NEWTAB}#foo`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
|
||||
[`${ABOUT_NEWTAB}#foo`, E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE],
|
||||
[TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[`${ABOUT_WELCOME}#bar`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
|
||||
[`${ABOUT_WELCOME}#bar`, E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE],
|
||||
[TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[`${ABOUT_HOME}#baz`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
|
||||
[`${ABOUT_HOME}#baz`, E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE],
|
||||
[TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[`${ABOUT_NEWTAB}?q=foo`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
|
||||
[`${ABOUT_NEWTAB}?q=foo`, E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE],
|
||||
[TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[`${ABOUT_WELCOME}?q=bar`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
|
||||
[`${ABOUT_WELCOME}?q=bar`, E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE],
|
||||
[TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[`${ABOUT_HOME}?q=baz`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
|
||||
[`${ABOUT_HOME}?q=baz`, E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE],
|
||||
[TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
|
||||
]) {
|
||||
BrowserTestUtils.loadURI(browser, url);
|
||||
|
@ -139,7 +115,7 @@ add_task(async function process_switching_through_navigation_features() {
|
|||
Services.ppmm.releaseCachedProcesses();
|
||||
|
||||
await BrowserTestUtils.withNewTab(ABOUT_NEWTAB, async function(browser) {
|
||||
checkBrowserRemoteType(browser, E10SUtils.PRIVILEGED_REMOTE_TYPE);
|
||||
checkBrowserRemoteType(browser, E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE);
|
||||
|
||||
// Note the processID for about:newtab for comparison later.
|
||||
let privilegedPid = browser.frameLoader.remoteTab.osPid;
|
||||
|
@ -155,20 +131,20 @@ add_task(async function process_switching_through_navigation_features() {
|
|||
});
|
||||
browser = newTab.linkedBrowser;
|
||||
is(browser.frameLoader.remoteTab.osPid, privilegedPid,
|
||||
"Check that new tab opened from about:newtab is loaded in privileged content process.");
|
||||
"Check that new tab opened from about:newtab is loaded in privileged about: content process.");
|
||||
|
||||
// Check that reload does not break the privileged content process affinity.
|
||||
// Check that reload does not break the privileged about: content process affinity.
|
||||
BrowserReload();
|
||||
await BrowserTestUtils.browserLoaded(browser, false, ABOUT_NEWTAB);
|
||||
is(browser.frameLoader.remoteTab.osPid, privilegedPid,
|
||||
"Check that about:newtab is still in privileged content process after reload.");
|
||||
"Check that about:newtab is still in privileged about: content process after reload.");
|
||||
|
||||
// Load http webpage
|
||||
BrowserTestUtils.loadURI(browser, TEST_HTTP);
|
||||
await BrowserTestUtils.browserLoaded(browser, false, TEST_HTTP);
|
||||
checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
|
||||
|
||||
// Check that using the history back feature switches back to privileged content process.
|
||||
// Check that using the history back feature switches back to privileged about: content process.
|
||||
let promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, ABOUT_NEWTAB);
|
||||
browser.goBack();
|
||||
await promiseLocation;
|
||||
|
@ -176,7 +152,7 @@ add_task(async function process_switching_through_navigation_features() {
|
|||
// the navigation history data will be available when we do browser.goForward();
|
||||
await BrowserTestUtils.waitForEvent(newTab, "SSTabRestored");
|
||||
is(browser.frameLoader.remoteTab.osPid, privilegedPid,
|
||||
"Check that about:newtab is still in privileged content process after history goBack.");
|
||||
"Check that about:newtab is still in privileged about: content process after history goBack.");
|
||||
|
||||
// Check that using the history forward feature switches back to the web content process.
|
||||
promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, TEST_HTTP);
|
||||
|
@ -193,7 +169,7 @@ add_task(async function process_switching_through_navigation_features() {
|
|||
browser.gotoIndex(0);
|
||||
await promiseLocation;
|
||||
is(browser.frameLoader.remoteTab.osPid, privilegedPid,
|
||||
"Check that about:newtab is in privileged content process after history gotoIndex.");
|
||||
"Check that about:newtab is in privileged about: content process after history gotoIndex.");
|
||||
|
||||
BrowserTestUtils.loadURI(browser, TEST_HTTP);
|
||||
await BrowserTestUtils.browserLoaded(browser, false, TEST_HTTP);
|
||||
|
@ -205,7 +181,7 @@ add_task(async function process_switching_through_navigation_features() {
|
|||
});
|
||||
await BrowserTestUtils.browserLoaded(browser, false, ABOUT_NEWTAB);
|
||||
is(browser.frameLoader.remoteTab.osPid, privilegedPid,
|
||||
"Check that about:newtab is in privileged content process after location change.");
|
||||
"Check that about:newtab is in privileged about: content process after location change.");
|
||||
});
|
||||
|
||||
Services.ppmm.releaseCachedProcesses();
|
|
@ -0,0 +1,183 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests to ensure that Mozilla Privileged Webpages load in the privileged
|
||||
* mozilla web content process. Normal http web pages should load in the web
|
||||
* content process.
|
||||
* Ref: Bug 1539595.
|
||||
*/
|
||||
|
||||
// High and Low Privilege
|
||||
const TEST_HIGH1 = "https://example.org/";
|
||||
const TEST_HIGH2 = "https://test1.example.org/";
|
||||
const TEST_LOW1 = "http://example.org/";
|
||||
const TEST_LOW2 = "https://example.com/";
|
||||
|
||||
add_task(async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", true],
|
||||
["browser.tabs.remote.separatedMozillaDomains", "example.org"],
|
||||
["dom.ipc.processCount.privilegedmozilla", 1],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* Test to ensure that the tabs open in privileged mozilla content process. We
|
||||
* will first open a page that acts as a reference to the privileged mozilla web
|
||||
* content process. With the reference, we can then open other links in a new tab
|
||||
* and ensure that the new tab opens in the same privileged mozilla content process
|
||||
* as our reference.
|
||||
*/
|
||||
add_task(async function webpages_in_privileged_content_process() {
|
||||
Services.ppmm.releaseCachedProcesses();
|
||||
|
||||
await BrowserTestUtils.withNewTab(TEST_HIGH1, async function(browser1) {
|
||||
checkBrowserRemoteType(browser1, E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE);
|
||||
|
||||
// Note the processID for about:newtab for comparison later.
|
||||
let privilegedPid = browser1.frameLoader.remoteTab.osPid;
|
||||
|
||||
for (let url of [
|
||||
TEST_HIGH1,
|
||||
`${TEST_HIGH1}#foo`,
|
||||
`${TEST_HIGH1}?q=foo`,
|
||||
TEST_HIGH2,
|
||||
`${TEST_HIGH2}#foo`,
|
||||
`${TEST_HIGH2}?q=foo`,
|
||||
]) {
|
||||
await BrowserTestUtils.withNewTab(url, async function(browser2) {
|
||||
is(browser2.frameLoader.remoteTab.osPid, privilegedPid,
|
||||
"Check that privileged pages are in the same privileged mozilla content process.");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Services.ppmm.releaseCachedProcesses();
|
||||
});
|
||||
|
||||
/*
|
||||
* Test to ensure that a process switch occurs when navigating between normal
|
||||
* web pages and unprivileged pages in the same tab.
|
||||
*/
|
||||
add_task(async function process_switching_through_loading_in_the_same_tab() {
|
||||
Services.ppmm.releaseCachedProcesses();
|
||||
|
||||
await BrowserTestUtils.withNewTab(TEST_LOW1, async function(browser) {
|
||||
checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
|
||||
|
||||
for (let [url, remoteType] of [
|
||||
[TEST_HIGH1, E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE],
|
||||
[TEST_LOW1, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[TEST_HIGH1, E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE],
|
||||
[TEST_LOW2, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[TEST_HIGH1, E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE],
|
||||
[TEST_LOW1, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[TEST_LOW2, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[`${TEST_HIGH1}#foo`, E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE],
|
||||
[TEST_LOW1, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[`${TEST_HIGH1}#bar`, E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE],
|
||||
[TEST_LOW2, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[`${TEST_HIGH1}#baz`, E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE],
|
||||
[TEST_LOW1, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[`${TEST_HIGH1}?q=foo`, E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE],
|
||||
[TEST_LOW2, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[`${TEST_HIGH1}?q=bar`, E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE],
|
||||
[TEST_LOW1, E10SUtils.WEB_REMOTE_TYPE],
|
||||
[`${TEST_HIGH1}?q=baz`, E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE],
|
||||
[TEST_LOW2, E10SUtils.WEB_REMOTE_TYPE],
|
||||
]) {
|
||||
BrowserTestUtils.loadURI(browser, url);
|
||||
await BrowserTestUtils.browserLoaded(browser, false, url);
|
||||
checkBrowserRemoteType(browser, remoteType);
|
||||
}
|
||||
});
|
||||
|
||||
Services.ppmm.releaseCachedProcesses();
|
||||
});
|
||||
|
||||
/*
|
||||
* Test to ensure that a process switch occurs when navigating between normal
|
||||
* web pages and privileged pages using the browser's navigation features
|
||||
* such as history and location change.
|
||||
*/
|
||||
add_task(async function process_switching_through_navigation_features() {
|
||||
Services.ppmm.releaseCachedProcesses();
|
||||
|
||||
await BrowserTestUtils.withNewTab(TEST_HIGH1, async function(browser) {
|
||||
checkBrowserRemoteType(browser, E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE);
|
||||
|
||||
// Note the processID for about:newtab for comparison later.
|
||||
let privilegedPid = browser.frameLoader.remoteTab.osPid;
|
||||
|
||||
// Check that about:newtab opened from JS in about:newtab page is in the same process.
|
||||
let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, TEST_HIGH1, true);
|
||||
await ContentTask.spawn(browser, TEST_HIGH1, uri => {
|
||||
content.open(uri, "_blank");
|
||||
});
|
||||
let newTab = await promiseTabOpened;
|
||||
registerCleanupFunction(async function() {
|
||||
BrowserTestUtils.removeTab(newTab);
|
||||
});
|
||||
browser = newTab.linkedBrowser;
|
||||
is(browser.frameLoader.remoteTab.osPid, privilegedPid,
|
||||
"Check that new tab opened from privileged page is loaded in privileged mozilla content process.");
|
||||
|
||||
// Check that reload does not break the privileged mozilla content process affinity.
|
||||
BrowserReload();
|
||||
await BrowserTestUtils.browserLoaded(browser, false, TEST_HIGH1);
|
||||
is(browser.frameLoader.remoteTab.osPid, privilegedPid,
|
||||
"Check that privileged page is still in privileged mozilla content process after reload.");
|
||||
|
||||
// Load http webpage
|
||||
BrowserTestUtils.loadURI(browser, TEST_LOW1);
|
||||
await BrowserTestUtils.browserLoaded(browser, false, TEST_LOW1);
|
||||
checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
|
||||
|
||||
// Check that using the history back feature switches back to privileged mozilla content process.
|
||||
let promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, TEST_HIGH1);
|
||||
browser.goBack();
|
||||
await promiseLocation;
|
||||
// We will need to ensure that the process flip has fully completed so that
|
||||
// the navigation history data will be available when we do browser.goForward();
|
||||
await BrowserTestUtils.waitForEvent(newTab, "SSTabRestored");
|
||||
is(browser.frameLoader.remoteTab.osPid, privilegedPid,
|
||||
"Check that privileged page is still in privileged mozilla content process after history goBack.");
|
||||
|
||||
// Check that using the history forward feature switches back to the web content process.
|
||||
promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, TEST_LOW1);
|
||||
browser.goForward();
|
||||
await promiseLocation;
|
||||
// We will need to ensure that the process flip has fully completed so that
|
||||
// the navigation history data will be available when we do browser.gotoIndex(0);
|
||||
await BrowserTestUtils.waitForEvent(newTab, "SSTabRestored");
|
||||
checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE,
|
||||
"Check that tab runs in the web content process after using history goForward.");
|
||||
|
||||
// Check that goto history index does not break the affinity.
|
||||
promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, TEST_HIGH1);
|
||||
browser.gotoIndex(0);
|
||||
await promiseLocation;
|
||||
is(browser.frameLoader.remoteTab.osPid, privilegedPid,
|
||||
"Check that privileged page is in privileged mozilla content process after history gotoIndex.");
|
||||
|
||||
BrowserTestUtils.loadURI(browser, TEST_LOW2);
|
||||
await BrowserTestUtils.browserLoaded(browser, false, TEST_LOW2);
|
||||
checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
|
||||
|
||||
// Check that location change causes a change in process type as well.
|
||||
await ContentTask.spawn(browser, TEST_HIGH1, uri => {
|
||||
content.location = uri;
|
||||
});
|
||||
await BrowserTestUtils.browserLoaded(browser, false, TEST_HIGH1);
|
||||
is(browser.frameLoader.remoteTab.osPid, privilegedPid,
|
||||
"Check that privileged page is in privileged mozilla content process after location change.");
|
||||
});
|
||||
|
||||
Services.ppmm.releaseCachedProcesses();
|
||||
});
|
|
@ -53,7 +53,7 @@ async function overflowTabs() {
|
|||
|
||||
function getLastCloseButton() {
|
||||
let lastTab = gBrowser.tabs[gBrowser.tabs.length - 1];
|
||||
return document.getAnonymousElementByAttribute(lastTab, "anonid", "close-button");
|
||||
return lastTab.closeButton;
|
||||
}
|
||||
|
||||
function getLastCloseButtonLocation() {
|
||||
|
|
|
@ -189,3 +189,80 @@ async function dragAndDrop(tab1, tab2, copy, destWindow = window) {
|
|||
function getUrl(tab) {
|
||||
return tab.linkedBrowser.currentURI.spec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a xul:browser and makes sure that the remoteTypes for the browser in
|
||||
* both the parent and the child processes are the same.
|
||||
*
|
||||
* @param {xul:browser} browser
|
||||
* A xul:browser.
|
||||
* @param {string} expectedRemoteType
|
||||
* The expected remoteType value for the browser in both the parent
|
||||
* and child processes.
|
||||
* @param {optional string} message
|
||||
* If provided, shows this string as the message when remoteType values
|
||||
* do not match. If not present, it uses the default message defined
|
||||
* in the function parameters.
|
||||
*/
|
||||
function checkBrowserRemoteType(
|
||||
browser,
|
||||
expectedRemoteType,
|
||||
message = `Ensures that tab runs in the ${expectedRemoteType} content process.`
|
||||
) {
|
||||
// Check both parent and child to ensure that they have the correct remoteType.
|
||||
is(browser.remoteType, expectedRemoteType, message);
|
||||
is(browser.messageManager.remoteType, expectedRemoteType,
|
||||
"Parent and child process should agree on the remote type.");
|
||||
}
|
||||
|
||||
function test_url_for_process_types(url, chromeResult, webContentResult, privilegedAboutContentResult, privilegedMozillaContentResult, extensionProcessResult) {
|
||||
const CHROME_PROCESS = E10SUtils.NOT_REMOTE;
|
||||
const WEB_CONTENT_PROCESS = E10SUtils.WEB_REMOTE_TYPE;
|
||||
const PRIVILEGEDABOUT_CONTENT_PROCESS = E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE;
|
||||
const PRIVILEGEDMOZILLA_CONTENT_PROCESS = E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE;
|
||||
const EXTENSION_PROCESS = E10SUtils.EXTENSION_REMOTE_TYPE;
|
||||
|
||||
is(E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, CHROME_PROCESS),
|
||||
chromeResult, "Check URL in chrome process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, WEB_CONTENT_PROCESS),
|
||||
webContentResult, "Check URL in web content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, PRIVILEGEDABOUT_CONTENT_PROCESS),
|
||||
privilegedAboutContentResult, "Check URL in privileged about content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, PRIVILEGEDMOZILLA_CONTENT_PROCESS),
|
||||
privilegedMozillaContentResult, "Check URL in privileged mozilla content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, EXTENSION_PROCESS),
|
||||
extensionProcessResult, "Check URL in extension process.");
|
||||
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, CHROME_PROCESS),
|
||||
chromeResult, "Check URL with ref in chrome process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, WEB_CONTENT_PROCESS),
|
||||
webContentResult, "Check URL with ref in web content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, PRIVILEGEDABOUT_CONTENT_PROCESS),
|
||||
privilegedAboutContentResult, "Check URL with ref in privileged about content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, PRIVILEGEDMOZILLA_CONTENT_PROCESS),
|
||||
privilegedMozillaContentResult, "Check URL with ref in privileged mozilla content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, EXTENSION_PROCESS),
|
||||
extensionProcessResult, "Check URL with ref in extension process.");
|
||||
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, CHROME_PROCESS),
|
||||
chromeResult, "Check URL with query in chrome process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, WEB_CONTENT_PROCESS),
|
||||
webContentResult, "Check URL with query in web content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, PRIVILEGEDABOUT_CONTENT_PROCESS),
|
||||
privilegedAboutContentResult, "Check URL with query in privileged about content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, PRIVILEGEDMOZILLA_CONTENT_PROCESS),
|
||||
privilegedMozillaContentResult, "Check URL with query in privileged mozilla content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, EXTENSION_PROCESS),
|
||||
extensionProcessResult, "Check URL with query in extension process.");
|
||||
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, CHROME_PROCESS),
|
||||
chromeResult, "Check URL with query and ref in chrome process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, WEB_CONTENT_PROCESS),
|
||||
webContentResult, "Check URL with query and ref in web content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, PRIVILEGEDABOUT_CONTENT_PROCESS),
|
||||
privilegedAboutContentResult, "Check URL with query and ref in privileged about content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, PRIVILEGEDMOZILLA_CONTENT_PROCESS),
|
||||
privilegedMozillaContentResult, "Check URL with query and ref in privileged mozilla content process.");
|
||||
is(E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, EXTENSION_PROCESS),
|
||||
extensionProcessResult, "Check URL with query and ref in extension process.");
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
<html>
|
||||
<body>
|
||||
<p>chrome: test page</p>
|
||||
<p><a href="chrome://mochitests/content/browser/browser/base/content/test/general/test_process_flags_chrome.html">chrome</a></p>
|
||||
<p><a href="chrome://mochitests-any/content/browser/browser/base/content/test/general/test_process_flags_chrome.html">canremote</a></p>
|
||||
<p><a href="chrome://mochitests-content/content/browser/browser/base/content/test/general/test_process_flags_chrome.html">mustremote</a></p>
|
||||
<p><a href="chrome://mochitests/content/browser/browser/base/content/test/tabs/test_process_flags_chrome.html">chrome</a></p>
|
||||
<p><a href="chrome://mochitests-any/content/browser/browser/base/content/test/tabs/test_process_flags_chrome.html">canremote</a></p>
|
||||
<p><a href="chrome://mochitests-content/content/browser/browser/base/content/test/tabs/test_process_flags_chrome.html">mustremote</a></p>
|
||||
</body>
|
||||
</html>
|
|
@ -33,8 +33,7 @@ var gTests = [
|
|||
let tab = gBrowser.selectedTab;
|
||||
is(tab.getAttribute("sharing"), aSharing,
|
||||
"the tab has the attribute to show the " + aSharing + " icon");
|
||||
let icon =
|
||||
document.getAnonymousElementByAttribute(tab, "anonid", "sharing-icon");
|
||||
let icon = tab.sharingIcon;
|
||||
is(window.getComputedStyle(icon).display, "none",
|
||||
"the animated sharing icon of the tab is hidden");
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ browser.jar:
|
|||
content/browser/contentSearchUI.css (content/contentSearchUI.css)
|
||||
content/browser/tabbrowser.css (content/tabbrowser.css)
|
||||
content/browser/tabbrowser.js (content/tabbrowser.js)
|
||||
content/browser/tabbrowser-tab.js (content/tabbrowser-tab.js)
|
||||
content/browser/tabbrowser.xml (content/tabbrowser.xml)
|
||||
* content/browser/urlbarBindings.xml (content/urlbarBindings.xml)
|
||||
content/browser/utilityOverlay.js (content/utilityOverlay.js)
|
||||
|
|
|
@ -27,7 +27,7 @@ bool AboutRedirector::sAboutLoginsEnabled = false;
|
|||
static const uint32_t ACTIVITY_STREAM_FLAGS =
|
||||
nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::ENABLE_INDEXED_DB |
|
||||
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
|
||||
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGED_CHILD |
|
||||
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT;
|
||||
|
||||
struct RedirEntry {
|
||||
|
@ -59,7 +59,7 @@ static const RedirEntry kRedirMap[] = {
|
|||
nsIAboutModule::HIDE_FROM_ABOUTABOUT},
|
||||
{"logins", "chrome://browser/content/aboutlogins/aboutLogins.html",
|
||||
nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
|
||||
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGED_CHILD |
|
||||
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT},
|
||||
{"tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
|
@ -85,7 +85,7 @@ static const RedirEntry kRedirMap[] = {
|
|||
{"newtab", "about:blank", ACTIVITY_STREAM_FLAGS},
|
||||
{"welcome", "about:blank",
|
||||
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
|
||||
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGED_CHILD |
|
||||
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::ALLOW_SCRIPT},
|
||||
{"library", "chrome://browser/content/aboutLibrary.xhtml",
|
||||
|
@ -118,7 +118,7 @@ static const RedirEntry kRedirMap[] = {
|
|||
{"protections", "chrome://browser/content/protections.html",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::URI_MUST_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
|
||||
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGED_CHILD},
|
||||
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS},
|
||||
};
|
||||
|
||||
static nsAutoCString GetAboutModuleName(nsIURI* aURI) {
|
||||
|
|
|
@ -23,15 +23,15 @@ XPCOMUtils.defineLazyGetter(this, "log", () => {
|
|||
const ABOUT_LOGINS_ORIGIN = "about:logins";
|
||||
const MASTER_PASSWORD_NOTIFICATION_ID = "master-password-login-required";
|
||||
|
||||
const PRIVILEGED_PROCESS_PREF =
|
||||
const PRIVILEGEDABOUT_PROCESS_PREF =
|
||||
"browser.tabs.remote.separatePrivilegedContentProcess";
|
||||
const PRIVILEGED_PROCESS_ENABLED =
|
||||
Services.prefs.getBoolPref(PRIVILEGED_PROCESS_PREF, false);
|
||||
const PRIVILEGEDABOUT_PROCESS_ENABLED =
|
||||
Services.prefs.getBoolPref(PRIVILEGEDABOUT_PROCESS_PREF, false);
|
||||
|
||||
// When the privileged content process is enabled, we expect about:logins
|
||||
// to load in it. Otherwise, it's in a normal web content process.
|
||||
const EXPECTED_ABOUTLOGINS_REMOTE_TYPE =
|
||||
PRIVILEGED_PROCESS_ENABLED ? E10SUtils.PRIVILEGED_REMOTE_TYPE
|
||||
PRIVILEGEDABOUT_PROCESS_ENABLED ? E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE
|
||||
: E10SUtils.DEFAULT_REMOTE_TYPE;
|
||||
|
||||
const isValidLogin = login => {
|
||||
|
|
|
@ -1065,8 +1065,11 @@ const menuTracker = {
|
|||
gMenuBuilder.build({menu, tab, pageUrl, inToolsMenu: true});
|
||||
}
|
||||
if (menu.id === "tabContextMenu") {
|
||||
const trigger = menu.triggerNode;
|
||||
const tab = trigger.localName === "tab" ? trigger : tabTracker.activeTab;
|
||||
let trigger = menu.triggerNode;
|
||||
while (trigger && trigger.localName != "tab") {
|
||||
trigger = trigger.parentNode;
|
||||
}
|
||||
const tab = trigger || tabTracker.activeTab;
|
||||
const pageUrl = tab.linkedBrowser.currentURI.spec;
|
||||
gMenuBuilder.build({menu, tab, pageUrl, onTab: true});
|
||||
}
|
||||
|
|
|
@ -26,18 +26,18 @@ const BASE_URL = "resource://activity-stream/";
|
|||
const ACTIVITY_STREAM_PAGES = new Set(["home", "newtab", "welcome"]);
|
||||
|
||||
const IS_MAIN_PROCESS = Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;
|
||||
const IS_PRIVILEGED_PROCESS = Services.appinfo.remoteType === E10SUtils.PRIVILEGED_REMOTE_TYPE;
|
||||
const IS_PRIVILEGED_PROCESS = Services.appinfo.remoteType === E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE;
|
||||
|
||||
const IS_RELEASE_OR_BETA = AppConstants.RELEASE_OR_BETA;
|
||||
|
||||
const PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS = "browser.tabs.remote.separatePrivilegedContentProcess";
|
||||
const PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS = "browser.tabs.remote.separatePrivilegedContentProcess";
|
||||
const PREF_ACTIVITY_STREAM_PRERENDER_ENABLED = "browser.newtabpage.activity-stream.prerender";
|
||||
const PREF_ACTIVITY_STREAM_DEBUG = "browser.newtabpage.activity-stream.debug";
|
||||
|
||||
function AboutNewTabService() {
|
||||
Services.obs.addObserver(this, TOPIC_APP_QUIT);
|
||||
Services.obs.addObserver(this, TOPIC_LOCALES_CHANGE);
|
||||
Services.prefs.addObserver(PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS, this);
|
||||
Services.prefs.addObserver(PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS, this);
|
||||
Services.prefs.addObserver(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED, this);
|
||||
if (!IS_RELEASE_OR_BETA) {
|
||||
Services.prefs.addObserver(PREF_ACTIVITY_STREAM_DEBUG, this);
|
||||
|
@ -93,7 +93,7 @@ AboutNewTabService.prototype = {
|
|||
_activityStreamPrerender: false,
|
||||
_activityStreamPath: "",
|
||||
_activityStreamDebug: false,
|
||||
_privilegedContentProcess: false,
|
||||
_privilegedAboutContentProcess: false,
|
||||
_overridden: false,
|
||||
willNotifyUser: false,
|
||||
|
||||
|
@ -106,8 +106,8 @@ AboutNewTabService.prototype = {
|
|||
observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "nsPref:changed":
|
||||
if (data === PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS) {
|
||||
this._privilegedContentProcess = Services.prefs.getBoolPref(PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS);
|
||||
if (data === PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS) {
|
||||
this._privilegedAboutContentProcess = Services.prefs.getBoolPref(PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS);
|
||||
this.updatePrerenderedPath();
|
||||
this.notifyChange();
|
||||
} else if (data === PREF_ACTIVITY_STREAM_PRERENDER_ENABLED) {
|
||||
|
@ -216,7 +216,7 @@ AboutNewTabService.prototype = {
|
|||
} else {
|
||||
this._activityStreamEnabled = false;
|
||||
}
|
||||
this._privilegedContentProcess = Services.prefs.getBoolPref(PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS);
|
||||
this._privilegedAboutContentProcess = Services.prefs.getBoolPref(PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS);
|
||||
this._activityStreamPrerender = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED);
|
||||
if (!IS_RELEASE_OR_BETA) {
|
||||
this._activityStreamDebug = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_DEBUG, false);
|
||||
|
@ -233,7 +233,7 @@ AboutNewTabService.prototype = {
|
|||
// Debug files are specially packaged in a non-localized directory, but with
|
||||
// dynamic script loading, localized debug is supported.
|
||||
this._activityStreamPath = `${this._activityStreamDebug &&
|
||||
!this._privilegedContentProcess ? "static" : this.activityStreamLocale}/`;
|
||||
!this._privilegedAboutContentProcess ? "static" : this.activityStreamLocale}/`;
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -253,8 +253,8 @@ AboutNewTabService.prototype = {
|
|||
"activity-stream",
|
||||
this._activityStreamPrerender ? "-prerendered" : "",
|
||||
// Debug version loads dev scripts but noscripts separately loads scripts
|
||||
this._activityStreamDebug && !this._privilegedContentProcess ? "-debug" : "",
|
||||
this._privilegedContentProcess ? "-noscripts" : "",
|
||||
this._activityStreamDebug && !this._privilegedAboutContentProcess ? "-debug" : "",
|
||||
this._privilegedAboutContentProcess ? "-noscripts" : "",
|
||||
".html",
|
||||
].join("");
|
||||
},
|
||||
|
@ -351,7 +351,7 @@ AboutNewTabService.prototype = {
|
|||
}
|
||||
Services.obs.removeObserver(this, TOPIC_APP_QUIT);
|
||||
Services.obs.removeObserver(this, TOPIC_LOCALES_CHANGE);
|
||||
Services.prefs.removeObserver(PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS, this);
|
||||
Services.prefs.removeObserver(PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS, this);
|
||||
Services.prefs.removeObserver(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED, this);
|
||||
if (!IS_RELEASE_OR_BETA) {
|
||||
Services.prefs.removeObserver(PREF_ACTIVITY_STREAM_DEBUG, this);
|
||||
|
|
|
@ -192,5 +192,15 @@ var TabStateInternal = {
|
|||
tabData[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// [Bug 1554512]
|
||||
// If the latest scroll position is on the top, we will delete scroll entry.
|
||||
// When scroll entry is deleted in TabStateCache, it cannot be updated.
|
||||
// To prevent losing the scroll position, we need to add a handing here.
|
||||
if (tabData.scroll) {
|
||||
if (!data.scroll) {
|
||||
delete tabData.scroll;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -9,17 +9,17 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const PRIVILEGED_PROCESS_PREF =
|
||||
const PRIVILEGEDABOUT_PROCESS_PREF =
|
||||
"browser.tabs.remote.separatePrivilegedContentProcess";
|
||||
const PRIVILEGED_PROCESS_ENABLED =
|
||||
Services.prefs.getBoolPref(PRIVILEGED_PROCESS_PREF);
|
||||
const PRIVILEGEDABOUT_PROCESS_ENABLED =
|
||||
Services.prefs.getBoolPref(PRIVILEGEDABOUT_PROCESS_PREF);
|
||||
|
||||
const REMOTE_BROWSER_SHOWN = "remote-browser-shown";
|
||||
|
||||
// When the privileged content process is enabled, we expect about:home
|
||||
// to load in it. Otherwise, it's in a normal web content process.
|
||||
const EXPECTED_ABOUTHOME_REMOTE_TYPE =
|
||||
PRIVILEGED_PROCESS_ENABLED ? E10SUtils.PRIVILEGED_REMOTE_TYPE
|
||||
PRIVILEGEDABOUT_PROCESS_ENABLED ? E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE
|
||||
: E10SUtils.DEFAULT_REMOTE_TYPE;
|
||||
|
||||
/**
|
||||
|
|
|
@ -172,9 +172,7 @@ var UITour = {
|
|||
["selectedTabIcon", {
|
||||
query: (aDocument) => {
|
||||
let selectedtab = aDocument.defaultView.gBrowser.selectedTab;
|
||||
let element = aDocument.getAnonymousElementByAttribute(selectedtab,
|
||||
"anonid",
|
||||
"tab-icon-image");
|
||||
let element = selectedtab.iconImage;
|
||||
if (!element || !UITour.isElementVisible(element)) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -388,6 +388,9 @@ class ImportHook(object):
|
|||
|
||||
def __call__(self, name, globals=None, locals=None, fromlist=None,
|
||||
level=-1):
|
||||
if sys.version_info[0] >= 3 and level < 0:
|
||||
level = 0
|
||||
|
||||
# name might be a relative import. Instead of figuring out what that
|
||||
# resolves to, which is complex, just rely on the real import.
|
||||
# Since we don't know the full module name, we can't check sys.modules,
|
||||
|
|
|
@ -288,55 +288,10 @@ class ElementStyle {
|
|||
* Optional pseudo-element for which to restrict marking CSS declarations as
|
||||
* overridden.
|
||||
*/
|
||||
/* eslint-disable complexity */
|
||||
updateDeclarations(pseudo = "") {
|
||||
// Gather all the text properties applied by these rules, ordered
|
||||
// from more- to less-specific. Text properties from keyframes rule are
|
||||
// excluded from being marked as overridden since a number of criteria such
|
||||
// as time, and animation overlay are required to be check in order to
|
||||
// determine if the property is overridden.
|
||||
const textProps = [];
|
||||
|
||||
for (const rule of this.rules) {
|
||||
// Skip @keyframes rules
|
||||
if (rule.keyframes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Style rules must be considered only when they have selectors that match the node.
|
||||
// When renaming a selector, the unmatched rule lingers in the Rule view, but it no
|
||||
// longer matches the node. This strict check avoids accidentally causing
|
||||
// declarations to be overridden in the remaining matching rules.
|
||||
const isStyleRule = rule.pseudoElement === "" && rule.matchedSelectors.length > 0;
|
||||
|
||||
// Style rules for pseudo-elements must always be considered, regardless if their
|
||||
// selector matches the node. As a convenience, declarations in rules for
|
||||
// pseudo-elements show up in a separate Pseudo-elements accordion when selecting
|
||||
// the host node (instead of the pseudo-element node directly, which is sometimes
|
||||
// impossible, for example with ::selection or ::first-line).
|
||||
// Loosening the strict check on matched selectors ensures these declarations
|
||||
// participate in the algorithm below to mark them as overridden.
|
||||
const isPseudoElementRule = rule.pseudoElement !== "" &&
|
||||
rule.pseudoElement === pseudo;
|
||||
|
||||
const isElementStyle = rule.domRule.type === ELEMENT_STYLE;
|
||||
|
||||
const filterCondition = pseudo === ""
|
||||
? (isStyleRule || isElementStyle)
|
||||
: isPseudoElementRule;
|
||||
|
||||
// First, gather all relevant CSS declarations (aka TextProperty instances).
|
||||
if (filterCondition) {
|
||||
for (const textProp of rule.textProps.slice(0).reverse()) {
|
||||
if (textProp.enabled) {
|
||||
textProps.push(textProp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gather all the computed properties applied by those text
|
||||
// properties.
|
||||
// Gather all text properties applicable to the selected element or pseudo-element.
|
||||
const textProps = this._getDeclarations(pseudo);
|
||||
// Gather all the computed properties applied by those text properties.
|
||||
let computedProps = [];
|
||||
for (const textProp of textProps) {
|
||||
computedProps = computedProps.concat(textProp.computed);
|
||||
|
@ -418,7 +373,71 @@ class ElementStyle {
|
|||
}
|
||||
}
|
||||
}
|
||||
/* eslint-enable complexity */
|
||||
|
||||
/**
|
||||
* Helper for |this.updateDeclarations()| to mark CSS declarations as overridden.
|
||||
*
|
||||
* Returns an array of CSS declarations (aka TextProperty instances) from all rules
|
||||
* applicable to the selected element ordered from more- to less-specific.
|
||||
*
|
||||
* If a pseudo-element type is given, restrict the result only to declarations
|
||||
* applicable to that pseudo-element.
|
||||
*
|
||||
* NOTE: this method skips CSS declarations in @keyframes rules because a number of
|
||||
* criteria such as time and animation delay need to be checked in order to determine
|
||||
* if the property is overridden at runtime.
|
||||
*
|
||||
* @param {String} pseudo
|
||||
* Optional pseudo-element for which to restrict marking CSS declarations as
|
||||
* overridden. If omitted, only declarations for regular style rules are
|
||||
* returned (no pseudo-element style rules).
|
||||
*
|
||||
* @return {Array}
|
||||
* Array of TextProperty instances.
|
||||
*/
|
||||
_getDeclarations(pseudo = "") {
|
||||
const textProps = [];
|
||||
|
||||
for (const rule of this.rules) {
|
||||
// Skip @keyframes rules
|
||||
if (rule.keyframes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Style rules must be considered only when they have selectors that match the node.
|
||||
// When renaming a selector, the unmatched rule lingers in the Rule view, but it no
|
||||
// longer matches the node. This strict check avoids accidentally causing
|
||||
// declarations to be overridden in the remaining matching rules.
|
||||
const isStyleRule = rule.pseudoElement === "" && rule.matchedSelectors.length > 0;
|
||||
|
||||
// Style rules for pseudo-elements must always be considered, regardless if their
|
||||
// selector matches the node. As a convenience, declarations in rules for
|
||||
// pseudo-elements show up in a separate Pseudo-elements accordion when selecting
|
||||
// the host node (instead of the pseudo-element node directly, which is sometimes
|
||||
// impossible, for example with ::selection or ::first-line).
|
||||
// Loosening the strict check on matched selectors ensures these declarations
|
||||
// participate in the algorithm below to mark them as overridden.
|
||||
const isPseudoElementRule = rule.pseudoElement !== "" &&
|
||||
rule.pseudoElement === pseudo;
|
||||
|
||||
const isElementStyle = rule.domRule.type === ELEMENT_STYLE;
|
||||
|
||||
const filterCondition = pseudo === ""
|
||||
? (isStyleRule || isElementStyle)
|
||||
: isPseudoElementRule;
|
||||
|
||||
// Collect all relevant CSS declarations (aka TextProperty instances).
|
||||
if (filterCondition) {
|
||||
for (const textProp of rule.textProps.slice(0).reverse()) {
|
||||
if (textProp.enabled) {
|
||||
textProps.push(textProp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return textProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new declaration to the rule.
|
||||
|
|
|
@ -199,6 +199,7 @@ skip-if = os == 'linux' # focusEditableField times out consistently on linux.
|
|||
[browser_rules_highlight-used-fonts.js]
|
||||
[browser_rules_inactive_css_flexbox.js]
|
||||
[browser_rules_inactive_css_grid.js]
|
||||
[browser_rules_inactive_css_inline.js]
|
||||
[browser_rules_inherited-properties_01.js]
|
||||
[browser_rules_inherited-properties_02.js]
|
||||
[browser_rules_inherited-properties_03.js]
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test css properties that are inactive on block-level elements.
|
||||
|
||||
const TEST_URI = `
|
||||
<style>
|
||||
#block {
|
||||
border: 1px solid #000;
|
||||
vertical-align: sub;
|
||||
}
|
||||
td {
|
||||
vertical-align: super;
|
||||
}
|
||||
#flex {
|
||||
display: inline-flex;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
</style>
|
||||
<h1 style="vertical-align:text-bottom;">browser_rules_inactive_css_inline.js</h1>
|
||||
<div id="block">Block</div>
|
||||
<table>
|
||||
<tr><td>A table cell</td></tr>
|
||||
</table>
|
||||
<div id="flex">Inline flex element</div>
|
||||
`;
|
||||
|
||||
const TEST_DATA = [
|
||||
{
|
||||
selector: "h1",
|
||||
inactiveDeclarations: [
|
||||
{
|
||||
declaration: { "vertical-align": "text-bottom" },
|
||||
ruleIndex: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
selector: "#block",
|
||||
inactiveDeclarations: [
|
||||
{
|
||||
declaration: { "vertical-align": "sub" },
|
||||
ruleIndex: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
selector: "td",
|
||||
activeDeclarations: [
|
||||
{
|
||||
declarations: { "vertical-align": "super" },
|
||||
ruleIndex: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
selector: "#flex",
|
||||
activeDeclarations: [
|
||||
{
|
||||
declarations: { "vertical-align": "text-bottom" },
|
||||
ruleIndex: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
add_task(async function() {
|
||||
await pushPref("devtools.inspector.inactive.css.enabled", true);
|
||||
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
const { inspector, view } = await openRuleView();
|
||||
|
||||
await runInactiveCSSTests(view, inspector, TEST_DATA);
|
||||
});
|
|
@ -35,6 +35,7 @@ loader.lazyRequireGetter(this, "setVariableTooltip",
|
|||
"devtools/client/shared/widgets/tooltip/VariableTooltipHelper", true);
|
||||
loader.lazyRequireGetter(this, "InactiveCssTooltipHelper",
|
||||
"devtools/client/shared/widgets/tooltip/inactive-css-tooltip-helper", false);
|
||||
loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry", false);
|
||||
|
||||
const PREF_IMAGE_TOOLTIP_SIZE = "devtools.inspector.imagePreviewTooltipSize";
|
||||
|
||||
|
@ -44,6 +45,9 @@ const TOOLTIP_FONTFAMILY_TYPE = "font-family";
|
|||
const TOOLTIP_INACTIVE_CSS = "inactive-css";
|
||||
const TOOLTIP_VARIABLE_TYPE = "variable";
|
||||
|
||||
// Telemetry
|
||||
const TOOLTIP_SHOWN_SCALAR = "devtools.tooltip.shown";
|
||||
|
||||
/**
|
||||
* Manages all tooltips in the style-inspector.
|
||||
*
|
||||
|
@ -53,6 +57,7 @@ const TOOLTIP_VARIABLE_TYPE = "variable";
|
|||
function TooltipsOverlay(view) {
|
||||
this.view = view;
|
||||
this._instances = new Map();
|
||||
this.telemetry = new Telemetry();
|
||||
|
||||
this._onNewSelection = this._onNewSelection.bind(this);
|
||||
this.view.inspector.selection.on("new-node-front", this._onNewSelection);
|
||||
|
@ -253,6 +258,9 @@ TooltipsOverlay.prototype = {
|
|||
await setBrokenImageTooltip(this.getTooltip("previewTooltip"),
|
||||
this.view.inspector.panelDoc);
|
||||
}
|
||||
|
||||
this.sendOpenScalarToTelemetry(type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -261,6 +269,8 @@ TooltipsOverlay.prototype = {
|
|||
const nodeFront = inspector.selection.nodeFront;
|
||||
await this._setFontPreviewTooltip(font, nodeFront);
|
||||
|
||||
this.sendOpenScalarToTelemetry(type);
|
||||
|
||||
if (nodeInfo.type === VIEW_NODE_FONT_TYPE) {
|
||||
// If the hovered element is on the font family span, anchor
|
||||
// the tooltip on the whole property value instead.
|
||||
|
@ -272,6 +282,9 @@ TooltipsOverlay.prototype = {
|
|||
if (type === TOOLTIP_VARIABLE_TYPE && nodeInfo.value.value.startsWith("--")) {
|
||||
const variable = nodeInfo.value.variable;
|
||||
await this._setVariablePreviewTooltip(variable);
|
||||
|
||||
this.sendOpenScalarToTelemetry(type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -320,12 +333,25 @@ TooltipsOverlay.prototype = {
|
|||
|
||||
await this.inactiveCssTooltipHelper.setContent(
|
||||
nodeInfo.value, this.getTooltip("interactiveTooltip"));
|
||||
|
||||
this.sendOpenScalarToTelemetry(type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a telemetry Scalar showing that a tooltip of `type` has been opened.
|
||||
*
|
||||
* @param {String} type
|
||||
* The node type from `devtools/client/inspector/shared/node-types`.
|
||||
*/
|
||||
sendOpenScalarToTelemetry(type) {
|
||||
this.telemetry.keyedScalarAdd(TOOLTIP_SHOWN_SCALAR, type, 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the content of the preview tooltip to display an image preview. The image URL can
|
||||
* be relative, a call will be made to the debuggee to retrieve the image content as an
|
||||
|
@ -414,6 +440,7 @@ TooltipsOverlay.prototype = {
|
|||
|
||||
this.view.inspector.selection.off("new-node-front", this._onNewSelection);
|
||||
this.view = null;
|
||||
this.telemetry = null;
|
||||
|
||||
this._isDestroyed = true;
|
||||
},
|
||||
|
|
|
@ -141,7 +141,7 @@ function swapToInnerBrowser({ tab, containerURL, getInnerBrowser }) {
|
|||
// Bug 1510806 has been filed to fix this properly, by making RDM resilient
|
||||
// to process flips.
|
||||
if (mustChangeProcess &&
|
||||
tab.linkedBrowser.remoteType == "privileged") {
|
||||
tab.linkedBrowser.remoteType == "privilegedabout") {
|
||||
debug(`Tab must flip away from the privileged content process ` +
|
||||
`on navigation`);
|
||||
gBrowser.updateBrowserRemoteness(tab.linkedBrowser, {
|
||||
|
|
|
@ -43,6 +43,7 @@ class InactiveCssTooltipHelper {
|
|||
// Size the content.
|
||||
tooltip.setContentSize({width: 275, height: Infinity});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template that the Fluent string will be merged with. This template
|
||||
* looks something like this but there is a variable amount of properties in the
|
||||
|
@ -79,7 +80,8 @@ class InactiveCssTooltipHelper {
|
|||
const { doc } = tooltip;
|
||||
|
||||
this._currentTooltip = tooltip;
|
||||
this._currentUrl = `https://developer.mozilla.org/docs/Web/CSS/${property}`;
|
||||
this._currentUrl = `https://developer.mozilla.org/docs/Web/CSS/${property}` +
|
||||
`?utm_source=devtools&utm_medium=inspector-inactive-css`;
|
||||
|
||||
const templateNode = doc.createElementNS(XHTML_NS, "template");
|
||||
|
||||
|
|
|
@ -143,9 +143,7 @@ class InactivePropertyHelper {
|
|||
const isFirstLetter = selectorText && selectorText.includes("::first-letter");
|
||||
const isFirstLine = selectorText && selectorText.includes("::first-line");
|
||||
|
||||
const isInlineLevel = this.checkStyle("display", ["inline", "table-cell"]);
|
||||
|
||||
return !isInlineLevel && !isFirstLetter && !isFirstLine;
|
||||
return !this.isInlineLevel() && !isFirstLetter && !isFirstLine;
|
||||
},
|
||||
fixId: "inactive-css-not-inline-or-tablecell-fix",
|
||||
msgId: "inactive-css-not-inline-or-tablecell",
|
||||
|
@ -299,6 +297,24 @@ class InactivePropertyHelper {
|
|||
return values.some(value => this.style[propName] === value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current node is an inline-level box.
|
||||
*/
|
||||
isInlineLevel() {
|
||||
return this.checkStyle("display", [
|
||||
"inline",
|
||||
"inline-block",
|
||||
"inline-table",
|
||||
"inline-flex",
|
||||
"inline-grid",
|
||||
"table-cell",
|
||||
"table-row",
|
||||
"table-row-group",
|
||||
"table-header-group",
|
||||
"table-footer-group",
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current node is a flex container i.e. a node that has a style
|
||||
* of `display:flex` or `display:inline-flex`.
|
||||
|
|
|
@ -91,7 +91,7 @@ static const RedirEntry kRedirMap[] = {
|
|||
nsIAboutModule::ALLOW_SCRIPT |
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
|
||||
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGED_CHILD},
|
||||
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS},
|
||||
{"mozilla", "chrome://global/content/mozilla.xhtml",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT},
|
||||
{"neterror", "chrome://global/content/netError.xhtml",
|
||||
|
|
|
@ -710,8 +710,9 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
|
|||
type = mozilla::ProcType::File;
|
||||
} else if (processType.EqualsLiteral(EXTENSION_REMOTE_TYPE)) {
|
||||
type = mozilla::ProcType::Extension;
|
||||
} else if (processType.EqualsLiteral(PRIVILEGED_REMOTE_TYPE)) {
|
||||
type = mozilla::ProcType::Privileged;
|
||||
} else if (processType.EqualsLiteral(
|
||||
PRIVILEGEDABOUT_REMOTE_TYPE)) {
|
||||
type = mozilla::ProcType::PrivilegedAbout;
|
||||
} else if (processType.EqualsLiteral(
|
||||
LARGE_ALLOCATION_REMOTE_TYPE)) {
|
||||
type = mozilla::ProcType::WebLargeAllocation;
|
||||
|
|
|
@ -185,7 +185,7 @@ PopupBlocker::PopupControlState PopupBlocker::GetEventPopupControlState(
|
|||
case eBasicEventClass:
|
||||
// For these following events only allow popups if they're
|
||||
// triggered while handling user input. See
|
||||
// PresShell::EventHandler::PrepareToDispatchEvent() for details.
|
||||
// EventStateManager::IsUserInteractionEvent() for details.
|
||||
if (EventStateManager::IsHandlingUserInput()) {
|
||||
abuse = PopupBlocker::openBlocked;
|
||||
switch (aEvent->mMessage) {
|
||||
|
@ -207,7 +207,7 @@ PopupBlocker::PopupControlState PopupBlocker::GetEventPopupControlState(
|
|||
case eEditorInputEventClass:
|
||||
// For this following event only allow popups if it's triggered
|
||||
// while handling user input. See
|
||||
// PresShell::EventHandler::PrepareToDispatchEvent() for details.
|
||||
// EventStateManager::IsUserInteractionEvent() for details.
|
||||
if (EventStateManager::IsHandlingUserInput()) {
|
||||
abuse = PopupBlocker::openBlocked;
|
||||
switch (aEvent->mMessage) {
|
||||
|
@ -224,7 +224,7 @@ PopupBlocker::PopupControlState PopupBlocker::GetEventPopupControlState(
|
|||
case eInputEventClass:
|
||||
// For this following event only allow popups if it's triggered
|
||||
// while handling user input. See
|
||||
// PresShell::EventHandler::PrepareToDispatchEvent() for details.
|
||||
// EventStateManager::IsUserInteractionEvent() for details.
|
||||
if (EventStateManager::IsHandlingUserInput()) {
|
||||
abuse = PopupBlocker::openBlocked;
|
||||
switch (aEvent->mMessage) {
|
||||
|
@ -370,7 +370,7 @@ PopupBlocker::PopupControlState PopupBlocker::GetEventPopupControlState(
|
|||
case eFormEventClass:
|
||||
// For these following events only allow popups if they're
|
||||
// triggered while handling user input. See
|
||||
// PresShell::EventHandler::PrepareToDispatchEvent() for details.
|
||||
// EventStateManager::IsUserInteractionEvent() for details.
|
||||
if (EventStateManager::IsHandlingUserInput()) {
|
||||
abuse = PopupBlocker::openBlocked;
|
||||
switch (aEvent->mMessage) {
|
||||
|
|
|
@ -415,7 +415,7 @@ enum ProcType {
|
|||
"web",
|
||||
"file",
|
||||
"extension",
|
||||
"privileged",
|
||||
"privilegedabout",
|
||||
"webLargeAllocation",
|
||||
"gpu",
|
||||
"rdd",
|
||||
|
|
|
@ -1141,8 +1141,11 @@ void EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
|
|||
aEvent->PreventDefault();
|
||||
}
|
||||
|
||||
Maybe<AutoHandlingUserInputStatePusher> userInputStatePusher;
|
||||
Maybe<AutoPopupStatePusher> popupStatePusher;
|
||||
if (mIsMainThreadELM) {
|
||||
userInputStatePusher.emplace(
|
||||
EventStateManager::IsUserInteractionEvent(aEvent), aEvent);
|
||||
popupStatePusher.emplace(
|
||||
PopupBlocker::GetEventPopupControlState(aEvent, *aDOMEvent));
|
||||
}
|
||||
|
|
|
@ -1558,8 +1558,7 @@ void EventStateManager::FireContextClick() {
|
|||
}
|
||||
}
|
||||
|
||||
Document* doc = mGestureDownContent->GetComposedDoc();
|
||||
AutoHandlingUserInputStatePusher userInpStatePusher(true, &event, doc);
|
||||
AutoHandlingUserInputStatePusher userInpStatePusher(true, &event);
|
||||
|
||||
// dispatch to DOM
|
||||
EventDispatcher::Dispatch(mGestureDownContent, mPresContext, &event,
|
||||
|
@ -4042,6 +4041,39 @@ class MOZ_STACK_CLASS ESMEventCB : public EventDispatchingCallback {
|
|||
nsCOMPtr<nsIContent> mTarget;
|
||||
};
|
||||
|
||||
/*static*/
|
||||
bool EventStateManager::IsUserInteractionEvent(const WidgetEvent* aEvent) {
|
||||
if (!aEvent->IsTrusted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (aEvent->mMessage) {
|
||||
// eKeyboardEventClass
|
||||
case eKeyPress:
|
||||
case eKeyDown:
|
||||
case eKeyUp:
|
||||
// Not all keyboard events are treated as user input, so that popups
|
||||
// can't be opened, fullscreen mode can't be started, etc at
|
||||
// unexpected time.
|
||||
return aEvent->AsKeyboardEvent()->CanTreatAsUserInput();
|
||||
// eBasicEventClass
|
||||
case eFormChange:
|
||||
// eMouseEventClass
|
||||
case eMouseClick:
|
||||
case eMouseDown:
|
||||
case eMouseUp:
|
||||
// ePointerEventClass
|
||||
case ePointerDown:
|
||||
case ePointerUp:
|
||||
// eTouchEventClass
|
||||
case eTouchStart:
|
||||
case eTouchEnd:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/
|
||||
bool EventStateManager::IsHandlingUserInput() {
|
||||
return sUserInputEventDepth > 0;
|
||||
|
@ -6257,36 +6289,13 @@ void EventStateManager::Prefs::Init() {
|
|||
/******************************************************************/
|
||||
|
||||
AutoHandlingUserInputStatePusher::AutoHandlingUserInputStatePusher(
|
||||
bool aIsHandlingUserInput, WidgetEvent* aEvent, Document* aDocument)
|
||||
bool aIsHandlingUserInput, WidgetEvent* aEvent)
|
||||
: mMessage(aEvent ? aEvent->mMessage : eVoidEvent),
|
||||
mIsHandlingUserInput(aIsHandlingUserInput) {
|
||||
if (!aIsHandlingUserInput) {
|
||||
return;
|
||||
}
|
||||
EventStateManager::StartHandlingUserInput(mMessage);
|
||||
if (mMessage == eMouseDown) {
|
||||
PresShell::ReleaseCapturingContent();
|
||||
PresShell::AllowMouseCapture(true);
|
||||
}
|
||||
if (!aDocument || !aEvent || !aEvent->IsTrusted()) {
|
||||
return;
|
||||
}
|
||||
if (NeedsToResetFocusManagerMouseButtonHandlingState()) {
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
NS_ENSURE_TRUE_VOID(fm);
|
||||
// If it's in modal state, mouse button event handling may be nested.
|
||||
// E.g., a modal dialog is opened at mousedown or mouseup event handler
|
||||
// and the dialog is clicked. Therefore, we should store current
|
||||
// mouse button event handling document if nsFocusManager already has it.
|
||||
mMouseButtonEventHandlingDocument =
|
||||
fm->SetMouseButtonHandlingDocument(aDocument);
|
||||
}
|
||||
if (NeedsToUpdateCurrentMouseBtnState()) {
|
||||
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
|
||||
if (mouseEvent) {
|
||||
EventStateManager::sCurrentMouseBtn = mouseEvent->mButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AutoHandlingUserInputStatePusher::~AutoHandlingUserInputStatePusher() {
|
||||
|
@ -6294,18 +6303,6 @@ AutoHandlingUserInputStatePusher::~AutoHandlingUserInputStatePusher() {
|
|||
return;
|
||||
}
|
||||
EventStateManager::StopHandlingUserInput(mMessage);
|
||||
if (mMessage == eMouseDown) {
|
||||
PresShell::AllowMouseCapture(false);
|
||||
}
|
||||
if (NeedsToResetFocusManagerMouseButtonHandlingState()) {
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
NS_ENSURE_TRUE_VOID(fm);
|
||||
nsCOMPtr<Document> handlingDocument =
|
||||
fm->SetMouseButtonHandlingDocument(mMouseButtonEventHandlingDocument);
|
||||
}
|
||||
if (NeedsToUpdateCurrentMouseBtnState()) {
|
||||
EventStateManager::sCurrentMouseBtn = MouseButton::eNotPressed;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -243,6 +243,12 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver {
|
|||
const Maybe<gfx::IntPoint>& aHotspot, nsIWidget* aWidget,
|
||||
bool aLockCursor);
|
||||
|
||||
/**
|
||||
* Returns true if the event is considered as user interaction event. I.e.,
|
||||
* enough obvious input to allow to open popup, etc. Otherwise, returns false.
|
||||
*/
|
||||
static bool IsUserInteractionEvent(const WidgetEvent* aEvent);
|
||||
|
||||
/**
|
||||
* StartHandlingUserInput() is called when we start to handle a user input.
|
||||
* StopHandlingUserInput() is called when we finish handling a user input.
|
||||
|
@ -1286,24 +1292,13 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver {
|
|||
*/
|
||||
class MOZ_RAII AutoHandlingUserInputStatePusher final {
|
||||
public:
|
||||
AutoHandlingUserInputStatePusher(bool aIsHandlingUserInput,
|
||||
WidgetEvent* aEvent,
|
||||
dom::Document* aDocument);
|
||||
explicit AutoHandlingUserInputStatePusher(bool aIsHandlingUserInput,
|
||||
WidgetEvent* aEvent = nullptr);
|
||||
~AutoHandlingUserInputStatePusher();
|
||||
|
||||
protected:
|
||||
RefPtr<dom::Document> mMouseButtonEventHandlingDocument;
|
||||
EventMessage mMessage;
|
||||
bool mIsHandlingUserInput;
|
||||
|
||||
bool NeedsToResetFocusManagerMouseButtonHandlingState() const {
|
||||
return mMessage == eMouseDown || mMessage == eMouseUp;
|
||||
}
|
||||
|
||||
bool NeedsToUpdateCurrentMouseBtnState() const {
|
||||
return mMessage == eMouseDown || mMessage == eMouseUp ||
|
||||
mMessage == ePointerDown || mMessage == ePointerUp;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -698,7 +698,7 @@ nsresult HTMLFormElement::SubmitSubmission(
|
|||
AutoPopupStatePusher popupStatePusher(mSubmitPopupState);
|
||||
|
||||
AutoHandlingUserInputStatePusher userInpStatePusher(
|
||||
aFormSubmission->IsInitiatedFromUserInput(), nullptr, doc);
|
||||
aFormSubmission->IsInitiatedFromUserInput());
|
||||
|
||||
nsCOMPtr<nsIInputStream> postDataStream;
|
||||
rv = aFormSubmission->GetEncodedSubmission(
|
||||
|
|
|
@ -2285,7 +2285,7 @@ mozilla::ipc::IPCResult BrowserParent::RecvReplyKeyEvent(
|
|||
NS_ENSURE_TRUE(presContext, IPC_OK());
|
||||
|
||||
AutoHandlingUserInputStatePusher userInpStatePusher(localEvent.IsTrusted(),
|
||||
&localEvent, doc);
|
||||
&localEvent);
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
|
||||
|
|
|
@ -672,6 +672,11 @@ bool ContentChild::Init(MessageLoop* aIOLoop, base::ProcessId aParentPid,
|
|||
// their own children.
|
||||
if (recordreplay::IsMiddleman()) {
|
||||
SetMiddlemanIPCChannel(recordreplay::parent::ChannelToUIProcess());
|
||||
|
||||
// Eagerly mark this child as connected, as using another IPC channel will
|
||||
// cause that channel's protocol to be marked as connected instead and
|
||||
// prevent this one from being able to send IPDL messages.
|
||||
ActorConnected();
|
||||
}
|
||||
|
||||
if (!Open(aChannel, aParentPid, aIOLoop)) {
|
||||
|
@ -2723,7 +2728,7 @@ mozilla::ipc::IPCResult ContentChild::RecvRemoteType(
|
|||
SetProcessName(NS_LITERAL_STRING("file:// Content"));
|
||||
} else if (aRemoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE)) {
|
||||
SetProcessName(NS_LITERAL_STRING("WebExtensions"));
|
||||
} else if (aRemoteType.EqualsLiteral(PRIVILEGED_REMOTE_TYPE)) {
|
||||
} else if (aRemoteType.EqualsLiteral(PRIVILEGEDABOUT_REMOTE_TYPE)) {
|
||||
SetProcessName(NS_LITERAL_STRING("Privileged Content"));
|
||||
} else if (aRemoteType.EqualsLiteral(LARGE_ALLOCATION_REMOTE_TYPE)) {
|
||||
SetProcessName(NS_LITERAL_STRING("Large Allocation Web Content"));
|
||||
|
|
|
@ -52,7 +52,8 @@
|
|||
#define DEFAULT_REMOTE_TYPE "web"
|
||||
#define FILE_REMOTE_TYPE "file"
|
||||
#define EXTENSION_REMOTE_TYPE "extension"
|
||||
#define PRIVILEGED_REMOTE_TYPE "privileged"
|
||||
#define PRIVILEGEDABOUT_REMOTE_TYPE "privilegedabout"
|
||||
#define PRIVILEGEDMOZILLA_REMOTE_TYPE "privilegedmozilla"
|
||||
|
||||
// This must start with the DEFAULT_REMOTE_TYPE above.
|
||||
#define LARGE_ALLOCATION_REMOTE_TYPE "webLargeAllocation"
|
||||
|
|
|
@ -92,7 +92,7 @@ declTest("getActor with remoteType match", {
|
|||
});
|
||||
|
||||
declTest("getActor with remoteType mismatch", {
|
||||
remoteTypes: ["privileged"],
|
||||
remoteTypes: ["privilegedabout"],
|
||||
url: TEST_URL,
|
||||
|
||||
async test(browser) {
|
||||
|
|
|
@ -800,7 +800,7 @@ static void DebugDoContentSecurityCheck(nsIChannel* aChannel,
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || defined(FUZZING)
|
||||
#ifdef EARLY_BETA_OR_EARLIER
|
||||
// Assert that we never use the SystemPrincipal to load remote documents
|
||||
// i.e., HTTP, HTTPS, FTP URLs
|
||||
static void AssertSystemPrincipalMustNotLoadRemoteDocuments(
|
||||
|
@ -860,7 +860,7 @@ static void AssertSystemPrincipalMustNotLoadRemoteDocuments(
|
|||
// but other mochitest are exempt from this
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(false, "SystemPrincipal must not load remote documents.");
|
||||
MOZ_RELEASE_ASSERT(false, "SystemPrincipal must not load remote documents.");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -890,7 +890,7 @@ nsresult nsContentSecurityManager::doContentSecurityCheck(
|
|||
DebugDoContentSecurityCheck(aChannel, loadInfo);
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || defined(FUZZING)
|
||||
#ifdef EARLY_BETA_OR_EARLIER
|
||||
AssertSystemPrincipalMustNotLoadRemoteDocuments(aChannel);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ support-files =
|
|||
support-files =
|
||||
file_FTP_console_warning.html
|
||||
[browser_test_assert_systemprincipal_documents.js]
|
||||
skip-if = !debug && !fuzzing
|
||||
skip-if = !nightly_build
|
||||
support-files =
|
||||
file_assert_systemprincipal_documents.html
|
||||
file_assert_systemprincipal_documents_iframe.html
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef jit_shared_IonAssemblerBufferWithConstantPools_h
|
||||
#define jit_shared_IonAssemblerBufferWithConstantPools_h
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -783,7 +784,7 @@ struct AssemblerBufferWithConstantPools
|
|||
id, sizeExcludingCurrentPool());
|
||||
}
|
||||
|
||||
finishPool();
|
||||
finishPool(numInst * InstSize);
|
||||
if (this->oom()) {
|
||||
return OOM_FAIL;
|
||||
}
|
||||
|
@ -813,7 +814,7 @@ struct AssemblerBufferWithConstantPools
|
|||
JitSpew(JitSpew_Pools,
|
||||
"[%d] nextInstrOffset @ %d caused a constant pool spill", id,
|
||||
this->nextOffset().getOffset());
|
||||
finishPool();
|
||||
finishPool(ShortRangeBranchHysteresis);
|
||||
}
|
||||
return this->nextOffset();
|
||||
}
|
||||
|
@ -944,27 +945,38 @@ struct AssemblerBufferWithConstantPools
|
|||
|
||||
private:
|
||||
// Are any short-range branches about to expire?
|
||||
bool hasExpirableShortRangeBranches() const {
|
||||
bool hasExpirableShortRangeBranches(size_t reservedBytes) const {
|
||||
if (branchDeadlines_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Include branches that would expire in the next N bytes.
|
||||
// The hysteresis avoids the needless creation of many tiny constant
|
||||
// pools.
|
||||
return this->nextOffset().getOffset() + ShortRangeBranchHysteresis >
|
||||
size_t(branchDeadlines_.earliestDeadline().getOffset());
|
||||
// Include branches that would expire in the next N bytes. The reservedBytes
|
||||
// argument avoids the needless creation of many tiny constant pools.
|
||||
//
|
||||
// As the reservedBytes could be of any sizes such as SIZE_MAX, in the case
|
||||
// of flushPool, we have to check for overflow when comparing the deadline
|
||||
// with our expected reserved bytes.
|
||||
size_t deadline = branchDeadlines_.earliestDeadline().getOffset();
|
||||
using CheckedSize = mozilla::CheckedInt<size_t>;
|
||||
CheckedSize current(this->nextOffset().getOffset());
|
||||
CheckedSize poolFreeSpace(reservedBytes);
|
||||
auto future = current + poolFreeSpace;
|
||||
return !future.isValid() || deadline < future.value();
|
||||
}
|
||||
|
||||
bool isPoolEmpty() const {
|
||||
return pool_.numEntries() == 0 && !hasExpirableShortRangeBranches();
|
||||
bool isPoolEmptyFor(size_t bytes) const {
|
||||
return pool_.numEntries() == 0 && !hasExpirableShortRangeBranches(bytes);
|
||||
}
|
||||
void finishPool() {
|
||||
void finishPool(size_t reservedBytes) {
|
||||
JitSpew(JitSpew_Pools,
|
||||
"[%d] Attempting to finish pool %zu with %u entries.", id,
|
||||
poolInfo_.length(), pool_.numEntries());
|
||||
|
||||
if (isPoolEmpty()) {
|
||||
if (reservedBytes < ShortRangeBranchHysteresis) {
|
||||
reservedBytes = ShortRangeBranchHysteresis;
|
||||
}
|
||||
|
||||
if (isPoolEmptyFor(reservedBytes)) {
|
||||
// If there is no data in the pool being dumped, don't dump anything.
|
||||
JitSpew(JitSpew_Pools, "[%d] Aborting because the pool is empty", id);
|
||||
return;
|
||||
|
@ -984,7 +996,7 @@ struct AssemblerBufferWithConstantPools
|
|||
|
||||
// Now generate branch veneers for any short-range branches that are
|
||||
// about to expire.
|
||||
while (hasExpirableShortRangeBranches()) {
|
||||
while (hasExpirableShortRangeBranches(reservedBytes)) {
|
||||
unsigned rangeIdx = branchDeadlines_.earliestDeadlineRange();
|
||||
BufferOffset deadline = branchDeadlines_.earliestDeadline();
|
||||
|
||||
|
@ -1052,7 +1064,7 @@ struct AssemblerBufferWithConstantPools
|
|||
return;
|
||||
}
|
||||
JitSpew(JitSpew_Pools, "[%d] Requesting a pool flush", id);
|
||||
finishPool();
|
||||
finishPool(SIZE_MAX);
|
||||
}
|
||||
|
||||
void enterNoPool(size_t maxInst) {
|
||||
|
@ -1070,7 +1082,8 @@ struct AssemblerBufferWithConstantPools
|
|||
if (!hasSpaceForInsts(maxInst, 0)) {
|
||||
JitSpew(JitSpew_Pools, "[%d] No-Pool instruction(%zu) caused a spill.",
|
||||
id, sizeExcludingCurrentPool());
|
||||
finishPool();
|
||||
finishPool(maxInst * InstSize);
|
||||
MOZ_ASSERT(hasSpaceForInsts(maxInst, 0));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -1105,7 +1118,7 @@ struct AssemblerBufferWithConstantPools
|
|||
inhibitNops_ = false;
|
||||
}
|
||||
void assertNoPoolAndNoNops() {
|
||||
MOZ_ASSERT(inhibitNops_ && (isPoolEmpty() || canNotPlacePool_));
|
||||
MOZ_ASSERT(inhibitNops_ && (isPoolEmptyFor(InstSize) || canNotPlacePool_));
|
||||
}
|
||||
|
||||
void align(unsigned alignment) { align(alignment, alignFillInst_); }
|
||||
|
@ -1130,7 +1143,7 @@ struct AssemblerBufferWithConstantPools
|
|||
// Alignment would cause a pool dump, so dump the pool now.
|
||||
JitSpew(JitSpew_Pools, "[%d] Alignment of %d at %zu caused a spill.", id,
|
||||
alignment, sizeExcludingCurrentPool());
|
||||
finishPool();
|
||||
finishPool(requiredFill);
|
||||
}
|
||||
|
||||
bool prevInhibitNops = inhibitNops_;
|
||||
|
|
|
@ -435,12 +435,14 @@ MSG_DEF(JSMSG_PROXY_DELETE_RETURNED_FALSE, 1, JSEXN_TYPEERR, "can't delete prope
|
|||
MSG_DEF(JSMSG_PROXY_PREVENTEXTENSIONS_RETURNED_FALSE, 0, JSEXN_TYPEERR, "proxy preventExtensions handler returned false")
|
||||
MSG_DEF(JSMSG_PROXY_SET_RETURNED_FALSE, 1, JSEXN_TYPEERR, "proxy set handler returned false for property '{0}'")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE, 0, JSEXN_TYPEERR, "proxy can't report an extensible object as non-extensible")
|
||||
MSG_DEF(JSMSG_CANT_DELETE_NON_EXTENSIBLE, 1, JSEXN_TYPEERR, "proxy can't delete property '{0}' on a non-extensible object")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_C_AS_NC, 1, JSEXN_TYPEERR, "proxy can't report existing configurable property '{0}' as non-configurable")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE, 1, JSEXN_TYPEERR, "proxy can't report an existing own property '{0}' as non-existent on a non-extensible object")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_INVALID, 2, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor ('{0}', {1})")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE, 1, JSEXN_TYPEERR, "proxy can't report a non-configurable own property '{0}' as non-existent")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_NEW, 1, JSEXN_TYPEERR, "proxy can't report a new property '{0}' on a non-extensible object")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_NE_AS_NC, 1, JSEXN_TYPEERR, "proxy can't report a non-existent property '{0}' as non-configurable")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_W_AS_NW, 1, JSEXN_TYPEERR, "proxy can't report existing writable property '{0}' as non-writable")
|
||||
MSG_DEF(JSMSG_CANT_SET_NW_NC, 1, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property '{0}'")
|
||||
MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 1, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property '{0}' without a setter")
|
||||
MSG_DEF(JSMSG_CANT_SKIP_NC, 1, JSEXN_TYPEERR, "proxy can't skip a non-configurable property '{0}'")
|
||||
|
|
|
@ -620,6 +620,12 @@ bool ScriptedProxyHandler::getOwnPropertyDescriptor(
|
|||
if (targetDesc.configurable()) {
|
||||
return js::Throw(cx, id, JSMSG_CANT_REPORT_C_AS_NC);
|
||||
}
|
||||
|
||||
if (resultDesc.hasWritable() && !resultDesc.writable()) {
|
||||
if (targetDesc.writable()) {
|
||||
return js::Throw(cx, id, JSMSG_CANT_REPORT_W_AS_NW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 18.
|
||||
|
@ -733,6 +739,17 @@ bool ScriptedProxyHandler::defineProperty(JSContext* cx, HandleObject proxy,
|
|||
return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID,
|
||||
DETAILS_CANT_REPORT_C_AS_NC);
|
||||
}
|
||||
|
||||
if (targetDesc.isDataDescriptor() && !targetDesc.configurable() &&
|
||||
targetDesc.writable()) {
|
||||
if (desc.hasWritable() && !desc.writable()) {
|
||||
static const char DETAILS_CANT_DEFINE_NW[] =
|
||||
"proxy can't define an existing non-configurable writable property "
|
||||
"as non-writable";
|
||||
return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID,
|
||||
DETAILS_CANT_DEFINE_NW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 17.
|
||||
|
@ -992,12 +1009,26 @@ bool ScriptedProxyHandler::delete_(JSContext* cx, HandleObject proxy,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Step 11.
|
||||
if (!desc.object()) {
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
// Step 12.
|
||||
if (desc.object() && !desc.configurable()) {
|
||||
if (!desc.configurable()) {
|
||||
return Throw(cx, id, JSMSG_CANT_DELETE);
|
||||
}
|
||||
|
||||
// Steps 11,13.
|
||||
bool extensible;
|
||||
if (!IsExtensible(cx, target, &extensible)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!extensible) {
|
||||
return Throw(cx, id, JSMSG_CANT_DELETE_NON_EXTENSIBLE);
|
||||
}
|
||||
|
||||
// Step 13.
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
"use strict";
|
||||
|
||||
var target = {};
|
||||
Object.defineProperty(target, "test", {configurable: false, writable: true, value: 5});
|
||||
|
||||
var proxy = new Proxy(target, {
|
||||
defineProperty(target, property) {
|
||||
assertEq(property, "test");
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
assertThrowsInstanceOf(
|
||||
() => Object.defineProperty(proxy, "test", {writable: false}), TypeError);
|
||||
|
||||
assertThrowsInstanceOf(
|
||||
() => Reflect.defineProperty(proxy, "test", {writable: false}), TypeError);
|
||||
|
||||
reportCompare(0, 0);
|
|
@ -0,0 +1,18 @@
|
|||
"use strict";
|
||||
|
||||
var target = { test: true };
|
||||
Object.preventExtensions(target);
|
||||
|
||||
var proxy = new Proxy(target, {
|
||||
deleteProperty(target, property) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
assertEq(delete proxy.missing, true);
|
||||
assertEq(Reflect.deleteProperty(proxy, "missing"), true);
|
||||
|
||||
assertThrowsInstanceOf(() => { delete proxy.test; }, TypeError);
|
||||
assertThrowsInstanceOf(() => Reflect.deleteProperty(proxy, "test"), TypeError);
|
||||
|
||||
reportCompare(0, 0);
|
|
@ -0,0 +1,20 @@
|
|||
"use strict";
|
||||
|
||||
var target = {};
|
||||
Object.defineProperty(target, "test",
|
||||
{configurable: false, writable: true, value: 1});
|
||||
|
||||
var proxy = new Proxy(target, {
|
||||
getOwnPropertyDescriptor(target, property) {
|
||||
assertEq(property, "test");
|
||||
return {configurable: false, writable: false, value: 1};
|
||||
}
|
||||
});
|
||||
|
||||
assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(proxy, "test"),
|
||||
TypeError);
|
||||
|
||||
assertThrowsInstanceOf(() => Reflect.getOwnPropertyDescriptor(proxy, "test"),
|
||||
TypeError);
|
||||
|
||||
reportCompare(0, 0);
|
|
@ -188,8 +188,8 @@ ProcessType ScriptPreloader::GetChildProcessType(const nsAString& remoteType) {
|
|||
if (remoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE)) {
|
||||
return ProcessType::Extension;
|
||||
}
|
||||
if (remoteType.EqualsLiteral(PRIVILEGED_REMOTE_TYPE)) {
|
||||
return ProcessType::Privileged;
|
||||
if (remoteType.EqualsLiteral(PRIVILEGEDABOUT_REMOTE_TYPE)) {
|
||||
return ProcessType::PrivilegedAbout;
|
||||
}
|
||||
return ProcessType::Web;
|
||||
}
|
||||
|
@ -346,9 +346,9 @@ void ScriptPreloader::FinishContentStartup() {
|
|||
|
||||
#ifdef DEBUG
|
||||
if (mContentStartupFinishedTopic.Equals(CONTENT_DOCUMENT_LOADED_TOPIC)) {
|
||||
MOZ_ASSERT(sProcessType == ProcessType::Privileged);
|
||||
MOZ_ASSERT(sProcessType == ProcessType::PrivilegedAbout);
|
||||
} else {
|
||||
MOZ_ASSERT(sProcessType != ProcessType::Privileged);
|
||||
MOZ_ASSERT(sProcessType != ProcessType::PrivilegedAbout);
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
|
@ -457,7 +457,7 @@ Result<Ok, nsresult> ScriptPreloader::InitCache(
|
|||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
MOZ_RELEASE_ASSERT(obs);
|
||||
|
||||
if (sProcessType == ProcessType::Privileged) {
|
||||
if (sProcessType == ProcessType::PrivilegedAbout) {
|
||||
// Since we control all of the documents loaded in the privileged
|
||||
// content process, we can increase the window of active time for the
|
||||
// ScriptPreloader to include the scripts that are loaded until the
|
||||
|
|
|
@ -46,7 +46,7 @@ enum class ProcessType : uint8_t {
|
|||
Parent,
|
||||
Web,
|
||||
Extension,
|
||||
Privileged,
|
||||
PrivilegedAbout,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -7730,6 +7730,67 @@ nsresult PresShell::EventHandler::HandleEventWithTarget(
|
|||
return rv;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class MOZ_RAII AutoEventHandler final {
|
||||
public:
|
||||
AutoEventHandler(WidgetEvent* aEvent, Document* aDocument) : mEvent(aEvent) {
|
||||
MOZ_ASSERT(mEvent);
|
||||
MOZ_ASSERT(mEvent->IsTrusted());
|
||||
|
||||
if (mEvent->mMessage == eMouseDown) {
|
||||
PresShell::ReleaseCapturingContent();
|
||||
PresShell::AllowMouseCapture(true);
|
||||
}
|
||||
if (aDocument && NeedsToResetFocusManagerMouseButtonHandlingState()) {
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
NS_ENSURE_TRUE_VOID(fm);
|
||||
// If it's in modal state, mouse button event handling may be nested.
|
||||
// E.g., a modal dialog is opened at mousedown or mouseup event handler
|
||||
// and the dialog is clicked. Therefore, we should store current
|
||||
// mouse button event handling document if nsFocusManager already has it.
|
||||
mMouseButtonEventHandlingDocument =
|
||||
fm->SetMouseButtonHandlingDocument(aDocument);
|
||||
}
|
||||
if (NeedsToUpdateCurrentMouseBtnState()) {
|
||||
WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent();
|
||||
if (mouseEvent) {
|
||||
EventStateManager::sCurrentMouseBtn = mouseEvent->mButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~AutoEventHandler() {
|
||||
if (mEvent->mMessage == eMouseDown) {
|
||||
PresShell::AllowMouseCapture(false);
|
||||
}
|
||||
if (NeedsToResetFocusManagerMouseButtonHandlingState()) {
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
NS_ENSURE_TRUE_VOID(fm);
|
||||
RefPtr<Document> document =
|
||||
fm->SetMouseButtonHandlingDocument(mMouseButtonEventHandlingDocument);
|
||||
}
|
||||
if (NeedsToUpdateCurrentMouseBtnState()) {
|
||||
EventStateManager::sCurrentMouseBtn = MouseButton::eNotPressed;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
bool NeedsToResetFocusManagerMouseButtonHandlingState() const {
|
||||
return mEvent->mMessage == eMouseDown || mEvent->mMessage == eMouseUp;
|
||||
}
|
||||
|
||||
bool NeedsToUpdateCurrentMouseBtnState() const {
|
||||
return mEvent->mMessage == eMouseDown || mEvent->mMessage == eMouseUp ||
|
||||
mEvent->mMessage == ePointerDown || mEvent->mMessage == ePointerUp;
|
||||
}
|
||||
|
||||
RefPtr<Document> mMouseButtonEventHandlingDocument;
|
||||
WidgetEvent* mEvent;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
nsresult PresShell::EventHandler::HandleEventWithCurrentEventInfo(
|
||||
WidgetEvent* aEvent, nsEventStatus* aEventStatus,
|
||||
bool aIsHandlingNativeEvent, nsIContent* aOverrideClickTarget) {
|
||||
|
@ -7756,10 +7817,8 @@ nsresult PresShell::EventHandler::HandleEventWithCurrentEventInfo(
|
|||
}
|
||||
}
|
||||
|
||||
bool isHandlingUserInput = false;
|
||||
bool touchIsNew = false;
|
||||
if (!PrepareToDispatchEvent(aEvent, aEventStatus, &isHandlingUserInput,
|
||||
&touchIsNew)) {
|
||||
if (!PrepareToDispatchEvent(aEvent, aEventStatus, &touchIsNew)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -7767,9 +7826,9 @@ nsresult PresShell::EventHandler::HandleEventWithCurrentEventInfo(
|
|||
// performance.
|
||||
RecordEventPreparationPerformance(aEvent);
|
||||
|
||||
AutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput,
|
||||
aEvent, GetDocument());
|
||||
|
||||
AutoHandlingUserInputStatePusher userInpStatePusher(
|
||||
EventStateManager::IsUserInteractionEvent(aEvent), aEvent);
|
||||
AutoEventHandler eventHandler(aEvent, GetDocument());
|
||||
AutoPopupStatePusher popupStatePusher(
|
||||
PopupBlocker::GetEventPopupControlState(aEvent));
|
||||
|
||||
|
@ -7877,11 +7936,9 @@ nsresult PresShell::EventHandler::DispatchEvent(
|
|||
}
|
||||
|
||||
bool PresShell::EventHandler::PrepareToDispatchEvent(
|
||||
WidgetEvent* aEvent, nsEventStatus* aEventStatus, bool* aIsUserInteraction,
|
||||
bool* aTouchIsNew) {
|
||||
WidgetEvent* aEvent, nsEventStatus* aEventStatus, bool* aTouchIsNew) {
|
||||
MOZ_ASSERT(aEvent->IsTrusted());
|
||||
MOZ_ASSERT(aEventStatus);
|
||||
MOZ_ASSERT(aIsUserInteraction);
|
||||
MOZ_ASSERT(aTouchIsNew);
|
||||
|
||||
*aTouchIsNew = false;
|
||||
|
@ -7895,26 +7952,14 @@ bool PresShell::EventHandler::PrepareToDispatchEvent(
|
|||
case eKeyUp: {
|
||||
WidgetKeyboardEvent* keyboardEvent = aEvent->AsKeyboardEvent();
|
||||
MaybeHandleKeyboardEventBeforeDispatch(keyboardEvent);
|
||||
// Not all keyboard events are treated as user input, so that popups
|
||||
// can't be opened, fullscreen mode can't be started, etc at unexpected
|
||||
// time.
|
||||
*aIsUserInteraction = keyboardEvent->CanTreatAsUserInput();
|
||||
return true;
|
||||
}
|
||||
case eMouseDown:
|
||||
case eMouseUp:
|
||||
case ePointerDown:
|
||||
case ePointerUp:
|
||||
*aIsUserInteraction = true;
|
||||
return true;
|
||||
|
||||
case eMouseMove: {
|
||||
bool allowCapture = EventStateManager::GetActiveEventStateManager() &&
|
||||
GetPresContext() &&
|
||||
GetPresContext()->EventStateManager() ==
|
||||
EventStateManager::GetActiveEventStateManager();
|
||||
PresShell::AllowMouseCapture(allowCapture);
|
||||
*aIsUserInteraction = false;
|
||||
return true;
|
||||
}
|
||||
case eDrop: {
|
||||
|
@ -7926,12 +7971,9 @@ bool PresShell::EventHandler::PrepareToDispatchEvent(
|
|||
aEvent->mFlags.mOnlyChromeDispatch = true;
|
||||
}
|
||||
}
|
||||
*aIsUserInteraction = false;
|
||||
return true;
|
||||
}
|
||||
case eContextMenu: {
|
||||
*aIsUserInteraction = false;
|
||||
|
||||
// If we cannot open context menu even though eContextMenu is fired, we
|
||||
// should stop dispatching it into the DOM.
|
||||
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
|
||||
|
@ -7956,10 +7998,8 @@ bool PresShell::EventHandler::PrepareToDispatchEvent(
|
|||
case eTouchCancel:
|
||||
case eTouchPointerCancel:
|
||||
return mPresShell->mTouchManager.PreHandleEvent(
|
||||
aEvent, aEventStatus, *aTouchIsNew, *aIsUserInteraction,
|
||||
mPresShell->mCurrentEventContent);
|
||||
aEvent, aEventStatus, *aTouchIsNew, mPresShell->mCurrentEventContent);
|
||||
default:
|
||||
*aIsUserInteraction = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2563,10 +2563,6 @@ class PresShell final : public nsStubDocumentObserver,
|
|||
*
|
||||
* @param aEvent The handling event.
|
||||
* @param aEventStatus [in/out] The status of aEvent.
|
||||
* @param aIsUserInteraction [out] Set to true if the event is user
|
||||
* interaction. I.e., enough obvious input
|
||||
* to allow to open popup, etc. Otherwise,
|
||||
* set to false.
|
||||
* @param aTouchIsNew [out] Set to true if the event is an
|
||||
* eTouchMove event and it represents new
|
||||
* touch. Otherwise, set to false.
|
||||
|
@ -2575,8 +2571,7 @@ class PresShell final : public nsStubDocumentObserver,
|
|||
*/
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
bool PrepareToDispatchEvent(WidgetEvent* aEvent,
|
||||
nsEventStatus* aEventStatus,
|
||||
bool* aIsUserInteraction, bool* aTouchIsNew);
|
||||
nsEventStatus* aEventStatus, bool* aTouchIsNew);
|
||||
|
||||
/**
|
||||
* MaybeHandleKeyboardEventBeforeDispatch() may handle aKeyboardEvent
|
||||
|
|
|
@ -220,7 +220,7 @@ nsIFrame* TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
|
|||
}
|
||||
|
||||
bool TouchManager::PreHandleEvent(WidgetEvent* aEvent, nsEventStatus* aStatus,
|
||||
bool& aTouchIsNew, bool& aIsHandlingUserInput,
|
||||
bool& aTouchIsNew,
|
||||
nsCOMPtr<nsIContent>& aCurrentEventContent) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aEvent->IsTrusted());
|
||||
|
||||
|
@ -228,7 +228,6 @@ bool TouchManager::PreHandleEvent(WidgetEvent* aEvent, nsEventStatus* aStatus,
|
|||
// cases in PresShell::EventHandler::PrepareToDispatchEvent().
|
||||
switch (aEvent->mMessage) {
|
||||
case eTouchStart: {
|
||||
aIsHandlingUserInput = true;
|
||||
WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
|
||||
// if there is only one touch in this touchstart event, assume that it is
|
||||
// the start of a new touch session and evict any old touches in the
|
||||
|
@ -335,9 +334,6 @@ bool TouchManager::PreHandleEvent(WidgetEvent* aEvent, nsEventStatus* aStatus,
|
|||
break;
|
||||
}
|
||||
case eTouchEnd:
|
||||
aIsHandlingUserInput = true;
|
||||
// Fall through to touchcancel code
|
||||
MOZ_FALLTHROUGH;
|
||||
case eTouchCancel: {
|
||||
// Remove the changed touches
|
||||
// need to make sure we only remove touches that are ending here
|
||||
|
|
|
@ -49,7 +49,7 @@ class TouchManager {
|
|||
WidgetTouchEvent* aEvent);
|
||||
|
||||
bool PreHandleEvent(mozilla::WidgetEvent* aEvent, nsEventStatus* aStatus,
|
||||
bool& aTouchIsNew, bool& aIsHandlingUserInput,
|
||||
bool& aTouchIsNew,
|
||||
nsCOMPtr<nsIContent>& aCurrentEventContent);
|
||||
|
||||
static already_AddRefed<nsIContent> GetAnyCapturedTouchTarget();
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
var SimpleTest = window.opener.SimpleTest;
|
||||
var SpecialPowers = window.opener.SpecialPowers;
|
||||
var is = window.opener.is;
|
||||
var t, e, utils, iterations;
|
||||
var smoothScrollPref = "general.smoothScroll";
|
||||
|
||||
function startTest() {
|
||||
|
|
|
@ -30,12 +30,12 @@ function newDir() {
|
|||
var dir = tmpDir.clone();
|
||||
dir.append("testdir" + Math.floor(Math.random() * 10000));
|
||||
dir.QueryInterface(Ci.nsIFile);
|
||||
dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0700);
|
||||
dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
|
||||
return dir;
|
||||
}
|
||||
|
||||
var dirs = [];
|
||||
for(var i = 0; i < 6; i++) {
|
||||
for(let i = 0; i < 6; i++) {
|
||||
dirs.push(newDir());
|
||||
}
|
||||
dirs.push(homeDir);
|
||||
|
@ -119,7 +119,7 @@ function runTest() {
|
|||
}
|
||||
|
||||
function endTest() {
|
||||
for(var i = 0; i < dirs.length - 1; i++) {
|
||||
for(let i = 0; i < dirs.length - 1; i++) {
|
||||
dirs[i].remove(true);
|
||||
}
|
||||
|
||||
|
@ -168,7 +168,7 @@ function testOnWindow(aIsPrivate, aCallback) {
|
|||
MockFilePicker.showCallback = function(filepicker) {
|
||||
var test = tests[testIndex];
|
||||
var returned = -1;
|
||||
for (var i = 0; i < dirs.length; i++) {
|
||||
for (let i = 0; i < dirs.length; i++) {
|
||||
var dir = MockFilePicker.displayDirectory
|
||||
? MockFilePicker.displayDirectory
|
||||
: Services.dirsvc.get(MockFilePicker.displaySpecialDirectory, Ci.nsIFile);
|
||||
|
|
|
@ -59,7 +59,7 @@ function runTests()
|
|||
select.addEventListener("popupshowing", function (aEvent) {
|
||||
setTimeout(function () {
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
select.addEventListener("popuphiding", function (aEvent) {
|
||||
select.addEventListener("popuphiding", function (aEventInner) {
|
||||
setTimeout(function () {
|
||||
// Enter key should cause closing the dropdown of the select element
|
||||
// and keypress event shouldn't be fired on the input element because
|
||||
|
|
|
@ -114,17 +114,17 @@ function checkClipRegionWithDoc(doc, offsetX, offsetY, id, rects, checkBounds) {
|
|||
"': expected " + dumpRegion(rects) + ", got " + dumpRegion(clipRects));
|
||||
}
|
||||
|
||||
checkClipRegion = function checkClipRegion(id, rects) {
|
||||
checkClipRegion = function(id, rects) {
|
||||
checkClipRegionWithDoc(document, 0, 0, id, rects, true);
|
||||
}
|
||||
|
||||
checkClipRegionForFrame = function checkClipRegionForFrame(fid, id, rects) {
|
||||
checkClipRegionForFrame = function(fid, id, rects) {
|
||||
var f = document.getElementById(fid);
|
||||
var bounds = f.getBoundingClientRect();
|
||||
checkClipRegionWithDoc(f.contentDocument, bounds.left, bounds.top, id, rects, true);
|
||||
}
|
||||
|
||||
checkClipRegionNoBounds = function checkClipRegionNoBounds(id, rects) {
|
||||
checkClipRegionNoBounds = function(id, rects) {
|
||||
checkClipRegionWithDoc(document, 0, 0, id, rects, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ limitations under the License.
|
|||
<p id="display"></p>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
/* eslint-disable no-shadow */
|
||||
var tests = [];
|
||||
var curDescribeMsg = '';
|
||||
var curItMsg = '';
|
||||
|
|
|
@ -60,7 +60,7 @@ var canvasNames = [ "brokenIconTest", "brokenIconReference",
|
|||
"loadingIconTest", "loadingIconReference",
|
||||
"loadedTest", "loadedReference" ];
|
||||
var windowElem = document.documentElement;
|
||||
for (var i in canvasNames) {
|
||||
for (let i in canvasNames) {
|
||||
var can = document.createElement("canvas");
|
||||
can.setAttribute("width", windowElem.getAttribute("width"));
|
||||
can.setAttribute("height", windowElem.getAttribute("height"));
|
||||
|
@ -81,7 +81,7 @@ for (var i in canvasNames) {
|
|||
// with the image, but not the bottom and right borders.
|
||||
|
||||
if ((i > 1) && (i < 4)) {
|
||||
var ctx = can.getContext("2d");
|
||||
let ctx = can.getContext("2d");
|
||||
ctx.beginPath();
|
||||
ctx.rect(0,0, 30, 30);
|
||||
ctx.clip();
|
||||
|
@ -327,7 +327,7 @@ function resetImage() {
|
|||
// debugging.
|
||||
//
|
||||
function makeCanvasesVisible() {
|
||||
for (var i = 0; i < canvasNames.length - 1; i += 2) {
|
||||
for (let i = 0; i < canvasNames.length - 1; i += 2) {
|
||||
var title = document.createElement("h3");
|
||||
title.innerHTML = canvasNames[i] + ", " + canvasNames[i+1] + ":";
|
||||
document.body.appendChild(title);
|
||||
|
@ -359,7 +359,7 @@ function disableBorderAndPad() {
|
|||
|
||||
function drawWindowToCanvas(canvasName) {
|
||||
var win = testFrameElem.contentWindow;
|
||||
var ctx = canvases[canvasName].getContext("2d");
|
||||
let ctx = canvases[canvasName].getContext("2d");
|
||||
// drawWindow always draws one canvas pixel for each CSS pixel in the source
|
||||
// window, so scale the drawing to show the zoom (making each canvas pixel be one
|
||||
// device pixel instead)
|
||||
|
|
|
@ -78,14 +78,14 @@ function runTest() {
|
|||
doc.getElementsByTagName('head')[0].appendChild(style);
|
||||
var names = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y1', 'y2', 'y3', 'y4'];
|
||||
for (var i in names) {
|
||||
var p = doc.createElement('p');
|
||||
let p = doc.createElement('p');
|
||||
p.id = names[i];
|
||||
doc.body.appendChild(p);
|
||||
}
|
||||
var count = 0;
|
||||
var check = function (c, e) {
|
||||
count += 1;
|
||||
var p = doc.getElementById(c);
|
||||
let p = doc.getElementById(c);
|
||||
is(doc.defaultView.getComputedStyle(p).textTransform, e ? 'uppercase' : 'none', "case " + c + " failed (index " + count + ")");
|
||||
}
|
||||
check('a', true); // 1
|
||||
|
@ -138,4 +138,3 @@ function runTest() {
|
|||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=696253
|
|||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* Utility function for getting computed style of "align-self":
|
||||
*/
|
||||
|
@ -160,23 +159,23 @@ function main() {
|
|||
// align-items/self tests:
|
||||
//
|
||||
//// Block tests
|
||||
var elem = document.body;
|
||||
var element = document.body;
|
||||
var child = document.getElementById("display");
|
||||
var abs = document.getElementById("absChild");
|
||||
is(getComputedAlignItems(elem), 'normal', "default align-items value for block container");
|
||||
var absChild = document.getElementById("absChild");
|
||||
is(getComputedAlignItems(element), 'normal', "default align-items value for block container");
|
||||
is(getComputedAlignSelf(child), 'auto', "default align-self value for block child");
|
||||
is(getComputedAlignSelf(abs), 'auto', "default align-self value for block container abs.pos. child");
|
||||
elem.style.alignItems = "end";
|
||||
is(getComputedAlignSelf(absChild), 'auto', "default align-self value for block container abs.pos. child");
|
||||
element.style.alignItems = "end";
|
||||
is(getComputedAlignSelf(child), 'auto', "align-self:auto value persists for block child");
|
||||
is(getComputedAlignSelf(abs), 'auto', "align-self:auto value persists for block container abs.pos. child");
|
||||
elem.style.alignItems = "left";
|
||||
is(getComputedAlignItems(elem), 'end', "align-items:left is an invalid declaration");
|
||||
is(getComputedAlignSelf(absChild), 'auto', "align-self:auto value persists for block container abs.pos. child");
|
||||
element.style.alignItems = "left";
|
||||
is(getComputedAlignItems(element), 'end', "align-items:left is an invalid declaration");
|
||||
is(getComputedAlignSelf(child), 'auto', "align-self:auto persists for block child");
|
||||
is(getComputedAlignSelf(abs), 'auto', "align-self:auto value persists for block container abs.pos. child");
|
||||
elem.style.alignItems = "right";
|
||||
is(getComputedAlignItems(elem), 'end', "align-items:right is an invalid declaration");
|
||||
is(getComputedAlignSelf(absChild), 'auto', "align-self:auto value persists for block container abs.pos. child");
|
||||
element.style.alignItems = "right";
|
||||
is(getComputedAlignItems(element), 'end', "align-items:right is an invalid declaration");
|
||||
is(getComputedAlignSelf(child), 'auto', "align-self:auto value persists for block child");
|
||||
is(getComputedAlignSelf(abs), 'auto', "align-self:auto value persists for block container abs.pos. child");
|
||||
is(getComputedAlignSelf(absChild), 'auto', "align-self:auto value persists for block container abs.pos. child");
|
||||
|
||||
//// Flexbox tests
|
||||
function testFlexAlignItemsSelf(elem) {
|
||||
|
@ -227,32 +226,32 @@ function main() {
|
|||
// justify-items/self tests:
|
||||
//
|
||||
//// Block tests
|
||||
var elem = document.body;
|
||||
var child = document.getElementById("display");
|
||||
var abs = document.getElementById("absChild");
|
||||
is(getComputedJustifyItems(elem), 'normal', "default justify-items value for block container");
|
||||
element = document.body;
|
||||
child = document.getElementById("display");
|
||||
absChild = document.getElementById("absChild");
|
||||
is(getComputedJustifyItems(element), 'normal', "default justify-items value for block container");
|
||||
is(getComputedJustifySelf(child), 'auto', "default justify-self value for block container child");
|
||||
is(getComputedJustifySelf(abs), 'auto', "default justify-self value for block container abs.pos. child");
|
||||
elem.style.justifyItems = "end";
|
||||
is(getComputedJustifySelf(absChild), 'auto', "default justify-self value for block container abs.pos. child");
|
||||
element.style.justifyItems = "end";
|
||||
is(getComputedJustifySelf(child), 'auto', "justify-self:auto value persists for block child");
|
||||
is(getComputedJustifySelf(abs), 'auto', "justify-self:auto value persists for block container abs.pos. child");
|
||||
elem.style.justifyItems = "left";
|
||||
is(getComputedJustifyItems(elem), 'left', "justify-items:left computes to itself on a block");
|
||||
is(getComputedJustifySelf(absChild), 'auto', "justify-self:auto value persists for block container abs.pos. child");
|
||||
element.style.justifyItems = "left";
|
||||
is(getComputedJustifyItems(element), 'left', "justify-items:left computes to itself on a block");
|
||||
is(getComputedJustifySelf(child), 'auto', "justify-self:auto value persists for block child");
|
||||
is(getComputedJustifySelf(abs), 'auto', "justify-self:auto value persists for block container abs.pos. child");
|
||||
elem.style.justifyItems = "right";
|
||||
is(getComputedJustifySelf(absChild), 'auto', "justify-self:auto value persists for block container abs.pos. child");
|
||||
element.style.justifyItems = "right";
|
||||
is(getComputedJustifySelf(child), 'auto', "justify-self:auto value persists for block child");
|
||||
is(getComputedJustifySelf(abs), 'auto', "justify-self:auto value persists for block container abs.pos. child");
|
||||
elem.style.justifyItems = "safe right";
|
||||
is(getComputedJustifySelf(absChild), 'auto', "justify-self:auto value persists for block container abs.pos. child");
|
||||
element.style.justifyItems = "safe right";
|
||||
is(getComputedJustifySelf(child), 'auto', "justify-self:auto value persists for block child");
|
||||
elem.style.justifyItems = "";
|
||||
element.style.justifyItems = "";
|
||||
child.style.justifySelf = "left";
|
||||
is(getComputedJustifySelf(child), 'left', "justify-self:left computes to left on block child");
|
||||
child.style.justifySelf = "right";
|
||||
is(getComputedJustifySelf(child), 'right', "justify-self:right computes to right on block child");
|
||||
child.style.justifySelf = "";
|
||||
abs.style.justifySelf = "right";
|
||||
is(getComputedJustifySelf(abs), 'right', "justify-self:right computes to right on block container abs.pos. child");
|
||||
absChild.style.justifySelf = "right";
|
||||
is(getComputedJustifySelf(absChild), 'right', "justify-self:right computes to right on block container abs.pos. child");
|
||||
|
||||
//// Flexbox tests
|
||||
function testFlexJustifyItemsSelf(elem) {
|
||||
|
@ -328,22 +327,22 @@ function main() {
|
|||
// align-content tests:
|
||||
//
|
||||
//// Block tests
|
||||
var elem = document.body;
|
||||
var child = document.getElementById("display");
|
||||
var abs = document.getElementById("absChild");
|
||||
is(getComputedAlignContent(elem), 'normal', "default align-content value for block container");
|
||||
element = document.body;
|
||||
child = document.getElementById("display");
|
||||
absChild = document.getElementById("absChild");
|
||||
is(getComputedAlignContent(element), 'normal', "default align-content value for block container");
|
||||
is(getComputedAlignContent(child), 'normal', "default align-content value for block child");
|
||||
is(getComputedAlignContent(abs), 'normal', "default align-content value for block container abs.pos. child");
|
||||
elem.style.alignContent = "end";
|
||||
is(getComputedAlignContent(absChild), 'normal', "default align-content value for block container abs.pos. child");
|
||||
element.style.alignContent = "end";
|
||||
is(getComputedAlignContent(child), 'normal', "default align-content isn't affected by parent align-content value for in-flow child");
|
||||
is(getComputedAlignContent(abs), 'normal', "default align-content isn't affected by parent align-content value for block container abs.pos. child");
|
||||
elem.style.alignContent = "left";
|
||||
is(getComputedAlignContent(elem), 'end', "align-content:left isn't a valid declaration");
|
||||
is(getComputedAlignContent(abs), 'normal', "default align-content isn't affected by parent align-content value for block container abs.pos. child");
|
||||
elem.style.alignContent = "right";
|
||||
is(getComputedAlignContent(elem), 'end', "align-content:right isn't a valid declaration");
|
||||
is(getComputedAlignContent(abs), 'normal', "default align-content isn't affected by parent align-content value for block container abs.pos. child");
|
||||
elem.style.alignContent = "";
|
||||
is(getComputedAlignContent(absChild), 'normal', "default align-content isn't affected by parent align-content value for block container abs.pos. child");
|
||||
element.style.alignContent = "left";
|
||||
is(getComputedAlignContent(element), 'end', "align-content:left isn't a valid declaration");
|
||||
is(getComputedAlignContent(absChild), 'normal', "default align-content isn't affected by parent align-content value for block container abs.pos. child");
|
||||
element.style.alignContent = "right";
|
||||
is(getComputedAlignContent(element), 'end', "align-content:right isn't a valid declaration");
|
||||
is(getComputedAlignContent(absChild), 'normal', "default align-content isn't affected by parent align-content value for block container abs.pos. child");
|
||||
element.style.alignContent = "";
|
||||
|
||||
//// Flexbox tests
|
||||
function testFlexAlignContent(elem) {
|
||||
|
@ -391,24 +390,24 @@ function main() {
|
|||
// justify-content tests:
|
||||
//
|
||||
//// Block tests
|
||||
var elem = document.body;
|
||||
var child = document.getElementById("display");
|
||||
var abs = document.getElementById("absChild");
|
||||
is(getComputedJustifyContent(elem), 'normal', "default justify-content value for block container");
|
||||
element = document.body;
|
||||
child = document.getElementById("display");
|
||||
absChild = document.getElementById("absChild");
|
||||
is(getComputedJustifyContent(element), 'normal', "default justify-content value for block container");
|
||||
is(getComputedJustifyContent(child), 'normal', "default justify-content value for block child");
|
||||
is(getComputedJustifyContent(abs), 'normal', "default justify-content value for block container abs.pos. child");
|
||||
elem.style.justifyContent = "end";
|
||||
is(getComputedJustifyContent(absChild), 'normal', "default justify-content value for block container abs.pos. child");
|
||||
element.style.justifyContent = "end";
|
||||
is(getComputedJustifyContent(child), 'normal', "default justify-content isn't affected by parent justify-content value for in-flow child");
|
||||
is(getComputedJustifyContent(abs), 'normal', "default justify-content isn't affected by parent justify-content value for block container abs.pos. child");
|
||||
elem.style.justifyContent = "left";
|
||||
is(getComputedJustifyContent(elem), 'left', "justify-content:left computes to left on block child");
|
||||
is(getComputedJustifyContent(abs), 'normal', "default justify-content isn't affected by parent justify-content value for block container abs.pos. child");
|
||||
elem.style.justifyContent = "right";
|
||||
is(getComputedJustifyContent(elem), 'right', "justify-content:right computes to right on block child");
|
||||
is(getComputedJustifyContent(abs), 'normal', "default justify-content isn't affected by parent justify-content value for block container abs.pos. child");
|
||||
elem.style.justifyContent = "safe right";
|
||||
is(getComputedJustifyContent(elem), 'safe right', "justify-content:'safe right' computes to 'justify-content:safe right'");
|
||||
elem.style.justifyContent = "";
|
||||
is(getComputedJustifyContent(absChild), 'normal', "default justify-content isn't affected by parent justify-content value for block container abs.pos. child");
|
||||
element.style.justifyContent = "left";
|
||||
is(getComputedJustifyContent(element), 'left', "justify-content:left computes to left on block child");
|
||||
is(getComputedJustifyContent(absChild), 'normal', "default justify-content isn't affected by parent justify-content value for block container abs.pos. child");
|
||||
element.style.justifyContent = "right";
|
||||
is(getComputedJustifyContent(element), 'right', "justify-content:right computes to right on block child");
|
||||
is(getComputedJustifyContent(absChild), 'normal', "default justify-content isn't affected by parent justify-content value for block container abs.pos. child");
|
||||
element.style.justifyContent = "safe right";
|
||||
is(getComputedJustifyContent(element), 'safe right', "justify-content:'safe right' computes to 'justify-content:safe right'");
|
||||
element.style.justifyContent = "";
|
||||
child.style.justifyContent = "left";
|
||||
is(getComputedJustifyContent(child), 'left', "justify-content:left computes to left on block child");
|
||||
child.style.justifyContent = "right";
|
||||
|
@ -416,9 +415,9 @@ function main() {
|
|||
child.style.justifyContent = "safe left";
|
||||
is(getComputedJustifyContent(child), 'safe left', "justify-content:safe left computes to 'safe left' on block child");
|
||||
child.style.justifyContent = "";
|
||||
abs.style.justifyContent = "right";
|
||||
is(getComputedJustifyContent(abs), 'right', "justify-content:right computes to right on block container abs.pos. child");
|
||||
abs.style.justifyContent = "";
|
||||
absChild.style.justifyContent = "right";
|
||||
is(getComputedJustifyContent(absChild), 'right', "justify-content:right computes to right on block container abs.pos. child");
|
||||
absChild.style.justifyContent = "";
|
||||
|
||||
//// Flexbox tests
|
||||
function testFlexJustifyContent(elem) {
|
||||
|
|
|
@ -34,6 +34,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1183461
|
|||
<script type="application/javascript">
|
||||
'use strict';
|
||||
|
||||
/* eslint-disable no-shadow */
|
||||
|
||||
// Take over the refresh driver right from the start.
|
||||
advance_clock(0);
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ function testTransitionTakingOver() {
|
|||
getComputedStyle(child).opacity;
|
||||
gUtils.advanceTimeAndRefresh(0);
|
||||
waitForAllPaints(function() {
|
||||
var opacity = gUtils.getOMTAStyle(child, "opacity");
|
||||
opacity = gUtils.getOMTAStyle(child, "opacity");
|
||||
is(opacity, "0.4",
|
||||
"transition that interrupted animation is correct");
|
||||
gUtils.advanceTimeAndRefresh(5000);
|
||||
|
|
|
@ -51,8 +51,6 @@ function check_children(p, check_cb) {
|
|||
}
|
||||
}
|
||||
|
||||
var display = document.getElementById("display");
|
||||
|
||||
function run_series(check_cb) {
|
||||
var display = document.getElementById("display");
|
||||
// Use a new parent node every time since the optimizations cause
|
||||
|
@ -189,4 +187,3 @@ run_series(function(child, elt, elts, node, nodes) {
|
|||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ for (i = 0; i < cnodes.length; ++i) {
|
|||
}
|
||||
|
||||
for (i in divs) {
|
||||
var div = divs[i];
|
||||
let div = divs[i];
|
||||
if (div.className.match(/makeemptytext/))
|
||||
div.insertBefore(document.createTextNode(""), div.firstChild);
|
||||
}
|
||||
|
@ -120,20 +120,20 @@ function bg(div) {
|
|||
}
|
||||
|
||||
for (i in divs) {
|
||||
var div = divs[i];
|
||||
let div = divs[i];
|
||||
is(bg(div), ORANGE, "should be orange");
|
||||
is(color(div), MAROON, "should be maroon");
|
||||
}
|
||||
|
||||
for (i in divs) {
|
||||
var div = divs[i];
|
||||
let div = divs[i];
|
||||
var e = document.createEvent("MouseEvents");
|
||||
e.initEvent("click", true, true);
|
||||
div.dispatchEvent(e);
|
||||
}
|
||||
|
||||
for (i in divs) {
|
||||
var div = divs[i];
|
||||
let div = divs[i];
|
||||
is(bg(div), GREEN, "should be green");
|
||||
is(color(div), NAVY, "should be navy");
|
||||
}
|
||||
|
|
|
@ -128,7 +128,6 @@ function is_pending(aPromise, aDescription, aTestID) {
|
|||
}
|
||||
|
||||
function fetchAsArrayBuffer(aURL) {
|
||||
var xhr;
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", aURL);
|
||||
|
@ -1818,10 +1817,9 @@ function runTest() {
|
|||
expected.vdocument.push(all[0]);
|
||||
|
||||
// Create a FontFace in each window and add it to each document's FontFaceSet.
|
||||
var i = 0;
|
||||
var faces = [];
|
||||
sourceWindows.forEach(function({ win, what: whatWin }) {
|
||||
var f = new win.FontFace("test" + ++i, "url(x)");
|
||||
sourceWindows.forEach(function({ win, what: whatWin }, index) {
|
||||
var f = new win.FontFace("test" + index, "url(x)");
|
||||
sourceDocuments.forEach(function({ doc, what: whatDoc }) {
|
||||
doc.fonts.add(f);
|
||||
expected[whatDoc].push(f);
|
||||
|
@ -1830,10 +1828,10 @@ function runTest() {
|
|||
});
|
||||
|
||||
sourceDocuments.forEach(function({ doc, what }) {
|
||||
var all = Array.from(doc.fonts);
|
||||
is(expected[what].length, all.length, "expected FontFaceSet size (TEST 48) (" + what + ")");
|
||||
for (var i = 0; i < expected[what].length; i++) {
|
||||
is(expected[what][i], all[i], "expected FontFace (" + expected[what][i]._description + ") at index " + i + " (TEST 48) (" + what + ")");
|
||||
let allFonts = Array.from(doc.fonts);
|
||||
is(expected[what].length, allFonts.length, "expected FontFaceSet size (TEST 48) (" + what + ")");
|
||||
for (let i = 0; i < expected[what].length; i++) {
|
||||
is(expected[what][i], allFonts[i], "expected FontFace (" + expected[what][i]._description + ") at index " + i + " (TEST 48) (" + what + ")");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -87,11 +87,11 @@ function test_property(property)
|
|||
check_final(info.subproperties[idx]);
|
||||
|
||||
// can all properties be removed from the style?
|
||||
function test_remove_all_properties(property, value) {
|
||||
function test_remove_all_properties(propName, value) {
|
||||
var i, p = [];
|
||||
for (i = 0; i < gDeclaration.length; i++) p.push(gDeclaration[i]);
|
||||
for (i = 0; i < p.length; i++) gDeclaration.removeProperty(p[i]);
|
||||
var errstr = "when setting property " + property + " to " + value;
|
||||
var errstr = "when setting property " + propName + " to " + value;
|
||||
is(gDeclaration.length, 0, "unremovable properties " + errstr);
|
||||
is(gDeclaration.cssText, "", "non-empty serialization after removing all properties " + errstr);
|
||||
}
|
||||
|
|
|
@ -101,11 +101,11 @@ function test_property(property)
|
|||
check_final(info.subproperties[idx]);
|
||||
|
||||
// can all properties be removed from the style?
|
||||
function test_remove_all_properties(property, value) {
|
||||
function test_remove_all_properties(propName, value) {
|
||||
var i, p = [];
|
||||
for (i = 0; i < gDeclaration.length; i++) p.push(gDeclaration[i]);
|
||||
for (i = 0; i < p.length; i++) gDeclaration.removeProperty(p[i]);
|
||||
var errstr = "when setting property " + property + " to " + value;
|
||||
var errstr = "when setting property " + propName + " to " + value;
|
||||
is(gDeclaration.length, 0, "unremovable properties " + errstr);
|
||||
is(gDeclaration.cssText, "", "non-empty serialization after removing all properties " + errstr);
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ function run() {
|
|||
expression_should_not_be_parseable(max_name);
|
||||
}
|
||||
|
||||
function test_serialization(q, test_application, should_apply) {
|
||||
function test_serialization(q, test_application, expected_to_apply) {
|
||||
style.setAttribute("media", q);
|
||||
var ser1 = style.sheet.media.mediaText;
|
||||
isnot(ser1, "", "serialization of '" + q + "' should not be empty");
|
||||
|
@ -145,9 +145,9 @@ function run() {
|
|||
var ser2 = style.sheet.media.mediaText;
|
||||
is(ser2, ser1, "parse+serialize of '" + q + "' should be idempotent");
|
||||
if (test_application) {
|
||||
var applies = body_cs.getPropertyValue("text-decoration") == "underline";
|
||||
is(applies, should_apply,
|
||||
"Media query '" + q + "' should " + (should_apply ? "" : "NOT ") +
|
||||
let applies = body_cs.getPropertyValue("text-decoration") == "underline";
|
||||
is(applies, expected_to_apply,
|
||||
"Media query '" + q + "' should " + (expected_to_apply ? "" : "NOT ") +
|
||||
"apply after serialize + reparse");
|
||||
}
|
||||
|
||||
|
@ -170,10 +170,10 @@ function run() {
|
|||
is(ser3, ser1, "cloning query '" + q + "' should not change " +
|
||||
"serialization");
|
||||
if (test_application) {
|
||||
var applies = clonewin.getComputedStyle(clonedoc.body).
|
||||
let applies = clonewin.getComputedStyle(clonedoc.body).
|
||||
textDecoration == "underline";
|
||||
is(applies, should_apply,
|
||||
"Media query '" + q + "' should " + (should_apply ? "" : "NOT ") +
|
||||
is(applies, expected_to_apply,
|
||||
"Media query '" + q + "' should " + (expected_to_apply ? "" : "NOT ") +
|
||||
"apply after cloning");
|
||||
}
|
||||
});
|
||||
|
@ -927,5 +927,3 @@ function handle_iframe_onload(event)
|
|||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
|
|
@ -266,20 +266,20 @@ function run() {
|
|||
|
||||
/* Bug 753777: test that things work in a freshly-created iframe */
|
||||
(function() {
|
||||
var iframe = document.createElement("iframe");
|
||||
document.body.appendChild(iframe);
|
||||
let newIframe = document.createElement("iframe");
|
||||
document.body.appendChild(newIframe);
|
||||
|
||||
is(iframe.contentWindow.matchMedia("all").matches, true,
|
||||
is(newIframe.contentWindow.matchMedia("all").matches, true,
|
||||
"matchMedia should work in newly-created iframe");
|
||||
// FIXME(emilio): All browsers fail this test right now. Probably should
|
||||
// pass, see https://github.com/w3c/csswg-drafts/issues/3101, bug 1458816,
|
||||
// and bug 1011468.
|
||||
todo_is(iframe.contentWindow.matchMedia("(min-width: 1px)").matches, true,
|
||||
todo_is(newIframe.contentWindow.matchMedia("(min-width: 1px)").matches, true,
|
||||
"(min-width: 1px) should match in newly-created iframe");
|
||||
todo_is(iframe.contentWindow.matchMedia("(max-width: 1px)").matches, false,
|
||||
todo_is(newIframe.contentWindow.matchMedia("(max-width: 1px)").matches, false,
|
||||
"(max-width: 1px) should not match in newly-created iframe");
|
||||
|
||||
document.body.removeChild(iframe);
|
||||
document.body.removeChild(newIframe);
|
||||
})();
|
||||
|
||||
/* Bug 716751: listeners lost due to GC */
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче