зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
Коммит
3a5c693e28
|
@ -17,7 +17,7 @@ add_task(async function() {
|
|||
isnot(searchInput, doc.activeElement, "Search bar should not be the active element.");
|
||||
|
||||
EventUtils.synthesizeKey("k", { accelKey: true });
|
||||
await promiseWaitForCondition(() => doc.activeElement === searchInput);
|
||||
await TestUtils.waitForCondition(() => doc.activeElement === searchInput);
|
||||
is(searchInput, doc.activeElement, "Search bar should be the active element.");
|
||||
});
|
||||
|
||||
|
|
|
@ -23,12 +23,6 @@ function waitForCondition(condition, nextTest, errorMsg, retryTimes) {
|
|||
var moveOn = function() { clearInterval(interval); nextTest(); };
|
||||
}
|
||||
|
||||
function promiseWaitForCondition(aConditionFn) {
|
||||
return new Promise(resolve => {
|
||||
waitForCondition(aConditionFn, resolve, "Condition didn't pass.");
|
||||
});
|
||||
}
|
||||
|
||||
function whenTabLoaded(aTab, aCallback) {
|
||||
promiseTabLoadEvent(aTab).then(aCallback);
|
||||
}
|
||||
|
|
|
@ -93,10 +93,6 @@ add_task(async function startup() {
|
|||
min: 10,
|
||||
max: 55,
|
||||
},
|
||||
"dom.max_chrome_script_run_time": {
|
||||
min: 20,
|
||||
max: 55,
|
||||
},
|
||||
};
|
||||
|
||||
let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
|
||||
|
|
|
@ -813,17 +813,17 @@ var gPrivacyPane = {
|
|||
readKeepCookiesUntil() {
|
||||
let privateBrowsing = Preferences.get("browser.privatebrowsing.autostart").value;
|
||||
if (privateBrowsing) {
|
||||
return "2";
|
||||
return Ci.nsICookieService.ACCEPT_SESSION;
|
||||
}
|
||||
|
||||
let lifetimePolicy = Preferences.get("network.cookie.lifetimePolicy").value;
|
||||
if (lifetimePolicy != Ci.nsICookieService.ACCEPT_NORMALLY &&
|
||||
lifetimePolicy != Ci.nsICookieService.ACCEPT_SESSION &&
|
||||
lifetimePolicy != Ci.nsICookieService.ACCEPT_FOR_N_DAYS) {
|
||||
return Ci.nsICookieService.ACCEPT_NORMALLY;
|
||||
if (lifetimePolicy == Ci.nsICookieService.ACCEPT_SESSION) {
|
||||
return Ci.nsICookieService.ACCEPT_SESSION;
|
||||
}
|
||||
|
||||
return lifetimePolicy;
|
||||
// network.cookie.lifetimePolicy can be set to any value, but we just
|
||||
// support ACCEPT_SESSION and ACCEPT_NORMALLY. Let's force ACCEPT_NORMALLY.
|
||||
return Ci.nsICookieService.ACCEPT_NORMALLY;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,17 +4,15 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { getCssProperties } = require("devtools/shared/fronts/css-properties");
|
||||
|
||||
const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
|
||||
|
||||
const {
|
||||
updateGeometryEditorEnabled,
|
||||
updateLayout,
|
||||
updateOffsetParent,
|
||||
} = require("./actions/box-model");
|
||||
|
||||
const EditingSession = require("./utils/editing-session");
|
||||
loader.lazyRequireGetter(this, "EditingSession", "devtools/client/inspector/boxmodel/utils/editing-session");
|
||||
loader.lazyRequireGetter(this, "InplaceEditor", "devtools/client/shared/inplace-editor", true);
|
||||
loader.lazyRequireGetter(this, "getCssProperties", "devtools/shared/fronts/css-properties", true);
|
||||
|
||||
const NUMERIC = /^-?[\d\.]+$/;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { getCssProperties } = require("devtools/shared/fronts/css-properties");
|
||||
loader.lazyRequireGetter(this, "getCssProperties", "devtools/shared/fronts/css-properties", true);
|
||||
|
||||
/**
|
||||
* An instance of EditingSession tracks changes that have been made during the
|
||||
|
@ -22,12 +22,20 @@ const { getCssProperties } = require("devtools/shared/fronts/css-properties");
|
|||
*/
|
||||
function EditingSession({inspector, doc, elementRules}) {
|
||||
this._doc = doc;
|
||||
this._inspector = inspector;
|
||||
this._rules = elementRules;
|
||||
this._modifications = new Map();
|
||||
this._cssProperties = getCssProperties(inspector.toolbox);
|
||||
}
|
||||
|
||||
EditingSession.prototype = {
|
||||
get cssProperties() {
|
||||
if (!this._cssProperties) {
|
||||
this._cssProperties = getCssProperties(this._inspector.toolbox);
|
||||
}
|
||||
|
||||
return this._cssProperties;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the value of a single property from the CSS rule.
|
||||
*
|
||||
|
@ -112,8 +120,7 @@ EditingSession.prototype = {
|
|||
// StyleRuleActor to make changes to CSS properties.
|
||||
// Note that RuleRewriter doesn't support modifying several properties at
|
||||
// once, so we do this in a sequence here.
|
||||
let modifications = this._rules[0].startModifyingProperties(
|
||||
this._cssProperties);
|
||||
let modifications = this._rules[0].startModifyingProperties(this.cssProperties);
|
||||
|
||||
// Remember the property so it can be reverted.
|
||||
if (!this._modifications.has(property.name)) {
|
||||
|
@ -147,8 +154,7 @@ EditingSession.prototype = {
|
|||
// Revert each property that we modified previously, one by one. See
|
||||
// setProperties for information about why.
|
||||
for (let [property, value] of this._modifications) {
|
||||
let modifications = this._rules[0].startModifyingProperties(
|
||||
this._cssProperties);
|
||||
let modifications = this._rules[0].startModifyingProperties(this.cssProperties);
|
||||
|
||||
// Find the index of the property to be reverted.
|
||||
let index = this.getPropertyIndex(property);
|
||||
|
@ -174,9 +180,13 @@ EditingSession.prototype = {
|
|||
},
|
||||
|
||||
destroy: function() {
|
||||
this._doc = null;
|
||||
this._rules = null;
|
||||
this._modifications.clear();
|
||||
|
||||
this._cssProperties = null;
|
||||
this._doc = null;
|
||||
this._inspector = null;
|
||||
this._modifications = null;
|
||||
this._rules = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -22,10 +22,11 @@ const {
|
|||
VIEW_NODE_IMAGE_URL_TYPE,
|
||||
VIEW_NODE_FONT_TYPE,
|
||||
} = require("devtools/client/inspector/shared/node-types");
|
||||
const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
|
||||
const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
|
||||
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
|
||||
const clipboardHelper = require("devtools/shared/platform/clipboard");
|
||||
|
||||
loader.lazyRequireGetter(this, "StyleInspectorMenu", "devtools/client/inspector/shared/style-inspector-menu");
|
||||
loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
|
||||
loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
|
||||
|
||||
const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
|
||||
const {LocalizationHelper} = require("devtools/shared/l10n");
|
||||
|
@ -203,8 +204,6 @@ function CssComputedView(inspector, document, pageStyle) {
|
|||
|
||||
this.createStyleViews();
|
||||
|
||||
this._contextmenu = new StyleInspectorMenu(this, { isRuleView: false });
|
||||
|
||||
// Add the tooltips and highlightersoverlay
|
||||
this.tooltips = new TooltipsOverlay(this);
|
||||
|
||||
|
@ -243,6 +242,14 @@ CssComputedView.prototype = {
|
|||
// Number of visible properties
|
||||
numVisibleProperties: 0,
|
||||
|
||||
get contextMenu() {
|
||||
if (!this._contextMenu) {
|
||||
this._contextMenu = new StyleInspectorMenu(this, { isRuleView: false });
|
||||
}
|
||||
|
||||
return this._contextMenu;
|
||||
},
|
||||
|
||||
setPageStyle: function(pageStyle) {
|
||||
this.pageStyle = pageStyle;
|
||||
},
|
||||
|
@ -672,7 +679,7 @@ CssComputedView.prototype = {
|
|||
* Context menu handler.
|
||||
*/
|
||||
_onContextMenu: function(event) {
|
||||
this._contextmenu.show(event);
|
||||
this.contextMenu.show(event);
|
||||
},
|
||||
|
||||
_onClick: function(event) {
|
||||
|
@ -733,10 +740,9 @@ CssComputedView.prototype = {
|
|||
this._refreshProcess.cancel();
|
||||
}
|
||||
|
||||
// Remove context menu
|
||||
if (this._contextmenu) {
|
||||
this._contextmenu.destroy();
|
||||
this._contextmenu = null;
|
||||
if (this._contextMenu) {
|
||||
this._contextMenu.destroy();
|
||||
this._contextMenu = null;
|
||||
}
|
||||
|
||||
this.tooltips.destroy();
|
||||
|
|
|
@ -164,7 +164,7 @@ function getComputedViewLinkByIndex(view, index) {
|
|||
*/
|
||||
function selectAllText(view) {
|
||||
info("Selecting all the text");
|
||||
view._contextmenu._onSelectAll();
|
||||
view.contextMenu._onSelectAll();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,7 +13,6 @@ const promise = require("promise");
|
|||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const {executeSoon} = require("devtools/shared/DevToolsUtils");
|
||||
const {Toolbox} = require("devtools/client/framework/toolbox");
|
||||
const {PrefObserver} = require("devtools/client/shared/prefs");
|
||||
const Telemetry = require("devtools/client/shared/telemetry");
|
||||
const HighlightersOverlay = require("devtools/client/inspector/shared/highlighters-overlay");
|
||||
const ReflowTracker = require("devtools/client/inspector/shared/reflow-tracker");
|
||||
|
@ -26,6 +25,7 @@ const Promise = require("Promise");
|
|||
|
||||
loader.lazyRequireGetter(this, "initCssProperties", "devtools/shared/fronts/css-properties", true);
|
||||
loader.lazyRequireGetter(this, "HTMLBreadcrumbs", "devtools/client/inspector/breadcrumbs", true);
|
||||
loader.lazyRequireGetter(this, "ThreePaneOnboardingTooltip", "devtools/client/inspector/shared/three-pane-onboarding-tooltip");
|
||||
loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
|
||||
loader.lazyRequireGetter(this, "InspectorSearch", "devtools/client/inspector/inspector-search", true);
|
||||
loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/inspector/toolsidebar", true);
|
||||
|
@ -55,6 +55,7 @@ const PORTRAIT_MODE_WIDTH_THRESHOLD = 700;
|
|||
// mode.
|
||||
const SIDE_PORTAIT_MODE_WIDTH_THRESHOLD = 1000;
|
||||
|
||||
const SHOW_THREE_PANE_ONBOARDING_PREF = "devtools.inspector.show-three-pane-tooltip";
|
||||
const SHOW_THREE_PANE_TOGGLE_PREF = "devtools.inspector.three-pane-toggle";
|
||||
const THREE_PANE_ENABLED_PREF = "devtools.inspector.three-pane-enabled";
|
||||
const THREE_PANE_ENABLED_SCALAR = "devtools.inspector.three_pane_enabled";
|
||||
|
@ -111,7 +112,6 @@ function Inspector(toolbox) {
|
|||
this._panels = new Map();
|
||||
|
||||
this.highlighters = new HighlightersOverlay(this);
|
||||
this.prefsObserver = new PrefObserver("devtools.");
|
||||
this.reflowTracker = new ReflowTracker(this._target);
|
||||
this.styleChangeTracker = new InspectorStyleChangeTracker(this);
|
||||
this.telemetry = new Telemetry();
|
||||
|
@ -120,8 +120,9 @@ function Inspector(toolbox) {
|
|||
// telemetry counts in the Grid Inspector are not double counted on reload.
|
||||
this.previousURL = this.target.url;
|
||||
|
||||
this.show3PaneToggle = Services.prefs.getBoolPref(SHOW_THREE_PANE_TOGGLE_PREF);
|
||||
this.is3PaneModeEnabled = Services.prefs.getBoolPref(THREE_PANE_ENABLED_PREF);
|
||||
this.show3PaneToggle = Services.prefs.getBoolPref(SHOW_THREE_PANE_TOGGLE_PREF);
|
||||
this.show3PaneTooltip = Services.prefs.getBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF);
|
||||
|
||||
this.nodeMenuTriggerInfo = null;
|
||||
|
||||
|
@ -296,6 +297,10 @@ Inspector.prototype = {
|
|||
// Setup the toolbar only now because it may depend on the document.
|
||||
await this.setupToolbar();
|
||||
|
||||
if (this.show3PaneTooltip) {
|
||||
this.threePaneTooltip = new ThreePaneOnboardingTooltip(this.toolbox, this.panelDoc);
|
||||
}
|
||||
|
||||
// Log the 3 pane inspector setting on inspector open. The question we want to answer
|
||||
// is:
|
||||
// "What proportion of users use the 3 pane vs 2 pane inspector on inspector open?"
|
||||
|
@ -460,6 +465,22 @@ Inspector.prototype = {
|
|||
return this._InspectorTabPanel;
|
||||
},
|
||||
|
||||
get InspectorSplitBox() {
|
||||
if (!this._InspectorSplitBox) {
|
||||
this._InspectorSplitBox = this.React.createFactory(this.browserRequire(
|
||||
"devtools/client/shared/components/splitter/SplitBox"));
|
||||
}
|
||||
return this._InspectorSplitBox;
|
||||
},
|
||||
|
||||
get TabBar() {
|
||||
if (!this._TabBar) {
|
||||
this._TabBar = this.React.createFactory(this.browserRequire(
|
||||
"devtools/client/shared/components/tabs/TabBar"));
|
||||
}
|
||||
return this._TabBar;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the inspector should use the landscape mode.
|
||||
*
|
||||
|
@ -477,11 +498,9 @@ Inspector.prototype = {
|
|||
* the Inspector panel.
|
||||
*/
|
||||
setupSplitter: function() {
|
||||
let SplitBox = this.React.createFactory(this.browserRequire(
|
||||
"devtools/client/shared/components/splitter/SplitBox"));
|
||||
let { width, height, splitSidebarWidth } = this.getSidebarSize();
|
||||
|
||||
let splitter = SplitBox({
|
||||
let splitter = this.InspectorSplitBox({
|
||||
className: "inspector-sidebar-splitter",
|
||||
initialWidth: width,
|
||||
initialHeight: height,
|
||||
|
@ -492,7 +511,7 @@ Inspector.prototype = {
|
|||
startPanel: this.InspectorTabPanel({
|
||||
id: "inspector-main-content"
|
||||
}),
|
||||
endPanel: SplitBox({
|
||||
endPanel: this.InspectorSplitBox({
|
||||
initialWidth: splitSidebarWidth,
|
||||
minSize: 10,
|
||||
maxSize: "80%",
|
||||
|
@ -1329,6 +1348,9 @@ Inspector.prototype = {
|
|||
|
||||
this.cancelUpdate();
|
||||
|
||||
this.selection.off("new-node-front", this.onNewSelection);
|
||||
this.selection.off("detached-front", this.onDetached);
|
||||
this.sidebar.off("select", this.onSidebarSelect);
|
||||
this.target.off("will-navigate", this._onBeforeNavigate);
|
||||
this.target.off("thread-paused", this.updateDebuggerPausedWarning);
|
||||
this.target.off("thread-resumed", this.updateDebuggerPausedWarning);
|
||||
|
@ -1351,25 +1373,21 @@ Inspector.prototype = {
|
|||
this.animationinspector.destroy();
|
||||
}
|
||||
|
||||
if (this.threePaneTooltip) {
|
||||
this.threePaneTooltip.destroy();
|
||||
}
|
||||
|
||||
let cssPropertiesDestroyer = this._cssProperties.front.destroy();
|
||||
|
||||
this.sidebar.off("select", this.onSidebarSelect);
|
||||
let sidebarDestroyer = this.sidebar.destroy();
|
||||
|
||||
let ruleViewSideBarDestroyer = this.ruleViewSideBar ?
|
||||
this.ruleViewSideBar.destroy() : null;
|
||||
let markupDestroyer = this._destroyMarkup();
|
||||
let highlighterDestroyer = this.highlighters.destroy();
|
||||
|
||||
this.teardownSplitter();
|
||||
|
||||
this.teardownToolbar();
|
||||
|
||||
this.breadcrumbs.destroy();
|
||||
this.selection.off("new-node-front", this.onNewSelection);
|
||||
this.selection.off("detached-front", this.onDetached);
|
||||
|
||||
let markupDestroyer = this._destroyMarkup();
|
||||
|
||||
let highlighterDestroyer = this.highlighters.destroy();
|
||||
this.prefsObserver.destroy();
|
||||
this.reflowTracker.destroy();
|
||||
this.styleChangeTracker.destroy();
|
||||
this.search.destroy();
|
||||
|
@ -1381,14 +1399,15 @@ Inspector.prototype = {
|
|||
this.panelDoc = null;
|
||||
this.panelWin.inspector = null;
|
||||
this.panelWin = null;
|
||||
this.prefsObserver = null;
|
||||
this.resultsLength = null;
|
||||
this.search = null;
|
||||
this.searchBox = null;
|
||||
this.show3PaneToggle = null;
|
||||
this.show3PaneTooltip = null;
|
||||
this.sidebar = null;
|
||||
this.store = null;
|
||||
this.target = null;
|
||||
this.threePaneTooltip = null;
|
||||
|
||||
this._panelDestroyer = promise.all([
|
||||
highlighterDestroyer,
|
||||
|
|
|
@ -15,7 +15,6 @@ const {PrefObserver} = require("devtools/client/shared/prefs");
|
|||
const ElementStyle = require("devtools/client/inspector/rules/models/element-style");
|
||||
const Rule = require("devtools/client/inspector/rules/models/rule");
|
||||
const RuleEditor = require("devtools/client/inspector/rules/views/rule-editor");
|
||||
const ClassListPreviewer = require("devtools/client/inspector/rules/views/class-list-previewer");
|
||||
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
|
||||
const {
|
||||
VIEW_NODE_SELECTOR_TYPE,
|
||||
|
@ -28,15 +27,17 @@ const {
|
|||
VIEW_NODE_VARIABLE_TYPE,
|
||||
VIEW_NODE_FONT_TYPE,
|
||||
} = require("devtools/client/inspector/shared/node-types");
|
||||
const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
|
||||
const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
|
||||
const {createChild, promiseWarn} = require("devtools/client/inspector/shared/utils");
|
||||
const {debounce} = require("devtools/shared/debounce");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
|
||||
const clipboardHelper = require("devtools/shared/platform/clipboard");
|
||||
const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
|
||||
|
||||
loader.lazyRequireGetter(this, "ClassListPreviewer", "devtools/client/inspector/rules/views/class-list-previewer");
|
||||
loader.lazyRequireGetter(this, "StyleInspectorMenu", "devtools/client/inspector/shared/style-inspector-menu");
|
||||
loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
|
||||
loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
|
||||
const PREF_DEFAULT_COLOR_UNIT = "devtools.defaultColorUnit";
|
||||
|
@ -183,14 +184,10 @@ function CssRuleView(inspector, document, store, pageStyle) {
|
|||
|
||||
this._showEmpty();
|
||||
|
||||
this._contextmenu = new StyleInspectorMenu(this, { isRuleView: true });
|
||||
|
||||
// Add the tooltips and highlighters to the view
|
||||
this.tooltips = new TooltipsOverlay(this);
|
||||
|
||||
this.highlighters.addToView(this);
|
||||
|
||||
this.classListPreviewer = new ClassListPreviewer(this.inspector, this.classPanel);
|
||||
}
|
||||
|
||||
CssRuleView.prototype = {
|
||||
|
@ -204,6 +201,22 @@ CssRuleView.prototype = {
|
|||
// to figure out how shorthand properties will be parsed.
|
||||
_dummyElement: null,
|
||||
|
||||
get classListPreviewer() {
|
||||
if (!this._classListPreviewer) {
|
||||
this._classListPreviewer = new ClassListPreviewer(this.inspector, this.classPanel);
|
||||
}
|
||||
|
||||
return this._classListPreviewer;
|
||||
},
|
||||
|
||||
get contextMenu() {
|
||||
if (!this._contextMenu) {
|
||||
this._contextMenu = new StyleInspectorMenu(this, { isRuleView: true });
|
||||
}
|
||||
|
||||
return this._contextMenu;
|
||||
},
|
||||
|
||||
// Get the dummy elemenet.
|
||||
get dummyElement() {
|
||||
return this._dummyElement;
|
||||
|
@ -445,7 +458,7 @@ CssRuleView.prototype = {
|
|||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this._contextmenu.show(event);
|
||||
this.contextMenu.show(event);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -725,15 +738,18 @@ CssRuleView.prototype = {
|
|||
|
||||
this._outputParser = null;
|
||||
|
||||
// Remove context menu
|
||||
if (this._contextmenu) {
|
||||
this._contextmenu.destroy();
|
||||
this._contextmenu = null;
|
||||
if (this._classListPreviewer) {
|
||||
this._classListPreviewer.destroy();
|
||||
this._classListPreviewer = null;
|
||||
}
|
||||
|
||||
if (this._contextMenu) {
|
||||
this._contextMenu.destroy();
|
||||
this._contextMenu = null;
|
||||
}
|
||||
|
||||
this.tooltips.destroy();
|
||||
this.highlighters.removeFromView(this);
|
||||
this.classListPreviewer.destroy();
|
||||
this.unselectAllRules();
|
||||
|
||||
// Remove bound listeners
|
||||
|
|
|
@ -90,7 +90,7 @@ async function checkSelectAll(view) {
|
|||
|
||||
info("Checking that _SelectAll() then copy returns the correct " +
|
||||
"clipboard value");
|
||||
view._contextmenu._onSelectAll();
|
||||
view.contextMenu._onSelectAll();
|
||||
let expectedPattern = "element {[\\r\\n]+" +
|
||||
" margin: 10em;[\\r\\n]+" +
|
||||
" font-size: 14pt;[\\r\\n]+" +
|
||||
|
|
|
@ -227,6 +227,8 @@ function ClassListPreviewer(inspector, containerEl) {
|
|||
this.inspector.selection.on("new-node-front", this.onNewSelection);
|
||||
this.containerEl.addEventListener("input", this.onCheckBoxChanged);
|
||||
this.model.on("current-node-class-changed", this.onCurrentNodeClassChanged);
|
||||
|
||||
this.onNewSelection();
|
||||
}
|
||||
|
||||
ClassListPreviewer.prototype = {
|
||||
|
|
|
@ -11,6 +11,7 @@ DevToolsModules(
|
|||
'reflow-tracker.js',
|
||||
'style-change-tracker.js',
|
||||
'style-inspector-menu.js',
|
||||
'three-pane-onboarding-tooltip.js',
|
||||
'tooltips-overlay.js',
|
||||
'utils.js'
|
||||
)
|
||||
|
|
|
@ -7,10 +7,6 @@
|
|||
"use strict";
|
||||
|
||||
const Services = require("Services");
|
||||
|
||||
const Menu = require("devtools/client/framework/menu");
|
||||
const MenuItem = require("devtools/client/framework/menu-item");
|
||||
|
||||
const {
|
||||
VIEW_NODE_SELECTOR_TYPE,
|
||||
VIEW_NODE_PROPERTY_TYPE,
|
||||
|
@ -18,7 +14,10 @@ const {
|
|||
VIEW_NODE_IMAGE_URL_TYPE,
|
||||
VIEW_NODE_LOCATION_TYPE,
|
||||
} = require("devtools/client/inspector/shared/node-types");
|
||||
const clipboardHelper = require("devtools/shared/platform/clipboard");
|
||||
|
||||
loader.lazyRequireGetter(this, "Menu", "devtools/client/framework/menu");
|
||||
loader.lazyRequireGetter(this, "MenuItem", "devtools/client/framework/menu-item");
|
||||
loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
|
||||
|
||||
const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
|
||||
const {LocalizationHelper} = require("devtools/shared/l10n");
|
||||
|
|
|
@ -68,13 +68,13 @@ function testIsColorPopupOnAllNodes(view) {
|
|||
function testIsColorPopupOnNode(view, node) {
|
||||
info("Testing node " + node);
|
||||
view.styleDocument.popupNode = node;
|
||||
view._contextmenu._colorToCopy = "";
|
||||
view.contextMenu._colorToCopy = "";
|
||||
|
||||
let result = view._contextmenu._isColorPopup();
|
||||
let result = view.contextMenu._isColorPopup();
|
||||
let correct = isColorValueNode(node);
|
||||
|
||||
is(result, correct, "_isColorPopup returned the expected value " + correct);
|
||||
is(view._contextmenu._colorToCopy, (correct) ? "rgb(18, 58, 188)" : "",
|
||||
is(view.contextMenu._colorToCopy, (correct) ? "rgb(18, 58, 188)" : "",
|
||||
"_colorToCopy was set to the expected value");
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ async function testManualEdit(inspector, view) {
|
|||
|
||||
view.styleDocument.popupNode = colorValueElement;
|
||||
|
||||
let contextMenu = view._contextmenu;
|
||||
let contextMenu = view.contextMenu;
|
||||
contextMenu._isColorPopup();
|
||||
is(contextMenu._colorToCopy, newColor, "_colorToCopy has the new value");
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ async function testColorPickerEdit(inspector, view) {
|
|||
"data-color was updated");
|
||||
view.styleDocument.popupNode = swatchElement;
|
||||
|
||||
let contextMenu = view._contextmenu;
|
||||
let contextMenu = view.contextMenu;
|
||||
contextMenu._isColorPopup();
|
||||
is(contextMenu._colorToCopy, rgbaColorText, "_colorToCopy has the new value");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Services = require("Services");
|
||||
const { openWebLink } = require("devtools/client/shared/link");
|
||||
const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
|
||||
|
||||
const { LocalizationHelper } = require("devtools/shared/l10n");
|
||||
const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");
|
||||
|
||||
const SHOW_THREE_PANE_ONBOARDING_PREF = "devtools.inspector.show-three-pane-tooltip";
|
||||
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const CONTAINER_WIDTH = 300;
|
||||
const LEARN_MORE_LINK = "https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/Use_the_3-pane_inspector?utm_source=devtools&utm_medium=3-pane-onboarding";
|
||||
|
||||
/**
|
||||
* Three pane inspector onboarding tooltip that is shown on the 3 pane inspector toggle
|
||||
* button when the pref is on.
|
||||
*/
|
||||
class ThreePaneOnboardingTooltip {
|
||||
constructor(toolbox, doc) {
|
||||
this.toolbox = toolbox;
|
||||
this.doc = doc;
|
||||
this.tooltip = new HTMLTooltip(this.toolbox.doc, {
|
||||
type: "arrow",
|
||||
useXulWrapper: true,
|
||||
});
|
||||
|
||||
this.onCloseButtonClick = this.onCloseButtonClick.bind(this);
|
||||
this.onLearnMoreLinkClick = this.onLearnMoreLinkClick.bind(this);
|
||||
|
||||
const container = doc.createElementNS(XHTML_NS, "div");
|
||||
container.className = "three-pane-onboarding-container";
|
||||
|
||||
const icon = doc.createElementNS(XHTML_NS, "span");
|
||||
icon.className = "three-pane-onboarding-icon";
|
||||
container.appendChild(icon);
|
||||
|
||||
const content = doc.createElementNS(XHTML_NS, "div");
|
||||
content.className = "three-pane-onboarding-content";
|
||||
container.appendChild(content);
|
||||
|
||||
const message = doc.createElementNS(XHTML_NS, "div");
|
||||
const learnMoreString = L10N.getStr("inspector.threePaneOnboarding.learnMoreLink");
|
||||
const messageString = L10N.getFormatStr("inspector.threePaneOnboarding.content",
|
||||
learnMoreString);
|
||||
const learnMoreStartIndex = messageString.indexOf(learnMoreString);
|
||||
|
||||
message.append(messageString.substring(0, learnMoreStartIndex));
|
||||
|
||||
this.learnMoreLink = doc.createElementNS(XHTML_NS, "a");
|
||||
this.learnMoreLink.className = "three-pane-onboarding-link";
|
||||
this.learnMoreLink.href = "#";
|
||||
this.learnMoreLink.textContent = learnMoreString;
|
||||
|
||||
message.append(this.learnMoreLink);
|
||||
message.append(messageString.substring(learnMoreStartIndex + learnMoreString.length));
|
||||
content.append(message);
|
||||
|
||||
this.closeButton = doc.createElementNS(XHTML_NS, "button");
|
||||
this.closeButton.className = "three-pane-onboarding-close-button devtools-button";
|
||||
container.appendChild(this.closeButton);
|
||||
|
||||
this.closeButton.addEventListener("click", this.onCloseButtonClick);
|
||||
this.learnMoreLink.addEventListener("click", this.onLearnMoreLinkClick);
|
||||
|
||||
this.tooltip.setContent(container, { width: CONTAINER_WIDTH });
|
||||
this.tooltip.show(this.doc.querySelector("#inspector-sidebar .sidebar-toggle"), {
|
||||
position: "top",
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.closeButton.removeEventListener("click", this.onCloseButtonClick);
|
||||
this.learnMoreLink.removeEventListener("click", this.onLearnMoreLinkClick);
|
||||
|
||||
this.tooltip.destroy();
|
||||
|
||||
this.closeButton = null;
|
||||
this.doc = null;
|
||||
this.learnMoreLink = null;
|
||||
this.toolbox = null;
|
||||
this.tooltip = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the "click" event on the close button. Hides the onboarding tooltip
|
||||
* and sets the show three pane onboarding tooltip pref to false.
|
||||
*/
|
||||
onCloseButtonClick() {
|
||||
Services.prefs.setBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF, false);
|
||||
this.tooltip.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the "click" event on the learn more button. Hides the onboarding tooltip
|
||||
* and opens the link to the mdn page in a new tab.
|
||||
*/
|
||||
onLearnMoreLinkClick() {
|
||||
Services.prefs.setBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF, false);
|
||||
this.tooltip.hide();
|
||||
openWebLink(LEARN_MORE_LINK);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ThreePaneOnboardingTooltip;
|
|
@ -594,7 +594,7 @@ function buildContextMenuItems(menu) {
|
|||
* @return An array of MenuItems
|
||||
*/
|
||||
function openStyleContextMenuAndGetAllItems(view, target) {
|
||||
const menu = view._contextmenu._openMenu({target: target});
|
||||
const menu = view.contextMenu._openMenu({target: target});
|
||||
return buildContextMenuItems(menu);
|
||||
}
|
||||
|
||||
|
|
|
@ -68,13 +68,14 @@ ToolSidebar.prototype = {
|
|||
return this._toolPanel.InspectorTabPanel;
|
||||
},
|
||||
|
||||
get TabBar() {
|
||||
return this._toolPanel.TabBar;
|
||||
},
|
||||
|
||||
// Rendering
|
||||
|
||||
render: function() {
|
||||
let Tabbar = this.React.createFactory(this.browserRequire(
|
||||
"devtools/client/shared/components/tabs/TabBar"));
|
||||
|
||||
let sidebar = Tabbar({
|
||||
let sidebar = this.TabBar({
|
||||
menuDocument: this._toolPanel._toolbox.doc,
|
||||
showAllTabsMenu: true,
|
||||
sidebarToggleButton: this._options.sidebarToggleButton,
|
||||
|
|
|
@ -117,6 +117,7 @@ devtools.jar:
|
|||
skin/images/breadcrumbs-divider.svg (themes/images/breadcrumbs-divider.svg)
|
||||
skin/images/filters.svg (themes/images/filters.svg)
|
||||
skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
|
||||
skin/images/fox-smiling.svg (themes/images/fox-smiling.svg)
|
||||
skin/images/grid.svg (themes/images/grid.svg)
|
||||
skin/images/angle-swatch.svg (themes/images/angle-swatch.svg)
|
||||
skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
|
||||
|
|
|
@ -464,3 +464,11 @@ inspector.classPanel.noClasses=No classes on this element
|
|||
# properties to display e.g. due to search criteria this message is
|
||||
# displayed.
|
||||
inspector.noProperties=No CSS properties found.
|
||||
|
||||
# LOCALIZATION NOTE (inspector.threePaneOnboarding.content,
|
||||
# inspector.threePaneOnboarding.learnMoreLink): This is the content shown in the 3 pane
|
||||
# inspector onboarding tooltip that is displayed on top of the 3 pane inspector toggle
|
||||
# button. %S in the content will be replaced by a link at run time with the learnMoreLink
|
||||
# string.
|
||||
inspector.threePaneOnboarding.content=New: 3-pane mode lets you see both CSS rules and Layout tools. Click this button to toggle. %S
|
||||
inspector.threePaneOnboarding.learnMoreLink=Learn more
|
||||
|
|
|
@ -57,6 +57,12 @@ pref("devtools.inspector.three-pane-toggle", false);
|
|||
#endif
|
||||
// Enable the 3 pane mode in the inspector
|
||||
pref("devtools.inspector.three-pane-enabled", false);
|
||||
// Show the 3 pane onboarding tooltip in the inspector
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
pref("devtools.inspector.show-three-pane-tooltip", true);
|
||||
#else
|
||||
pref("devtools.inspector.show-three-pane-tooltip", false);
|
||||
#endif
|
||||
// Collapse pseudo-elements by default in the rule-view
|
||||
pref("devtools.inspector.show_pseudo_elements", false);
|
||||
// The default size for image preview tooltips in the rule-view/computed-view/markup-view
|
||||
|
@ -288,12 +294,8 @@ pref("devtools.webconsole.timestampMessages", false);
|
|||
// to automatically trigger multiline editing (equivalent to shift + enter).
|
||||
pref("devtools.webconsole.autoMultiline", true);
|
||||
|
||||
// Enable the webconsole sidebar toggle in Nightly builds.
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
pref("devtools.webconsole.sidebarToggle", true);
|
||||
#else
|
||||
// Enable the webconsole sidebar toggle
|
||||
pref("devtools.webconsole.sidebarToggle", false);
|
||||
#endif
|
||||
|
||||
// Disable the new performance recording panel by default
|
||||
pref("devtools.performance.new-panel-enabled", false);
|
||||
|
|
|
@ -108,8 +108,10 @@ function loadFrameScriptUtils(browser = gBrowser.selectedBrowser) {
|
|||
return mm;
|
||||
}
|
||||
|
||||
Services.prefs.setBoolPref("devtools.inspector.show-three-pane-tooltip", false);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("devtools.dump.emit");
|
||||
Services.prefs.clearUserPref("devtools.inspector.show-three-pane-tooltip");
|
||||
Services.prefs.clearUserPref("devtools.toolbox.host");
|
||||
Services.prefs.clearUserPref("devtools.toolbox.previousHost");
|
||||
Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<!-- 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/. -->
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="72px" height="72px" viewBox="0 0 72 72" enable-background="new 0 0 72 72" xml:space="preserve">
|
||||
<path fill="#FFD8CA" d="M30.4,61C15.5,60,7.7,54.7,0.3,41.4C0.1,41.1,0,40.7,0,40.4h8.5l0-3.2h0c0-0.2,0-0.4,0-0.6
|
||||
c0-4.9,3.9-8.8,8.8-8.8c4.9,0,8.8,3.9,8.8,8.8c0,0.2,0,0.4,0,0.6h0v15.7C26.1,56.2,27.8,59.2,30.4,61z M63.5,40.4v-3.2h0
|
||||
c0-0.2,0-0.5,0-0.7c0-4.9-3.9-8.8-8.8-8.8c-4.8,0-8.7,3.8-8.8,8.5v0.5c0,0.2,0,0.3,0,0.5h0v15.7c0,3.4-1.7,6.3-4.3,8.1
|
||||
c14.9-1,22.7-6.2,30.1-19.5c0.2-0.3,0.3-0.7,0.2-1.1H63.5z"/>
|
||||
<path fill="#FF6F1F" d="M42.5,60.3c0.1,0,0.1-0.1,0.2-0.1c0.2-0.2,0.4-0.4,0.6-0.6c0.1-0.1,0.1-0.1,0.2-0.2c0.2-0.2,0.4-0.5,0.6-0.7
|
||||
c0,0,0.1-0.1,0.1-0.1c0.2-0.3,0.4-0.6,0.6-1c0,0,0,0,0,0c0.2-0.3,0.3-0.6,0.4-1c0-0.1,0.1-0.2,0.1-0.3c0.1-0.2,0.2-0.5,0.2-0.7
|
||||
c0-0.1,0.1-0.2,0.1-0.4c0.1-0.3,0.1-0.5,0.1-0.8c0-0.1,0-0.2,0.1-0.3c0-0.4,0.1-0.8,0.1-1.1v-1.2V37.1h0c0-0.2,0-0.3,0-0.5v-0.5
|
||||
c0.1-4.7,4-8.5,8.8-8.5c4.9,0,8.8,3.9,8.8,8.8c0,0.2,0,0.5,0,0.7h0v3.2H72c0-0.4-0.1-0.8-0.3-1.1c-2-3.7-4.1-7.2-6.3-10.4
|
||||
c1.6-1.2,2.9-2.8,4-4.6c3-5.2,2.8-11.3,0.1-16.2c-0.2-0.3-0.5-0.5-0.9-0.5c-5.5,0-10.4,3-13.4,8.2c-0.4,0.6-0.7,1.3-1,1.9
|
||||
C49.2,14.7,43.4,13,36,13s-13.2,1.8-18.1,4.8c-0.3-0.7-0.6-1.3-1-1.9c-3-5.2-7.9-8.2-13.4-8.2c-0.4,0-0.7,0.2-0.9,0.5
|
||||
c-2.7,4.8-2.9,11,0.1,16.2c1.1,1.8,2.4,3.3,4,4.6C4.4,32,2.3,35.5,0.3,39.3C0.1,39.6,0,40,0,40.4h8.5l0-3.2h0c0-0.2,0-0.4,0-0.6
|
||||
c0-4.9,3.9-8.8,8.8-8.8c4.9,0,8.8,3.9,8.8,8.8c0,0.2,0,0.4,0,0.6h0v14.5v1.2c0,0.4,0,0.8,0.1,1.1c0,0.1,0,0.2,0.1,0.3
|
||||
c0,0.3,0.1,0.5,0.1,0.8c0,0.1,0.1,0.2,0.1,0.4c0.1,0.3,0.2,0.5,0.2,0.7c0,0.1,0.1,0.2,0.1,0.3c0.1,0.3,0.3,0.6,0.4,1c0,0,0,0,0,0
|
||||
c0.2,0.3,0.4,0.7,0.6,1c0,0,0.1,0.1,0.1,0.1c0.2,0.3,0.4,0.5,0.6,0.7c0.1,0.1,0.1,0.1,0.2,0.2c0.2,0.2,0.4,0.4,0.6,0.6
|
||||
c0.1,0,0.1,0.1,0.2,0.1c0.3,0.2,0.6,0.5,0.9,0.7c1.6,1.1,3.5,1.7,5.6,1.7c2.1,0,4-0.6,5.6-1.7C41.9,60.8,42.2,60.6,42.5,60.3z"/>
|
||||
<g>
|
||||
<path fill="#2B3B47" d="M32.9,45.7c0,0,0.4,0,1,0c0.6,0,1.3,0,2.1,0c0.8,0,1.5,0,2.1,0c0.6,0,1-0.1,1-0.1c1.2-0.1,2.3,0.8,2.4,2.1
|
||||
c0.1,0.6-0.2,1.3-0.6,1.7l-1.9,2.1c0,0-0.4,0.4-1,0.7c-0.3,0.2-0.6,0.3-1,0.4c-0.2,0.1-0.4,0.1-0.6,0.1c-0.2,0-0.4,0-0.6,0
|
||||
c-0.2,0-0.4,0-0.6,0c-0.2,0-0.4-0.1-0.6-0.1c-0.2,0-0.4-0.1-0.5-0.2c-0.2,0-0.3-0.1-0.5-0.2c-0.6-0.3-1-0.6-1-0.6l-1.8-1.8
|
||||
c-0.9-0.9-0.9-2.4,0-3.3C31.6,45.9,32.3,45.6,32.9,45.7z"/>
|
||||
</g>
|
||||
<path fill="#2B3B47" stroke="#2B3B47" stroke-width="1.0904" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
|
||||
M30.6,55c0,0,2.1,1.3,5.4,1.3s5.4-1.3,5.4-1.3s-0.3,4.9-5.4,4.9S30.6,55,30.6,55z"/>
|
||||
<path fill="#2B3B47" d="M17.3,32.5c1.7,0,3,1.3,3,3v6.8c0,1.7-1.3,3-3,3l0,0c-1.7,0-3-1.3-3-3v-6.8C14.3,33.8,15.7,32.5,17.3,32.5
|
||||
L17.3,32.5z"/>
|
||||
<path fill="#2B3B47" d="M54.8,32.5c1.7,0,3,1.3,3,3v6.8c0,1.7-1.3,3-3,3l0,0c-1.7,0-3-1.3-3-3v-6.8C51.8,33.8,53.1,32.5,54.8,32.5
|
||||
L54.8,32.5z"/>
|
||||
<path fill="#2B3B47" d="M57.3,20.1c3.6-8,10-11.5,11.5-12.3c0,0,0-0.1,0-0.1c-0.1,0-0.2,0-0.3,0c-0.1,0-0.3,0-0.4,0
|
||||
c-0.2,0-0.4,0-0.6,0c-0.1,0-0.1,0-0.2,0c-5.1,0.4-9.5,3.3-12.3,8.1c-0.4,0.6-0.7,1.3-1,1.9C55.2,18.5,56.3,19.2,57.3,20.1z"/>
|
||||
<path fill="#2B3B47" d="M14.7,20.1c1.1-0.9,2.3-1.8,3.6-2.5c-0.2-0.7-0.5-1.3-0.9-1.8c-3.1-5.2-8.6-8.1-14.2-8.1
|
||||
c-0.1,0-0.1,0.1,0,0.2C4.7,8.5,11.1,12.1,14.7,20.1z"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 3.4 KiB |
|
@ -13,11 +13,15 @@
|
|||
.theme-dark {
|
||||
--bezier-diagonal-color: #eee;
|
||||
--bezier-grid-color: rgba(0, 0, 0, 0.2);
|
||||
--onboarding-link-color: var(--theme-highlight-blue);
|
||||
--onboarding-link-active-color: var(--blue-40);
|
||||
}
|
||||
|
||||
.theme-light {
|
||||
--bezier-diagonal-color: rgba(0, 0, 0, 0.2);
|
||||
--bezier-grid-color: rgba(0, 0, 0, 0.05);
|
||||
--onboarding-link-color: var(--blue-60);
|
||||
--onboarding-link-active-color: var(--blue-70);
|
||||
}
|
||||
|
||||
/* Tooltip widget (see devtools/client/shared/widgets/tooltip/Tooltip.js) */
|
||||
|
@ -480,3 +484,53 @@
|
|||
height: 100%;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
/* Tooltip: 3 Pane Inspecot Onboarding Tooltip */
|
||||
|
||||
.three-pane-onboarding-container {
|
||||
align-items: center;
|
||||
background-color: var(--theme-toolbar-background);
|
||||
box-sizing: border-box;
|
||||
color: var(--theme-body-color);
|
||||
display: flex;
|
||||
font-size: 12px;
|
||||
padding: 7px;
|
||||
width: 100%;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.three-pane-onboarding-icon {
|
||||
display: inline-block;
|
||||
background-size: 21px;
|
||||
width: 21px;
|
||||
height: 21px;
|
||||
margin: 8px;
|
||||
background-image: url("chrome://devtools/skin/images/fox-smiling.svg");
|
||||
}
|
||||
|
||||
.three-pane-onboarding-content {
|
||||
flex: 1;
|
||||
padding-inline-start: 5px;
|
||||
}
|
||||
|
||||
.three-pane-onboarding-link {
|
||||
color: var(--onboarding-link-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.three-pane-onboarding-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.three-pane-onboarding-link:active {
|
||||
color: var(--onboarding-link-active-color);
|
||||
}
|
||||
|
||||
.three-pane-onboarding-close-button {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.three-pane-onboarding-close-button::before {
|
||||
background-image: url("chrome://devtools/skin/images/close.svg");
|
||||
margin: -6px 0 0 -6px;
|
||||
}
|
||||
|
|
|
@ -214,6 +214,7 @@
|
|||
|
||||
--purple-60: #8000d7;
|
||||
|
||||
--blue-40: #45a1ff;
|
||||
--blue-50: #0a84ff;
|
||||
--blue-55: #0074e8;
|
||||
--blue-60: #0060df;
|
||||
|
|
|
@ -32,8 +32,7 @@ add_task(async function() {
|
|||
"#console-menu-store (S) [disabled]",
|
||||
"#console-menu-copy (C)",
|
||||
"#console-menu-copy-object (o) [disabled]",
|
||||
"#console-menu-select (A)",
|
||||
"#console-menu-open-sidebar (V) [disabled]",
|
||||
"#console-menu-select (A)"
|
||||
];
|
||||
is(getSimplifiedContextMenu(menuPopup).join("\n"), expectedContextMenu.join("\n"),
|
||||
"The context menu has the expected entries for a network message");
|
||||
|
@ -52,8 +51,7 @@ add_task(async function() {
|
|||
"#console-menu-store (S) [disabled]",
|
||||
"#console-menu-copy (C)",
|
||||
"#console-menu-copy-object (o) [disabled]",
|
||||
"#console-menu-select (A)",
|
||||
"#console-menu-open-sidebar (V) [disabled]",
|
||||
"#console-menu-select (A)"
|
||||
];
|
||||
is(getSimplifiedContextMenu(menuPopup).join("\n"), expectedContextMenu.join("\n"),
|
||||
"The context menu has the expected entries for a simple log message");
|
||||
|
|
|
@ -36,6 +36,7 @@ class AccessibilityParent {
|
|||
this.messageManager.sendAsyncMessage(`${this._msgName}:event`, {
|
||||
topic: "initialized",
|
||||
data: {
|
||||
enabled: this.enabled,
|
||||
canBeDisabled: this.canBeDisabled,
|
||||
canBeEnabled: this.canBeEnabled
|
||||
}
|
||||
|
|
|
@ -1025,6 +1025,15 @@ const AccessibilityActor = ActorClassWithSpec(accessibilitySpec, {
|
|||
case "initialized":
|
||||
this._canBeEnabled = data.canBeEnabled;
|
||||
this._canBeDisabled = data.canBeDisabled;
|
||||
|
||||
// Sometimes when the tool is reopened content process accessibility service is
|
||||
// not shut down yet because GC did not run in that process (though it did in
|
||||
// parent process and the service was shut down there). We need to sync the two
|
||||
// services if possible.
|
||||
if (!data.enabled && this.enabled && data.canBeEnabled) {
|
||||
this.messageManager.sendAsyncMessage(this._msgName, { action: "enable" });
|
||||
}
|
||||
|
||||
this.initializedDeferred.resolve();
|
||||
break;
|
||||
case "can-be-disabled-change":
|
||||
|
|
|
@ -3246,7 +3246,7 @@ Element::GetEventTargetParentForLinks(EventChainPreVisitor& aVisitor)
|
|||
nsAutoString target;
|
||||
GetLinkTarget(target);
|
||||
nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
|
||||
false, true, true);
|
||||
/* click */ false, /* isTrusted */ true);
|
||||
// Make sure any ancestor links don't also TriggerLink
|
||||
aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
|
||||
}
|
||||
|
@ -3358,7 +3358,7 @@ Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor)
|
|||
const InternalUIEvent* activeEvent = aVisitor.mEvent->AsUIEvent();
|
||||
MOZ_ASSERT(activeEvent);
|
||||
nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
|
||||
true, true, activeEvent->IsTrustable());
|
||||
/* click */ true, activeEvent->IsTrustable());
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5491,8 +5491,7 @@ nsContentUtils::CombineResourcePrincipals(nsCOMPtr<nsIPrincipal>* aResourcePrinc
|
|||
void
|
||||
nsContentUtils::TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
|
||||
nsIURI *aLinkURI, const nsString &aTargetSpec,
|
||||
bool aClick, bool aIsUserTriggered,
|
||||
bool aIsTrusted)
|
||||
bool aClick, bool aIsTrusted)
|
||||
{
|
||||
NS_ASSERTION(aPresContext, "Need a nsPresContext");
|
||||
NS_PRECONDITION(aLinkURI, "No link URI");
|
||||
|
@ -5515,10 +5514,7 @@ nsContentUtils::TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
|
|||
nsresult proceed = NS_OK;
|
||||
|
||||
if (sSecurityManager) {
|
||||
uint32_t flag =
|
||||
aIsUserTriggered ?
|
||||
(uint32_t)nsIScriptSecurityManager::STANDARD :
|
||||
(uint32_t)nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT;
|
||||
uint32_t flag = static_cast<uint32_t>(nsIScriptSecurityManager::STANDARD);
|
||||
proceed =
|
||||
sSecurityManager->CheckLoadURIWithPrincipal(aContent->NodePrincipal(),
|
||||
aLinkURI, flag);
|
||||
|
|
|
@ -1862,16 +1862,12 @@ public:
|
|||
* @param aTargetSpec the target (like target=, may be empty).
|
||||
* @param aClick whether this was a click or not (if false, this method
|
||||
* assumes you just hovered over the link).
|
||||
* @param aIsUserTriggered whether the user triggered the link. This would be
|
||||
* false for loads from auto XLinks or from the
|
||||
* click() method if we ever implement it.
|
||||
* @param aIsTrusted If false, JS Context will be pushed to stack
|
||||
* when the link is triggered.
|
||||
*/
|
||||
static void TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
|
||||
nsIURI *aLinkURI, const nsString& aTargetSpec,
|
||||
bool aClick, bool aIsUserTriggered,
|
||||
bool aIsTrusted);
|
||||
bool aClick, bool aIsTrusted);
|
||||
|
||||
/**
|
||||
* Get the link location.
|
||||
|
|
|
@ -161,75 +161,96 @@ IPCBlobInputStream::~IPCBlobInputStream()
|
|||
NS_IMETHODIMP
|
||||
IPCBlobInputStream::Available(uint64_t* aLength)
|
||||
{
|
||||
// We don't have a remoteStream yet: let's return 0.
|
||||
if (mState == eInit || mState == ePending) {
|
||||
*aLength = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mState == eRunning) {
|
||||
// We don't have a remoteStream yet: let's return 0.
|
||||
if (mState == eInit || mState == ePending) {
|
||||
*aLength = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mState == eClosed) {
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mState == eRunning);
|
||||
MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
|
||||
|
||||
nsresult rv = EnsureAsyncRemoteStream();
|
||||
nsresult rv = EnsureAsyncRemoteStream(lock);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mAsyncRemoteStream);
|
||||
return mAsyncRemoteStream->Available(aLength);
|
||||
asyncRemoteStream = mAsyncRemoteStream;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mState == eClosed);
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
MOZ_ASSERT(asyncRemoteStream);
|
||||
return asyncRemoteStream->Available(aLength);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IPCBlobInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
|
||||
{
|
||||
// Read is not available is we don't have a remoteStream.
|
||||
if (mState == eInit || mState == ePending) {
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mState == eRunning) {
|
||||
// Read is not available is we don't have a remoteStream.
|
||||
if (mState == eInit || mState == ePending) {
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
|
||||
if (mState == eClosed) {
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mState == eRunning);
|
||||
MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
|
||||
|
||||
nsresult rv = EnsureAsyncRemoteStream();
|
||||
nsresult rv = EnsureAsyncRemoteStream(lock);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mAsyncRemoteStream);
|
||||
return mAsyncRemoteStream->Read(aBuffer, aCount, aReadCount);
|
||||
asyncRemoteStream = mAsyncRemoteStream;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mState == eClosed);
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
MOZ_ASSERT(asyncRemoteStream);
|
||||
return asyncRemoteStream->Read(aBuffer, aCount, aReadCount);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IPCBlobInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
|
||||
uint32_t aCount, uint32_t *aResult)
|
||||
{
|
||||
// ReadSegments is not available is we don't have a remoteStream.
|
||||
if (mState == eInit || mState == ePending) {
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mState == eRunning) {
|
||||
// ReadSegments is not available is we don't have a remoteStream.
|
||||
if (mState == eInit || mState == ePending) {
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
|
||||
if (mState == eClosed) {
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mState == eRunning);
|
||||
MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
|
||||
|
||||
nsresult rv = EnsureAsyncRemoteStream();
|
||||
nsresult rv = EnsureAsyncRemoteStream(lock);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mAsyncRemoteStream);
|
||||
return mAsyncRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
|
||||
asyncRemoteStream = mAsyncRemoteStream;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mState == eClosed);
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
MOZ_ASSERT(asyncRemoteStream);
|
||||
return asyncRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -242,32 +263,36 @@ IPCBlobInputStream::IsNonBlocking(bool* aNonBlocking)
|
|||
NS_IMETHODIMP
|
||||
IPCBlobInputStream::Close()
|
||||
{
|
||||
if (mActor) {
|
||||
mActor->ForgetStream(this);
|
||||
mActor = nullptr;
|
||||
}
|
||||
|
||||
if (mAsyncRemoteStream) {
|
||||
mAsyncRemoteStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
|
||||
mAsyncRemoteStream = nullptr;
|
||||
}
|
||||
|
||||
if (mRemoteStream) {
|
||||
mRemoteStream->Close();
|
||||
mRemoteStream = nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
|
||||
nsCOMPtr<nsIInputStream> remoteStream;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mActor) {
|
||||
mActor->ForgetStream(this);
|
||||
mActor = nullptr;
|
||||
}
|
||||
|
||||
asyncRemoteStream.swap(mAsyncRemoteStream);
|
||||
remoteStream.swap(mRemoteStream);
|
||||
|
||||
mInputStreamCallback = nullptr;
|
||||
mInputStreamCallbackEventTarget = nullptr;
|
||||
|
||||
mFileMetadataCallback = nullptr;
|
||||
mFileMetadataCallbackEventTarget = nullptr;
|
||||
|
||||
mState = eClosed;
|
||||
}
|
||||
|
||||
mFileMetadataCallback = nullptr;
|
||||
mFileMetadataCallbackEventTarget = nullptr;
|
||||
if (asyncRemoteStream) {
|
||||
asyncRemoteStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
|
||||
}
|
||||
|
||||
if (remoteStream) {
|
||||
remoteStream->Close();
|
||||
}
|
||||
|
||||
mState = eClosed;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -276,6 +301,7 @@ IPCBlobInputStream::Close()
|
|||
NS_IMETHODIMP
|
||||
IPCBlobInputStream::GetCloneable(bool* aCloneable)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
*aCloneable = mState != eClosed;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -283,6 +309,8 @@ IPCBlobInputStream::GetCloneable(bool* aCloneable)
|
|||
NS_IMETHODIMP
|
||||
IPCBlobInputStream::Clone(nsIInputStream** aResult)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mState == eClosed) {
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
@ -294,7 +322,7 @@ IPCBlobInputStream::Clone(nsIInputStream** aResult)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
stream->InitWithExistingRange(mStart, mLength);
|
||||
stream->InitWithExistingRange(mStart, mLength, lock);
|
||||
|
||||
stream.forget(aResult);
|
||||
return NS_OK;
|
||||
|
@ -306,6 +334,8 @@ NS_IMETHODIMP
|
|||
IPCBlobInputStream::CloneWithRange(uint64_t aStart, uint64_t aLength,
|
||||
nsIInputStream** aResult)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mState == eClosed) {
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
@ -332,7 +362,7 @@ IPCBlobInputStream::CloneWithRange(uint64_t aStart, uint64_t aLength,
|
|||
aLength = streamSize.value();
|
||||
}
|
||||
|
||||
stream->InitWithExistingRange(aStart + mStart, aLength);
|
||||
stream->InitWithExistingRange(aStart + mStart, aLength, lock);
|
||||
|
||||
stream.forget(aResult);
|
||||
return NS_OK;
|
||||
|
@ -351,56 +381,63 @@ IPCBlobInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
|
|||
uint32_t aFlags, uint32_t aRequestedCount,
|
||||
nsIEventTarget* aEventTarget)
|
||||
{
|
||||
// See IPCBlobInputStream.h for more information about this state machine.
|
||||
|
||||
switch (mState) {
|
||||
// First call, we need to retrieve the stream from the parent actor.
|
||||
case eInit:
|
||||
MOZ_ASSERT(mActor);
|
||||
|
||||
mInputStreamCallback = aCallback;
|
||||
mInputStreamCallbackEventTarget = aEventTarget;
|
||||
mState = ePending;
|
||||
|
||||
mActor->StreamNeeded(this, aEventTarget);
|
||||
return NS_OK;
|
||||
|
||||
// We are still waiting for the remote inputStream
|
||||
case ePending: {
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mInputStreamCallback && aCallback) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// See IPCBlobInputStream.h for more information about this state machine.
|
||||
|
||||
mInputStreamCallback = aCallback;
|
||||
mInputStreamCallbackEventTarget = aEventTarget;
|
||||
return NS_OK;
|
||||
}
|
||||
switch (mState) {
|
||||
// First call, we need to retrieve the stream from the parent actor.
|
||||
case eInit:
|
||||
MOZ_ASSERT(mActor);
|
||||
|
||||
// We have the remote inputStream, let's check if we can execute the callback.
|
||||
case eRunning: {
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mInputStreamCallback = aCallback;
|
||||
mInputStreamCallbackEventTarget = aEventTarget;
|
||||
mState = ePending;
|
||||
|
||||
mActor->StreamNeeded(this, aEventTarget);
|
||||
return NS_OK;
|
||||
|
||||
// We are still waiting for the remote inputStream
|
||||
case ePending: {
|
||||
if (mInputStreamCallback && aCallback) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mInputStreamCallback = aCallback;
|
||||
mInputStreamCallbackEventTarget = aEventTarget;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = EnsureAsyncRemoteStream();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
// We have the remote inputStream, let's check if we can execute the callback.
|
||||
case eRunning: {
|
||||
if (mInputStreamCallback && aCallback) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv = EnsureAsyncRemoteStream(lock);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mInputStreamCallback = aCallback;
|
||||
mInputStreamCallbackEventTarget = aEventTarget;
|
||||
|
||||
asyncRemoteStream = mAsyncRemoteStream;
|
||||
break;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mAsyncRemoteStream);
|
||||
return mAsyncRemoteStream->AsyncWait(aCallback ? this : nullptr,
|
||||
0, 0, aEventTarget);
|
||||
// Stream is closed.
|
||||
default:
|
||||
MOZ_ASSERT(mState == eClosed);
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
// Stream is closed.
|
||||
default:
|
||||
MOZ_ASSERT(mState == eClosed);
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
MOZ_ASSERT(asyncRemoteStream);
|
||||
return asyncRemoteStream->AsyncWait(aCallback ? this : nullptr,
|
||||
0, 0, aEventTarget);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -408,14 +445,6 @@ IPCBlobInputStream::StreamReady(already_AddRefed<nsIInputStream> aInputStream)
|
|||
{
|
||||
nsCOMPtr<nsIInputStream> inputStream = Move(aInputStream);
|
||||
|
||||
// We have been closed in the meantime.
|
||||
if (mState == eClosed) {
|
||||
if (inputStream) {
|
||||
inputStream->Close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If inputStream is null, it means that the serialization went wrong or the
|
||||
// stream is not available anymore. We keep the state as pending just to block
|
||||
// any additional operation.
|
||||
|
@ -424,22 +453,50 @@ IPCBlobInputStream::StreamReady(already_AddRefed<nsIInputStream> aInputStream)
|
|||
return;
|
||||
}
|
||||
|
||||
// Now it's the right time to apply a slice if needed.
|
||||
if (mStart > 0 || mLength < mActor->Size()) {
|
||||
inputStream =
|
||||
new SlicedInputStream(inputStream.forget(), mStart, mLength);
|
||||
}
|
||||
|
||||
mRemoteStream = inputStream;
|
||||
|
||||
MOZ_ASSERT(mState == ePending);
|
||||
mState = eRunning;
|
||||
|
||||
nsCOMPtr<nsIFileMetadataCallback> fileMetadataCallback;
|
||||
fileMetadataCallback.swap(mFileMetadataCallback);
|
||||
|
||||
nsCOMPtr<nsIEventTarget> fileMetadataCallbackEventTarget;
|
||||
fileMetadataCallbackEventTarget.swap(mFileMetadataCallbackEventTarget);
|
||||
nsCOMPtr<nsIInputStreamCallback> inputStreamCallback;
|
||||
nsCOMPtr<nsIEventTarget> inputStreamCallbackEventTarget;
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
// We have been closed in the meantime.
|
||||
if (mState == eClosed) {
|
||||
if (inputStream) {
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
inputStream->Close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Now it's the right time to apply a slice if needed.
|
||||
if (mStart > 0 || mLength < mActor->Size()) {
|
||||
inputStream =
|
||||
new SlicedInputStream(inputStream.forget(), mStart, mLength);
|
||||
}
|
||||
|
||||
mRemoteStream = inputStream;
|
||||
|
||||
MOZ_ASSERT(mState == ePending);
|
||||
mState = eRunning;
|
||||
|
||||
fileMetadataCallback.swap(mFileMetadataCallback);
|
||||
fileMetadataCallbackEventTarget.swap(mFileMetadataCallbackEventTarget);
|
||||
|
||||
inputStreamCallback = mInputStreamCallback ? this : nullptr;
|
||||
inputStreamCallbackEventTarget = mInputStreamCallbackEventTarget;
|
||||
|
||||
if (inputStreamCallback) {
|
||||
nsresult rv = EnsureAsyncRemoteStream(lock);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mAsyncRemoteStream);
|
||||
asyncRemoteStream = mAsyncRemoteStream;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileMetadataCallback) {
|
||||
FileMetadataCallbackRunnable::Execute(fileMetadataCallback,
|
||||
|
@ -447,32 +504,18 @@ IPCBlobInputStream::StreamReady(already_AddRefed<nsIInputStream> aInputStream)
|
|||
this);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> inputStreamCallback = this;
|
||||
nsCOMPtr<nsIEventTarget> inputStreamCallbackEventTarget;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
inputStreamCallbackEventTarget = mInputStreamCallbackEventTarget;
|
||||
if (!mInputStreamCallback) {
|
||||
inputStreamCallback = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (inputStreamCallback) {
|
||||
nsresult rv = EnsureAsyncRemoteStream();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(asyncRemoteStream);
|
||||
|
||||
MOZ_ASSERT(mAsyncRemoteStream);
|
||||
|
||||
rv = mAsyncRemoteStream->AsyncWait(inputStreamCallback, 0, 0,
|
||||
inputStreamCallbackEventTarget);
|
||||
nsresult rv = asyncRemoteStream->AsyncWait(inputStreamCallback, 0, 0,
|
||||
inputStreamCallbackEventTarget);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IPCBlobInputStream::InitWithExistingRange(uint64_t aStart, uint64_t aLength)
|
||||
IPCBlobInputStream::InitWithExistingRange(uint64_t aStart, uint64_t aLength,
|
||||
const MutexAutoLock& aProofOfLock)
|
||||
{
|
||||
MOZ_ASSERT(mActor->Size() >= aStart + aLength);
|
||||
mStart = aStart;
|
||||
|
@ -529,6 +572,8 @@ void
|
|||
IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
|
||||
FileDescriptorArray& aFileDescriptors)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
mozilla::ipc::IPCBlobInputStreamParams params;
|
||||
params.id() = mActor->ID();
|
||||
params.start() = mStart;
|
||||
|
@ -554,8 +599,8 @@ IPCBlobInputStream::ExpectedSerializedLength()
|
|||
// nsIAsyncFileMetadata
|
||||
|
||||
NS_IMETHODIMP
|
||||
IPCBlobInputStream::AsyncWait(nsIFileMetadataCallback* aCallback,
|
||||
nsIEventTarget* aEventTarget)
|
||||
IPCBlobInputStream::AsyncFileMetadataWait(nsIFileMetadataCallback* aCallback,
|
||||
nsIEventTarget* aEventTarget)
|
||||
{
|
||||
MOZ_ASSERT(!!aCallback == !!aEventTarget);
|
||||
|
||||
|
@ -566,38 +611,46 @@ IPCBlobInputStream::AsyncWait(nsIFileMetadataCallback* aCallback,
|
|||
|
||||
// See IPCBlobInputStream.h for more information about this state machine.
|
||||
|
||||
switch (mState) {
|
||||
// First call, we need to retrieve the stream from the parent actor.
|
||||
case eInit:
|
||||
MOZ_ASSERT(mActor);
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
mFileMetadataCallback = aCallback;
|
||||
mFileMetadataCallbackEventTarget = aEventTarget;
|
||||
mState = ePending;
|
||||
switch (mState) {
|
||||
// First call, we need to retrieve the stream from the parent actor.
|
||||
case eInit:
|
||||
MOZ_ASSERT(mActor);
|
||||
|
||||
mActor->StreamNeeded(this, aEventTarget);
|
||||
return NS_OK;
|
||||
mFileMetadataCallback = aCallback;
|
||||
mFileMetadataCallbackEventTarget = aEventTarget;
|
||||
mState = ePending;
|
||||
|
||||
// We are still waiting for the remote inputStream
|
||||
case ePending:
|
||||
if (mFileMetadataCallback && aCallback) {
|
||||
return NS_ERROR_FAILURE;
|
||||
mActor->StreamNeeded(this, aEventTarget);
|
||||
return NS_OK;
|
||||
|
||||
// We are still waiting for the remote inputStream
|
||||
case ePending:
|
||||
if (mFileMetadataCallback && aCallback) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mFileMetadataCallback = aCallback;
|
||||
mFileMetadataCallbackEventTarget = aEventTarget;
|
||||
return NS_OK;
|
||||
|
||||
// We have the remote inputStream, let's check if we can execute the callback.
|
||||
case eRunning:
|
||||
break;
|
||||
|
||||
// Stream is closed.
|
||||
default:
|
||||
MOZ_ASSERT(mState == eClosed);
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
mFileMetadataCallback = aCallback;
|
||||
mFileMetadataCallbackEventTarget = aEventTarget;
|
||||
return NS_OK;
|
||||
|
||||
// We have the remote inputStream, let's check if we can execute the callback.
|
||||
case eRunning:
|
||||
FileMetadataCallbackRunnable::Execute(aCallback, aEventTarget, this);
|
||||
return NS_OK;
|
||||
|
||||
// Stream is closed.
|
||||
default:
|
||||
MOZ_ASSERT(mState == eClosed);
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
MOZ_ASSERT(mState == eRunning);
|
||||
}
|
||||
|
||||
FileMetadataCallbackRunnable::Execute(aCallback, aEventTarget, this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIFileMetadata
|
||||
|
@ -605,9 +658,13 @@ IPCBlobInputStream::AsyncWait(nsIFileMetadataCallback* aCallback,
|
|||
NS_IMETHODIMP
|
||||
IPCBlobInputStream::GetSize(int64_t* aRetval)
|
||||
{
|
||||
nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
|
||||
if (!fileMetadata) {
|
||||
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
||||
nsCOMPtr<nsIFileMetadata> fileMetadata;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
fileMetadata = do_QueryInterface(mRemoteStream);
|
||||
if (!fileMetadata) {
|
||||
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return fileMetadata->GetSize(aRetval);
|
||||
|
@ -616,9 +673,13 @@ IPCBlobInputStream::GetSize(int64_t* aRetval)
|
|||
NS_IMETHODIMP
|
||||
IPCBlobInputStream::GetLastModified(int64_t* aRetval)
|
||||
{
|
||||
nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
|
||||
if (!fileMetadata) {
|
||||
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
||||
nsCOMPtr<nsIFileMetadata> fileMetadata;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
fileMetadata = do_QueryInterface(mRemoteStream);
|
||||
if (!fileMetadata) {
|
||||
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return fileMetadata->GetLastModified(aRetval);
|
||||
|
@ -627,16 +688,20 @@ IPCBlobInputStream::GetLastModified(int64_t* aRetval)
|
|||
NS_IMETHODIMP
|
||||
IPCBlobInputStream::GetFileDescriptor(PRFileDesc** aRetval)
|
||||
{
|
||||
nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
|
||||
if (!fileMetadata) {
|
||||
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
||||
nsCOMPtr<nsIFileMetadata> fileMetadata;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
fileMetadata = do_QueryInterface(mRemoteStream);
|
||||
if (!fileMetadata) {
|
||||
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return fileMetadata->GetFileDescriptor(aRetval);
|
||||
}
|
||||
|
||||
nsresult
|
||||
IPCBlobInputStream::EnsureAsyncRemoteStream()
|
||||
IPCBlobInputStream::EnsureAsyncRemoteStream(const MutexAutoLock& aProofOfLock)
|
||||
{
|
||||
// We already have an async remote stream.
|
||||
if (mAsyncRemoteStream) {
|
||||
|
|
|
@ -45,10 +45,11 @@ private:
|
|||
~IPCBlobInputStream();
|
||||
|
||||
nsresult
|
||||
EnsureAsyncRemoteStream();
|
||||
EnsureAsyncRemoteStream(const MutexAutoLock& aProofOfLock);
|
||||
|
||||
void
|
||||
InitWithExistingRange(uint64_t aStart, uint64_t aLength);
|
||||
InitWithExistingRange(uint64_t aStart, uint64_t aLength,
|
||||
const MutexAutoLock& aProofOfLock);
|
||||
|
||||
RefPtr<IPCBlobInputStreamChild> mActor;
|
||||
|
||||
|
@ -80,7 +81,6 @@ private:
|
|||
nsCOMPtr<nsIAsyncInputStream> mAsyncRemoteStream;
|
||||
|
||||
// These 2 values are set only if mState is ePending.
|
||||
// They are protected by mutex.
|
||||
nsCOMPtr<nsIInputStreamCallback> mInputStreamCallback;
|
||||
nsCOMPtr<nsIEventTarget> mInputStreamCallbackEventTarget;
|
||||
|
||||
|
@ -88,6 +88,8 @@ private:
|
|||
nsCOMPtr<nsIFileMetadataCallback> mFileMetadataCallback;
|
||||
nsCOMPtr<nsIEventTarget> mFileMetadataCallbackEventTarget;
|
||||
|
||||
// Any member of this class is protected by mutex because touched on
|
||||
// multiple threads.
|
||||
Mutex mMutex;
|
||||
};
|
||||
|
||||
|
|
|
@ -434,7 +434,7 @@ public:
|
|||
RefPtr<ReleasingTimerHolder> holder =
|
||||
new ReleasingTimerHolder(aURI, aBroadcastToOtherProcesses);
|
||||
|
||||
auto raii = mozilla::MakeScopeExit([&] {
|
||||
auto raii = mozilla::MakeScopeExit([holder] {
|
||||
holder->CancelTimerAndRevokeURI();
|
||||
});
|
||||
|
||||
|
|
|
@ -4814,8 +4814,8 @@ nsresult HTMLMediaElement::InitializeDecoderForChannel(nsIChannel* aChannel,
|
|||
NS_ASSERTION(!mimeType.IsEmpty(), "We should have the Content-Type.");
|
||||
NS_ConvertUTF8toUTF16 mimeUTF16(mimeType);
|
||||
|
||||
HTMLMediaElement* self = this;
|
||||
auto reportCanPlay = [&](bool aCanPlay) {
|
||||
RefPtr<HTMLMediaElement> self = this;
|
||||
auto reportCanPlay = [&, self](bool aCanPlay) {
|
||||
diagnostics.StoreFormatDiagnostics(
|
||||
self->OwnerDoc(), mimeUTF16, aCanPlay, __func__);
|
||||
if (!aCanPlay) {
|
||||
|
@ -4827,7 +4827,7 @@ nsresult HTMLMediaElement::InitializeDecoderForChannel(nsIChannel* aChannel,
|
|||
}
|
||||
};
|
||||
|
||||
auto onExit = MakeScopeExit([&] {
|
||||
auto onExit = MakeScopeExit([self] {
|
||||
if (self->mChannelLoader) {
|
||||
self->mChannelLoader->Done();
|
||||
self->mChannelLoader = nullptr;
|
||||
|
|
|
@ -3555,7 +3555,8 @@ PreprocessHelper::WaitForStreamReady(nsIInputStream* aInputStream)
|
|||
nsCOMPtr<nsIAsyncFileMetadata> asyncFileMetadata =
|
||||
do_QueryInterface(aInputStream);
|
||||
if (asyncFileMetadata) {
|
||||
nsresult rv = asyncFileMetadata->AsyncWait(this, mTaskQueueEventTarget);
|
||||
nsresult rv =
|
||||
asyncFileMetadata->AsyncFileMetadataWait(this, mTaskQueueEventTarget);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -4270,7 +4270,7 @@ ContentParent::RecvRequestAnonymousTemporaryFile(const uint64_t& aID)
|
|||
// Make sure to send a callback to the child if we bail out early.
|
||||
nsresult rv = NS_OK;
|
||||
RefPtr<ContentParent> self(this);
|
||||
auto autoNotifyChildOnError = MakeScopeExit([&]() {
|
||||
auto autoNotifyChildOnError = MakeScopeExit([&, self]() {
|
||||
if (NS_FAILED(rv)) {
|
||||
FileDescOrError result(rv);
|
||||
Unused << self->SendProvideAnonymousTemporaryFile(aID, result);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsIInputStreamPump.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsIOService.h"
|
||||
#include "nsIProtocolHandler.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
|
@ -154,6 +155,18 @@ ChannelFromScriptURL(nsIPrincipal* principal,
|
|||
uint32_t secFlags = aIsMainScript ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
|
||||
: nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
|
||||
|
||||
bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
|
||||
principal, uri, true /* aInheritForAboutBlank */, false /* aForceInherit */);
|
||||
|
||||
bool isData = false;
|
||||
rv = uri->SchemeIs("data", &isData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool isURIUniqueOrigin = nsIOService::IsDataURIUniqueOpaqueOrigin() && isData;
|
||||
if (inheritAttrs && !isURIUniqueOrigin) {
|
||||
secFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
|
||||
}
|
||||
|
||||
if (aWorkerScriptType == DebuggerScript) {
|
||||
// A DebuggerScript needs to be a local resource like chrome: or resource:
|
||||
bool isUIResource = false;
|
||||
|
@ -172,8 +185,7 @@ ChannelFromScriptURL(nsIPrincipal* principal,
|
|||
|
||||
// Note: this is for backwards compatibility and goes against spec.
|
||||
// We should find a better solution.
|
||||
bool isData = false;
|
||||
if (aIsMainScript && NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData) {
|
||||
if (aIsMainScript && isData) {
|
||||
secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,8 @@ WorkerHolder::HoldWorker(WorkerPrivate* aWorkerPrivate,
|
|||
{
|
||||
AssertOnOwningThread(mThread);
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
MOZ_ASSERT(aFailStatus >= Terminating);
|
||||
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (!aWorkerPrivate->AddHolder(this, aFailStatus)) {
|
||||
|
|
|
@ -18,19 +18,19 @@ class WorkerPrivate;
|
|||
* Use this chart to help figure out behavior during each of the closing
|
||||
* statuses. Details below.
|
||||
*
|
||||
* +=============================================+
|
||||
* | Closing Statuses |
|
||||
* +=============+=============+=================+
|
||||
* | status | clear queue | abort execution |
|
||||
* +=============+=============+=================+
|
||||
* | Closing | yes | no |
|
||||
* +-------------+-------------+-----------------+
|
||||
* | Terminating | yes | yes |
|
||||
* +-------------+-------------+-----------------+
|
||||
* | Canceling | yes | yes |
|
||||
* +-------------+-------------+-----------------+
|
||||
* | Killing | yes | yes |
|
||||
* +-------------+-------------+-----------------+
|
||||
* +========================================================+
|
||||
* | Closing Statuses |
|
||||
* +=============+=============+=================+==========+
|
||||
* | status | clear queue | abort execution | notified |
|
||||
* +=============+=============+=================+==========+
|
||||
* | Closing | yes | no | no |
|
||||
* +-------------+-------------+-----------------+----------+
|
||||
* | Terminating | yes | yes | yes |
|
||||
* +-------------+-------------+-----------------+----------+
|
||||
* | Canceling | yes | yes | yes |
|
||||
* +-------------+-------------+-----------------+----------+
|
||||
* | Killing | yes | yes | yes |
|
||||
* +-------------+-------------+-----------------+----------+
|
||||
*/
|
||||
|
||||
enum WorkerStatus
|
||||
|
@ -43,7 +43,9 @@ enum WorkerStatus
|
|||
|
||||
// Inner script called close() on the worker global scope. Setting this
|
||||
// status causes the worker to clear its queue of events but does not abort
|
||||
// the currently running script.
|
||||
// the currently running script. WorkerHolder/WorkerRef objects are not going
|
||||
// to be notified because the behavior of APIs/Components should not change
|
||||
// during this status yet.
|
||||
Closing,
|
||||
|
||||
// Outer script called terminate() on the worker or the worker object was
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "nsINetworkInterceptController.h"
|
||||
#include "nsIProtocolHandler.h"
|
||||
#include "nsITabChild.h"
|
||||
#include "nsScriptSecurityManager.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -379,11 +380,20 @@ WorkerLoadInfo::PrincipalURIMatchesScriptURL()
|
|||
NS_ENSURE_SUCCESS(rv, false);
|
||||
NS_ENSURE_TRUE(principalURI, false);
|
||||
|
||||
bool equal = false;
|
||||
rv = principalURI->Equals(mBaseURI, &equal);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
if (nsScriptSecurityManager::SecurityCompareURIs(mBaseURI, principalURI)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return equal;
|
||||
// If strict file origin policy is in effect, local files will always fail
|
||||
// SecurityCompareURIs unless they are identical. Explicitly check file origin
|
||||
// policy, in that case.
|
||||
if (nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
|
||||
NS_URIIsLocalFile(mBaseURI) &&
|
||||
NS_RelaxStrictFileOriginPolicy(mBaseURI, principalURI)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
|
||||
|
|
|
@ -4058,11 +4058,7 @@ WorkerPrivate::NotifyHolders(WorkerStatus aStatus)
|
|||
{
|
||||
AssertIsOnWorkerThread();
|
||||
|
||||
NS_ASSERTION(aStatus > Running, "Bad status!");
|
||||
|
||||
if (aStatus >= Closing) {
|
||||
CancelAllTimeouts();
|
||||
}
|
||||
NS_ASSERTION(aStatus > Closing, "Bad status!");
|
||||
|
||||
nsTObserverArray<WorkerHolder*>::ForwardIterator iter(mHolders);
|
||||
while (iter.HasMore()) {
|
||||
|
@ -4566,8 +4562,14 @@ WorkerPrivate::NotifyInternal(WorkerStatus aStatus)
|
|||
|
||||
MOZ_ASSERT(previousStatus != Pending);
|
||||
|
||||
if (aStatus >= Closing) {
|
||||
CancelAllTimeouts();
|
||||
}
|
||||
|
||||
// Let all our holders know the new status.
|
||||
NotifyHolders(aStatus);
|
||||
if (aStatus > Closing) {
|
||||
NotifyHolders(aStatus);
|
||||
}
|
||||
|
||||
// If this is the first time our status has changed then we need to clear the
|
||||
// main event queue.
|
||||
|
|
|
@ -67,11 +67,12 @@ UNIFIED_SOURCES += [
|
|||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'../system',
|
||||
'/caps',
|
||||
'/dom/base',
|
||||
'/dom/bindings',
|
||||
'/dom/system',
|
||||
'/js/xpconnect/loader',
|
||||
'/netwerk/base',
|
||||
'/xpcom/build',
|
||||
'/xpcom/threads',
|
||||
]
|
||||
|
|
|
@ -9,3 +9,5 @@ support-files =
|
|||
[browser_bug1047663.js]
|
||||
[browser_bug1104623.js]
|
||||
run-if = buildapp == 'browser'
|
||||
[browser_fileURL.js]
|
||||
support-files = empty.html empty_worker.js
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const WORKER_BODY = "postMessage(42);\n";
|
||||
|
||||
// file:// tests.
|
||||
add_task(async function() {
|
||||
info("Creating the tmp directory.");
|
||||
let parent = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIDirectoryService)
|
||||
.QueryInterface(Ci.nsIProperties)
|
||||
.get('TmpD', Ci.nsIFile);
|
||||
parent.append('worker-dir-test');
|
||||
parent.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
|
||||
|
||||
let dir_a = parent.clone();
|
||||
dir_a.append('a');
|
||||
dir_a.create(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
|
||||
|
||||
let page_a = dir_a.clone();
|
||||
page_a.append('empty.html');
|
||||
page_a.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
|
||||
|
||||
let url_a = Services.io.newFileURI(page_a)
|
||||
|
||||
let worker = dir_a.clone();
|
||||
worker.append('worker.js');
|
||||
|
||||
let stream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
stream.init(worker, 0x02 | 0x08 | 0x20, // write, create, truncate
|
||||
0o666, 0);
|
||||
stream.write(WORKER_BODY, WORKER_BODY.length);
|
||||
stream.close();
|
||||
|
||||
let url_worker = Services.io.newFileURI(worker);
|
||||
|
||||
let dir_b = parent.clone();
|
||||
dir_b.append('b');
|
||||
dir_b.create(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
|
||||
|
||||
let page_b = dir_b.clone();
|
||||
page_b.append('empty.html');
|
||||
page_b.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
|
||||
|
||||
let url_b = Services.io.newFileURI(page_b)
|
||||
|
||||
let tab = BrowserTestUtils.addTab(gBrowser, url_a.spec);
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
await ContentTask.spawn(browser, url_worker.spec, function(spec) {
|
||||
return new content.Promise((resolve, reject) => {
|
||||
let w = new content.window.Worker(spec);
|
||||
w.onerror = _ => { reject(); }
|
||||
w.onmessage = _ => { resolve() };
|
||||
});
|
||||
});
|
||||
ok(true, "The worker is loaded when the script is on the same directory.");
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
tab = BrowserTestUtils.addTab(gBrowser, url_b.spec);
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
browser = gBrowser.getBrowserForTab(tab);
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
await ContentTask.spawn(browser, url_worker.spec, function(spec) {
|
||||
return new content.Promise((resolve, reject) => {
|
||||
let w = new content.window.Worker(spec);
|
||||
w.onerror = _ => { resolve(); }
|
||||
w.onmessage = _ => { reject() };
|
||||
});
|
||||
});
|
||||
ok(true, "The worker is not loaded when the script is on a different directory.");
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
info("Removing the tmp directory.");
|
||||
parent.remove(true);
|
||||
});
|
||||
|
||||
const EMPTY_URL = "/browser/dom/workers/test/empty.html";
|
||||
const WORKER_URL = "/browser/dom/workers/test/empty_worker.js";
|
||||
|
||||
add_task(async function() {
|
||||
let tab = BrowserTestUtils.addTab(gBrowser, "http://mochi.test:8888" + EMPTY_URL);
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
await ContentTask.spawn(browser, "http://example.org" + WORKER_URL, function(spec) {
|
||||
return new content.Promise((resolve, reject) => {
|
||||
let w = new content.window.Worker(spec);
|
||||
w.onerror = _ => { resolve(); }
|
||||
w.onmessage = _ => { reject() };
|
||||
});
|
||||
});
|
||||
ok(true, "The worker is not loaded when the script is from different origin.");
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function() {
|
||||
let tab = BrowserTestUtils.addTab(gBrowser, "https://example.org" + EMPTY_URL);
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
await ContentTask.spawn(browser, "http://example.org" + WORKER_URL, function(spec) {
|
||||
return new content.Promise((resolve, reject) => {
|
||||
let w = new content.window.Worker(spec);
|
||||
w.onerror = _ => { resolve(); }
|
||||
w.onmessage = _ => { reject() };
|
||||
});
|
||||
});
|
||||
ok(true, "The worker is not loaded when the script is from different origin.");
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
postMessage(42);
|
|
@ -165,7 +165,10 @@ PaintThread::Init()
|
|||
}
|
||||
sThread = thread;
|
||||
|
||||
if (gfxPlatform::GetPlatform()->UsesTiling()) {
|
||||
// Only create paint workers for tiling if we are using tiling or could
|
||||
// expect to dynamically switch to tiling in the future
|
||||
if (gfxPlatform::GetPlatform()->UsesTiling() ||
|
||||
gfxPrefs::LayersTilesEnabledIfSkiaPOMTP()) {
|
||||
int32_t paintWorkerCount = PaintThread::CalculatePaintWorkerCount();
|
||||
mPaintWorkers = SharedThreadPool::Get(NS_LITERAL_CSTRING("PaintWorker"), paintWorkerCount);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,11 @@
|
|||
#include "nsSize.h" // for IntSize, nsSize
|
||||
#include "nscore.h" // for NS_BUILD_REFCNT_LOGGING
|
||||
#if !defined(ANDROID) && (defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#include "smmintrin.h"
|
||||
#else
|
||||
#include "emmintrin.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef mozilla::gfx::IntRect nsIntRect;
|
||||
|
|
|
@ -620,7 +620,7 @@ private:
|
|||
DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.force-disabled", LayersOffMainThreadCompositionForceDisabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.offmainthreadcomposition.frame-rate", LayersCompositionFrameRate, int32_t,-1);
|
||||
DECL_GFX_PREF(Live, "layers.omtp.dump-capture", LayersOMTPDumpCapture, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.omtp.paint-workers", LayersOMTPPaintWorkers, int32_t, 1);
|
||||
DECL_GFX_PREF(Once, "layers.omtp.paint-workers", LayersOMTPPaintWorkers, int32_t, 1);
|
||||
DECL_GFX_PREF(Live, "layers.omtp.release-capture-on-main-thread", LayersOMTPReleaseCaptureOnMainThread, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.orientation.sync.timeout", OrientationSyncMillis, uint32_t, (uint32_t)0);
|
||||
DECL_GFX_PREF(Once, "layers.prefer-opengl", LayersPreferOpenGL, bool, false);
|
||||
|
|
|
@ -302,7 +302,7 @@ HadSimulatedInterrupt()
|
|||
# define JS_INTERRUPT_POSSIBLY_FAIL() \
|
||||
do { \
|
||||
if (MOZ_UNLIKELY(js::oom::ShouldFailWithInterrupt())) { \
|
||||
cx->interrupt_ = true; \
|
||||
cx->requestInterrupt(js::InterruptReason::CallbackUrgent); \
|
||||
return cx->handleInterrupt(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
|
|
@ -564,6 +564,43 @@ class MOZ_STACK_CLASS AutoInitializeSourceObject
|
|||
}
|
||||
};
|
||||
|
||||
// RAII class to check the frontend reports an exception when it fails to
|
||||
// compile a script.
|
||||
class MOZ_RAII AutoAssertReportedException
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JSContext* cx_;
|
||||
bool check_;
|
||||
|
||||
public:
|
||||
explicit AutoAssertReportedException(JSContext* cx)
|
||||
: cx_(cx),
|
||||
check_(true)
|
||||
{}
|
||||
void reset() {
|
||||
check_ = false;
|
||||
}
|
||||
~AutoAssertReportedException() {
|
||||
if (!check_)
|
||||
return;
|
||||
|
||||
if (!cx_->helperThread()) {
|
||||
MOZ_ASSERT(cx_->isExceptionPending());
|
||||
return;
|
||||
}
|
||||
|
||||
ParseTask* task = cx_->helperThread()->parseTask();
|
||||
MOZ_ASSERT(task->outOfMemory ||
|
||||
task->overRecursed ||
|
||||
!task->errors.empty());
|
||||
}
|
||||
#else
|
||||
public:
|
||||
explicit AutoAssertReportedException(JSContext*) {}
|
||||
void reset() {}
|
||||
#endif
|
||||
};
|
||||
|
||||
JSScript*
|
||||
frontend::CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind,
|
||||
const ReadOnlyCompileOptions& options,
|
||||
|
@ -571,9 +608,14 @@ frontend::CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKi
|
|||
ScriptSourceObject** sourceObjectOut)
|
||||
{
|
||||
MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
|
||||
AutoAssertReportedException assertException(cx);
|
||||
BytecodeCompiler compiler(cx, alloc, options, srcBuf, /* enclosingScope = */ nullptr);
|
||||
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
|
||||
return compiler.compileGlobalScript(scopeKind);
|
||||
JSScript* script = compiler.compileGlobalScript(scopeKind);
|
||||
if (!script)
|
||||
return nullptr;
|
||||
assertException.reset();
|
||||
return script;
|
||||
}
|
||||
|
||||
#if defined(JS_BUILD_BINAST)
|
||||
|
@ -582,6 +624,8 @@ JSScript*
|
|||
frontend::CompileGlobalBinASTScript(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
|
||||
const uint8_t* src, size_t len)
|
||||
{
|
||||
AutoAssertReportedException assertException(cx);
|
||||
|
||||
frontend::UsedNameTracker usedNames(cx);
|
||||
if (!usedNames.init())
|
||||
return nullptr;
|
||||
|
@ -617,6 +661,7 @@ frontend::CompileGlobalBinASTScript(JSContext* cx, LifoAlloc& alloc, const ReadO
|
|||
if (!NameFunctions(cx, pn))
|
||||
return nullptr;
|
||||
|
||||
assertException.reset();
|
||||
return script;
|
||||
}
|
||||
|
||||
|
@ -629,9 +674,15 @@ frontend::CompileEvalScript(JSContext* cx, LifoAlloc& alloc,
|
|||
SourceBufferHolder& srcBuf,
|
||||
ScriptSourceObject** sourceObjectOut)
|
||||
{
|
||||
AutoAssertReportedException assertException(cx);
|
||||
BytecodeCompiler compiler(cx, alloc, options, srcBuf, enclosingScope);
|
||||
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
|
||||
return compiler.compileEvalScript(environment, enclosingScope);
|
||||
JSScript* script = compiler.compileEvalScript(environment, enclosingScope);
|
||||
if (!script)
|
||||
return nullptr;
|
||||
assertException.reset();
|
||||
return script;
|
||||
|
||||
}
|
||||
|
||||
ModuleObject*
|
||||
|
@ -642,6 +693,8 @@ frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& optionsInpu
|
|||
MOZ_ASSERT(srcBuf.get());
|
||||
MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
|
||||
|
||||
AutoAssertReportedException assertException(cx);
|
||||
|
||||
CompileOptions options(cx, optionsInput);
|
||||
options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
|
||||
options.setIsRunOnce(true);
|
||||
|
@ -650,13 +703,20 @@ frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& optionsInpu
|
|||
RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
|
||||
BytecodeCompiler compiler(cx, alloc, options, srcBuf, emptyGlobalScope);
|
||||
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
|
||||
return compiler.compileModule();
|
||||
ModuleObject* module = compiler.compileModule();
|
||||
if (!module)
|
||||
return nullptr;
|
||||
|
||||
assertException.reset();
|
||||
return module;
|
||||
}
|
||||
|
||||
ModuleObject*
|
||||
frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
SourceBufferHolder& srcBuf)
|
||||
{
|
||||
AutoAssertReportedException assertException(cx);
|
||||
|
||||
if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global()))
|
||||
return nullptr;
|
||||
|
||||
|
@ -670,6 +730,7 @@ frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|||
if (!ModuleObject::Freeze(cx, module))
|
||||
return nullptr;
|
||||
|
||||
assertException.reset();
|
||||
return module;
|
||||
}
|
||||
|
||||
|
@ -678,6 +739,8 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
|
|||
{
|
||||
MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
|
||||
|
||||
AutoAssertReportedException assertException(cx);
|
||||
|
||||
CompileOptions options(cx);
|
||||
options.setMutedErrors(lazy->mutedErrors())
|
||||
.setFileAndLine(lazy->filename(), lazy->lineno())
|
||||
|
@ -745,6 +808,7 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
|
|||
if (!NameFunctions(cx, pn))
|
||||
return false;
|
||||
|
||||
assertException.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -755,14 +819,22 @@ frontend::CompileStandaloneFunction(JSContext* cx, MutableHandleFunction fun,
|
|||
const Maybe<uint32_t>& parameterListEnd,
|
||||
HandleScope enclosingScope /* = nullptr */)
|
||||
{
|
||||
AutoAssertReportedException assertException(cx);
|
||||
|
||||
RootedScope scope(cx, enclosingScope);
|
||||
if (!scope)
|
||||
scope = &cx->global()->emptyGlobalScope();
|
||||
|
||||
BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, scope);
|
||||
return compiler.compileStandaloneFunction(fun, GeneratorKind::NotGenerator,
|
||||
FunctionAsyncKind::SyncFunction,
|
||||
parameterListEnd);
|
||||
if (!compiler.compileStandaloneFunction(fun, GeneratorKind::NotGenerator,
|
||||
FunctionAsyncKind::SyncFunction,
|
||||
parameterListEnd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
assertException.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -771,12 +843,20 @@ frontend::CompileStandaloneGenerator(JSContext* cx, MutableHandleFunction fun,
|
|||
JS::SourceBufferHolder& srcBuf,
|
||||
const Maybe<uint32_t>& parameterListEnd)
|
||||
{
|
||||
AutoAssertReportedException assertException(cx);
|
||||
|
||||
RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
|
||||
|
||||
BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
|
||||
return compiler.compileStandaloneFunction(fun, GeneratorKind::Generator,
|
||||
FunctionAsyncKind::SyncFunction,
|
||||
parameterListEnd);
|
||||
if (!compiler.compileStandaloneFunction(fun, GeneratorKind::Generator,
|
||||
FunctionAsyncKind::SyncFunction,
|
||||
parameterListEnd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
assertException.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -785,12 +865,20 @@ frontend::CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fu
|
|||
JS::SourceBufferHolder& srcBuf,
|
||||
const Maybe<uint32_t>& parameterListEnd)
|
||||
{
|
||||
AutoAssertReportedException assertException(cx);
|
||||
|
||||
RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
|
||||
|
||||
BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
|
||||
return compiler.compileStandaloneFunction(fun, GeneratorKind::NotGenerator,
|
||||
FunctionAsyncKind::AsyncFunction,
|
||||
parameterListEnd);
|
||||
if (!compiler.compileStandaloneFunction(fun, GeneratorKind::NotGenerator,
|
||||
FunctionAsyncKind::AsyncFunction,
|
||||
parameterListEnd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
assertException.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -799,10 +887,18 @@ frontend::CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction f
|
|||
JS::SourceBufferHolder& srcBuf,
|
||||
const Maybe<uint32_t>& parameterListEnd)
|
||||
{
|
||||
AutoAssertReportedException assertException(cx);
|
||||
|
||||
RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
|
||||
|
||||
BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
|
||||
return compiler.compileStandaloneFunction(fun, GeneratorKind::Generator,
|
||||
FunctionAsyncKind::AsyncFunction,
|
||||
parameterListEnd);
|
||||
if (!compiler.compileStandaloneFunction(fun, GeneratorKind::Generator,
|
||||
FunctionAsyncKind::AsyncFunction,
|
||||
parameterListEnd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
assertException.reset();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -314,7 +314,7 @@ GCRuntime::gcIfNeededAtAllocation(JSContext* cx)
|
|||
|
||||
// Invoking the interrupt callback can fail and we can't usefully
|
||||
// handle that here. Just check in case we need to collect instead.
|
||||
if (cx->hasPendingInterrupt())
|
||||
if (cx->hasAnyPendingInterrupt())
|
||||
gcIfRequested();
|
||||
|
||||
// If we have grown past our GC heap threshold while in the middle of
|
||||
|
|
|
@ -1321,8 +1321,8 @@ GCRuntime::finish()
|
|||
* memory.
|
||||
*/
|
||||
helperState.finish();
|
||||
allocTask.cancel(GCParallelTask::CancelAndWait);
|
||||
decommitTask.cancel(GCParallelTask::CancelAndWait);
|
||||
allocTask.cancelAndWait();
|
||||
decommitTask.cancelAndWait();
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
/* Free memory associated with GC verification. */
|
||||
|
@ -3285,11 +3285,7 @@ GCRuntime::requestMajorGC(JS::gcreason::Reason reason)
|
|||
return;
|
||||
|
||||
majorGCTriggerReason = reason;
|
||||
|
||||
// There's no need to use RequestInterruptUrgent here. It's slower because
|
||||
// it has to interrupt (looping) Ion code, but loops in Ion code that
|
||||
// affect GC will have an explicit interrupt check.
|
||||
rt->mainContextFromOwnThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
|
||||
rt->mainContextFromOwnThread()->requestInterrupt(InterruptReason::GC);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -3302,9 +3298,7 @@ Nursery::requestMinorGC(JS::gcreason::Reason reason) const
|
|||
return;
|
||||
|
||||
minorGCTriggerReason_ = reason;
|
||||
|
||||
// See comment in requestMajorGC.
|
||||
runtime()->mainContextFromOwnThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
|
||||
runtime()->mainContextFromOwnThread()->requestInterrupt(InterruptReason::GC);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -7465,7 +7459,7 @@ GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::
|
|||
// avoid taking the GC lock when manipulating the chunks during the GC.
|
||||
// The background alloc task can run between slices, so we must wait
|
||||
// for it at the start of every slice.
|
||||
allocTask.cancel(GCParallelTask::CancelAndWait);
|
||||
allocTask.cancelAndWait();
|
||||
}
|
||||
|
||||
// We don't allow off-thread parsing to start while we're doing an
|
||||
|
@ -7788,7 +7782,7 @@ void
|
|||
GCRuntime::onOutOfMallocMemory()
|
||||
{
|
||||
// Stop allocating new chunks.
|
||||
allocTask.cancel(GCParallelTask::CancelAndWait);
|
||||
allocTask.cancelAndWait();
|
||||
|
||||
// Make sure we release anything queued for release.
|
||||
decommitTask.join();
|
||||
|
|
|
@ -20,12 +20,12 @@ class GCParallelTask
|
|||
JSRuntime* const runtime_;
|
||||
|
||||
// The state of the parallel computation.
|
||||
enum TaskState {
|
||||
enum class State {
|
||||
NotStarted,
|
||||
Dispatched,
|
||||
Finished,
|
||||
Finished
|
||||
};
|
||||
UnprotectedData<TaskState> state;
|
||||
UnprotectedData<State> state_;
|
||||
|
||||
// Amount of time this task took to execute.
|
||||
MainThreadOrGCTaskData<mozilla::TimeDuration> duration_;
|
||||
|
@ -34,15 +34,20 @@ class GCParallelTask
|
|||
|
||||
protected:
|
||||
// A flag to signal a request for early completion of the off-thread task.
|
||||
mozilla::Atomic<bool> cancel_;
|
||||
mozilla::Atomic<bool, mozilla::MemoryOrdering::ReleaseAcquire> cancel_;
|
||||
|
||||
virtual void run() = 0;
|
||||
|
||||
public:
|
||||
explicit GCParallelTask(JSRuntime* runtime) : runtime_(runtime), state(NotStarted), duration_(nullptr) {}
|
||||
explicit GCParallelTask(JSRuntime* runtime)
|
||||
: runtime_(runtime),
|
||||
state_(State::NotStarted),
|
||||
duration_(nullptr),
|
||||
cancel_(false)
|
||||
{}
|
||||
GCParallelTask(GCParallelTask&& other)
|
||||
: runtime_(other.runtime_),
|
||||
state(other.state),
|
||||
state_(other.state_),
|
||||
duration_(nullptr),
|
||||
cancel_(false)
|
||||
{}
|
||||
|
@ -69,17 +74,45 @@ class GCParallelTask
|
|||
void runFromMainThread(JSRuntime* rt);
|
||||
|
||||
// Dispatch a cancelation request.
|
||||
enum CancelMode { CancelNoWait, CancelAndWait};
|
||||
void cancel(CancelMode mode = CancelNoWait) {
|
||||
void cancelAndWait() {
|
||||
cancel_ = true;
|
||||
if (mode == CancelAndWait)
|
||||
join();
|
||||
join();
|
||||
}
|
||||
|
||||
// Check if a task is actively running.
|
||||
bool isRunningWithLockHeld(const AutoLockHelperThreadState& locked) const;
|
||||
bool isRunningWithLockHeld(const AutoLockHelperThreadState& lock) const {
|
||||
return isDispatched(lock);
|
||||
}
|
||||
bool isRunning() const;
|
||||
|
||||
private:
|
||||
void assertNotStarted() const {
|
||||
// Don't lock here because that adds extra synchronization in debug
|
||||
// builds that may hide bugs. There's no race if the assertion passes.
|
||||
MOZ_ASSERT(state_ == State::NotStarted);
|
||||
}
|
||||
bool isNotStarted(const AutoLockHelperThreadState& lock) const {
|
||||
return state_ == State::NotStarted;
|
||||
}
|
||||
bool isDispatched(const AutoLockHelperThreadState& lock) const {
|
||||
return state_ == State::Dispatched;
|
||||
}
|
||||
bool isFinished(const AutoLockHelperThreadState& lock) const {
|
||||
return state_ == State::Finished;
|
||||
}
|
||||
void setDispatched(const AutoLockHelperThreadState& lock) {
|
||||
MOZ_ASSERT(state_ == State::NotStarted);
|
||||
state_ = State::Dispatched;
|
||||
}
|
||||
void setFinished(const AutoLockHelperThreadState& lock) {
|
||||
MOZ_ASSERT(state_ == State::Dispatched);
|
||||
state_ = State::Finished;
|
||||
}
|
||||
void setNotStarted(const AutoLockHelperThreadState& lock) {
|
||||
MOZ_ASSERT(state_ == State::Finished);
|
||||
state_ = State::NotStarted;
|
||||
}
|
||||
|
||||
// This should be friended to HelperThread, but cannot be because it
|
||||
// would introduce several circular dependencies.
|
||||
public:
|
||||
|
|
|
@ -318,7 +318,7 @@ class GCRuntime
|
|||
void waitBackgroundSweepEnd() { helperState.waitBackgroundSweepEnd(); }
|
||||
void waitBackgroundSweepOrAllocEnd() {
|
||||
helperState.waitBackgroundSweepEnd();
|
||||
allocTask.cancel(GCParallelTask::CancelAndWait);
|
||||
allocTask.cancelAndWait();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -566,12 +566,14 @@ NativeRegExpMacroAssembler::Backtrack()
|
|||
{
|
||||
JitSpew(SPEW_PREFIX "Backtrack");
|
||||
|
||||
// Check for an interrupt.
|
||||
// Check for an urgent interrupt. We don't want to leave JIT code and enter
|
||||
// the regex interpreter for non-urgent interrupts. Note that interruptBits_
|
||||
// is a bitfield.
|
||||
Label noInterrupt;
|
||||
masm.branch32(Assembler::Equal,
|
||||
AbsoluteAddress(cx->addressOfInterruptRegExpJit()),
|
||||
Imm32(0),
|
||||
&noInterrupt);
|
||||
masm.branchTest32(Assembler::Zero,
|
||||
AbsoluteAddress(cx->addressOfInterruptBits()),
|
||||
Imm32(uint32_t(InterruptReason::CallbackUrgent)),
|
||||
&noInterrupt);
|
||||
masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
|
||||
masm.jump(&exit_label_);
|
||||
masm.bind(&noInterrupt);
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
// |jit-test| --arm-asm-nop-fill=1
|
||||
var f = wasmEvalText(`(module (func (result i32) (param i32)
|
||||
(block $0
|
||||
(block $1
|
||||
(block $2
|
||||
(block $default
|
||||
(br_table $0 $1 $2 $default (get_local 0))))))
|
||||
(return (i32.const 0)))
|
||||
(export "" 0)
|
||||
)`).exports[""];
|
||||
|
||||
f(0);
|
|
@ -701,7 +701,7 @@ BaselineCompiler::emitInterruptCheck()
|
|||
|
||||
Label done;
|
||||
masm.branch32(Assembler::Equal,
|
||||
AbsoluteAddress(cx->addressOfInterrupt()), Imm32(0),
|
||||
AbsoluteAddress(cx->addressOfInterruptBits()), Imm32(0),
|
||||
&done);
|
||||
|
||||
prepareVMCall();
|
||||
|
|
|
@ -12872,7 +12872,7 @@ CodeGenerator::visitInterruptCheck(LInterruptCheck* lir)
|
|||
{
|
||||
OutOfLineCode* ool = oolCallVM(InterruptCheckInfo, lir, ArgList(), StoreNothing());
|
||||
|
||||
const void* interruptAddr = gen->runtime->addressOfInterrupt();
|
||||
const void* interruptAddr = gen->runtime->addressOfInterruptBits();
|
||||
masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0), ool->entry());
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
@ -13045,7 +13045,7 @@ CodeGenerator::visitNewTarget(LNewTarget *ins)
|
|||
|
||||
Label useNFormals;
|
||||
|
||||
size_t numFormalArgs = ins->mirRaw()->block()->info().funMaybeLazy()->nargs();
|
||||
size_t numFormalArgs = ins->mirRaw()->block()->info().nargs();
|
||||
masm.branchPtr(Assembler::Below, argvLen, Imm32(numFormalArgs),
|
||||
&useNFormals);
|
||||
|
||||
|
|
|
@ -247,13 +247,14 @@ class CompileInfo
|
|||
// If the script uses an environment in body, the environment chain
|
||||
// will need to be observable.
|
||||
needsBodyEnvironmentObject_ = script->needsBodyEnvironment();
|
||||
funNeedsSomeEnvironmentObject_ = fun ? fun->needsSomeEnvironmentObject() : false;
|
||||
}
|
||||
|
||||
explicit CompileInfo(unsigned nlocals)
|
||||
: script_(nullptr), fun_(nullptr), osrPc_(nullptr),
|
||||
analysisMode_(Analysis_None), scriptNeedsArgsObj_(false),
|
||||
mayReadFrameArgsDirectly_(false), inlineScriptTree_(nullptr),
|
||||
needsBodyEnvironmentObject_(false)
|
||||
needsBodyEnvironmentObject_(false), funNeedsSomeEnvironmentObject_(false)
|
||||
{
|
||||
nimplicit_ = 0;
|
||||
nargs_ = 0;
|
||||
|
@ -484,7 +485,7 @@ class CompileInfo
|
|||
if (thisSlotForDerivedClassConstructor_ && *thisSlotForDerivedClassConstructor_ == slot)
|
||||
return true;
|
||||
|
||||
if (funMaybeLazy()->needsSomeEnvironmentObject() && slot == environmentChainSlot())
|
||||
if (funNeedsSomeEnvironmentObject_ && slot == environmentChainSlot())
|
||||
return true;
|
||||
|
||||
// If the function may need an arguments object, then make sure to
|
||||
|
@ -574,6 +575,7 @@ class CompileInfo
|
|||
// Whether a script needs environments within its body. This informs us
|
||||
// that the environment chain is not easy to reconstruct.
|
||||
bool needsBodyEnvironmentObject_;
|
||||
bool funNeedsSomeEnvironmentObject_;
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
|
|
@ -114,9 +114,9 @@ CompileRuntime::addressOfJitStackLimit()
|
|||
}
|
||||
|
||||
const void*
|
||||
CompileRuntime::addressOfInterrupt()
|
||||
CompileRuntime::addressOfInterruptBits()
|
||||
{
|
||||
return runtime()->mainContextFromAnyThread()->addressOfInterrupt();
|
||||
return runtime()->mainContextFromAnyThread()->addressOfInterruptBits();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -49,7 +49,7 @@ class CompileRuntime
|
|||
|
||||
const void* mainContextPtr();
|
||||
const void* addressOfJitStackLimit();
|
||||
const void* addressOfInterrupt();
|
||||
const void* addressOfInterruptBits();
|
||||
|
||||
#ifdef DEBUG
|
||||
bool isInsideNursery(gc::Cell* cell);
|
||||
|
|
|
@ -2756,6 +2756,18 @@ Assembler::leaveNoPool()
|
|||
m_buffer.leaveNoPool();
|
||||
}
|
||||
|
||||
void
|
||||
Assembler::enterNoNops()
|
||||
{
|
||||
m_buffer.enterNoNops();
|
||||
}
|
||||
|
||||
void
|
||||
Assembler::leaveNoNops()
|
||||
{
|
||||
m_buffer.leaveNoNops();
|
||||
}
|
||||
|
||||
ptrdiff_t
|
||||
Assembler::GetBranchOffset(const Instruction* i_)
|
||||
{
|
||||
|
|
|
@ -1958,6 +1958,8 @@ class Assembler : public AssemblerShared
|
|||
void flushBuffer();
|
||||
void enterNoPool(size_t maxInst);
|
||||
void leaveNoPool();
|
||||
void enterNoNops();
|
||||
void leaveNoNops();
|
||||
// This should return a BOffImm, but we didn't want to require everyplace
|
||||
// that used the AssemblerBuffer to make that class.
|
||||
static ptrdiff_t GetBranchOffset(const Instruction* i);
|
||||
|
@ -2487,6 +2489,19 @@ class AutoForbidPools
|
|||
}
|
||||
};
|
||||
|
||||
// Forbids nop filling for testing purposes. Not nestable.
|
||||
class AutoForbidNops
|
||||
{
|
||||
Assembler* masm_;
|
||||
public:
|
||||
explicit AutoForbidNops(Assembler* masm) : masm_(masm) {
|
||||
masm_->enterNoNops();
|
||||
}
|
||||
~AutoForbidNops() {
|
||||
masm_->leaveNoNops();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -521,19 +521,32 @@ void PatchJump(CodeLocationJump& jump_, CodeLocationLabel label);
|
|||
class AutoForbidPools
|
||||
{
|
||||
Assembler* asm_;
|
||||
|
||||
public:
|
||||
AutoForbidPools(Assembler* asm_, size_t maxInst)
|
||||
: asm_(asm_)
|
||||
{
|
||||
asm_->enterNoPool(maxInst);
|
||||
}
|
||||
|
||||
~AutoForbidPools() {
|
||||
asm_->leaveNoPool();
|
||||
}
|
||||
};
|
||||
|
||||
// Forbids nop filling for testing purposes. Not nestable.
|
||||
class AutoForbidNops
|
||||
{
|
||||
Assembler* asm_;
|
||||
public:
|
||||
explicit AutoForbidNops(Assembler* asm_)
|
||||
: asm_(asm_)
|
||||
{
|
||||
asm_->enterNoNops();
|
||||
}
|
||||
~AutoForbidNops() {
|
||||
asm_->leaveNoNops();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -286,6 +286,13 @@ class MozBaseAssembler : public js::jit::AssemblerShared {
|
|||
armbuffer_.leaveNoPool();
|
||||
}
|
||||
|
||||
void enterNoNops() {
|
||||
armbuffer_.enterNoNops();
|
||||
}
|
||||
void leaveNoNops() {
|
||||
armbuffer_.leaveNoNops();
|
||||
}
|
||||
|
||||
public:
|
||||
// Static interface used by IonAssemblerBufferWithConstantPools.
|
||||
static void InsertIndexIntoTag(uint8_t* load, uint32_t index);
|
||||
|
|
|
@ -642,6 +642,7 @@ struct AssemblerBufferWithConstantPools : public AssemblerBuffer<SliceSize, Inst
|
|||
// followed.
|
||||
const uint32_t nopFillInst_;
|
||||
const unsigned nopFill_;
|
||||
|
||||
// For inhibiting the insertion of fill NOPs in the dynamic context in which
|
||||
// they are being inserted.
|
||||
bool inhibitNops_;
|
||||
|
@ -1083,7 +1084,17 @@ struct AssemblerBufferWithConstantPools : public AssemblerBuffer<SliceSize, Inst
|
|||
canNotPlacePool_ = false;
|
||||
|
||||
// Validate the maxInst argument supplied to enterNoPool().
|
||||
MOZ_ASSERT(this->nextOffset().getOffset() - canNotPlacePoolStartOffset_ <= canNotPlacePoolMaxInst_ * InstSize);
|
||||
MOZ_ASSERT(this->nextOffset().getOffset() - canNotPlacePoolStartOffset_ <=
|
||||
canNotPlacePoolMaxInst_ * InstSize);
|
||||
}
|
||||
|
||||
void enterNoNops() {
|
||||
MOZ_ASSERT(!inhibitNops_);
|
||||
inhibitNops_ = true;
|
||||
}
|
||||
void leaveNoNops() {
|
||||
MOZ_ASSERT(inhibitNops_);
|
||||
inhibitNops_ = false;
|
||||
}
|
||||
|
||||
void align(unsigned alignment) {
|
||||
|
|
|
@ -5725,13 +5725,13 @@ JS::InitConsumeStreamCallback(JSContext* cx, ConsumeStreamCallback callback)
|
|||
JS_PUBLIC_API(void)
|
||||
JS_RequestInterruptCallback(JSContext* cx)
|
||||
{
|
||||
cx->requestInterrupt(JSContext::RequestInterruptUrgent);
|
||||
cx->requestInterrupt(InterruptReason::CallbackUrgent);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_RequestInterruptCallbackCanWait(JSContext* cx)
|
||||
{
|
||||
cx->requestInterrupt(JSContext::RequestInterruptCanWait);
|
||||
cx->requestInterrupt(InterruptReason::CallbackCanWait);
|
||||
}
|
||||
|
||||
JS::AutoSetAsyncStackForNewCalls::AutoSetAsyncStackForNewCalls(
|
||||
|
|
|
@ -1453,19 +1453,13 @@ js::GCParallelTask::~GCParallelTask()
|
|||
// destructors run after those for derived classes' members, so a join in a
|
||||
// base class can't ensure that the task is done using the members. All we
|
||||
// can do now is check that someone has previously stopped the task.
|
||||
#ifdef DEBUG
|
||||
Maybe<AutoLockHelperThreadState> helperLock;
|
||||
if (!HelperThreadState().isLockedByCurrentThread())
|
||||
helperLock.emplace();
|
||||
MOZ_ASSERT(state == NotStarted);
|
||||
#endif
|
||||
assertNotStarted();
|
||||
}
|
||||
|
||||
bool
|
||||
js::GCParallelTask::startWithLockHeld(AutoLockHelperThreadState& lock)
|
||||
{
|
||||
// Tasks cannot be started twice.
|
||||
MOZ_ASSERT(state == NotStarted);
|
||||
assertNotStarted();
|
||||
|
||||
// If we do the shutdown GC before running anything, we may never
|
||||
// have initialized the helper threads. Just use the serial path
|
||||
|
@ -1475,7 +1469,7 @@ js::GCParallelTask::startWithLockHeld(AutoLockHelperThreadState& lock)
|
|||
|
||||
if (!HelperThreadState().gcParallelWorklist(lock).append(this))
|
||||
return false;
|
||||
state = Dispatched;
|
||||
setDispatched(lock);
|
||||
|
||||
HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
|
||||
|
||||
|
@ -1490,14 +1484,15 @@ js::GCParallelTask::start()
|
|||
}
|
||||
|
||||
void
|
||||
js::GCParallelTask::joinWithLockHeld(AutoLockHelperThreadState& locked)
|
||||
js::GCParallelTask::joinWithLockHeld(AutoLockHelperThreadState& lock)
|
||||
{
|
||||
if (state == NotStarted)
|
||||
if (isNotStarted(lock))
|
||||
return;
|
||||
|
||||
while (state != Finished)
|
||||
HelperThreadState().wait(locked, GlobalHelperThreadState::CONSUMER);
|
||||
state = NotStarted;
|
||||
while (!isFinished(lock))
|
||||
HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
|
||||
|
||||
setNotStarted(lock);
|
||||
cancel_ = false;
|
||||
}
|
||||
|
||||
|
@ -1523,7 +1518,7 @@ TimeSince(TimeStamp prev)
|
|||
void
|
||||
js::GCParallelTask::runFromMainThread(JSRuntime* rt)
|
||||
{
|
||||
MOZ_ASSERT(state == NotStarted);
|
||||
assertNotStarted();
|
||||
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
|
||||
TimeStamp timeStart = TimeStamp::Now();
|
||||
run();
|
||||
|
@ -1531,13 +1526,15 @@ js::GCParallelTask::runFromMainThread(JSRuntime* rt)
|
|||
}
|
||||
|
||||
void
|
||||
js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& locked)
|
||||
js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& lock)
|
||||
{
|
||||
MOZ_ASSERT(isDispatched(lock));
|
||||
|
||||
AutoSetContextRuntime ascr(runtime());
|
||||
gc::AutoSetThreadIsPerformingGC performingGC;
|
||||
|
||||
{
|
||||
AutoUnlockHelperThreadState parallelSection(locked);
|
||||
AutoUnlockHelperThreadState parallelSection(lock);
|
||||
TimeStamp timeStart = TimeStamp::Now();
|
||||
TlsContext.get()->heapState = JS::HeapState::MajorCollecting;
|
||||
run();
|
||||
|
@ -1545,34 +1542,28 @@ js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& locked)
|
|||
duration_ = TimeSince(timeStart);
|
||||
}
|
||||
|
||||
state = Finished;
|
||||
HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
|
||||
}
|
||||
|
||||
bool
|
||||
js::GCParallelTask::isRunningWithLockHeld(const AutoLockHelperThreadState& locked) const
|
||||
{
|
||||
return state == Dispatched;
|
||||
setFinished(lock);
|
||||
HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, lock);
|
||||
}
|
||||
|
||||
bool
|
||||
js::GCParallelTask::isRunning() const
|
||||
{
|
||||
AutoLockHelperThreadState helperLock;
|
||||
return isRunningWithLockHeld(helperLock);
|
||||
AutoLockHelperThreadState lock;
|
||||
return isRunningWithLockHeld(lock);
|
||||
}
|
||||
|
||||
void
|
||||
HelperThread::handleGCParallelWorkload(AutoLockHelperThreadState& locked)
|
||||
HelperThread::handleGCParallelWorkload(AutoLockHelperThreadState& lock)
|
||||
{
|
||||
MOZ_ASSERT(HelperThreadState().canStartGCParallelTask(locked));
|
||||
MOZ_ASSERT(HelperThreadState().canStartGCParallelTask(lock));
|
||||
MOZ_ASSERT(idle());
|
||||
|
||||
TraceLoggerThread* logger = TraceLoggerForCurrentThread();
|
||||
AutoTraceLog logCompile(logger, TraceLogger_GC);
|
||||
|
||||
currentTask.emplace(HelperThreadState().gcParallelWorklist(locked).popCopy());
|
||||
gcParallelTask()->runFromHelperThread(locked);
|
||||
currentTask.emplace(HelperThreadState().gcParallelWorklist(lock).popCopy());
|
||||
gcParallelTask()->runFromHelperThread(lock);
|
||||
currentTask.reset();
|
||||
}
|
||||
|
||||
|
@ -1916,15 +1907,13 @@ HelperThread::handleIonWorkload(AutoLockHelperThreadState& locked)
|
|||
FinishOffThreadIonCompile(builder, locked);
|
||||
|
||||
// Ping the main thread so that the compiled code can be incorporated at the
|
||||
// next interrupt callback. Don't interrupt Ion code for this, as this
|
||||
// incorporation can be delayed indefinitely without affecting performance
|
||||
// as long as the main thread is actually executing Ion code.
|
||||
// next interrupt callback.
|
||||
//
|
||||
// This must happen before the current task is reset. DestroyContext
|
||||
// cancels in progress Ion compilations before destroying its target
|
||||
// context, and after we reset the current task we are no longer considered
|
||||
// to be Ion compiling.
|
||||
rt->mainContextFromAnyThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
|
||||
rt->mainContextFromAnyThread()->requestInterrupt(InterruptReason::AttachIonCompilations);
|
||||
|
||||
currentTask.reset();
|
||||
|
||||
|
|
|
@ -392,7 +392,7 @@ CheckForInterrupt(JSContext* cx)
|
|||
MOZ_ASSERT(!cx->isExceptionPending());
|
||||
// Add an inline fast-path since we have to check for interrupts in some hot
|
||||
// C++ loops of library builtins.
|
||||
if (MOZ_UNLIKELY(cx->hasPendingInterrupt()))
|
||||
if (MOZ_UNLIKELY(cx->hasAnyPendingInterrupt()))
|
||||
return cx->handleInterrupt();
|
||||
|
||||
JS_INTERRUPT_POSSIBLY_FAIL();
|
||||
|
|
|
@ -1286,8 +1286,7 @@ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
|
|||
asyncCauseForNewActivations(nullptr),
|
||||
asyncCallIsExplicit(false),
|
||||
interruptCallbackDisabled(false),
|
||||
interrupt_(false),
|
||||
interruptRegExpJit_(false),
|
||||
interruptBits_(0),
|
||||
osrTempData_(nullptr),
|
||||
ionReturnOverride_(MagicValue(JS_ARG_POISON)),
|
||||
jitStackLimit(UINTPTR_MAX),
|
||||
|
|
|
@ -92,6 +92,14 @@ bool
|
|||
CurrentThreadIsParseThread();
|
||||
#endif
|
||||
|
||||
enum class InterruptReason : uint32_t
|
||||
{
|
||||
GC = 1 << 0,
|
||||
AttachIonCompilations = 1 << 1,
|
||||
CallbackUrgent = 1 << 2,
|
||||
CallbackCanWait = 1 << 3,
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/*
|
||||
|
@ -787,26 +795,21 @@ struct JSContext : public JS::RootingContext,
|
|||
|
||||
js::ThreadData<bool> interruptCallbackDisabled;
|
||||
|
||||
mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_;
|
||||
mozilla::Atomic<uint32_t, mozilla::Relaxed> interruptRegExpJit_;
|
||||
|
||||
enum InterruptMode {
|
||||
RequestInterruptUrgent,
|
||||
RequestInterruptCanWait
|
||||
};
|
||||
// Bitfield storing InterruptReason values.
|
||||
mozilla::Atomic<uint32_t, mozilla::Relaxed> interruptBits_;
|
||||
|
||||
// Any thread can call requestInterrupt() to request that this thread
|
||||
// stop running and call the interrupt callback (allowing the interrupt
|
||||
// callback to halt execution). To stop this thread, requestInterrupt
|
||||
// sets two fields: interrupt_ (set to true) and jitStackLimit_ (set to
|
||||
// stop running. To stop this thread, requestInterrupt sets two fields:
|
||||
// interruptBits_ (a bitset of InterruptReasons) and jitStackLimit_ (set to
|
||||
// UINTPTR_MAX). The JS engine must continually poll one of these fields
|
||||
// and call handleInterrupt if either field has the interrupt value. (The
|
||||
// point of setting jitStackLimit_ to UINTPTR_MAX is that JIT code already
|
||||
// needs to guard on jitStackLimit_ in every function prologue to avoid
|
||||
// stack overflow, so we avoid a second branch on interrupt_ by setting
|
||||
// jitStackLimit_ to a value that is guaranteed to fail the guard.)
|
||||
// and call handleInterrupt if either field has the interrupt value.
|
||||
//
|
||||
// Note that the writes to interrupt_ and jitStackLimit_ use a Relaxed
|
||||
// The point of setting jitStackLimit_ to UINTPTR_MAX is that JIT code
|
||||
// already needs to guard on jitStackLimit_ in every function prologue to
|
||||
// avoid stack overflow, so we avoid a second branch on interruptBits_ by
|
||||
// setting jitStackLimit_ to a value that is guaranteed to fail the guard.)
|
||||
//
|
||||
// Note that the writes to interruptBits_ and jitStackLimit_ use a Relaxed
|
||||
// Atomic so, while the writes are guaranteed to eventually be visible to
|
||||
// this thread, it can happen in any order. handleInterrupt calls the
|
||||
// interrupt callback if either is set, so it really doesn't matter as long
|
||||
|
@ -814,20 +817,20 @@ struct JSContext : public JS::RootingContext,
|
|||
// cases, this relaxed ordering could lead to an interrupt handler being
|
||||
// called twice in succession after a single requestInterrupt call, but
|
||||
// that's fine.
|
||||
void requestInterrupt(InterruptMode mode);
|
||||
void requestInterrupt(js::InterruptReason reason);
|
||||
bool handleInterrupt();
|
||||
|
||||
MOZ_ALWAYS_INLINE bool hasPendingInterrupt() const {
|
||||
static_assert(sizeof(interrupt_) == sizeof(uint32_t), "Assumed by JIT callers");
|
||||
return interrupt_;
|
||||
MOZ_ALWAYS_INLINE bool hasAnyPendingInterrupt() const {
|
||||
static_assert(sizeof(interruptBits_) == sizeof(uint32_t), "Assumed by JIT callers");
|
||||
return interruptBits_ != 0;
|
||||
}
|
||||
bool hasPendingInterrupt(js::InterruptReason reason) const {
|
||||
return interruptBits_ & uint32_t(reason);
|
||||
}
|
||||
|
||||
public:
|
||||
void* addressOfInterrupt() {
|
||||
return &interrupt_;
|
||||
}
|
||||
void* addressOfInterruptRegExpJit() {
|
||||
return &interruptRegExpJit_;
|
||||
void* addressOfInterruptBits() {
|
||||
return &interruptBits_;
|
||||
}
|
||||
void* addressOfJitStackLimit() {
|
||||
return &jitStackLimit;
|
||||
|
|
|
@ -78,6 +78,8 @@ class JSFunction : public js::NativeObject
|
|||
INTERPRETED_LAZY = 0x0200, /* function is interpreted but doesn't have a script yet */
|
||||
RESOLVED_LENGTH = 0x0400, /* f.length has been resolved (see fun_resolve). */
|
||||
RESOLVED_NAME = 0x0800, /* f.name has been resolved (see fun_resolve). */
|
||||
NEW_SCRIPT_CLEARED = 0x1000, /* For a function used as an interpreted constructor, whether
|
||||
a 'new' type had constructor information cleared. */
|
||||
|
||||
FUNCTION_KIND_SHIFT = 13,
|
||||
FUNCTION_KIND_MASK = 0x7 << FUNCTION_KIND_SHIFT,
|
||||
|
@ -369,6 +371,14 @@ class JSFunction : public js::NativeObject
|
|||
flags_ |= RESOLVED_NAME;
|
||||
}
|
||||
|
||||
// Mark a function as having its 'new' script information cleared.
|
||||
bool wasNewScriptCleared() const {
|
||||
return flags_ & NEW_SCRIPT_CLEARED;
|
||||
}
|
||||
void setNewScriptCleared() {
|
||||
flags_ |= NEW_SCRIPT_CLEARED;
|
||||
}
|
||||
|
||||
void setAsyncKind(js::FunctionAsyncKind asyncKind) {
|
||||
if (isInterpretedLazy())
|
||||
lazyScript()->setAsyncKind(asyncKind);
|
||||
|
|
|
@ -3567,8 +3567,6 @@ JSObject::dump(js::GenericPrinter& out) const
|
|||
out.put(" had_elements_access");
|
||||
if (nobj->isIndexed())
|
||||
out.put(" indexed");
|
||||
if (nobj->wasNewScriptCleared())
|
||||
out.put(" new_script_cleared");
|
||||
} else {
|
||||
out.put(" not_native\n");
|
||||
}
|
||||
|
|
|
@ -767,14 +767,6 @@ class NativeObject : public ShapedObject
|
|||
return hasAllFlags(js::BaseShape::HAD_ELEMENTS_ACCESS);
|
||||
}
|
||||
|
||||
// Mark an object as having its 'new' script information cleared.
|
||||
bool wasNewScriptCleared() const {
|
||||
return hasAllFlags(js::BaseShape::NEW_SCRIPT_CLEARED);
|
||||
}
|
||||
static bool setNewScriptCleared(JSContext* cx, HandleNativeObject obj) {
|
||||
return setFlags(cx, obj, js::BaseShape::NEW_SCRIPT_CLEARED);
|
||||
}
|
||||
|
||||
bool hasInterestingSymbol() const {
|
||||
return hasAllFlags(js::BaseShape::HAS_INTERESTING_SYMBOL);
|
||||
}
|
||||
|
|
|
@ -431,7 +431,7 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim
|
|||
}
|
||||
|
||||
static bool
|
||||
InvokeInterruptCallback(JSContext* cx)
|
||||
HandleInterrupt(JSContext* cx, bool invokeCallback)
|
||||
{
|
||||
MOZ_ASSERT(cx->requestDepth >= 1);
|
||||
MOZ_ASSERT(!cx->compartment()->isAtomsCompartment());
|
||||
|
@ -442,6 +442,10 @@ InvokeInterruptCallback(JSContext* cx)
|
|||
// compilation.
|
||||
jit::AttachFinishedCompilations(cx);
|
||||
|
||||
// Don't call the interrupt callback if we only interrupted for GC or Ion.
|
||||
if (!invokeCallback)
|
||||
return true;
|
||||
|
||||
// Important: Additional callbacks can occur inside the callback handler
|
||||
// if it re-enters the JS engine. The embedding must ensure that the
|
||||
// callback is disconnected before attempting such re-entry.
|
||||
|
@ -502,16 +506,15 @@ InvokeInterruptCallback(JSContext* cx)
|
|||
}
|
||||
|
||||
void
|
||||
JSContext::requestInterrupt(InterruptMode mode)
|
||||
JSContext::requestInterrupt(InterruptReason reason)
|
||||
{
|
||||
interrupt_ = true;
|
||||
interruptBits_ |= uint32_t(reason);
|
||||
jitStackLimit = UINTPTR_MAX;
|
||||
|
||||
if (mode == JSContext::RequestInterruptUrgent) {
|
||||
if (reason == InterruptReason::CallbackUrgent) {
|
||||
// If this interrupt is urgent (slow script dialog for instance), take
|
||||
// additional steps to interrupt corner cases where the above fields are
|
||||
// not regularly polled. Wake Atomics.wait() and irregexp JIT code.
|
||||
interruptRegExpJit_ = true;
|
||||
// not regularly polled.
|
||||
FutexThread::lock();
|
||||
if (fx.isWaiting())
|
||||
fx.wake(FutexThread::WakeForJSInterrupt);
|
||||
|
@ -524,11 +527,13 @@ bool
|
|||
JSContext::handleInterrupt()
|
||||
{
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime()));
|
||||
if (interrupt_ || jitStackLimit == UINTPTR_MAX) {
|
||||
interrupt_ = false;
|
||||
interruptRegExpJit_ = false;
|
||||
if (hasAnyPendingInterrupt() || jitStackLimit == UINTPTR_MAX) {
|
||||
bool invokeCallback =
|
||||
hasPendingInterrupt(InterruptReason::CallbackUrgent) ||
|
||||
hasPendingInterrupt(InterruptReason::CallbackCanWait);
|
||||
interruptBits_ = 0;
|
||||
resetJitStackLimit();
|
||||
return InvokeInterruptCallback(this);
|
||||
return HandleInterrupt(this, invokeCallback);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -484,10 +484,7 @@ class BaseShape : public gc::TenuredCell
|
|||
QUALIFIED_VAROBJ = 0x2000,
|
||||
|
||||
// 0x4000 is unused.
|
||||
|
||||
// For a function used as an interpreted constructor, whether a 'new'
|
||||
// type had constructor information cleared.
|
||||
NEW_SCRIPT_CLEARED = 0x8000,
|
||||
// 0x8000 is unused.
|
||||
|
||||
OBJECT_FLAG_MASK = 0xfff8
|
||||
};
|
||||
|
|
|
@ -3071,9 +3071,7 @@ ObjectGroup::clearNewScript(JSContext* cx, ObjectGroup* replacement /* = nullptr
|
|||
|
||||
// Mark the constructing function as having its 'new' script cleared, so we
|
||||
// will not try to construct another one later.
|
||||
RootedFunction fun(cx, newScript->function());
|
||||
if (!NativeObject::setNewScriptCleared(cx, fun))
|
||||
cx->recoverFromOutOfMemory();
|
||||
newScript->function()->setNewScriptCleared();
|
||||
}
|
||||
|
||||
detachNewScript(/* writeBarrier = */ true, replacement);
|
||||
|
|
|
@ -3835,6 +3835,10 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
// constant pool entries.
|
||||
masm.flush();
|
||||
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
|
||||
// Prevent nop sequences to appear in the jump table.
|
||||
AutoForbidNops afn(&masm);
|
||||
#endif
|
||||
masm.bind(theTable);
|
||||
|
||||
for (uint32_t i = 0; i < labels.length(); i++) {
|
||||
|
|
|
@ -2084,7 +2084,7 @@ nsImageFrame::HandleEvent(nsPresContext* aPresContext,
|
|||
clicked = true;
|
||||
}
|
||||
nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
|
||||
clicked, true, true);
|
||||
clicked, /* isTrusted */ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,12 +220,13 @@ interface nsIAsyncFileMetadata : nsIFileMetadata
|
|||
* @param aEventTarget The event target where aCallback will be executed.
|
||||
* If aCallback is passed, aEventTarget cannot be null.
|
||||
*/
|
||||
void asyncWait(in nsIFileMetadataCallback aCallback,
|
||||
in nsIEventTarget aEventTarget);
|
||||
void asyncFileMetadataWait(in nsIFileMetadataCallback aCallback,
|
||||
in nsIEventTarget aEventTarget);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a companion interface for nsIAsyncFileMetadata::asyncWait.
|
||||
* This is a companion interface for
|
||||
* nsIAsyncFileMetadata::asyncFileMetadataWait.
|
||||
*/
|
||||
[function, scriptable, uuid(d01c7ead-7ba3-4726-b399-618ec8ec7057)]
|
||||
interface nsIFileMetadataCallback : nsISupports
|
||||
|
|
|
@ -1163,4 +1163,4 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
|
|||
|
||||
static const int32_t kUnknownId = -1;
|
||||
|
||||
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1533550046602000);
|
||||
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1533809451638000);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
/*****************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
const PRTime gPreloadListExpirationTime = INT64_C(1535969034685000);
|
||||
const PRTime gPreloadListExpirationTime = INT64_C(1536228415037000);
|
||||
%%
|
||||
0-1.party, 1
|
||||
0.me.uk, 1
|
||||
|
|
|
@ -13,25 +13,18 @@
|
|||
const SCOPE = '../resources/basic.html';
|
||||
const BODY_METHODS = ['arrayBuffer', 'blob', 'formData', 'json', 'text'];
|
||||
|
||||
async function cleanup() {
|
||||
for (const iframe of document.querySelectorAll('.test-iframe')) {
|
||||
iframe.parentNode.removeChild(iframe);
|
||||
}
|
||||
|
||||
const reg = await navigator.serviceWorker.getRegistration(SCOPE);
|
||||
if (reg) await reg.unregister();
|
||||
}
|
||||
|
||||
async function setupRegistration(t) {
|
||||
await cleanup();
|
||||
const reg = await navigator.serviceWorker.register('../resources/sw-intercept.js', { scope: SCOPE });
|
||||
async function setupRegistration(t, scope) {
|
||||
const reg = await navigator.serviceWorker.register('../resources/sw-intercept.js', { scope });
|
||||
await wait_for_state(t, reg.installing, 'activated');
|
||||
add_completion_callback(_ => reg.unregister());
|
||||
return reg;
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
await setupRegistration(t);
|
||||
const iframe = await with_iframe(SCOPE);
|
||||
const scope = SCOPE + "?q=aborted-not-intercepted";
|
||||
await setupRegistration(t, scope);
|
||||
const iframe = await with_iframe(scope);
|
||||
add_completion_callback(_ => iframe.remove());
|
||||
const w = iframe.contentWindow;
|
||||
|
||||
const controller = new w.AbortController();
|
||||
|
@ -56,8 +49,10 @@
|
|||
|
||||
for (const bodyMethod of BODY_METHODS) {
|
||||
promise_test(async t => {
|
||||
await setupRegistration(t);
|
||||
const iframe = await with_iframe(SCOPE);
|
||||
const scope = SCOPE + "?q=aborted-" + bodyMethod + "-rejects";
|
||||
await setupRegistration(t, scope);
|
||||
const iframe = await with_iframe(scope);
|
||||
add_completion_callback(_ => iframe.remove());
|
||||
const w = iframe.contentWindow;
|
||||
|
||||
const controller = new w.AbortController();
|
||||
|
@ -82,8 +77,10 @@
|
|||
}
|
||||
|
||||
promise_test(async t => {
|
||||
await setupRegistration(t);
|
||||
const iframe = await with_iframe(SCOPE);
|
||||
const scope = SCOPE + "?q=aborted-stream-errors";
|
||||
await setupRegistration(t, scope);
|
||||
const iframe = await with_iframe(scope);
|
||||
add_completion_callback(_ => iframe.remove());
|
||||
const w = iframe.contentWindow;
|
||||
|
||||
const controller = new w.AbortController();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 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/.import json
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import json
|
||||
import mozunit
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 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/.import json
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import mozunit
|
||||
import sys
|
||||
|
|
Загрузка…
Ссылка в новой задаче