diff --git a/Makefile.in b/Makefile.in index 682af0080ef1..078ac34edf90 100644 --- a/Makefile.in +++ b/Makefile.in @@ -136,7 +136,7 @@ $(addprefix install-,$(filter dist/%,$(install_manifests))): install-dist/%: $(i install-dist_%: install-dist/% ; install-_tests: $(install_manifest_depends) - $(call py_action,process_install_manifest,$(if $(NO_REMOVE),--no-remove )_tests _build_manifests/install/tests) + $(call py_action,process_install_manifest,$(if $(NO_REMOVE),--no-remove )_tests _build_manifests/install/_tests) # For compatibility .PHONY: install-tests diff --git a/accessible/xpcom/Makefile.in b/accessible/xpcom/Makefile.in deleted file mode 100644 index 3b2ceab61504..000000000000 --- a/accessible/xpcom/Makefile.in +++ /dev/null @@ -1,10 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -# We'd like this to be defined in a future GENERATED_EXPORTS list. -# Bug 1160185 has a few proposals for this. -INSTALL_TARGETS += xpcaccevents -xpcaccevents_FILES := xpcAccEvents.h -xpcaccevents_DEST = $(DIST)/include -xpcaccevents_TARGET := export diff --git a/accessible/xpcom/moz.build b/accessible/xpcom/moz.build index 4bcc76998d21..6dd72d576543 100644 --- a/accessible/xpcom/moz.build +++ b/accessible/xpcom/moz.build @@ -24,6 +24,10 @@ SOURCES += [ '!xpcAccEvents.cpp', ] +EXPORTS += [ + '!xpcAccEvents.h', +] + LOCAL_INCLUDES += [ '/accessible/base', '/accessible/generic', diff --git a/browser/components/extensions/ext-tabs.js b/browser/components/extensions/ext-tabs.js index f3f856e2cda0..6ed5b7fc6f4d 100644 --- a/browser/components/extensions/ext-tabs.js +++ b/browser/components/extensions/ext-tabs.js @@ -109,7 +109,7 @@ global.currentWindow = function(context) { // TODO: activeTab permission -extensions.registerAPI((extension, context) => { +extensions.registerSchemaAPI("tabs", null, (extension, context) => { let self = { tabs: { onActivated: new WindowEventManager(context, "tabs.onActivated", "TabSelect", (fire, event) => { @@ -296,7 +296,7 @@ extensions.registerAPI((extension, context) => { } } - let window = "windowId" in createProperties ? + let window = createProperties.windowId !== null ? WindowManager.getWindow(createProperties.windowId) : WindowManager.topWindow; if (!window.gBrowser) { @@ -328,34 +328,27 @@ extensions.registerAPI((extension, context) => { } }, - update: function(...args) { - let tabId, updateProperties, callback; - if (args.length == 1) { - updateProperties = args[0]; - } else { - [tabId, updateProperties, callback] = args; - } - - let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab; + update: function(tabId, updateProperties, callback) { + let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab; let tabbrowser = tab.ownerDocument.defaultView.gBrowser; - if ("url" in updateProperties) { + if (updateProperties.url !== null) { tab.linkedBrowser.loadURI(updateProperties.url); } - if ("active" in updateProperties) { + if (updateProperties.active !== null) { if (updateProperties.active) { tabbrowser.selectedTab = tab; } else { // Not sure what to do here? Which tab should we select? } } - if ("pinned" in updateProperties) { + if (updateProperties.pinned !== null) { if (updateProperties.pinned) { tabbrowser.pinTab(tab); } else { tabbrowser.unpinTab(tab); } } - // FIXME: highlighted/selected, openerTabId + // FIXME: highlighted/selected, muted, openerTabId if (callback) { runSafe(context, callback, TabManager.convert(extension, tab)); @@ -363,7 +356,7 @@ extensions.registerAPI((extension, context) => { }, reload: function(tabId, reloadProperties, callback) { - let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab; + let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab; let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE; if (reloadProperties && reloadProperties.bypassCache) { flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE; @@ -388,51 +381,39 @@ extensions.registerAPI((extension, context) => { runSafe(context, callback, tab); }, - getAllInWindow: function(...args) { - let window, callback; - if (args.length == 1) { - callback = args[0]; - } else { - window = WindowManager.getWindow(args[0]); - callback = args[1]; + getAllInWindow: function(windowId, callback) { + if (windowId === null) { + windowId = WindowManager.topWindow.windowId; } - if (!window) { - window = WindowManager.topWindow; - } - - return self.tabs.query({windowId: WindowManager.getId(window)}, callback); + return self.tabs.query({windowId}, callback); }, query: function(queryInfo, callback) { - if (!queryInfo) { - queryInfo = {}; - } - let pattern = null; - if (queryInfo.url) { + if (queryInfo.url !== null) { pattern = new MatchPattern(queryInfo.url); } function matches(window, tab) { let props = ["active", "pinned", "highlighted", "status", "title", "index"]; for (let prop of props) { - if (prop in queryInfo && queryInfo[prop] != tab[prop]) { + if (queryInfo[prop] !== null && queryInfo[prop] != tab[prop]) { return false; } } let lastFocused = window == WindowManager.topWindow; - if ("lastFocusedWindow" in queryInfo && queryInfo.lastFocusedWindow != lastFocused) { + if (queryInfo.lastFocusedWindow !== null && queryInfo.lastFocusedWindow != lastFocused) { return false; } let windowType = WindowManager.windowType(window); - if ("windowType" in queryInfo && queryInfo.windowType != windowType) { + if (queryInfo.windowType !== null && queryInfo.windowType != windowType) { return false; } - if ("windowId" in queryInfo) { + if (queryInfo.windowId !== null) { if (queryInfo.windowId == WindowManager.WINDOW_ID_CURRENT) { if (currentWindow(context) != window) { return false; @@ -442,7 +423,7 @@ extensions.registerAPI((extension, context) => { } } - if ("currentWindow" in queryInfo) { + if (queryInfo.currentWindow !== null) { let eq = window == currentWindow(context); if (queryInfo.currentWindow != eq) { return false; @@ -469,7 +450,7 @@ extensions.registerAPI((extension, context) => { }, _execute: function(tabId, details, kind, callback) { - let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab; + let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab; let mm = tab.linkedBrowser.messageManager; let options = { @@ -494,10 +475,10 @@ extensions.registerAPI((extension, context) => { options.matchesHost = extension.whiteListedHosts.serialize(); } - if (details.code) { + if (details.code !== null) { options[kind + "Code"] = details.code; } - if (details.file) { + if (details.file !== null) { let url = context.uri.resolve(details.file); if (extension.isExtensionURL(url)) { // We should really set |lastError| here, and go straight to @@ -511,7 +492,7 @@ extensions.registerAPI((extension, context) => { if (details.matchAboutBlank) { options.match_about_blank = details.matchAboutBlank; } - if (details.runAt) { + if (details.runAt !== null) { options.run_at = details.runAt; } mm.sendAsyncMessage("Extension:Execute", @@ -520,29 +501,24 @@ extensions.registerAPI((extension, context) => { // TODO: Call the callback with the result (which is what???). }, - executeScript: function(...args) { - if (args.length == 1) { - self.tabs._execute(undefined, args[0], "js", undefined); - } else { - self.tabs._execute(args[0], args[1], "js", args[2]); - } + executeScript: function(tabId, details, callback) { + self.tabs._execute(tabId, details, 'js', callback); }, - insertCss: function(...args) { - if (args.length == 1) { - self.tabs._execute(undefined, args[0], "css", undefined); - } else { - self.tabs._execute(args[0], args[1], "css", args[2]); - } + insertCss: function(tabId, details, callback) { + self.tabs._execute(tabId, details, 'css', callback); }, connect: function(tabId, connectInfo) { let tab = TabManager.getTab(tabId); let mm = tab.linkedBrowser.messageManager; - let name = connectInfo.name || ""; + let name = ""; + if (connectInfo && connectInfo.name !== null) { + name = connectInfo.name; + } let recipient = {extensionId: extension.id}; - if ("frameId" in connectInfo) { + if (connectInfo && connectInfo.frameId !== null) { recipient.frameId = connectInfo.frameId; } return context.messenger.connect(mm, name, recipient); @@ -557,7 +533,7 @@ extensions.registerAPI((extension, context) => { let mm = tab.linkedBrowser.messageManager; let recipient = {extensionId: extension.id}; - if (options && "frameId" in options) { + if (options && options.frameId !== null) { recipient.frameId = options.frameId; } return context.messenger.sendMessage(mm, message, recipient, responseCallback); diff --git a/browser/components/extensions/ext-utils.js b/browser/components/extensions/ext-utils.js index 7e6de97a8749..4f66fba252c1 100644 --- a/browser/components/extensions/ext-utils.js +++ b/browser/components/extensions/ext-utils.js @@ -430,6 +430,7 @@ global.WindowManager = { _windows: new WeakMap(), _nextId: 0, + // Note: These must match the values in windows.json. WINDOW_ID_NONE: -1, WINDOW_ID_CURRENT: -2, diff --git a/browser/components/extensions/ext-windows.js b/browser/components/extensions/ext-windows.js index 38d258bbfaf7..15b5e7e89ad0 100644 --- a/browser/components/extensions/ext-windows.js +++ b/browser/components/extensions/ext-windows.js @@ -14,12 +14,9 @@ var { runSafe, } = ExtensionUtils; -extensions.registerAPI((extension, context) => { +extensions.registerSchemaAPI("windows", null, (extension, context) => { return { windows: { - WINDOW_ID_CURRENT: WindowManager.WINDOW_ID_CURRENT, - WINDOW_ID_NONE: WindowManager.WINDOW_ID_NONE, - onCreated: new WindowEventManager(context, "windows.onCreated", "domwindowopened", (fire, window) => { fire(WindowManager.convert(extension, window)); @@ -51,21 +48,11 @@ extensions.registerAPI((extension, context) => { }, getCurrent: function(getInfo, callback) { - if (!callback) { - callback = getInfo; - getInfo = {}; - } let window = currentWindow(context); runSafe(context, callback, WindowManager.convert(extension, window, getInfo)); }, - getLastFocused: function(...args) { - let getInfo, callback; - if (args.length == 1) { - callback = args[0]; - } else { - [getInfo, callback] = args; - } + getLastFocused: function(getInfo, callback) { let window = WindowManager.topWindow; runSafe(context, callback, WindowManager.convert(extension, window, getInfo)); }, @@ -90,7 +77,7 @@ extensions.registerAPI((extension, context) => { } let args = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray); - if ("url" in createData) { + if (createData.url !== null) { if (Array.isArray(createData.url)) { let array = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray); for (let url of createData.url) { @@ -105,7 +92,7 @@ extensions.registerAPI((extension, context) => { } let extraFeatures = ""; - if ("incognito" in createData) { + if (createData.incognito !== null) { if (createData.incognito) { extraFeatures += ",private"; } else { @@ -116,14 +103,14 @@ extensions.registerAPI((extension, context) => { let window = Services.ww.openWindow(null, "chrome://browser/content/browser.xul", "_blank", "chrome,dialog=no,all" + extraFeatures, args); - if ("left" in createData || "top" in createData) { - let left = "left" in createData ? createData.left : window.screenX; - let top = "top" in createData ? createData.top : window.screenY; + if (createData.left !== null || createData.top !== null) { + let left = createData.left !== null ? createData.left : window.screenX; + let top = createData.top !== null ? createData.top : window.screenY; window.moveTo(left, top); } - if ("width" in createData || "height" in createData) { - let width = "width" in createData ? createData.width : window.outerWidth; - let height = "height" in createData ? createData.height : window.outerHeight; + if (createData.width !== null || createData.height !== null) { + let width = createData.width !== null ? createData.width : window.outerWidth; + let height = createData.height !== null ? createData.height : window.outerHeight; window.resizeTo(width, height); } diff --git a/browser/components/extensions/moz.build b/browser/components/extensions/moz.build index df2aee900d1b..61ca062f73c6 100644 --- a/browser/components/extensions/moz.build +++ b/browser/components/extensions/moz.build @@ -6,4 +6,6 @@ JAR_MANIFESTS += ['jar.mn'] +DIRS += ['schemas'] + BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini'] diff --git a/browser/components/extensions/schemas/LICENSE b/browser/components/extensions/schemas/LICENSE new file mode 100644 index 000000000000..9314092fdc34 --- /dev/null +++ b/browser/components/extensions/schemas/LICENSE @@ -0,0 +1,27 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/browser/components/extensions/schemas/jar.mn b/browser/components/extensions/schemas/jar.mn new file mode 100644 index 000000000000..f379c8a093e3 --- /dev/null +++ b/browser/components/extensions/schemas/jar.mn @@ -0,0 +1,7 @@ +# 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/. + +browser.jar: + content/browser/schemas/tabs.json + content/browser/schemas/windows.json diff --git a/browser/components/extensions/schemas/moz.build b/browser/components/extensions/schemas/moz.build new file mode 100644 index 000000000000..3bbe6729759c --- /dev/null +++ b/browser/components/extensions/schemas/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +JAR_MANIFESTS += ['jar.mn'] diff --git a/browser/components/extensions/schemas/tabs.json b/browser/components/extensions/schemas/tabs.json new file mode 100644 index 000000000000..03a1c2080b00 --- /dev/null +++ b/browser/components/extensions/schemas/tabs.json @@ -0,0 +1,1190 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ + { + "namespace": "tabs", + "description": "Use the browser.tabs API to interact with the browser's tab system. You can use this API to create, modify, and rearrange tabs in the browser.", + "types": [ + { "id": "MutedInfoReason", + "type": "string", + "description": "An event that caused a muted state change.", + "enum": [ + {"name": "user", "description": "A user input action has set/overridden the muted state."}, + {"name": "capture", "description": "Tab capture started, forcing a muted state change."}, + {"name": "extension", "description": "An extension, identified by the extensionId field, set the muted state."} + ] + }, + { + "id": "MutedInfo", + "type": "object", + "description": "Tab muted state and the reason for the last state change.", + "properties": { + "muted": { + "type": "boolean", + "description": "Whether the tab is prevented from playing sound (but hasn't necessarily recently produced sound). Equivalent to whether the muted audio indicator is showing." + }, + "reason": { + "$ref": "MutedInfoReason", + "optional": true, + "description": "The reason the tab was muted or unmuted. Not set if the tab's mute state has never been changed." + }, + "extensionId": { + "type": "string", + "optional": true, + "description": "The ID of the extension that changed the muted state. Not set if an extension was not the reason the muted state last changed." + } + } + }, + { + "id": "Tab", + "type": "object", + "properties": { + "id": {"type": "integer", "minimum": -1, "optional": true, "description": "The ID of the tab. Tab IDs are unique within a browser session. Under some circumstances a Tab may not be assigned an ID, for example when querying foreign tabs using the $(ref:sessions) API, in which case a session ID may be present. Tab ID can also be set to $(ref:tabs.TAB_ID_NONE) for apps and devtools windows."}, + "index": {"type": "integer", "minimum": -1, "description": "The zero-based index of the tab within its window."}, + "windowId": {"type": "integer", "minimum": 0, "description": "The ID of the window the tab is contained within."}, + "openerTabId": {"unsupported": true, "type": "integer", "minimum": 0, "optional": true, "description": "The ID of the tab that opened this tab, if any. This property is only present if the opener tab still exists."}, + "selected": {"type": "boolean", "description": "Whether the tab is selected.", "deprecated": "Please use $(ref:tabs.Tab.highlighted).", "unsupported": true}, + "highlighted": {"type": "boolean", "description": "Whether the tab is highlighted."}, + "active": {"type": "boolean", "description": "Whether the tab is active in its window. (Does not necessarily mean the window is focused.)"}, + "pinned": {"type": "boolean", "description": "Whether the tab is pinned."}, + "audible": {"unsupported": true, "type": "boolean", "optional": true, "description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the speaker audio indicator is showing."}, + "mutedInfo": {"unsupported": true, "$ref": "MutedInfo", "optional": true, "description": "Current tab muted state and the reason for the last state change."}, + "url": {"type": "string", "optional": true, "description": "The URL the tab is displaying. This property is only present if the extension's manifest includes the \"tabs\" permission."}, + "title": {"type": "string", "optional": true, "description": "The title of the tab. This property is only present if the extension's manifest includes the \"tabs\" permission."}, + "favIconUrl": {"type": "string", "optional": true, "description": "The URL of the tab's favicon. This property is only present if the extension's manifest includes the \"tabs\" permission. It may also be an empty string if the tab is loading."}, + "status": {"type": "string", "optional": true, "description": "Either loading or complete."}, + "incognito": {"type": "boolean", "description": "Whether the tab is in an incognito window."}, + "width": {"type": "integer", "optional": true, "description": "The width of the tab in pixels."}, + "height": {"type": "integer", "optional": true, "description": "The height of the tab in pixels."}, + "sessionId": {"unsupported": true, "type": "string", "optional": true, "description": "The session ID used to uniquely identify a Tab obtained from the $(ref:sessions) API."} + } + }, + { + "id": "ZoomSettingsMode", + "type": "string", + "description": "Defines how zoom changes are handled, i.e. which entity is responsible for the actual scaling of the page; defaults to automatic.", + "enum": [ + { + "name": "automatic", + "description": "Zoom changes are handled automatically by the browser." + }, + { + "name": "manual", + "description": "Overrides the automatic handling of zoom changes. The onZoomChange event will still be dispatched, and it is the responsibility of the extension to listen for this event and manually scale the page. This mode does not support per-origin zooming, and will thus ignore the scope zoom setting and assume per-tab." + }, + { + "name": "disabled", + "description": "Disables all zooming in the tab. The tab will revert to the default zoom level, and all attempted zoom changes will be ignored." + } + ] + }, + { + "id": "ZoomSettingsScope", + "type": "string", + "description": "Defines whether zoom changes will persist for the page's origin, or only take effect in this tab; defaults to per-origin when in automatic mode, and per-tab otherwise.", + "enum": [ + { + "name": "per-origin", + "description": "Zoom changes will persist in the zoomed page's origin, i.e. all other tabs navigated to that same origin will be zoomed as well. Moreover, per-origin zoom changes are saved with the origin, meaning that when navigating to other pages in the same origin, they will all be zoomed to the same zoom factor. The per-origin scope is only available in the automatic mode." + }, + { + "name": "per-tab", + "description": "Zoom changes only take effect in this tab, and zoom changes in other tabs will not affect the zooming of this tab. Also, per-tab zoom changes are reset on navigation; navigating a tab will always load pages with their per-origin zoom factors." + } + ] + }, + { + "id": "ZoomSettings", + "type": "object", + "description": "Defines how zoom changes in a tab are handled and at what scope.", + "properties": { + "mode": { + "$ref": "ZoomSettingsMode", + "description": "Defines how zoom changes are handled, i.e. which entity is responsible for the actual scaling of the page; defaults to automatic.", + "optional": true + }, + "scope": { + "$ref": "ZoomSettingsScope", + "description": "Defines whether zoom changes will persist for the page's origin, or only take effect in this tab; defaults to per-origin when in automatic mode, and per-tab otherwise.", + "optional": true + }, + "defaultZoomFactor": { + "type": "number", + "optional": true, + "description": "Used to return the default zoom level for the current tab in calls to tabs.getZoomSettings." + } + } + }, + { + "id": "TabStatus", + "type": "string", + "enum": ["loading", "complete"], + "description": "Whether the tabs have completed loading." + }, + { + "id": "WindowType", + "type": "string", + "enum": ["normal", "popup", "panel", "app", "devtools"], + "description": "The type of window." + } + ], + "properties": { + "TAB_ID_NONE": { + "value": -1, + "description": "An ID which represents the absence of a browser tab." + } + }, + "functions": [ + { + "name": "get", + "type": "function", + "description": "Retrieves details about the specified tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0 + }, + { + "type": "function", + "name": "callback", + "parameters": [ + {"name": "tab", "$ref": "Tab"} + ] + } + ] + }, + { + "name": "getCurrent", + "type": "function", + "description": "Gets the tab that this script call is being made from. May be undefined if called from a non-tab context (for example: a background page or popup view).", + "parameters": [ + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "tab", + "$ref": "Tab", + "optional": true + } + ] + } + ] + }, + { + "name": "connect", + "type": "function", + "description": "Connects to the content script(s) in the specified tab. The $(ref:runtime.onConnect) event is fired in each content script running in the specified tab for the current extension. For more details, see $(topic:messaging)[Content Script Messaging].", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0 + }, + { + "type": "object", + "name": "connectInfo", + "properties": { + "name": { "type": "string", "optional": true, "description": "Will be passed into onConnect for content scripts that are listening for the connection event." }, + "frameId": { + "type": "integer", + "optional": true, + "minimum": 0, + "description": "Open a port to a specific $(topic:frame_ids)[frame] identified by frameId instead of all frames in the tab." + } + }, + "optional": true + } + ], + "returns": { + "$ref": "runtime.Port", + "description": "A port that can be used to communicate with the content scripts running in the specified tab. The port's $(ref:runtime.Port) event is fired if the tab closes or does not exist. " + } + }, + { + "name": "sendRequest", + "deprecated": "Please use $(ref:runtime.sendMessage).", + "unsupported": true, + "type": "function", + "description": "Sends a single request to the content script(s) in the specified tab, with an optional callback to run when a response is sent back. The $(ref:extension.onRequest) event is fired in each content script running in the specified tab for the current extension.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0 + }, + { + "type": "any", + "name": "request" + }, + { + "type": "function", + "name": "responseCallback", + "optional": true, + "parameters": [ + { + "name": "response", + "type": "any", + "description": "The JSON response object sent by the handler of the request. If an error occurs while connecting to the specified tab, the callback will be called with no arguments and $(ref:runtime.lastError) will be set to the error message." + } + ] + } + ] + }, + { + "name": "sendMessage", + "type": "function", + "description": "Sends a single message to the content script(s) in the specified tab, with an optional callback to run when a response is sent back. The $(ref:runtime.onMessage) event is fired in each content script running in the specified tab for the current extension.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0 + }, + { + "type": "any", + "name": "message" + }, + { + "type": "object", + "name": "options", + "properties": { + "frameId": { + "type": "integer", + "optional": true, + "minimum": 0, + "description": "Send a message to a specific $(topic:frame_ids)[frame] identified by frameId instead of all frames in the tab." + } + }, + "optional": true + }, + { + "type": "function", + "name": "responseCallback", + "optional": true, + "parameters": [ + { + "name": "response", + "type": "any", + "description": "The JSON response object sent by the handler of the message. If an error occurs while connecting to the specified tab, the callback will be called with no arguments and $(ref:runtime.lastError) will be set to the error message." + } + ] + } + ] + }, + { + "name": "getSelected", + "deprecated": "Please use $(ref:tabs.query) {active: true}.", + "unsupported": true, + "type": "function", + "description": "Gets the tab that is selected in the specified window.", + "parameters": [ + { + "type": "integer", + "name": "windowId", + "minimum": -2, + "optional": true, + "description": "Defaults to the $(topic:current-window)[current window]." + }, + { + "type": "function", + "name": "callback", + "parameters": [ + {"name": "tab", "$ref": "Tab"} + ] + } + ] + }, + { + "name": "getAllInWindow", + "type": "function", + "deprecated": "Please use $(ref:tabs.query) {windowId: windowId}.", + "description": "Gets details about all tabs in the specified window.", + "parameters": [ + { + "type": "integer", + "name": "windowId", + "minimum": -2, + "optional": true, + "description": "Defaults to the $(topic:current-window)[current window]." + }, + { + "type": "function", + "name": "callback", + "parameters": [ + {"name": "tabs", "type": "array", "items": { "$ref": "Tab" } } + ] + } + ] + }, + { + "name": "create", + "type": "function", + "description": "Creates a new tab.", + "parameters": [ + { + "type": "object", + "name": "createProperties", + "properties": { + "windowId": { + "type": "integer", + "minimum": -2, + "optional": true, + "description": "The window to create the new tab in. Defaults to the $(topic:current-window)[current window]." + }, + "index": { + "type": "integer", + "minimum": 0, + "optional": true, + "description": "The position the tab should take in the window. The provided value will be clamped to between zero and the number of tabs in the window." + }, + "url": { + "type": "string", + "optional": true, + "description": "The URL to navigate the tab to initially. Fully-qualified URLs must include a scheme (i.e. 'http://www.google.com', not 'www.google.com'). Relative URLs will be relative to the current page within the extension. Defaults to the New Tab Page." + }, + "active": { + "type": "boolean", + "optional": true, + "description": "Whether the tab should become the active tab in the window. Does not affect whether the window is focused (see $(ref:windows.update)). Defaults to true." + }, + "selected": { + "deprecated": "Please use active.", + "unsupported": true, + "type": "boolean", + "optional": true, + "description": "Whether the tab should become the selected tab in the window. Defaults to true" + }, + "pinned": { + "type": "boolean", + "optional": true, + "description": "Whether the tab should be pinned. Defaults to false" + }, + "openerTabId": { + "unsupported": true, + "type": "integer", + "minimum": 0, + "optional": true, + "description": "The ID of the tab that opened this tab. If specified, the opener tab must be in the same window as the newly created tab." + } + } + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [ + { + "name": "tab", + "$ref": "Tab", + "description": "Details about the created tab. Will contain the ID of the new tab." + } + ] + } + ] + }, + { + "name": "duplicate", + "type": "function", + "description": "Duplicates a tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0, + "description": "The ID of the tab which is to be duplicated." + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [ + { + "name": "tab", + "optional": true, + "description": "Details about the duplicated tab. The $(ref:tabs.Tab) object doesn't contain url, title and favIconUrl if the \"tabs\" permission has not been requested.", + "$ref": "Tab" + } + ] + } + ] + }, + { + "name": "query", + "type": "function", + "description": "Gets all tabs that have the specified properties, or all tabs if no properties are specified.", + "parameters": [ + { + "type": "object", + "name": "queryInfo", + "properties": { + "active": { + "type": "boolean", + "optional": true, + "description": "Whether the tabs are active in their windows." + }, + "pinned": { + "type": "boolean", + "optional": true, + "description": "Whether the tabs are pinned." + }, + "audible": { + "unsupported": true, + "type": "boolean", + "optional": true, + "description": "Whether the tabs are audible." + }, + "muted": { + "unsupported": true, + "type": "boolean", + "optional": true, + "description": "Whether the tabs are muted." + }, + "highlighted": { + "type": "boolean", + "optional": true, + "description": "Whether the tabs are highlighted." + }, + "currentWindow": { + "type": "boolean", + "optional": true, + "description": "Whether the tabs are in the $(topic:current-window)[current window]." + }, + "lastFocusedWindow": { + "type": "boolean", + "optional": true, + "description": "Whether the tabs are in the last focused window." + }, + "status": { + "$ref": "TabStatus", + "optional": true, + "description": "Whether the tabs have completed loading." + }, + "title": { + "type": "string", + "optional": true, + "description": "Match page titles against a pattern." + }, + "url": { + "choices": [ + {"type": "string"}, + {"type": "array", "items": {"type": "string"}} + ], + "optional": true, + "description": "Match tabs against one or more $(topic:match_patterns)[URL patterns]. Note that fragment identifiers are not matched." + }, + "windowId": { + "type": "integer", + "optional": true, + "minimum": -2, + "description": "The ID of the parent window, or $(ref:windows.WINDOW_ID_CURRENT) for the $(topic:current-window)[current window]." + }, + "windowType": { + "$ref": "WindowType", + "optional": true, + "description": "The type of window the tabs are in." + }, + "index": { + "type": "integer", + "optional": true, + "minimum": 0, + "description": "The position of the tabs within their windows." + } + } + }, + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "result", + "type": "array", + "items": { + "$ref": "Tab" + } + } + ] + } + ] + }, + { + "name": "highlight", + "type": "function", + "description": "Highlights the given tabs.", + "parameters": [ + { + "type": "object", + "name": "highlightInfo", + "properties": { + "windowId": { + "type": "integer", + "optional": true, + "description": "The window that contains the tabs.", + "minimum": -2 + }, + "tabs": { + "description": "One or more tab indices to highlight.", + "choices": [ + {"type": "array", "items": {"type": "integer", "minimum": 0}}, + {"type": "integer"} + ] + } + } + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [ + { + "name": "window", + "$ref": "windows.Window", + "description": "Contains details about the window whose tabs were highlighted." + } + ] + } + ] + }, + { + "name": "update", + "type": "function", + "description": "Modifies the properties of a tab. Properties that are not specified in updateProperties are not modified.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0, + "optional": true, + "description": "Defaults to the selected tab of the $(topic:current-window)[current window]." + }, + { + "type": "object", + "name": "updateProperties", + "properties": { + "url": { + "type": "string", + "optional": true, + "description": "A URL to navigate the tab to." + }, + "active": { + "type": "boolean", + "optional": true, + "description": "Whether the tab should be active. Does not affect whether the window is focused (see $(ref:windows.update))." + }, + "highlighted": { + "unsupported": true, + "type": "boolean", + "optional": true, + "description": "Adds or removes the tab from the current selection." + }, + "selected": { + "unsupported": true, + "deprecated": "Please use highlighted.", + "type": "boolean", + "optional": true, + "description": "Whether the tab should be selected." + }, + "pinned": { + "type": "boolean", + "optional": true, + "description": "Whether the tab should be pinned." + }, + "muted": { + "unsupported": true, + "type": "boolean", + "optional": true, + "description": "Whether the tab should be muted." + }, + "openerTabId": { + "unsupported": true, + "type": "integer", + "minimum": 0, + "optional": true, + "description": "The ID of the tab that opened this tab. If specified, the opener tab must be in the same window as this tab." + } + } + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [ + { + "name": "tab", + "$ref": "Tab", + "optional": true, + "description": "Details about the updated tab. The $(ref:tabs.Tab) object doesn't contain url, title and favIconUrl if the \"tabs\" permission has not been requested." + } + ] + } + ] + }, + { + "name": "move", + "type": "function", + "description": "Moves one or more tabs to a new position within its window, or to a new window. Note that tabs can only be moved to and from normal (window.type === \"normal\") windows.", + "parameters": [ + { + "name": "tabIds", + "description": "The tab or list of tabs to move.", + "choices": [ + {"type": "integer", "minimum": 0}, + {"type": "array", "items": {"type": "integer", "minimum": 0}} + ] + }, + { + "type": "object", + "name": "moveProperties", + "properties": { + "windowId": { + "type": "integer", + "minimum": -2, + "optional": true, + "description": "Defaults to the window the tab is currently in." + }, + "index": { + "type": "integer", + "minimum": -1, + "description": "The position to move the window to. -1 will place the tab at the end of the window." + } + } + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [ + { + "name": "tabs", + "description": "Details about the moved tabs.", + "choices": [ + {"$ref": "Tab"}, + {"type": "array", "items": {"$ref": "Tab"}} + ] + } + ] + } + ] + }, + { + "name": "reload", + "type": "function", + "description": "Reload a tab.", + "parameters": [ + {"type": "integer", "name": "tabId", "minimum": 0, "optional": true, "description": "The ID of the tab to reload; defaults to the selected tab of the current window."}, + { + "type": "object", + "name": "reloadProperties", + "optional": true, + "properties": { + "bypassCache": { + "type": "boolean", + "optional": true, + "description": "Whether using any local cache. Default is false." + } + } + }, + {"type": "function", "name": "callback", "optional": true, "parameters": []} + ] + }, + { + "name": "remove", + "type": "function", + "description": "Closes one or more tabs.", + "parameters": [ + { + "name": "tabIds", + "description": "The tab or list of tabs to close.", + "choices": [ + {"type": "integer", "minimum": 0}, + {"type": "array", "items": {"type": "integer", "minimum": 0}} + ] + }, + {"type": "function", "name": "callback", "optional": true, "parameters": []} + ] + }, + { + "name": "detectLanguage", + "type": "function", + "description": "Detects the primary language of the content in a tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0, + "optional": true, + "description": "Defaults to the active tab of the $(topic:current-window)[current window]." + }, + { + "type": "function", + "name": "callback", + "parameters": [ + { + "type": "string", + "name": "language", + "description": "An ISO language code such as en or fr. For a complete list of languages supported by this method, see kLanguageInfoTable. The 2nd to 4th columns will be checked and the first non-NULL value will be returned except for Simplified Chinese for which zh-CN will be returned. For an unknown language, und will be returned." + } + ] + } + ] + }, + { + "name": "captureVisibleTab", + "type": "function", + "description": "Captures the visible area of the currently active tab in the specified window. You must have $(topic:declare_permissions)[<all_urls>] permission to use this method.", + "parameters": [ + { + "type": "integer", + "name": "windowId", + "minimum": -2, + "optional": true, + "description": "The target window. Defaults to the $(topic:current-window)[current window]." + }, + { + "$ref": "extensionTypes.ImageDetails", + "name": "options", + "optional": true + }, + { + "type": "function", "name": "callback", "parameters": [ + {"type": "string", "name": "dataUrl", "description": "A data URL which encodes an image of the visible area of the captured tab. May be assigned to the 'src' property of an HTML Image element for display."} + ] + } + ] + }, + { + "name": "executeScript", + "type": "function", + "description": "Injects JavaScript code into a page. For details, see the $(topic:content_scripts)[programmatic injection] section of the content scripts doc.", + "parameters": [ + {"type": "integer", "name": "tabId", "minimum": 0, "optional": true, "description": "The ID of the tab in which to run the script; defaults to the active tab of the current window."}, + { + "$ref": "extensionTypes.InjectDetails", + "name": "details", + "description": "Details of the script to run." + }, + { + "type": "function", + "name": "callback", + "optional": true, + "description": "Called after all the JavaScript has been executed.", + "parameters": [ + { + "name": "result", + "optional": true, + "type": "array", + "items": {"type": "any", "minimum": 0}, + "description": "The result of the script in every injected frame." + } + ] + } + ] + }, + { + "name": "insertCSS", + "type": "function", + "description": "Injects CSS into a page. For details, see the $(topic:content_scripts)[programmatic injection] section of the content scripts doc.", + "parameters": [ + {"type": "integer", "name": "tabId", "minimum": 0, "optional": true, "description": "The ID of the tab in which to insert the CSS; defaults to the active tab of the current window."}, + { + "$ref": "extensionTypes.InjectDetails", + "name": "details", + "description": "Details of the CSS text to insert." + }, + { + "type": "function", + "name": "callback", + "optional": true, + "description": "Called when all the CSS has been inserted.", + "parameters": [] + } + ] + }, + { + "name": "setZoom", + "type": "function", + "description": "Zooms a specified tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0, + "optional": true, + "description": "The ID of the tab to zoom; defaults to the active tab of the current window." + }, + { + "type": "number", + "name": "zoomFactor", + "description": "The new zoom factor. Use a value of 0 here to set the tab to its current default zoom factor. Values greater than zero specify a (possibly non-default) zoom factor for the tab." + }, + { + "type": "function", + "name": "callback", + "optional": true, + "description": "Called after the zoom factor has been changed.", + "parameters": [] + } + ] + }, + { + "name": "getZoom", + "type": "function", + "description": "Gets the current zoom factor of a specified tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0, + "optional": true, + "description": "The ID of the tab to get the current zoom factor from; defaults to the active tab of the current window." + }, + { + "type": "function", + "name": "callback", + "description": "Called with the tab's current zoom factor after it has been fetched.", + "parameters": [ + { + "type": "number", + "name": "zoomFactor", + "description": "The tab's current zoom factor." + } + ] + } + ] + }, + { + "name": "setZoomSettings", + "type": "function", + "description": "Sets the zoom settings for a specified tab, which define how zoom changes are handled. These settings are reset to defaults upon navigating the tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "optional": true, + "minimum": 0, + "description": "The ID of the tab to change the zoom settings for; defaults to the active tab of the current window." + }, + { + "$ref": "ZoomSettings", + "name": "zoomSettings", + "description": "Defines how zoom changes are handled and at what scope." + }, + { + "type": "function", + "name": "callback", + "optional": true, + "description": "Called after the zoom settings have been changed.", + "parameters": [] + } + ] + }, + { + "name": "getZoomSettings", + "type": "function", + "description": "Gets the current zoom settings of a specified tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "optional": true, + "minimum": 0, + "description": "The ID of the tab to get the current zoom settings from; defaults to the active tab of the current window." + }, + { + "type": "function", + "name": "callback", + "description": "Called with the tab's current zoom settings.", + "parameters": [ + { + "$ref": "ZoomSettings", + "name": "zoomSettings", + "description": "The tab's current zoom settings." + } + ] + } + ] + } + ], + "events": [ + { + "name": "onCreated", + "type": "function", + "description": "Fired when a tab is created. Note that the tab's URL may not be set at the time this event fired, but you can listen to onUpdated events to be notified when a URL is set.", + "parameters": [ + { + "$ref": "Tab", + "name": "tab", + "description": "Details of the tab that was created." + } + ] + }, + { + "name": "onUpdated", + "type": "function", + "description": "Fired when a tab is updated.", + "parameters": [ + {"type": "integer", "name": "tabId", "minimum": 0}, + { + "type": "object", + "name": "changeInfo", + "description": "Lists the changes to the state of the tab that was updated.", + "properties": { + "status": { + "type": "string", + "optional": true, + "description": "The status of the tab. Can be either loading or complete." + }, + "url": { + "type": "string", + "optional": true, + "description": "The tab's URL if it has changed." + }, + "pinned": { + "type": "boolean", + "optional": true, + "description": "The tab's new pinned state." + }, + "audible": { + "unsupported": true, + "type": "boolean", + "optional": true, + "description": "The tab's new audible state." + }, + "mutedInfo": { + "unsupported": true, + "$ref": "MutedInfo", + "optional": true, + "description": "The tab's new muted state and the reason for the change." + }, + "favIconUrl": { + "type": "string", + "optional": true, + "description": "The tab's new favicon URL." + } + } + }, + { + "$ref": "Tab", + "name": "tab", + "description": "Gives the state of the tab that was updated." + } + ] + }, + { + "name": "onMoved", + "type": "function", + "description": "Fired when a tab is moved within a window. Only one move event is fired, representing the tab the user directly moved. Move events are not fired for the other tabs that must move in response. This event is not fired when a tab is moved between windows. For that, see $(ref:tabs.onDetached).", + "parameters": [ + {"type": "integer", "name": "tabId", "minimum": 0}, + { + "type": "object", + "name": "moveInfo", + "properties": { + "windowId": {"type": "integer", "minimum": 0}, + "fromIndex": {"type": "integer", "minimum": 0}, + "toIndex": {"type": "integer", "minimum": 0} + } + } + ] + }, + { + "name": "onSelectionChanged", + "deprecated": "Please use $(ref:tabs.onActivated).", + "unsupported": true, + "type": "function", + "description": "Fires when the selected tab in a window changes.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0, + "description": "The ID of the tab that has become active." + }, + { + "type": "object", + "name": "selectInfo", + "properties": { + "windowId": { + "type": "integer", + "minimum": 0, + "description": "The ID of the window the selected tab changed inside of." + } + } + } + ] + }, + { + "name": "onActiveChanged", + "deprecated": "Please use $(ref:tabs.onActivated).", + "unsupported": true, + "type": "function", + "description": "Fires when the selected tab in a window changes. Note that the tab's URL may not be set at the time this event fired, but you can listen to $(ref:tabs.onUpdated) events to be notified when a URL is set.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0, + "description": "The ID of the tab that has become active." + }, + { + "type": "object", + "name": "selectInfo", + "properties": { + "windowId": { + "type": "integer", + "minimum": 0, + "description": "The ID of the window the selected tab changed inside of." + } + } + } + ] + }, + { + "name": "onActivated", + "type": "function", + "description": "Fires when the active tab in a window changes. Note that the tab's URL may not be set at the time this event fired, but you can listen to onUpdated events to be notified when a URL is set.", + "parameters": [ + { + "type": "object", + "name": "activeInfo", + "properties": { + "tabId": { + "type": "integer", + "minimum": 0, + "description": "The ID of the tab that has become active." + }, + "windowId": { + "type": "integer", + "minimum": 0, + "description": "The ID of the window the active tab changed inside of." + } + } + } + ] + }, + { + "name": "onHighlightChanged", + "deprecated": "Please use $(ref:tabs.onHighlighted).", + "unsupported": true, + "type": "function", + "description": "Fired when the highlighted or selected tabs in a window changes.", + "parameters": [ + { + "type": "object", + "name": "selectInfo", + "properties": { + "windowId": { + "type": "integer", + "minimum": 0, + "description": "The window whose tabs changed." + }, + "tabIds": { + "type": "array", + "items": {"type": "integer", "minimum": 0}, + "description": "All highlighted tabs in the window." + } + } + } + ] + }, + { + "name": "onHighlighted", + "type": "function", + "description": "Fired when the highlighted or selected tabs in a window changes.", + "parameters": [ + { + "type": "object", + "name": "highlightInfo", + "properties": { + "windowId": { + "type": "integer", + "minimum": 0, + "description": "The window whose tabs changed." + }, + "tabIds": { + "type": "array", + "items": {"type": "integer", "minimum": 0}, + "description": "All highlighted tabs in the window." + } + } + } + ] + }, + { + "name": "onDetached", + "type": "function", + "description": "Fired when a tab is detached from a window, for example because it is being moved between windows.", + "parameters": [ + {"type": "integer", "name": "tabId", "minimum": 0}, + { + "type": "object", + "name": "detachInfo", + "properties": { + "oldWindowId": {"type": "integer", "minimum": 0}, + "oldPosition": {"type": "integer", "minimum": 0} + } + } + ] + }, + { + "name": "onAttached", + "type": "function", + "description": "Fired when a tab is attached to a window, for example because it was moved between windows.", + "parameters": [ + {"type": "integer", "name": "tabId", "minimum": 0}, + { + "type": "object", + "name": "attachInfo", + "properties": { + "newWindowId": {"type": "integer", "minimum": 0}, + "newPosition": {"type": "integer", "minimum": 0} + } + } + ] + }, + { + "name": "onRemoved", + "type": "function", + "description": "Fired when a tab is closed.", + "parameters": [ + {"type": "integer", "name": "tabId", "minimum": 0}, + { + "type": "object", + "name": "removeInfo", + "properties": { + "windowId": {"type": "integer", "minimum": 0, "description": "The window whose tab is closed." }, + "isWindowClosing": {"type": "boolean", "description": "True when the tab is being closed because its window is being closed." } + } + } + ] + }, + { + "name": "onReplaced", + "type": "function", + "description": "Fired when a tab is replaced with another tab due to prerendering or instant.", + "parameters": [ + {"type": "integer", "name": "addedTabId", "minimum": 0}, + {"type": "integer", "name": "removedTabId", "minimum": 0} + ] + }, + { + "name": "onZoomChange", + "type": "function", + "description": "Fired when a tab is zoomed.", + "parameters": [{ + "type": "object", + "name": "ZoomChangeInfo", + "properties": { + "tabId": {"type": "integer", "minimum": 0}, + "oldZoomFactor": {"type": "number"}, + "newZoomFactor": {"type": "number"}, + "zoomSettings": {"$ref": "ZoomSettings"} + } + }] + } + ] + } +] diff --git a/browser/components/extensions/schemas/windows.json b/browser/components/extensions/schemas/windows.json new file mode 100644 index 000000000000..258ec1f338a1 --- /dev/null +++ b/browser/components/extensions/schemas/windows.json @@ -0,0 +1,324 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ + { + "namespace": "windows", + "description": "Use the browser.windows API to interact with browser windows. You can use this API to create, modify, and rearrange windows in the browser.", + "types": [ + { + "id": "WindowType", + "type": "string", + "description": "The type of browser window this is. Under some circumstances a Window may not be assigned type property, for example when querying closed windows from the $(ref:sessions) API.", + "enum": ["normal", "popup", "panel", "app", "devtools"] + }, + { + "id": "WindowState", + "type": "string", + "description": "The state of this browser window. Under some circumstances a Window may not be assigned state property, for example when querying closed windows from the $(ref:sessions) API.", + "enum": ["normal", "minimized", "maximized", "fullscreen", "docked"] + }, + { + "id": "Window", + "type": "object", + "properties": { + "id": {"type": "integer", "optional": true, "minimum": 0, "description": "The ID of the window. Window IDs are unique within a browser session. Under some circumstances a Window may not be assigned an ID, for example when querying windows using the $(ref:sessions) API, in which case a session ID may be present."}, + "focused": {"type": "boolean", "description": "Whether the window is currently the focused window."}, + "top": {"type": "integer", "optional": true, "description": "The offset of the window from the top edge of the screen in pixels. Under some circumstances a Window may not be assigned top property, for example when querying closed windows from the $(ref:sessions) API."}, + "left": {"type": "integer", "optional": true, "description": "The offset of the window from the left edge of the screen in pixels. Under some circumstances a Window may not be assigned left property, for example when querying closed windows from the $(ref:sessions) API."}, + "width": {"type": "integer", "optional": true, "description": "The width of the window, including the frame, in pixels. Under some circumstances a Window may not be assigned width property, for example when querying closed windows from the $(ref:sessions) API."}, + "height": {"type": "integer", "optional": true, "description": "The height of the window, including the frame, in pixels. Under some circumstances a Window may not be assigned height property, for example when querying closed windows from the $(ref:sessions) API."}, + "tabs": {"type": "array", "items": { "$ref": "tabs.Tab" }, "optional": true, "description": "Array of $(ref:tabs.Tab) objects representing the current tabs in the window."}, + "incognito": {"type": "boolean", "description": "Whether the window is incognito."}, + "type": { + "$ref": "WindowType", + "optional": true, + "description": "The type of browser window this is." + }, + "state": { + "$ref": "WindowState", + "optional": true, + "description": "The state of this browser window." + }, + "alwaysOnTop": {"unsupported": true, "type": "boolean", "description": "Whether the window is set to be always on top."}, + "sessionId": {"unsupported": true, "type": "string", "optional": true, "description": "The session ID used to uniquely identify a Window obtained from the $(ref:sessions) API."} + } + }, + { + "id": "CreateType", + "type": "string", + "description": "Specifies what type of browser window to create. The 'panel' and 'detached_panel' types create a popup unless the '--enable-panels' flag is set.", + "enum": ["normal", "popup", "panel", "detached_panel"] + } + ], + "properties": { + "WINDOW_ID_NONE": { + "value": -1, + "description": "The windowId value that represents the absence of a browser window." + }, + "WINDOW_ID_CURRENT": { + "value": -2, + "description": "The windowId value that represents the $(topic:current-window)[current window]." + } + }, + "functions": [ + { + "name": "get", + "type": "function", + "description": "Gets details about a window.", + "parameters": [ + {"type": "integer", "name": "windowId", "minimum": -2}, + { + "type": "object", + "name": "getInfo", + "optional": true, + "description": "", + "properties": { + "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a tabs property that contains a list of the $(ref:tabs.Tab) objects. The Tab objects only contain the url, title and favIconUrl properties if the extension's manifest file includes the \"tabs\" permission." }, + "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to ['app', 'normal', 'panel', 'popup'], with 'app' and 'panel' window types limited to the extension's own windows." } + } + }, + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "window", "$ref": "Window" + } + ] + } + ] + }, + { + "name": "getCurrent", + "type": "function", + "description": "Gets the $(topic:current-window)[current window].", + "parameters": [ + { + "type": "object", + "name": "getInfo", + "optional": true, + "description": "", + "properties": { + "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a tabs property that contains a list of the $(ref:tabs.Tab) objects. The Tab objects only contain the url, title and favIconUrl properties if the extension's manifest file includes the \"tabs\" permission." }, + "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to ['app', 'normal', 'panel', 'popup'], with 'app' and 'panel' window types limited to the extension's own windows." } + } + }, + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "window", "$ref": "Window" + } + ] + } + ] + }, + { + "name": "getLastFocused", + "type": "function", + "description": "Gets the window that was most recently focused — typically the window 'on top'.", + "parameters": [ + { + "type": "object", + "name": "getInfo", + "optional": true, + "description": "", + "properties": { + "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a tabs property that contains a list of the $(ref:tabs.Tab) objects. The Tab objects only contain the url, title and favIconUrl properties if the extension's manifest file includes the \"tabs\" permission." }, + "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to ['app', 'normal', 'panel', 'popup'], with 'app' and 'panel' window types limited to the extension's own windows." } + } + }, + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "window", "$ref": "Window" + } + ] + } + ] + }, + { + "name": "getAll", + "type": "function", + "description": "Gets all windows.", + "parameters": [ + { + "type": "object", + "name": "getInfo", + "optional": true, + "description": "", + "properties": { + "populate": {"type": "boolean", "optional": true, "description": "If true, each $(ref:windows.Window) object will have a tabs property that contains a list of the $(ref:tabs.Tab) objects for that window. The Tab objects only contain the url, title and favIconUrl properties if the extension's manifest file includes the \"tabs\" permission." }, + "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to ['app', 'normal', 'panel', 'popup'], with 'app' and 'panel' window types limited to the extension's own windows." } + } + }, + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "windows", "type": "array", "items": { "$ref": "Window" } + } + ] + } + ] + }, + { + "name": "create", + "type": "function", + "description": "Creates (opens) a new browser with any optional sizing, position or default URL provided.", + "parameters": [ + { + "type": "object", + "name": "createData", + "properties": { + "url": { + "description": "A URL or array of URLs to open as tabs in the window. Fully-qualified URLs must include a scheme (i.e. 'http://www.google.com', not 'www.google.com'). Relative URLs will be relative to the current page within the extension. Defaults to the New Tab Page.", + "optional": true, + "choices": [ + {"type": "string"}, + {"type": "array", "items": {"type": "string"}} + ] + }, + "tabId": {"type": "integer", "minimum": 0, "optional": true, "description": "The id of the tab for which you want to adopt to the new window."}, + "left": {"type": "integer", "optional": true, "description": "The number of pixels to position the new window from the left edge of the screen. If not specified, the new window is offset naturally from the last focused window. This value is ignored for panels."}, + "top": {"type": "integer", "optional": true, "description": "The number of pixels to position the new window from the top edge of the screen. If not specified, the new window is offset naturally from the last focused window. This value is ignored for panels."}, + "width": {"type": "integer", "minimum": 0, "optional": true, "description": "The width in pixels of the new window, including the frame. If not specified defaults to a natural width."}, + "height": {"type": "integer", "minimum": 0, "optional": true, "description": "The height in pixels of the new window, including the frame. If not specified defaults to a natural height."}, + "focused": {"unsupported": true, "type": "boolean", "optional": true, "description": "If true, opens an active window. If false, opens an inactive window."}, + "incognito": {"type": "boolean", "optional": true, "description": "Whether the new window should be an incognito window."}, + "type": { + "unsupported": true, + "$ref": "CreateType", + "optional": true, + "description": "Specifies what type of browser window to create. The 'panel' and 'detached_panel' types create a popup unless the '--enable-panels' flag is set." + }, + "state": { + "unsupported": true, + "$ref": "WindowState", + "optional": true, + "description": "The initial state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'." + } + }, + "optional": true + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [ + { + "name": "window", "$ref": "Window", "description": "Contains details about the created window.", + "optional": true + } + ] + } + ] + }, + { + "name": "update", + "type": "function", + "description": "Updates the properties of a window. Specify only the properties that you want to change; unspecified properties will be left unchanged.", + "parameters": [ + {"type": "integer", "name": "windowId", "minimum": -2}, + { + "type": "object", + "name": "updateInfo", + "properties": { + "left": {"unsupported": true, "type": "integer", "optional": true, "description": "The offset from the left edge of the screen to move the window to in pixels. This value is ignored for panels."}, + "top": {"unsupported": true, "type": "integer", "optional": true, "description": "The offset from the top edge of the screen to move the window to in pixels. This value is ignored for panels."}, + "width": {"unsupported": true, "type": "integer", "minimum": 0, "optional": true, "description": "The width to resize the window to in pixels. This value is ignored for panels."}, + "height": {"unsupported": true, "type": "integer", "minimum": 0, "optional": true, "description": "The height to resize the window to in pixels. This value is ignored for panels."}, + "focused": {"type": "boolean", "optional": true, "description": "If true, brings the window to the front. If false, brings the next window in the z-order to the front."}, + "drawAttention": {"unsupported": true, "type": "boolean", "optional": true, "description": "If true, causes the window to be displayed in a manner that draws the user's attention to the window, without changing the focused window. The effect lasts until the user changes focus to the window. This option has no effect if the window already has focus. Set to false to cancel a previous draw attention request."}, + "state": { + "unsupported": true, + "$ref": "WindowState", + "optional": true, + "description": "The new state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'." + } + } + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [ + { + "name": "window", "$ref": "Window" + } + ] + } + ] + }, + { + "name": "remove", + "type": "function", + "description": "Removes (closes) a window, and all the tabs inside it.", + "parameters": [ + {"type": "integer", "name": "windowId", "minimum": 0}, + {"type": "function", "name": "callback", "optional": true, "parameters": []} + ] + } + ], + "events": [ + { + "name": "onCreated", + "type": "function", + "description": "Fired when a window is created.", + "filters": [ + { + "name": "windowTypes", + "type": "array", + "items": { "$ref": "WindowType" }, + "description": "Conditions that the window's type being created must satisfy. By default it will satisfy ['app', 'normal', 'panel', 'popup'], with 'app' and 'panel' window types limited to the extension's own windows." + } + ], + "parameters": [ + { + "$ref": "Window", + "name": "window", + "description": "Details of the window that was created." + } + ] + }, + { + "name": "onRemoved", + "type": "function", + "description": "Fired when a window is removed (closed).", + "filters": [ + { + "name": "windowTypes", + "type": "array", + "items": { "$ref": "WindowType" }, + "description": "Conditions that the window's type being removed must satisfy. By default it will satisfy ['app', 'normal', 'panel', 'popup'], with 'app' and 'panel' window types limited to the extension's own windows." + } + ], + "parameters": [ + {"type": "integer", "name": "windowId", "minimum": 0, "description": "ID of the removed window."} + ] + }, + { + "name": "onFocusChanged", + "type": "function", + "description": "Fired when the currently focused window changes. Will be $(ref:windows.WINDOW_ID_NONE) if all browser windows have lost focus. Note: On some Linux window managers, WINDOW_ID_NONE will always be sent immediately preceding a switch from one browser window to another.", + "filters": [ + { + "name": "windowTypes", + "type": "array", + "items": { "$ref": "WindowType" }, + "description": "Conditions that the window's type being removed must satisfy. By default it will satisfy ['app', 'normal', 'panel', 'popup'], with 'app' and 'panel' window types limited to the extension's own windows." + } + ], + "parameters": [ + {"type": "integer", "name": "windowId", "minimum": -1, "description": "ID of the newly focused window."} + ] + } + ] + } +] diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js index 006f4f772368..9bc037cc38c4 100644 --- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js @@ -14,7 +14,7 @@ function* testHasNoPermission(params) { browser.test.onMessage.addListener(msg => { browser.test.assertEq(msg, "execute-script"); - browser.tabs.query({ activeWindow: true }, tabs => { + browser.tabs.query({ currentWindow: true }, tabs => { browser.tabs.executeScript({ file: "script.js", }); diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js b/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js index adc3665e0660..ce05577ffd0b 100644 --- a/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js @@ -107,7 +107,7 @@ function* do_test_update(background) { add_task(function* test_pinned() { yield do_test_update(function background() { // Create a new tab for testing update. - browser.tabs.create(null, function(tab) { + browser.tabs.create({}, function(tab) { browser.tabs.onUpdated.addListener(function onUpdated(tabId, changeInfo) { // Check callback browser.test.assertEq(tabId, tab.id, "Check tab id"); @@ -151,7 +151,7 @@ add_task(function* test_unpinned() { add_task(function* test_url() { yield do_test_update(function background() { // Create a new tab for testing update. - browser.tabs.create(null, function(tab) { + browser.tabs.create({}, function(tab) { browser.tabs.onUpdated.addListener(function onUpdated(tabId, changeInfo) { // Check callback browser.test.assertEq(tabId, tab.id, "Check tab id"); diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index e8faeeedbc6a..e1ed93a5dd90 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -623,6 +623,9 @@ BrowserGlue.prototype = { ExtensionManagement.registerScript("chrome://browser/content/ext-windows.js"); ExtensionManagement.registerScript("chrome://browser/content/ext-bookmarks.js"); + ExtensionManagement.registerSchema("chrome://browser/content/schemas/tabs.json"); + ExtensionManagement.registerSchema("chrome://browser/content/schemas/windows.json"); + this._flashHangCount = 0; this._firstWindowReady = new Promise(resolve => this._firstWindowLoaded = resolve); }, diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py index 7e53666fb151..0ed91c625b84 100644 --- a/build/mach_bootstrap.py +++ b/build/mach_bootstrap.py @@ -73,6 +73,7 @@ SEARCH_PATHS = [ 'testing/xpcshell', 'testing/web-platform', 'testing/web-platform/harness', + 'testing/web-platform/tests/tools/wptserve', 'testing/marionette/client', 'testing/marionette/client/marionette/runner/mixins/browsermob-proxy-py', 'testing/marionette/transport', diff --git a/build/virtualenv_packages.txt b/build/virtualenv_packages.txt index 95a4afc0c0bb..3b000729afd8 100644 --- a/build/virtualenv_packages.txt +++ b/build/virtualenv_packages.txt @@ -1,6 +1,7 @@ marionette_transport.pth:testing/marionette/transport marionette_driver.pth:testing/marionette/driver browsermobproxy.pth:testing/marionette/client/marionette/runner/mixins/browsermob-proxy-py +wptserve.pth:testing/web-platform/tests/tools/wptserve marionette.pth:testing/marionette/client blessings.pth:python/blessings configobj.pth:python/configobj diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index 664278f64532..0fbc68c7c45c 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -431,7 +431,6 @@ nsScriptSecurityManager::IsSystemPrincipal(nsIPrincipal* aPrincipal, //////////////////////////////////// NS_IMPL_ISUPPORTS(nsScriptSecurityManager, nsIScriptSecurityManager, - nsIChannelEventSink, nsIObserver) /////////////////////////////////////////////////// @@ -1236,41 +1235,6 @@ nsScriptSecurityManager::CanGetService(JSContext *cx, return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED; } -///////////////////////////////////////////// -// Method implementing nsIChannelEventSink // -///////////////////////////////////////////// -NS_IMETHODIMP -nsScriptSecurityManager::AsyncOnChannelRedirect(nsIChannel* oldChannel, - nsIChannel* newChannel, - uint32_t redirFlags, - nsIAsyncVerifyRedirectCallback *cb) -{ - nsCOMPtr oldPrincipal; - GetChannelResultPrincipal(oldChannel, getter_AddRefs(oldPrincipal)); - - nsCOMPtr newURI; - newChannel->GetURI(getter_AddRefs(newURI)); - nsCOMPtr newOriginalURI; - newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI)); - - NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI); - - const uint32_t flags = - nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT | - nsIScriptSecurityManager::DISALLOW_SCRIPT; - nsresult rv = CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags); - if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) { - rv = CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags); - } - - if (NS_FAILED(rv)) - return rv; - - cb->OnRedirectVerifyCallback(NS_OK); - return NS_OK; -} - - ///////////////////////////////////// // Method implementing nsIObserver // ///////////////////////////////////// diff --git a/caps/nsScriptSecurityManager.h b/caps/nsScriptSecurityManager.h index 2636ecb72946..361879dc582d 100644 --- a/caps/nsScriptSecurityManager.h +++ b/caps/nsScriptSecurityManager.h @@ -14,7 +14,6 @@ #include "nsIAddonPolicyService.h" #include "nsIPrincipal.h" #include "nsCOMPtr.h" -#include "nsIChannelEventSink.h" #include "nsIObserver.h" #include "nsServiceManagerUtils.h" #include "plstr.h" @@ -39,7 +38,6 @@ class PrincipalOriginAttributes; { 0xba, 0x18, 0x00, 0x60, 0xb0, 0xf1, 0x99, 0xa2 }} class nsScriptSecurityManager final : public nsIScriptSecurityManager, - public nsIChannelEventSink, public nsIObserver { public: @@ -49,7 +47,6 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSISCRIPTSECURITYMANAGER - NS_DECL_NSICHANNELEVENTSINK NS_DECL_NSIOBSERVER static nsScriptSecurityManager* diff --git a/config/faster/rules.mk b/config/faster/rules.mk index 28ca22ea3d2a..cf665a6b59c9 100644 --- a/config/faster/rules.mk +++ b/config/faster/rules.mk @@ -105,8 +105,7 @@ $(addprefix install-,$(INSTALL_MANIFESTS)): install-%: $(TOPOBJDIR)/config/build @# support for defines tracking in process_install_manifest. @touch install_$(subst /,_,$*) $(PYTHON) -m mozbuild.action.process_install_manifest \ - --no-remove \ - --no-remove-empty-directories \ + --track install_$(subst /,_,$*).track \ $(TOPOBJDIR)/$* \ -DAB_CD=en-US \ -DMOZ_APP_BUILDID=$(shell cat $(TOPOBJDIR)/config/buildid) \ diff --git a/dom/base/BlobSet.h b/dom/base/BlobSet.h index 8e5aa0af6204..7b2151aeed3f 100644 --- a/dom/base/BlobSet.h +++ b/dom/base/BlobSet.h @@ -32,7 +32,8 @@ public: nsTArray>& GetBlobImpls() { Flush(); return mBlobImpls; } already_AddRefed GetBlobInternal(nsISupports* aParent, - const nsACString& aContentType); + const nsACString& aContentType, + ErrorResult& aRv); protected: bool ExpandBufferSize(uint64_t aSize) diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 9458d0cf50f8..cae3a49eb6fc 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1233,9 +1233,11 @@ Element::GetAttributeNode(const nsAString& aName) already_AddRefed Element::SetAttributeNode(Attr& aNewAttr, ErrorResult& aError) { + // XXXbz can we just remove this warning and the one in setAttributeNodeNS and + // alias setAttributeNode to setAttributeNodeNS? OwnerDoc()->WarnOnceAbout(nsIDocument::eSetAttributeNode); - return Attributes()->SetNamedItem(aNewAttr, aError); + return Attributes()->SetNamedItemNS(aNewAttr, aError); } already_AddRefed diff --git a/dom/base/EventSource.cpp b/dom/base/EventSource.cpp index f287e74e5b8d..6b2139ca18c4 100644 --- a/dom/base/EventSource.cpp +++ b/dom/base/EventSource.cpp @@ -678,7 +678,7 @@ EventSource::InitChannelAndRequestEventSource() nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; if (mWithCredentials) { - securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS; + securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; } nsCOMPtr channel; diff --git a/dom/base/File.cpp b/dom/base/File.cpp index e3df761998a9..5cc20a76e46e 100644 --- a/dom/base/File.cpp +++ b/dom/base/File.cpp @@ -257,7 +257,7 @@ Blob::ToFile() } already_AddRefed -Blob::ToFile(const nsAString& aName) const +Blob::ToFile(const nsAString& aName, ErrorResult& aRv) const { nsAutoTArray, 1> blobImpls; blobImpls.AppendElement(mImpl); @@ -266,7 +266,10 @@ Blob::ToFile(const nsAString& aName) const mImpl->GetType(contentType); RefPtr impl = - new MultipartBlobImpl(blobImpls, aName, contentType); + MultipartBlobImpl::Create(blobImpls, aName, contentType, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } RefPtr file = new File(mParent, impl); return file.forget(); @@ -347,7 +350,11 @@ Blob::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) { RefPtr impl = new MultipartBlobImpl(); - impl->InitializeBlob(); + impl->InitializeBlob(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + MOZ_ASSERT(!impl->IsFile()); RefPtr blob = Blob::Create(aGlobal.GetAsSupports(), impl); @@ -851,7 +858,7 @@ BlobImplFile::GetMozFullPathInternal(nsAString& aFilename, ErrorResult& aRv) con uint64_t BlobImplFile::GetSize(ErrorResult& aRv) { - if (IsSizeUnknown()) { + if (BlobImplBase::IsSizeUnknown()) { NS_ASSERTION(mWholeFile, "Should only use lazy size when using the whole file"); int64_t fileSize; @@ -902,7 +909,7 @@ int64_t BlobImplFile::GetLastModified(ErrorResult& aRv) { NS_ASSERTION(mIsFile, "Should only be called on files"); - if (IsDateUnknown()) { + if (BlobImplBase::IsDateUnknown()) { PRTime msecs; aRv = mFile->GetLastModifiedTime(&msecs); if (NS_WARN_IF(aRv.Failed())) { @@ -1121,11 +1128,19 @@ BlobImplTemporaryBlob::GetInternalStream(nsIInputStream** aStream, // BlobSet implementation already_AddRefed -BlobSet::GetBlobInternal(nsISupports* aParent, const nsACString& aContentType) +BlobSet::GetBlobInternal(nsISupports* aParent, + const nsACString& aContentType, + ErrorResult& aRv) { - RefPtr blob = Blob::Create(aParent, - new MultipartBlobImpl(GetBlobImpls(), - NS_ConvertASCIItoUTF16(aContentType))); + RefPtr blobImpl = + MultipartBlobImpl::Create(GetBlobImpls(), + NS_ConvertASCIItoUTF16(aContentType), + aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RefPtr blob = Blob::Create(aParent, blobImpl); return blob.forget(); } diff --git a/dom/base/File.h b/dom/base/File.h index 8264a9bc679b..43c2ed3b0b3a 100644 --- a/dom/base/File.h +++ b/dom/base/File.h @@ -118,7 +118,8 @@ public: // This method creates a new File object with the given name and the same // BlobImpl. - already_AddRefed ToFile(const nsAString& aName) const; + already_AddRefed ToFile(const nsAString& aName, + ErrorResult& aRv) const; already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType, @@ -798,6 +799,10 @@ public: virtual void LookupAndCacheIsDirectory() override; + // We always have size and date for this kind of blob. + virtual bool IsSizeUnknown() const override { return false; } + virtual bool IsDateUnknown() const override { return false; } + protected: virtual ~BlobImplFile() { if (mFile && mIsTemporary) { diff --git a/dom/base/MultipartBlobImpl.cpp b/dom/base/MultipartBlobImpl.cpp index 4d2c309c2fe5..98671429edad 100644 --- a/dom/base/MultipartBlobImpl.cpp +++ b/dom/base/MultipartBlobImpl.cpp @@ -25,6 +25,37 @@ using namespace mozilla::dom; NS_IMPL_ISUPPORTS_INHERITED0(MultipartBlobImpl, BlobImpl) +/* static */ already_AddRefed +MultipartBlobImpl::Create(const nsTArray>& aBlobImpls, + const nsAString& aName, + const nsAString& aContentType, + ErrorResult& aRv) +{ + RefPtr blobImpl = + new MultipartBlobImpl(aBlobImpls, aName, aContentType); + blobImpl->SetLengthAndModifiedDate(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return blobImpl.forget(); +} + +/* static */ already_AddRefed +MultipartBlobImpl::Create(const nsTArray>& aBlobImpls, + const nsAString& aContentType, + ErrorResult& aRv) +{ + RefPtr blobImpl = + new MultipartBlobImpl(aBlobImpls, aContentType); + blobImpl->SetLengthAndModifiedDate(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return blobImpl.forget(); +} + void MultipartBlobImpl::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) @@ -125,15 +156,19 @@ MultipartBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength, } // we can create our blob now - RefPtr impl = - new MultipartBlobImpl(blobImpls, aContentType); + RefPtr impl = Create(blobImpls, aContentType, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + return impl.forget(); } void -MultipartBlobImpl::InitializeBlob() +MultipartBlobImpl::InitializeBlob(ErrorResult& aRv) { - SetLengthAndModifiedDate(); + SetLengthAndModifiedDate(aRv); + NS_WARN_IF(aRv.Failed()); } void @@ -187,11 +222,12 @@ MultipartBlobImpl::InitializeBlob( mBlobImpls = blobSet.GetBlobImpls(); - SetLengthAndModifiedDate(); + SetLengthAndModifiedDate(aRv); + NS_WARN_IF(aRv.Failed()); } void -MultipartBlobImpl::SetLengthAndModifiedDate() +MultipartBlobImpl::SetLengthAndModifiedDate(ErrorResult& aRv) { MOZ_ASSERT(mLength == UINT64_MAX); MOZ_ASSERT(mLastModificationDate == INT64_MAX); @@ -208,16 +244,19 @@ MultipartBlobImpl::SetLengthAndModifiedDate() MOZ_ASSERT(!blob->IsDateUnknown()); #endif - ErrorResult error; - uint64_t subBlobLength = blob->GetSize(error); - MOZ_ALWAYS_TRUE(!error.Failed()); + uint64_t subBlobLength = blob->GetSize(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } MOZ_ASSERT(UINT64_MAX - subBlobLength >= totalLength); totalLength += subBlobLength; if (blob->IsFile()) { - int64_t partLastModified = blob->GetLastModified(error); - MOZ_ALWAYS_TRUE(!error.Failed()); + int64_t partLastModified = blob->GetLastModified(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } if (lastModified < partLastModified) { lastModified = partLastModified; @@ -314,7 +353,8 @@ MultipartBlobImpl::InitializeChromeFile(Blob& aBlob, blobSet.AppendBlobImpl(aBlob.Impl()); mBlobImpls = blobSet.GetBlobImpls(); - SetLengthAndModifiedDate(); + SetLengthAndModifiedDate(aRv); + NS_WARN_IF(aRv.Failed()); } void @@ -385,7 +425,8 @@ MultipartBlobImpl::InitializeChromeFile(nsPIDOMWindow* aWindow, blobSet.AppendBlobImpl(static_cast(blob.get())->Impl()); mBlobImpls = blobSet.GetBlobImpls(); - SetLengthAndModifiedDate(); + SetLengthAndModifiedDate(aRv); + NS_WARN_IF(aRv.Failed()); } void diff --git a/dom/base/MultipartBlobImpl.h b/dom/base/MultipartBlobImpl.h index 2a0778be28cc..2239a4936ed3 100644 --- a/dom/base/MultipartBlobImpl.h +++ b/dom/base/MultipartBlobImpl.h @@ -25,25 +25,17 @@ public: NS_DECL_ISUPPORTS_INHERITED // Create as a file - MultipartBlobImpl(const nsTArray>& aBlobImpls, - const nsAString& aName, - const nsAString& aContentType) - : BlobImplBase(aName, aContentType, UINT64_MAX), - mBlobImpls(aBlobImpls), - mIsFromNsIFile(false) - { - SetLengthAndModifiedDate(); - } + static already_AddRefed + Create(const nsTArray>& aBlobImpls, + const nsAString& aName, + const nsAString& aContentType, + ErrorResult& aRv); // Create as a blob - MultipartBlobImpl(const nsTArray>& aBlobImpls, - const nsAString& aContentType) - : BlobImplBase(aContentType, UINT64_MAX), - mBlobImpls(aBlobImpls), - mIsFromNsIFile(false) - { - SetLengthAndModifiedDate(); - } + static already_AddRefed + Create(const nsTArray>& aBlobImpls, + const nsAString& aContentType, + ErrorResult& aRv); // Create as a file to be later initialized explicit MultipartBlobImpl(const nsAString& aName) @@ -59,7 +51,7 @@ public: { } - void InitializeBlob(); + void InitializeBlob(ErrorResult& aRv); void InitializeBlob( JSContext* aCx, @@ -120,9 +112,26 @@ public: virtual bool MayBeClonedToOtherThreads() const override; protected: + MultipartBlobImpl(const nsTArray>& aBlobImpls, + const nsAString& aName, + const nsAString& aContentType) + : BlobImplBase(aName, aContentType, UINT64_MAX), + mBlobImpls(aBlobImpls), + mIsFromNsIFile(false) + { + } + + MultipartBlobImpl(const nsTArray>& aBlobImpls, + const nsAString& aContentType) + : BlobImplBase(aContentType, UINT64_MAX), + mBlobImpls(aBlobImpls), + mIsFromNsIFile(false) + { + } + virtual ~MultipartBlobImpl() {} - void SetLengthAndModifiedDate(); + void SetLengthAndModifiedDate(ErrorResult& aRv); nsTArray> mBlobImpls; bool mIsFromNsIFile; diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 13acc2db6d75..e04ec7d25466 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1204,7 +1204,8 @@ Navigator::SendBeacon(const nsAString& aUrl, rv = NS_NewChannel(getter_AddRefs(channel), uri, doc, - nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, + nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS | + nsILoadInfo::SEC_COOKIES_INCLUDE, nsIContentPolicy::TYPE_BEACON); if (NS_FAILED(rv)) { @@ -1315,41 +1316,6 @@ Navigator::SendBeacon(const nsAString& aUrl, channel->SetLoadGroup(loadGroup); RefPtr beaconListener = new BeaconStreamListener(); - - // Start a preflight if cross-origin and content type is not whitelisted - nsCOMPtr secMan = nsContentUtils::GetSecurityManager(); - rv = secMan->CheckSameOriginURI(documentURI, uri, false); - bool crossOrigin = NS_FAILED(rv); - nsAutoCString contentType, parsedCharset; - rv = NS_ParseRequestContentType(mimeType, contentType, parsedCharset); - if (crossOrigin && - mimeType.Length() > 0 && - !contentType.Equals(APPLICATION_WWW_FORM_URLENCODED) && - !contentType.Equals(MULTIPART_FORM_DATA) && - !contentType.Equals(TEXT_PLAIN)) { - - // we need to set the sameOriginChecker as a notificationCallback - // so we can tell the channel not to follow redirects - nsCOMPtr soc = nsContentUtils::SameOriginChecker(); - channel->SetNotificationCallbacks(soc); - - nsCOMPtr internalChannel = - do_QueryInterface(channel); - if (!internalChannel) { - aRv.Throw(NS_ERROR_FAILURE); - return false; - } - nsTArray unsafeHeaders; - unsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type")); - rv = internalChannel->SetCorsPreflightParameters(unsafeHeaders, - true, - doc->NodePrincipal()); - if (NS_WARN_IF(NS_FAILED(rv))) { - aRv.Throw(rv); - return false; - } - } - rv = channel->AsyncOpen2(beaconListener); if (NS_FAILED(rv)) { aRv.Throw(rv); diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp index b069f7d7b1f7..d6932d376dd7 100644 --- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -555,7 +555,8 @@ namespace { // Recursive! already_AddRefed EnsureBlobForBackgroundManager(BlobImpl* aBlobImpl, - PBackgroundChild* aManager = nullptr) + PBackgroundChild* aManager, + ErrorResult& aRv) { MOZ_ASSERT(aBlobImpl); RefPtr blobImpl = aBlobImpl; @@ -604,7 +605,11 @@ EnsureBlobForBackgroundManager(BlobImpl* aBlobImpl, RefPtr& newSubBlobImpl = newSubBlobImpls[index]; - newSubBlobImpl = EnsureBlobForBackgroundManager(subBlobImpl, aManager); + newSubBlobImpl = EnsureBlobForBackgroundManager(subBlobImpl, aManager, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + MOZ_ASSERT(newSubBlobImpl); if (subBlobImpl != newSubBlobImpl) { @@ -620,9 +625,14 @@ EnsureBlobForBackgroundManager(BlobImpl* aBlobImpl, nsString name; blobImpl->GetName(name); - blobImpl = new MultipartBlobImpl(newSubBlobImpls, name, contentType); + blobImpl = MultipartBlobImpl::Create(newSubBlobImpls, name, + contentType, aRv); } else { - blobImpl = new MultipartBlobImpl(newSubBlobImpls, contentType); + blobImpl = MultipartBlobImpl::Create(newSubBlobImpls, contentType, aRv); + } + + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; } MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false))); @@ -640,7 +650,13 @@ ReadBlob(JSContext* aCx, MOZ_ASSERT(aIndex < aHolder->BlobImpls().Length()); RefPtr blobImpl = aHolder->BlobImpls()[aIndex]; - blobImpl = EnsureBlobForBackgroundManager(blobImpl); + ErrorResult rv; + blobImpl = EnsureBlobForBackgroundManager(blobImpl, nullptr, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return nullptr; + } + MOZ_ASSERT(blobImpl); // RefPtr needs to go out of scope before toObjectOrNull() is @@ -668,7 +684,14 @@ WriteBlob(JSStructuredCloneWriter* aWriter, MOZ_ASSERT(aBlob); MOZ_ASSERT(aHolder); - RefPtr blobImpl = EnsureBlobForBackgroundManager(aBlob->Impl()); + ErrorResult rv; + RefPtr blobImpl = + EnsureBlobForBackgroundManager(aBlob->Impl(), nullptr, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return false; + } + MOZ_ASSERT(blobImpl); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false))); @@ -714,7 +737,13 @@ ReadFileList(JSContext* aCx, RefPtr blobImpl = aHolder->BlobImpls()[index]; MOZ_ASSERT(blobImpl->IsFile()); - blobImpl = EnsureBlobForBackgroundManager(blobImpl); + ErrorResult rv; + blobImpl = EnsureBlobForBackgroundManager(blobImpl, nullptr, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return nullptr; + } + MOZ_ASSERT(blobImpl); RefPtr file = File::Create(aHolder->ParentDuringRead(), blobImpl); @@ -753,14 +782,22 @@ WriteFileList(JSStructuredCloneWriter* aWriter, return false; } + ErrorResult rv; + nsTArray> blobImpls; + for (uint32_t i = 0; i < aFileList->Length(); ++i) { RefPtr blobImpl = - EnsureBlobForBackgroundManager(aFileList->Item(i)->Impl()); - MOZ_ASSERT(blobImpl); + EnsureBlobForBackgroundManager(aFileList->Item(i)->Impl(), nullptr, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return false; + } - aHolder->BlobImpls().AppendElement(blobImpl); + MOZ_ASSERT(blobImpl); + blobImpls.AppendElement(blobImpl); } + aHolder->BlobImpls().AppendElements(blobImpls); return true; } @@ -804,7 +841,12 @@ ReadFormData(JSContext* aCx, File::Create(aHolder->ParentDuringRead(), blobImpl); MOZ_ASSERT(file); - formData->Append(name, *file, thirdArg); + ErrorResult rv; + formData->Append(name, *file, thirdArg, rv); + if (NS_WARN_IF(rv.Failed())) { + return nullptr; + } + } else { MOZ_ASSERT(tag == 0); @@ -816,7 +858,11 @@ ReadFormData(JSContext* aCx, return nullptr; } - formData->Append(name, value); + ErrorResult rv; + formData->Append(name, value, rv); + if (NS_WARN_IF(rv.Failed())) { + return nullptr; + } } } diff --git a/dom/base/ThirdPartyUtil.cpp b/dom/base/ThirdPartyUtil.cpp index 47cb40679291..6e8c819e85b7 100644 --- a/dom/base/ThirdPartyUtil.cpp +++ b/dom/base/ThirdPartyUtil.cpp @@ -46,7 +46,9 @@ ThirdPartyUtil::IsThirdPartyInternal(const nsCString& aFirstDomain, nsIURI* aSecondURI, bool* aResult) { - NS_ENSURE_ARG(aSecondURI); + if (!aSecondURI) { + return NS_ERROR_INVALID_ARG; + } // Get the base domain for aSecondURI. nsCString secondDomain; diff --git a/dom/base/nsDOMAttributeMap.cpp b/dom/base/nsDOMAttributeMap.cpp index 1f98ec3c24b9..60d6a5a1c0ac 100644 --- a/dom/base/nsDOMAttributeMap.cpp +++ b/dom/base/nsDOMAttributeMap.cpp @@ -240,7 +240,7 @@ nsDOMAttributeMap::SetNamedItem(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn) NS_ENSURE_ARG(attribute); ErrorResult rv; - *aReturn = SetNamedItem(*attribute, rv).take(); + *aReturn = SetNamedItemNS(*attribute, rv).take(); return rv.StealNSResult(); } @@ -256,9 +256,7 @@ nsDOMAttributeMap::SetNamedItemNS(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn) } already_AddRefed -nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr, - bool aWithNS, - ErrorResult& aError) +nsDOMAttributeMap::SetNamedItemNS(Attr& aAttr, ErrorResult& aError) { NS_ENSURE_TRUE(mContent, nullptr); @@ -293,24 +291,18 @@ nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr, // Get nodeinfo and preexisting attribute (if it exists) RefPtr oldNi; - if (!aWithNS) { - nsAutoString name; - aAttr.GetName(name); - oldNi = mContent->GetExistingAttrNameFromQName(name); - } else { - uint32_t i, count = mContent->GetAttrCount(); - for (i = 0; i < count; ++i) { - const nsAttrName* name = mContent->GetAttrNameAt(i); - int32_t attrNS = name->NamespaceID(); - nsIAtom* nameAtom = name->LocalName(); + uint32_t i, count = mContent->GetAttrCount(); + for (i = 0; i < count; ++i) { + const nsAttrName* name = mContent->GetAttrNameAt(i); + int32_t attrNS = name->NamespaceID(); + nsIAtom* nameAtom = name->LocalName(); - // we're purposefully ignoring the prefix. - if (aAttr.NodeInfo()->Equals(nameAtom, attrNS)) { - oldNi = mContent->NodeInfo()->NodeInfoManager()-> - GetNodeInfo(nameAtom, name->GetPrefix(), aAttr.NodeInfo()->NamespaceID(), - nsIDOMNode::ATTRIBUTE_NODE); - break; - } + // we're purposefully ignoring the prefix. + if (aAttr.NodeInfo()->Equals(nameAtom, attrNS)) { + oldNi = mContent->NodeInfo()->NodeInfoManager()-> + GetNodeInfo(nameAtom, name->GetPrefix(), aAttr.NodeInfo()->NamespaceID(), + nsIDOMNode::ATTRIBUTE_NODE); + break; } } diff --git a/dom/base/nsDOMAttributeMap.h b/dom/base/nsDOMAttributeMap.h index 9fb4a30a6b45..3893525942d5 100644 --- a/dom/base/nsDOMAttributeMap.h +++ b/dom/base/nsDOMAttributeMap.h @@ -141,11 +141,6 @@ public: Attr* NamedGetter(const nsAString& aAttrName, bool& aFound); bool NameIsEnumerable(const nsAString& aName); already_AddRefed - SetNamedItem(Attr& aAttr, ErrorResult& aError) - { - return SetNamedItemInternal(aAttr, false, aError); - } - already_AddRefed RemoveNamedItem(mozilla::dom::NodeInfo* aNodeInfo, ErrorResult& aError); already_AddRefed RemoveNamedItem(const nsAString& aName, ErrorResult& aError); @@ -158,10 +153,7 @@ public: GetNamedItemNS(const nsAString& aNamespaceURI, const nsAString& aLocalName); already_AddRefed - SetNamedItemNS(Attr& aNode, ErrorResult& aError) - { - return SetNamedItemInternal(aNode, true, aError); - } + SetNamedItemNS(Attr& aNode, ErrorResult& aError); already_AddRefed RemoveNamedItemNS(const nsAString& aNamespaceURI, const nsAString& aLocalName, ErrorResult& aError); @@ -184,13 +176,6 @@ private: */ AttrCache mAttributeCache; - /** - * SetNamedItem() (aWithNS = false) and SetNamedItemNS() (aWithNS = - * true) implementation. - */ - already_AddRefed - SetNamedItemInternal(Attr& aNode, bool aWithNS, ErrorResult& aError); - already_AddRefed GetAttrNodeInfo(const nsAString& aNamespaceURI, const nsAString& aLocalName); diff --git a/dom/base/nsDOMFileReader.cpp b/dom/base/nsDOMFileReader.cpp index 3a13e7bba3b7..ee9a82e6fbcd 100644 --- a/dom/base/nsDOMFileReader.cpp +++ b/dom/base/nsDOMFileReader.cpp @@ -275,7 +275,6 @@ nsDOMFileReader::DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent, nsAString& aTerminationEvent) { - // Make sure we drop all the objects that could hold files open now. nsCOMPtr stream; mAsyncStream.swap(stream); @@ -283,25 +282,34 @@ nsDOMFileReader::DoOnLoadEnd(nsresult aStatus, RefPtr blob; mBlob.swap(blob); - aSuccessEvent = NS_LITERAL_STRING(LOAD_STR); - aTerminationEvent = NS_LITERAL_STRING(LOADEND_STR); - // Clear out the data if necessary if (NS_FAILED(aStatus)) { FreeFileData(); return NS_OK; } + // In case we read a different number of bytes, we can assume that the + // underlying storage has changed. We should not continue. + if (mDataLen != mTotal) { + DispatchError(NS_ERROR_FAILURE, aTerminationEvent); + FreeFileData(); + return NS_ERROR_FAILURE; + } + + aSuccessEvent = NS_LITERAL_STRING(LOAD_STR); + aTerminationEvent = NS_LITERAL_STRING(LOADEND_STR); + nsresult rv = NS_OK; switch (mDataFormat) { case FILE_AS_ARRAYBUFFER: { AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(mozilla::DOMEventTargetHelper::GetParentObject()))) { + FreeFileData(); return NS_ERROR_FAILURE; } RootResultArrayBuffer(); - mResultArrayBuffer = JS_NewArrayBufferWithContents(jsapi.cx(), mTotal, mFileData); + mResultArrayBuffer = JS_NewArrayBufferWithContents(jsapi.cx(), mDataLen, mFileData); if (!mResultArrayBuffer) { JS_ClearPendingException(jsapi.cx()); rv = NS_ERROR_OUT_OF_MEMORY; @@ -343,8 +351,7 @@ nsDOMFileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount) if (mDataFormat == FILE_AS_BINARY) { //Continuously update our binary string as data comes in uint32_t oldLen = mResult.Length(); - NS_ASSERTION(mResult.Length() == mDataLen, - "unexpected mResult length"); + NS_ASSERTION(mResult.Length() == mDataLen, "unexpected mResult length"); if (uint64_t(oldLen) + aCount > UINT32_MAX) return NS_ERROR_OUT_OF_MEMORY; char16_t *buf = nullptr; diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 03e6b16a55d3..3927d461713d 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -1075,9 +1075,10 @@ nsDOMWindowUtils::SendNativeTouchPoint(uint32_t aPointerId, } NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs - + (widget, &nsIWidget::SynthesizeNativeTouchPoint, aPointerId, - (nsIWidget::TouchPointerState)aTouchState, nsIntPoint(aScreenX, aScreenY), + (nsIWidget::TouchPointerState)aTouchState, + ScreenIntPoint(aScreenX, aScreenY), aPressure, aOrientation, aObserver)); return NS_OK; } @@ -1094,9 +1095,9 @@ nsDOMWindowUtils::SendNativeTouchTap(int32_t aScreenX, } NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs - + (widget, &nsIWidget::SynthesizeNativeTouchTap, - nsIntPoint(aScreenX, aScreenY), aLongTap, aObserver)); + ScreenIntPoint(aScreenX, aScreenY), aLongTap, aObserver)); return NS_OK; } diff --git a/dom/base/nsFormData.cpp b/dom/base/nsFormData.cpp index 97f83ff36a7d..c47f7a0bdb49 100644 --- a/dom/base/nsFormData.cpp +++ b/dom/base/nsFormData.cpp @@ -25,7 +25,8 @@ namespace { // Implements steps 3 and 4 of the "create an entry" algorithm of FormData. already_AddRefed -CreateNewFileInstance(Blob& aBlob, const Optional& aFilename) +CreateNewFileInstance(Blob& aBlob, const Optional& aFilename, + ErrorResult& aRv) { // Step 3 "If value is a Blob object and not a File object, set value to // a new File object, representing the same bytes, whose name attribute value @@ -47,7 +48,12 @@ CreateNewFileInstance(Blob& aBlob, const Optional& aFilename) filename = NS_LITERAL_STRING("blob"); } - return aBlob.ToFile(filename); + RefPtr file = aBlob.ToFile(filename, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return file.forget(); } } // namespace @@ -101,16 +107,22 @@ nsFormData::GetEncodedSubmission(nsIURI* aURI, } void -nsFormData::Append(const nsAString& aName, const nsAString& aValue) +nsFormData::Append(const nsAString& aName, const nsAString& aValue, + ErrorResult& aRv) { AddNameValuePair(aName, aValue); } void nsFormData::Append(const nsAString& aName, Blob& aBlob, - const Optional& aFilename) + const Optional& aFilename, + ErrorResult& aRv) { - RefPtr file = CreateNewFileInstance(aBlob, aFilename); + RefPtr file = CreateNewFileInstance(aBlob, aFilename, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + AddNameFilePair(aName, file); } @@ -196,25 +208,31 @@ nsFormData::RemoveAllOthersAndGetFirstFormDataTuple(const nsAString& aName) void nsFormData::Set(const nsAString& aName, Blob& aBlob, - const Optional& aFilename) + const Optional& aFilename, + ErrorResult& aRv) { FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName); if (tuple) { - RefPtr file = CreateNewFileInstance(aBlob, aFilename); + RefPtr file = CreateNewFileInstance(aBlob, aFilename, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + SetNameFilePair(tuple, aName, file); } else { - Append(aName, aBlob, aFilename); + Append(aName, aBlob, aFilename, aRv); } } void -nsFormData::Set(const nsAString& aName, const nsAString& aValue) +nsFormData::Set(const nsAString& aName, const nsAString& aValue, + ErrorResult& aRv) { FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName); if (tuple) { SetNameValuePair(tuple, aName, aValue); } else { - Append(aName, aValue); + Append(aName, aValue, aRv); } } @@ -261,7 +279,12 @@ nsFormData::Append(const nsAString& aName, nsIVariant* aValue) RefPtr blob = static_cast(domBlob.get()); if (domBlob) { Optional temp; - Append(aName, *blob, temp); + ErrorResult rv; + Append(aName, *blob, temp, rv); + if (NS_WARN_IF(rv.Failed())) { + return rv.StealNSResult(); + } + return NS_OK; } } @@ -274,7 +297,12 @@ nsFormData::Append(const nsAString& aName, nsIVariant* aValue) nsString valAsString; valAsString.Adopt(stringData, stringLen); - Append(aName, valAsString); + ErrorResult error; + Append(aName, valAsString, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } + return NS_OK; } diff --git a/dom/base/nsFormData.h b/dom/base/nsFormData.h index 1b428f7d7ea9..d6e06b05e52a 100644 --- a/dom/base/nsFormData.h +++ b/dom/base/nsFormData.h @@ -93,16 +93,20 @@ public: Constructor(const mozilla::dom::GlobalObject& aGlobal, const mozilla::dom::Optional >& aFormElement, mozilla::ErrorResult& aRv); - void Append(const nsAString& aName, const nsAString& aValue); + void Append(const nsAString& aName, const nsAString& aValue, + mozilla::ErrorResult& aRv); void Append(const nsAString& aName, Blob& aBlob, - const mozilla::dom::Optional& aFilename); + const mozilla::dom::Optional& aFilename, + mozilla::ErrorResult& aRv); void Delete(const nsAString& aName); void Get(const nsAString& aName, mozilla::dom::Nullable& aOutValue); void GetAll(const nsAString& aName, nsTArray& aValues); bool Has(const nsAString& aName); void Set(const nsAString& aName, Blob& aBlob, - const mozilla::dom::Optional& aFilename); - void Set(const nsAString& aName, const nsAString& aValue); + const mozilla::dom::Optional& aFilename, + mozilla::ErrorResult& aRv); + void Set(const nsAString& aName, const nsAString& aValue, + mozilla::ErrorResult& aRv); uint32_t GetIterableLength() const; const nsAString& GetKeyAtIndex(uint32_t aIndex) const; diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index d80574247a91..d7c2182d3d49 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -298,7 +298,7 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; if (aRequest->mCORSMode == CORS_USE_CREDENTIALS) { - securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS; + securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; } securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME; diff --git a/dom/base/nsXMLHttpRequest.cpp b/dom/base/nsXMLHttpRequest.cpp index 95b2b4165cf4..74cc2fd5cd0f 100644 --- a/dom/base/nsXMLHttpRequest.cpp +++ b/dom/base/nsXMLHttpRequest.cpp @@ -122,11 +122,10 @@ using namespace mozilla::dom; #define XML_HTTP_REQUEST_PARSEBODY (1 << 9) // Internal #define XML_HTTP_REQUEST_SYNCLOOPING (1 << 10) // Internal #define XML_HTTP_REQUEST_BACKGROUND (1 << 13) // Internal -#define XML_HTTP_REQUEST_USE_XSITE_AC (1 << 14) // Internal -#define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE (1 << 15) // Internal -#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 16) // Internal -#define XML_HTTP_REQUEST_TIMED_OUT (1 << 17) // Internal -#define XML_HTTP_REQUEST_DELETED (1 << 18) // Internal +#define XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND (1 << 14) // Internal +#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 15) // Internal +#define XML_HTTP_REQUEST_TIMED_OUT (1 << 16) // Internal +#define XML_HTTP_REQUEST_DELETED (1 << 17) // Internal #define XML_HTTP_REQUEST_LOADSTATES \ (XML_HTTP_REQUEST_UNSENT | \ @@ -798,7 +797,7 @@ nsXMLHttpRequest::CreateResponseParsedJSON(JSContext* aCx) } void -nsXMLHttpRequest::CreatePartialBlob() +nsXMLHttpRequest::CreatePartialBlob(ErrorResult& aRv) { if (mDOMBlob) { // Use progress info to determine whether load is complete, but use @@ -807,9 +806,8 @@ nsXMLHttpRequest::CreatePartialBlob() if (mLoadTotal == mLoadTransferred) { mResponseBlob = mDOMBlob; } else { - ErrorResult rv; mResponseBlob = mDOMBlob->CreateSlice(0, mDataAvailable, - EmptyString(), rv); + EmptyString(), aRv); } return; } @@ -824,7 +822,7 @@ nsXMLHttpRequest::CreatePartialBlob() mChannel->GetContentType(contentType); } - mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType); + mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType, aRv); } NS_IMETHODIMP nsXMLHttpRequest::GetResponseType(nsAString& aResponseType) @@ -1018,7 +1016,7 @@ nsXMLHttpRequest::GetResponse(JSContext* aCx, } if (!mResponseBlob) { - CreatePartialBlob(); + CreatePartialBlob(aRv); } } @@ -1071,9 +1069,22 @@ nsXMLHttpRequest::GetResponse(JSContext* aCx, } bool -nsXMLHttpRequest::IsDeniedCrossSiteRequest() +nsXMLHttpRequest::IsCrossSiteCORSRequest() { - if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) && mChannel) { + if (!mChannel) { + return false; + } + + nsCOMPtr loadInfo = mChannel->GetLoadInfo(); + MOZ_ASSERT(loadInfo); + + return loadInfo->GetTainting() == LoadTainting::CORS; +} + +bool +nsXMLHttpRequest::IsDeniedCrossSiteCORSRequest() +{ + if (IsCrossSiteCORSRequest()) { nsresult rv; mChannel->GetStatus(&rv); if (NS_FAILED(rv)) { @@ -1095,7 +1106,7 @@ nsXMLHttpRequest::GetResponseURL(nsAString& aUrl) // Make sure we don't leak responseURL information from denied cross-site // requests. - if (IsDeniedCrossSiteRequest()) { + if (IsDeniedCrossSiteCORSRequest()) { return; } @@ -1123,7 +1134,7 @@ nsXMLHttpRequest::Status() { // Make sure we don't leak status information from denied cross-site // requests. - if (IsDeniedCrossSiteRequest()) { + if (IsDeniedCrossSiteCORSRequest()) { return 0; } @@ -1173,7 +1184,7 @@ nsXMLHttpRequest::GetStatusText(nsCString& aStatusText) // Make sure we don't leak status information from denied cross-site // requests. - if (IsDeniedCrossSiteRequest()) { + if (IsDeniedCrossSiteCORSRequest()) { return; } @@ -1268,7 +1279,7 @@ nsXMLHttpRequest::IsSafeHeader(const nsACString& header, nsIHttpChannel* httpCha return false; } // if this is not a CORS call all headers are safe - if (!(mState & XML_HTTP_REQUEST_USE_XSITE_AC)){ + if (!IsCrossSiteCORSRequest()) { return true; } // Check for dangerous headers @@ -1530,17 +1541,6 @@ nsXMLHttpRequest::IsSystemXHR() return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal); } -void -nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel) -{ - // A system XHR (chrome code or a web app with the right permission) can - // load anything, and same-origin loads are always allowed. - if (!IsSystemXHR() && - !nsContentUtils::CheckMayLoad(mPrincipal, aChannel, true)) { - mState |= XML_HTTP_REQUEST_USE_XSITE_AC; - } -} - NS_IMETHODIMP nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url, bool async, const nsAString& user, @@ -1691,6 +1691,10 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url, nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL; } + if (mIsAnon) { + secFlags |= nsILoadInfo::SEC_COOKIES_OMIT; + } + // If we have the document, use it. Unfortunately, for dedicated workers // 'doc' ends up being the parent document, which is not the document // that we want to use. So make sure to avoid using 'doc' in that situation. @@ -1717,8 +1721,7 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url, NS_ENSURE_SUCCESS(rv, rv); - mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC | - XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE); + mState &= ~XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND; nsCOMPtr httpChannel(do_QueryInterface(mChannel)); if (httpChannel) { @@ -1925,12 +1928,6 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt) return NS_OK; } - // Always treat tainted channels as cross-origin. - nsCOMPtr loadInfo = mChannel->GetLoadInfo(); - if (loadInfo && loadInfo->GetTainting() != LoadTainting::Basic) { - mState |= XML_HTTP_REQUEST_USE_XSITE_AC; - } - // Don't do anything if we have been aborted if (mState & XML_HTTP_REQUEST_UNSENT) return NS_OK; @@ -2116,7 +2113,11 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt) mResponseXML->ForceEnableXULXBL(); } - if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) { + nsCOMPtr loadInfo = mChannel->GetLoadInfo(); + MOZ_ASSERT(loadInfo); + bool isCrossSite = loadInfo->GetTainting() != LoadTainting::Basic; + + if (isCrossSite) { nsCOMPtr htmlDoc = do_QueryInterface(mResponseXML); if (htmlDoc) { htmlDoc->DisableCookieAccess(); @@ -2129,7 +2130,7 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt) rv = mResponseXML->StartDocumentLoad(kLoadAsData, channel, loadGroup, nullptr, getter_AddRefs(listener), - !(mState & XML_HTTP_REQUEST_USE_XSITE_AC)); + !isCrossSite); NS_ENSURE_SUCCESS(rv, rv); mXMLParserStreamListener = listener; @@ -2211,8 +2212,14 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult // Also, no-store response cannot be written in persistent cache. nsAutoCString contentType; mChannel->GetContentType(contentType); - mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType); + + ErrorResult rv; + mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType, rv); mBlobSet = nullptr; + + if (NS_WARN_IF(rv.Failed())) { + return rv.StealNSResult(); + } } NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty"); NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty"); @@ -2789,33 +2796,19 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable& aBody) } } - if (httpChannel) { - nsAutoCString contentTypeHeader; - rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), - contentTypeHeader); - if (NS_SUCCEEDED(rv)) { - if (!nsContentUtils::IsAllowedNonCorsContentType(contentTypeHeader)) { - mCORSUnsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type")); - } - } - } - ResetResponse(); - CheckChannelForCrossSiteRequest(mChannel); - - bool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS); - - if (!IsSystemXHR() && withCredentials) { + if (!IsSystemXHR() && !mIsAnon && + (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS)) { // This is quite sad. We have to create the channel in .open(), since the // chrome-only xhr.channel API depends on that. However .withCredentials // can be modified after, so we don't know what to set the - // SEC_REQUIRE_CORS_WITH_CREDENTIALS flag to when the channel is + // SEC_COOKIES_INCLUDE flag to when the channel is // created. So set it here using a hacky internal API. // Not doing this for system XHR uses since those don't use CORS. nsCOMPtr loadInfo = mChannel->GetLoadInfo(); - static_cast(loadInfo.get())->SetWithCredentialsSecFlag(); + static_cast(loadInfo.get())->SetIncludeCookiesSecFlag(); } // Blocking gets are common enough out of XHR that we should mark @@ -2837,10 +2830,7 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable& aBody) internalHttpChannel->SetResponseTimeoutEnabled(false); } - if (mIsAnon) { - AddLoadFlags(mChannel, nsIRequest::LOAD_ANONYMOUS); - } - else { + if (!mIsAnon) { AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS); } @@ -2876,23 +2866,16 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable& aBody) mRequestSentTime = PR_Now(); StartTimeoutTimer(); - // Check if we need to do a preflight request. - if (!mCORSUnsafeHeaders.IsEmpty() || - (mUpload && mUpload->HasListeners()) || - (!method.LowerCaseEqualsLiteral("get") && - !method.LowerCaseEqualsLiteral("post") && - !method.LowerCaseEqualsLiteral("head"))) { - mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE; + // Check if we should enabled cross-origin upload listeners. + if (mUpload && mUpload->HasListeners()) { + mState |= XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND; } // Set up the preflight if needed - if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) && - (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE)) { - NS_ENSURE_TRUE(internalHttpChannel, NS_ERROR_DOM_BAD_URI); - - rv = internalHttpChannel->SetCorsPreflightParameters(mCORSUnsafeHeaders, - withCredentials, mPrincipal); - NS_ENSURE_SUCCESS(rv, rv); + if (!IsSystemXHR()) { + nsCOMPtr loadInfo = mChannel->GetLoadInfo(); + loadInfo->SetCorsPreflightInfo(mCORSUnsafeHeaders, + mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND); } mIsMappedArrayBuffer = false; @@ -3062,7 +3045,7 @@ nsXMLHttpRequest::SetRequestHeader(const nsACString& header, } if (!safeHeader) { - if (!mCORSUnsafeHeaders.Contains(header)) { + if (!mCORSUnsafeHeaders.Contains(header, nsCaseInsensitiveCStringArrayComparator())) { mCORSUnsafeHeaders.AppendElement(header); } } @@ -3267,8 +3250,9 @@ nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv) // Return error if we're already processing a request. Note that we can't use // ReadyState() here, because it can't differentiate between "opened" and // "sent", so we use mState directly. - if (!(mState & XML_HTTP_REQUEST_UNSENT) && - !(mState & XML_HTTP_REQUEST_OPENED)) { + if ((!(mState & XML_HTTP_REQUEST_UNSENT) && + !(mState & XML_HTTP_REQUEST_OPENED)) || + mIsAnon) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } @@ -3321,46 +3305,6 @@ nsXMLHttpRequest::ChangeState(uint32_t aState, bool aBroadcast) return rv; } -/* - * Simple helper class that just forwards the redirect callback back - * to the nsXMLHttpRequest. - */ -class AsyncVerifyRedirectCallbackForwarder final : public nsIAsyncVerifyRedirectCallback -{ -public: - explicit AsyncVerifyRedirectCallbackForwarder(nsXMLHttpRequest* xhr) - : mXHR(xhr) - { - } - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder) - - // nsIAsyncVerifyRedirectCallback implementation - NS_IMETHOD OnRedirectVerifyCallback(nsresult result) override - { - mXHR->OnRedirectVerifyCallback(result); - - return NS_OK; - } - -private: - ~AsyncVerifyRedirectCallbackForwarder() {} - - RefPtr mXHR; -}; - -NS_IMPL_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder, mXHR) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder) - NS_INTERFACE_MAP_ENTRY(nsISupports) - NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackForwarder) -NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackForwarder) - - ///////////////////////////////////////////////////// // nsIChannelEventSink methods: // @@ -3372,30 +3316,17 @@ nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel *aOldChannel, { NS_PRECONDITION(aNewChannel, "Redirect without a channel?"); - nsresult rv; - - if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) { - CheckChannelForCrossSiteRequest(aNewChannel); - - // Disable redirects for preflighted cross-site requests entirely for now - if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) && - (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE)) { - aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI); - return NS_ERROR_DOM_BAD_URI; - } - } - // Prepare to receive callback mRedirectCallback = callback; mNewRedirectChannel = aNewChannel; if (mChannelEventSink) { - RefPtr fwd = - new AsyncVerifyRedirectCallbackForwarder(this); + nsCOMPtr fwd = + EnsureXPCOMifier(); - rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel, - aNewChannel, - aFlags, fwd); + nsresult rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel, + aNewChannel, + aFlags, fwd); if (NS_FAILED(rv)) { mRedirectCallback = nullptr; mNewRedirectChannel = nullptr; @@ -3406,7 +3337,7 @@ nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel *aOldChannel, return NS_OK; } -void +nsresult nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result) { NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback"); @@ -3418,13 +3349,12 @@ nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result) nsCOMPtr httpChannel(do_QueryInterface(mChannel)); if (httpChannel) { // Ensure all original headers are duplicated for the new channel (bug #553888) - for (uint32_t i = mModifiedRequestHeaders.Length(); i > 0; ) { - --i; - if (mModifiedRequestHeaders[i].value.IsEmpty()) { - httpChannel->SetEmptyRequestHeader(mModifiedRequestHeaders[i].header); + for (RequestHeader& requestHeader : mModifiedRequestHeaders) { + if (requestHeader.value.IsEmpty()) { + httpChannel->SetEmptyRequestHeader(requestHeader.header); } else { - httpChannel->SetRequestHeader(mModifiedRequestHeaders[i].header, - mModifiedRequestHeaders[i].value, + httpChannel->SetRequestHeader(requestHeader.header, + requestHeader.value, false); } } @@ -3437,6 +3367,8 @@ nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result) mRedirectCallback->OnRedirectVerifyCallback(result); mRedirectCallback = nullptr; + + return result; } ///////////////////////////////////////////////////// @@ -3540,8 +3472,8 @@ nsXMLHttpRequest::OnStatus(nsIRequest *aRequest, nsISupports *aContext, nsresult bool nsXMLHttpRequest::AllowUploadProgress() { - return !(mState & XML_HTTP_REQUEST_USE_XSITE_AC) || - (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE); + return !IsCrossSiteCORSRequest() || + (mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND); } ///////////////////////////////////////////////////// @@ -3601,7 +3533,7 @@ nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult) // If authentication fails, XMLHttpRequest origin and // the request URL are same origin, ... /* Disabled - bug: 799540 - if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) { + if (IsCrossSiteCORSRequest()) { showPrompt = false; } */ @@ -3808,6 +3740,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier) NS_INTERFACE_MAP_ENTRY(nsIStreamListener) NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink) + NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback) NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsITimerCallback) diff --git a/dom/base/nsXMLHttpRequest.h b/dom/base/nsXMLHttpRequest.h index a317fcfe4952..1659e19ff85c 100644 --- a/dom/base/nsXMLHttpRequest.h +++ b/dom/base/nsXMLHttpRequest.h @@ -41,7 +41,6 @@ #undef Status #endif -class AsyncVerifyRedirectCallbackForwarder; class nsFormData; class nsIJARChannel; class nsILoadGroup; @@ -426,7 +425,8 @@ private: return Send(Nullable(aBody)); } - bool IsDeniedCrossSiteRequest(); + bool IsCrossSiteCORSRequest(); + bool IsDeniedCrossSiteCORSRequest(); // Tell our channel what network interface ID we were told to use. // If it's an HTTP channel and we were told to use a non-default @@ -605,7 +605,7 @@ protected: uint32_t count, uint32_t *writeCount); nsresult CreateResponseParsedJSON(JSContext* aCx); - void CreatePartialBlob(); + void CreatePartialBlob(ErrorResult& aRv); bool CreateDOMBlob(nsIRequest *request); // Change the state of the object with this. The broadcast argument // determines if the onreadystatechange listener should be called. @@ -620,18 +620,9 @@ protected: void ChangeStateToDone(); - /** - * Check if aChannel is ok for a cross-site request by making sure no - * inappropriate headers are set, and no username/password is set. - * - * Also updates the XML_HTTP_REQUEST_USE_XSITE_AC bit. - */ - void CheckChannelForCrossSiteRequest(nsIChannel* aChannel); - void StartProgressEventTimer(); - friend class AsyncVerifyRedirectCallbackForwarder; - void OnRedirectVerifyCallback(nsresult result); + nsresult OnRedirectVerifyCallback(nsresult result); nsresult Open(const nsACString& method, const nsACString& url, bool async, const mozilla::dom::Optional& user, @@ -840,6 +831,7 @@ private: // XMLHttpRequest via XPCOM stuff. class nsXMLHttpRequestXPCOMifier final : public nsIStreamListener, public nsIChannelEventSink, + public nsIAsyncVerifyRedirectCallback, public nsIProgressEventSink, public nsIInterfaceRequestor, public nsITimerCallback @@ -864,6 +856,7 @@ public: NS_FORWARD_NSISTREAMLISTENER(mXHR->) NS_FORWARD_NSIREQUESTOBSERVER(mXHR->) NS_FORWARD_NSICHANNELEVENTSINK(mXHR->) + NS_FORWARD_NSIASYNCVERIFYREDIRECTCALLBACK(mXHR->) NS_FORWARD_NSIPROGRESSEVENTSINK(mXHR->) NS_FORWARD_NSITIMERCALLBACK(mXHR->) diff --git a/dom/base/test/bug435425_redirect.sjs b/dom/base/test/bug435425_redirect.sjs index f6d97929c1cf..e8c8f2aa4a77 100644 --- a/dom/base/test/bug435425_redirect.sjs +++ b/dom/base/test/bug435425_redirect.sjs @@ -1,6 +1,6 @@ function handleRequest(request, response) { response.setStatusLine(null, 302, "Moved"); - response.setHeader("Location", "http://www.mozilla.org", false); + response.setHeader("Location", "http://nosuchdomain.localhost", false); } diff --git a/dom/base/test/file_bug1198095.js b/dom/base/test/file_bug1198095.js new file mode 100644 index 000000000000..d85e474019ce --- /dev/null +++ b/dom/base/test/file_bug1198095.js @@ -0,0 +1,26 @@ +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.importGlobalProperties(["File"]); + +function createFileWithData(message) { + var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); + var testFile = dirSvc.get("ProfD", Ci.nsIFile); + testFile.append("fileAPItestfileBug1198095"); + + var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + + outStream.write(message, message.length); + outStream.close(); + + var domFile = new File(testFile); + return domFile; +} + +addMessageListener("file.open", function (message) { + sendAsyncMessage("file.opened", createFileWithData(message)); +}); + +addMessageListener("file.modify", function (message) { + sendAsyncMessage("file.modified", createFileWithData(message)); +}); diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 9772af7fca31..ba013f8829d4 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -257,6 +257,7 @@ support-files = referrer_change_server.sjs file_change_policy_redirect.html empty_worker.js + file_bug1198095.js [test_anonymousContent_api.html] [test_anonymousContent_append_after_reflow.html] @@ -860,3 +861,4 @@ skip-if = e10s || os != 'linux' || buildapp != 'browser' skip-if = buildapp == 'b2g' #no ssl support [test_document.all_iteration.html] [test_performance_translate.html] +[test_bug1198095.html] diff --git a/dom/base/test/test_XHRDocURI.html b/dom/base/test/test_XHRDocURI.html index beb9eaf8501e..7a13dd27004a 100644 --- a/dom/base/test/test_XHRDocURI.html +++ b/dom/base/test/test_XHRDocURI.html @@ -371,7 +371,7 @@ function runTest() { yield undefined; - // use chrome XHR and access URI properties from chrome privileged script + // use the systemXHR special privilege SpecialPowers.addPermission("systemXHR", true, document); xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true}); xhr.open("GET", "http://mochi.test:8888/tests/dom/base/test/file_XHRDocURI.xml"); @@ -384,8 +384,7 @@ function runTest() { baseURI: "http://mochi.test:8888/tests/dom/base/test/file_XHRDocURI.xml", elementBaseURI: "http://www.example.com/" }; - var xml = SpecialPowers.wrap(xhr.responseXML); - testChromeXMLDocURI(xml, expects); + testXMLDocURI(xhr.responseXML, expects); if (xhr.readyState == 4) { gen.next(); } @@ -404,8 +403,7 @@ function runTest() { documentURI: "http://mochi.test:8888/tests/dom/base/test/file_XHRDocURI.html", baseURI: "http://mochi.test:8888/tests/dom/base/test/file_XHRDocURI.html" }; - var doc = SpecialPowers.wrap(xhr.response); - testChromeHTMLDocURI(doc, "http://mochi.test:8888/tests/dom/base/test/file_XHRDocURI.html", expects); + testHTMLDocURI(xhr.response, expects); if (xhr.readyState == 4) { gen.next(); } @@ -424,8 +422,7 @@ function runTest() { baseURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.xml", elementBaseURI: "http://www.example.com/" }; - var xml = SpecialPowers.wrap(xhr.responseXML); - testChromeXMLDocURI(xml, expects); + testXMLDocURI(xhr.responseXML, expects); if (xhr.readyState == 4) { gen.next(); } @@ -444,8 +441,7 @@ function runTest() { documentURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.html", baseURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.html" }; - var doc = SpecialPowers.wrap(xhr.response); - testChromeHTMLDocURI(doc, "http://example.com/tests/dom/base/test/file_XHRDocURI.html", expects); + testHTMLDocURI(xhr.response, expects); if (xhr.readyState == 4) { gen.next(); } @@ -464,8 +460,7 @@ function runTest() { baseURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.xml", elementBaseURI: "http://www.example.com/" }; - var xml = SpecialPowers.wrap(xhr.responseXML); - testChromeXMLDocURI(xml, expects); + testXMLDocURI(xhr.responseXML, expects); if (xhr.readyState == 4) { gen.next(); } @@ -484,8 +479,7 @@ function runTest() { documentURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.html", baseURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.html" }; - var doc = SpecialPowers.wrap(xhr.response); - testChromeHTMLDocURI(doc, "http://example.com/tests/dom/base/test/file_XHRDocURI.html", expects); + testHTMLDocURI(xhr.response, expects); if (xhr.readyState == 4) { gen.next(); } diff --git a/dom/base/test/test_bug1075702.html b/dom/base/test/test_bug1075702.html index 0798e4217041..ccee454df569 100644 --- a/dom/base/test/test_bug1075702.html +++ b/dom/base/test/test_bug1075702.html @@ -29,7 +29,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1075702 document.documentElement.setAttributeNode(a1); document.documentElement.setAttributeNode(a2); - is(document.documentElement.getAttributeNS("", "aa"), null, "Should be NULL!"); + is(document.documentElement.getAttributeNS("", "aa"), "lowercase", "Should be lowercase!"); is(document.documentElement.getAttributeNS("", "AA"), "UPPERCASE", "Should be UPPERCASE!"); var a3 = document.createAttribute("AA"); diff --git a/dom/base/test/test_bug1198095.html b/dom/base/test/test_bug1198095.html new file mode 100644 index 000000000000..dfdfa017f13c --- /dev/null +++ b/dom/base/test/test_bug1198095.html @@ -0,0 +1,77 @@ + + + + + Test for Bug 1198095 + + + + + +Mozilla Bug 1198095 + +
+
+
+ diff --git a/dom/base/test/test_bug338583.html b/dom/base/test/test_bug338583.html index a8d054d22ca7..bbf2abe216a5 100644 --- a/dom/base/test/test_bug338583.html +++ b/dom/base/test/test_bug338583.html @@ -466,7 +466,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=338583 { // credentials using the auth cache var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true}); - xhr.withCredentials = true; // also, test mixed mode UI xhr.open("GET", "https://example.com/tests/dom/base/test/file_restrictedEventSource.sjs?test=user1_xhr", true, "user 1", "password 1"); xhr.send(); @@ -495,7 +494,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=338583 function doTest5_d(test_id) { var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true}); - xhr.withCredentials = true; xhr.open("GET", "https://example.com/tests/dom/base/test/file_restrictedEventSource.sjs?test=user2_xhr", true, "user 2", "password 2"); xhr.send(); xhr.onloadend = function() { @@ -523,7 +521,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=338583 { // credentials using the auth cache var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true}); - xhr.withCredentials = true; xhr.open("GET", "http://example.org/tests/dom/base/test/file_restrictedEventSource.sjs?test=user1_xhr", true, "user 1", "password 1"); xhr.send(); xhr.onloadend = function() { @@ -551,7 +548,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=338583 function doTest5_f(test_id) { var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true}); - xhr.withCredentials = true; xhr.open("GET", "http://example.org/tests/dom/base/test/file_restrictedEventSource.sjs?test=user2_xhr", true, "user 2", "password 2"); xhr.send(); xhr.onloadend = function() { diff --git a/dom/base/test/test_bug469304.html b/dom/base/test/test_bug469304.html index 37ec497c7bce..3dae9287f355 100644 --- a/dom/base/test/test_bug469304.html +++ b/dom/base/test/test_bug469304.html @@ -27,17 +27,17 @@ function testGetAttribute() { document.body.setAttributeNode(a1); document.body.setAttributeNode(a2); var log = document.getElementById("log"); - is(document.body.getAttribute('aa'), null, "Attribute has the localName AA and not aa."); - is(document.body.getAttribute('AA'), null, "Attribute has the localName AA and not aa."); - is(document.body.getAttributeNS("", "aa"), null, "Attribute should have localName AA."); - is(document.body.getAttributeNS("", "AA"), "UPPERCASE", "Attribute should have value UPPERCASE!"); + is(document.body.getAttribute('aa'), "lowercase", "First attribute should have localname aa (1)."); + is(document.body.getAttribute('AA'), "lowercase", "First attribute should have localname aa (2)."); + is(document.body.getAttributeNS("", "aa"), "lowercase", "First attribute should have localName aa (3)."); + is(document.body.getAttributeNS("", "AA"), "UPPERCASE", "Second attribute should have value UPPERCASE!"); var s = ""; for (var i = 0; i < document.body.attributes.length; ++i) { s += document.body.attributes[i].nodeName + "=" + document.body.attributes[i].nodeValue; } - is(s, "AA=UPPERCASE", "Wrong attribute!"); + is(s, "aa=lowercaseAA=UPPERCASE", "Wrong attribute!"); is(document.body.getAttributeNode("aa"), document.body.getAttributeNode("AA"), "Wrong node!"); @@ -45,9 +45,9 @@ function testGetAttribute() { document.body.getAttributeNodeNS("", "AA").nodeValue = "FOO"; is(document.body.getAttributeNS("", "AA"), "FOO", "Wrong value!"); - document.body.removeAttributeNode(document.body.getAttributeNodeNS("", "AA")); - ok(!document.body.getAttributeNode("AA"), "Should not have attribute node!"); - ok(!document.body.getAttributeNode("aa"), "Should not have attribute node!"); + document.body.removeAttributeNode(document.body.getAttributeNodeNS("", "aa")); + ok(!document.body.getAttributeNode("AA"), "Should not have attribute node! (1)"); + ok(!document.body.getAttributeNode("aa"), "Should not have attribute node! (2)"); is(a2.nodeValue, "FOO", "Wrong value!"); a2.nodeValue = "UPPERCASE"; diff --git a/dom/base/test/test_fileapi.html b/dom/base/test/test_fileapi.html index e0299866ec5e..fc33c7ae6c04 100644 --- a/dom/base/test/test_fileapi.html +++ b/dom/base/test/test_fileapi.html @@ -382,7 +382,7 @@ function onFilesOpened(message) { testHasRun(); }; r.onload = function (event) { - todo(false, "nonexistent file shouldn't load! (FIXME: bug 1122788)"); + is(false, "nonexistent file shouldn't load! (FIXME: bug 1122788)"); testHasRun(); }; try { diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 824c7e18a4cf..2c097761cc43 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1008,6 +1008,8 @@ class CGHeaders(CGWrapper): d.interface.hasInterfaceObject() and NeedsGeneratedHasInstance(d) and d.interface.hasInterfacePrototypeObject()) + if len(hasInstanceIncludes) > 0: + hasInstanceIncludes.add("nsContentUtils.h") # Now find all the things we'll need as arguments because we # need to wrap or unwrap them. @@ -13120,7 +13122,11 @@ class CGBindingRoot(CGThing): # interface object might have a ChromeOnly constructor. (desc.interface.hasInterfaceObject() and (desc.interface.isJSImplemented() or - (ctor and isChromeOnly(ctor))))) + (ctor and isChromeOnly(ctor)))) or + # JS-implemented interfaces with clearable cached + # attrs have chromeonly _clearFoo methods. + (desc.interface.isJSImplemented() and + any(clearableCachedAttrs(desc)))) bindingHeaders["nsContentUtils.h"] = any( descriptorHasChromeOnly(d) for d in descriptors) diff --git a/dom/browser-element/BrowserElementParent.cpp b/dom/browser-element/BrowserElementParent.cpp index fdc705a823d9..4b07aa6dae2d 100644 --- a/dom/browser-element/BrowserElementParent.cpp +++ b/dom/browser-element/BrowserElementParent.cpp @@ -119,8 +119,7 @@ DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName, // Dispatch the event. // We don't initialize aStatus here, as our callers have already done so. nsresult rv = - EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr, - static_cast(event), + EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr, event, presContext, aStatus); return NS_SUCCEEDED(rv); } diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index 2b4a96ecf99e..a5e17ccb48da 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -43,7 +43,7 @@ namespace mozilla { namespace dom { NS_IMPL_ISUPPORTS(FetchDriver, - nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor, + nsIStreamListener, nsIInterfaceRequestor, nsIThreadRetargetableStreamListener) FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal, @@ -51,7 +51,6 @@ FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal, : mPrincipal(aPrincipal) , mLoadGroup(aLoadGroup) , mRequest(aRequest) - , mHasBeenCrossSite(false) , mResponseAvailableCalled(false) , mFetchCalled(false) { @@ -86,61 +85,6 @@ FetchDriver::Fetch(FetchDriverObserver* aObserver) return NS_DispatchToCurrentThread(r); } -nsresult -FetchDriver::SetTainting() -{ - workers::AssertIsOnMainThread(); - - // If we've already been cross-site then we should be fully updated - if (mHasBeenCrossSite) { - return NS_OK; - } - - nsAutoCString url; - mRequest->GetURL(url); - nsCOMPtr requestURI; - nsresult rv = NS_NewURI(getter_AddRefs(requestURI), url, - nullptr, nullptr); - NS_ENSURE_SUCCESS(rv, rv); - - // Begin Step 8 of the Main Fetch algorithm - // https://fetch.spec.whatwg.org/#fetching - - // request's current url's origin is request's origin and the CORS flag is unset - // request's current url's scheme is "data" and request's same-origin data-URL flag is set - // request's current url's scheme is "about" - - // We have to manually check about:blank here since it's not treated as - // an inheriting URL by CheckMayLoad. - if (NS_IsAboutBlank(requestURI) || - NS_SUCCEEDED(mPrincipal->CheckMayLoad(requestURI, false /* report */, - true /*allowIfInheritsPrincipal*/))) { - // What the spec calls "basic fetch" is handled within our necko channel - // code. Therefore everything goes through HTTP Fetch - return NS_OK; - } - - mHasBeenCrossSite = true; - - // request's mode is "same-origin" - if (mRequest->Mode() == RequestMode::Same_origin) { - return NS_ERROR_DOM_BAD_URI; - } - - // request's mode is "no-cors" - if (mRequest->Mode() == RequestMode::No_cors) { - mRequest->MaybeIncreaseResponseTainting(LoadTainting::Opaque); - // What the spec calls "basic fetch" is handled within our necko channel - // code. Therefore everything goes through HTTP Fetch - return NS_OK; - } - - // Otherwise - mRequest->MaybeIncreaseResponseTainting(LoadTainting::CORS); - - return NS_OK; -} - nsresult FetchDriver::ContinueFetch() { @@ -177,8 +121,14 @@ FetchDriver::HttpFetch() ios); NS_ENSURE_SUCCESS(rv, rv); - rv = SetTainting(); - NS_ENSURE_SUCCESS(rv, rv); + // Unsafe requests aren't allowed with when using no-core mode. + if (mRequest->Mode() == RequestMode::No_cors && + mRequest->UnsafeRequest() && + (!mRequest->HasSimpleMethod() || + !mRequest->Headers()->HasOnlySimpleHeaders())) { + MOZ_ASSERT(false, "The API should have caught this"); + return NS_ERROR_DOM_BAD_URI; + } // Step 2 deals with letting ServiceWorkers intercept requests. This is // handled by Necko after the channel is opened. @@ -202,35 +152,19 @@ FetchDriver::HttpFetch() // is unset or response tainting is "opaque" // is true, and unset otherwise." - // This is effectivetly the opposite of the use credentials flag in "HTTP - // network or cache fetch" in the spec and decides whether to transmit - // cookies and other identifying information. LOAD_ANONYMOUS also prevents - // new cookies sent by the server from being stored. This value will - // propagate across redirects, which is what we want. - const nsLoadFlags credentialsFlag = - (mRequest->GetCredentialsMode() == RequestCredentials::Omit || - (mHasBeenCrossSite && - mRequest->GetCredentialsMode() == RequestCredentials::Same_origin && - mRequest->Mode() == RequestMode::No_cors)) ? - nsIRequest::LOAD_ANONYMOUS : 0; - // Set skip serviceworker flag. // While the spec also gates on the client being a ServiceWorker, we can't // infer that here. Instead we rely on callers to set the flag correctly. const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker() ? nsIChannel::LOAD_BYPASS_SERVICE_WORKER : 0; - nsSecurityFlags secFlags; - if (mRequest->Mode() == RequestMode::Cors && - mRequest->GetCredentialsMode() == RequestCredentials::Include) { - secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS | - nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS; - } else if (mRequest->Mode() == RequestMode::Cors) { - secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; + nsSecurityFlags secFlags = nsILoadInfo::SEC_ABOUT_BLANK_INHERITS; + if (mRequest->Mode() == RequestMode::Cors) { + secFlags |= nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; } else if (mRequest->Mode() == RequestMode::Same_origin) { - secFlags = nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS; + secFlags |= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS; } else if (mRequest->Mode() == RequestMode::No_cors) { - secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS; + secFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS; } else { MOZ_ASSERT_UNREACHABLE("Unexpected request mode!"); return NS_ERROR_UNEXPECTED; @@ -240,20 +174,33 @@ FetchDriver::HttpFetch() secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS; } + // This is handles the use credentials flag in "HTTP + // network or cache fetch" in the spec and decides whether to transmit + // cookies and other identifying information. + if (mRequest->GetCredentialsMode() == RequestCredentials::Include) { + secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; + } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) { + secFlags |= nsILoadInfo::SEC_COOKIES_OMIT; + } else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin) { + secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; + } else { + MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!"); + return NS_ERROR_UNEXPECTED; + } + // From here on we create a channel and set its properties with the // information from the InternalRequest. This is an implementation detail. MOZ_ASSERT(mLoadGroup); nsCOMPtr chan; - nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL | credentialsFlag | + nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL | bypassFlag | nsIChannel::LOAD_CLASSIFY_URI; if (mDocument) { MOZ_ASSERT(mDocument->NodePrincipal() == mPrincipal); rv = NS_NewChannel(getter_AddRefs(chan), uri, mDocument, - secFlags | - nsILoadInfo::SEC_ABOUT_BLANK_INHERITS, + secFlags, mRequest->ContentPolicyType(), mLoadGroup, nullptr, /* aCallbacks */ @@ -263,8 +210,7 @@ FetchDriver::HttpFetch() rv = NS_NewChannel(getter_AddRefs(chan), uri, mPrincipal, - secFlags | - nsILoadInfo::SEC_ABOUT_BLANK_INHERITS, + secFlags, mRequest->ContentPolicyType(), mLoadGroup, nullptr, /* aCallbacks */ @@ -275,17 +221,6 @@ FetchDriver::HttpFetch() mLoadGroup = nullptr; - // Insert ourselves into the notification callbacks chain so we can handle - // cross-origin redirects. -#ifdef DEBUG - { - nsCOMPtr notificationCallbacks; - chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks)); - MOZ_ASSERT(!notificationCallbacks); - } -#endif - chan->SetNotificationCallbacks(this); - // FIXME(nsm): Bug 1120715. // Step 3.4 "If request's cache mode is default and request's header list // contains a header named `If-Modified-Since`, `If-None-Match`, @@ -410,24 +345,11 @@ FetchDriver::HttpFetch() // implementation is handled by the http channel calling into // nsCORSListenerProxy. We just inform it which unsafe headers are included // in the request. - if (IsUnsafeRequest()) { - if (mRequest->Mode() == RequestMode::No_cors) { - return NS_ERROR_DOM_BAD_URI; - } - - mRequest->SetRedirectMode(RequestRedirect::Error); - + if (mRequest->Mode() == RequestMode::Cors) { nsAutoTArray unsafeHeaders; mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders); - - nsCOMPtr internalChan = do_QueryInterface(httpChan); - NS_ENSURE_TRUE(internalChan, NS_ERROR_DOM_BAD_URI); - - rv = internalChan->SetCorsPreflightParameters( - unsafeHeaders, - mRequest->GetCredentialsMode() == RequestCredentials::Include, - mPrincipal); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr loadInfo = chan->GetLoadInfo(); + loadInfo->SetCorsPreflightInfo(unsafeHeaders, false); } rv = chan->AsyncOpen2(this); @@ -437,15 +359,6 @@ FetchDriver::HttpFetch() return NS_OK; } -bool -FetchDriver::IsUnsafeRequest() -{ - return mHasBeenCrossSite && - (mRequest->UnsafeRequest() && - (!mRequest->HasSimpleMethod() || - !mRequest->Headers()->HasOnlySimpleHeaders())); -} - already_AddRefed FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse, nsIURI* aFinalURI, @@ -559,6 +472,23 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest, nsCOMPtr httpChannel = do_QueryInterface(aRequest); nsCOMPtr jarChannel = do_QueryInterface(aRequest); + // On a successful redirect we perform the following substeps of HTTP Fetch, + // step 5, "redirect status", step 11. + + // Step 11.5 "Append locationURL to request's url list." so that when we set the + // Response's URL from the Request's URL in Main Fetch, step 15, we get the + // final value. Note, we still use a single URL value instead of a list. + // Because of that we only need to do this after the request finishes. + nsCOMPtr newURI; + rv = NS_GetFinalChannelURI(channel, getter_AddRefs(newURI)); + if (NS_FAILED(rv)) { + FailWithNetworkError(); + return rv; + } + nsAutoCString newUrl; + newURI->GetSpec(newUrl); + mRequest->SetURL(newUrl); + bool foundOpaqueRedirect = false; if (httpChannel) { @@ -665,18 +595,13 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest, return rv; } - LoadTainting channelTainting = LoadTainting::Basic; - if (loadInfo) { - channelTainting = loadInfo->GetTainting(); - } - // Propagate any tainting from the channel back to our response here. This // step is not reflected in the spec because the spec is written such that // FetchEvent.respondWith() just passes the already-tainted Response back to // the outer fetch(). In gecko, however, we serialize the Response through // the channel and must regenerate the tainting from the channel in the // interception case. - mRequest->MaybeIncreaseResponseTainting(channelTainting); + mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting()); // Resolves fetch() promise which may trigger code running in a worker. Make // sure the Response is fully initialized before calling this. @@ -749,143 +674,6 @@ FetchDriver::OnStopRequest(nsIRequest* aRequest, return NS_OK; } -// This is called when the channel is redirected. -NS_IMETHODIMP -FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel, - nsIChannel* aNewChannel, - uint32_t aFlags, - nsIAsyncVerifyRedirectCallback *aCallback) -{ - NS_PRECONDITION(aNewChannel, "Redirect without a channel?"); - - if (NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags) || - NS_IsHSTSUpgradeRedirect(aOldChannel, aNewChannel, aFlags)) { - aCallback->OnRedirectVerifyCallback(NS_OK); - return NS_OK; - } - - // We should only ever get here if we use a "follow" redirect policy, - // or if if we set an "error" policy as a result of a CORS policy. - MOZ_ASSERT(mRequest->GetRedirectMode() == RequestRedirect::Follow || - (mRequest->GetRedirectMode() == RequestRedirect::Error && - IsUnsafeRequest())); - - // HTTP Fetch step 5, "redirect status", step 1 - if (NS_WARN_IF(mRequest->GetRedirectMode() == RequestRedirect::Error)) { - aOldChannel->Cancel(NS_BINDING_FAILED); - return NS_BINDING_FAILED; - } - - // HTTP Fetch step 5, "redirect status", steps 2 through 6 are automatically - // handled by necko before calling AsyncOnChannelRedirect() with the new - // nsIChannel. - - // HTTP Fetch step 5, "redirect status", steps 7 and 8 enforcing a redirect - // count are done by Necko. The pref used is "network.http.redirection-limit" - // which is set to 20 by default. - - // HTTP Fetch Step 9, "redirect status". This is enforced by the - // nsCORSListenerProxy. It forbids redirecting to data: - - // HTTP Fetch step 5, "redirect status", step 10 requires us to halt the - // redirect, but successfully return an opaqueredirect Response to the - // initiating Fetch. - - // The following steps are from HTTP Fetch step 5, "redirect status", step 11 - // which requires the RequestRedirect to be "follow". We asserted that we're - // in either "follow" or "error" mode here. - - // HTTP Fetch step 5, "redirect status", steps 11.1 and 11.2 block redirecting - // to a URL with credentials in CORS mode. This is implemented in - // nsCORSListenerProxy. - - // On a successful redirect we perform the following substeps of HTTP Fetch, - // step 5, "redirect status", step 11. - - // Step 11.5 "Append locationURL to request's url list." so that when we set the - // Response's URL from the Request's URL in Main Fetch, step 15, we get the - // final value. Note, we still use a single URL value instead of a list. - nsCOMPtr newURI; - nsresult rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI)); - if (NS_FAILED(rv)) { - aOldChannel->Cancel(rv); - return rv; - } - - // We need to update our request's URL. - nsAutoCString newUrl; - newURI->GetSpec(newUrl); - mRequest->SetURL(newUrl); - - // Implement Main Fetch step 8 again on redirect. - rv = SetTainting(); - if (NS_FAILED(rv)) { - aOldChannel->Cancel(rv); - return rv; - } - - // Requests that require preflight are not permitted to redirect. - // Fetch spec section 4.2 "HTTP Fetch", step 4.9 just uses the manual - // redirect flag to decide whether to execute step 4.10 or not. We do not - // represent it in our implementation. - // The only thing we do is to check if the request requires a preflight (part - // of step 4.9), in which case we abort. This part cannot be done by - // nsCORSListenerProxy since it does not have access to mRequest. - // which case. Step 4.10.3 is handled by OnRedirectVerifyCallback(), and all - // the other steps are handled by nsCORSListenerProxy. - - if (IsUnsafeRequest()) { - // We can't handle redirects that require preflight yet. - // This is especially true for no-cors requests, which much always be - // blocked if they require preflight. - - // Simply fire an error here. - aOldChannel->Cancel(NS_BINDING_FAILED); - return NS_BINDING_FAILED; - } - - // Otherwise, we rely on necko and the CORS proxy to do the right thing - // as the redirect is followed. In general this means http - // fetch. If we've ever been CORS, we need to stay CORS. - - // Possibly set the LOAD_ANONYMOUS flag on the channel. - if (mHasBeenCrossSite && - mRequest->GetCredentialsMode() == RequestCredentials::Same_origin && - mRequest->Mode() == RequestMode::No_cors) { - // In the case of a "no-cors" mode request with "same-origin" credentials, - // we have to set LOAD_ANONYMOUS manually here in order to avoid sending - // credentials on a cross-origin redirect. - nsLoadFlags flags; - rv = aNewChannel->GetLoadFlags(&flags); - if (NS_SUCCEEDED(rv)) { - flags |= nsIRequest::LOAD_ANONYMOUS; - rv = aNewChannel->SetLoadFlags(flags); - } - if (NS_FAILED(rv)) { - aOldChannel->Cancel(rv); - return rv; - } - } -#ifdef DEBUG - { - // Make sure nothing in the redirect chain screws up our credentials - // settings. LOAD_ANONYMOUS must be set if we RequestCredentials is "omit" - // or "same-origin". - nsLoadFlags flags; - aNewChannel->GetLoadFlags(&flags); - bool shouldBeAnon = - mRequest->GetCredentialsMode() == RequestCredentials::Omit || - (mHasBeenCrossSite && - mRequest->GetCredentialsMode() == RequestCredentials::Same_origin); - MOZ_ASSERT(!!(flags & nsIRequest::LOAD_ANONYMOUS) == shouldBeAnon); - } -#endif - - aCallback->OnRedirectVerifyCallback(NS_OK); - - return NS_OK; -} - NS_IMETHODIMP FetchDriver::CheckListenerChain() { @@ -895,12 +683,6 @@ FetchDriver::CheckListenerChain() NS_IMETHODIMP FetchDriver::GetInterface(const nsIID& aIID, void **aResult) { - if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { - *aResult = static_cast(this); - NS_ADDREF_THIS(); - return NS_OK; - } - if (aIID.Equals(NS_GET_IID(nsIStreamListener))) { *aResult = static_cast(this); NS_ADDREF_THIS(); diff --git a/dom/fetch/FetchDriver.h b/dom/fetch/FetchDriver.h index 8071316a6f8e..1652192c2506 100644 --- a/dom/fetch/FetchDriver.h +++ b/dom/fetch/FetchDriver.h @@ -55,7 +55,6 @@ private: }; class FetchDriver final : public nsIStreamListener, - public nsIChannelEventSink, public nsIInterfaceRequestor, public nsIThreadRetargetableStreamListener { @@ -63,7 +62,6 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER - NS_DECL_NSICHANNELEVENTSINK NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER @@ -82,7 +80,6 @@ private: nsCOMPtr mPipeOutputStream; RefPtr mObserver; nsCOMPtr mDocument; - bool mHasBeenCrossSite; DebugOnly mResponseAvailableCalled; DebugOnly mFetchCalled; @@ -92,10 +89,8 @@ private: FetchDriver& operator=(const FetchDriver&) = delete; ~FetchDriver(); - nsresult SetTainting(); nsresult ContinueFetch(); nsresult HttpFetch(); - bool IsUnsafeRequest(); // Returns the filtered response sent to the observer. // Callers who don't have access to a channel can pass null for aFinalURI. already_AddRefed diff --git a/dom/fetch/FetchUtil.cpp b/dom/fetch/FetchUtil.cpp index 94f4dd99c5a5..943e63707b8d 100644 --- a/dom/fetch/FetchUtil.cpp +++ b/dom/fetch/FetchUtil.cpp @@ -203,7 +203,9 @@ public: bool URLParamsIterator(const nsString& aName, const nsString& aValue) override { - mFormData->Append(aName, aValue); + ErrorResult rv; + mFormData->Append(aName, aValue, rv); + MOZ_ASSERT(!rv.Failed()); return true; } @@ -392,7 +394,9 @@ private: NS_ConvertUTF8toUTF16 name(mName); if (mFilename.IsVoid()) { - mFormData->Append(name, NS_ConvertUTF8toUTF16(body)); + ErrorResult rv; + mFormData->Append(name, NS_ConvertUTF8toUTF16(body), rv); + MOZ_ASSERT(!rv.Failed()); } else { // Unfortunately we've to copy the data first since all our strings are // going to free it. We also need fallible alloc, so we can't just use @@ -417,7 +421,11 @@ private: NS_ConvertUTF8toUTF16(mFilename), NS_ConvertUTF8toUTF16(mContentType), /* aLastModifiedDate */ 0); Optional dummy; - mFormData->Append(name, *file, dummy); + ErrorResult rv; + mFormData->Append(name, *file, dummy, rv); + if (NS_WARN_IF(rv.Failed())) { + return false; + } } return true; diff --git a/dom/geolocation/nsGeolocation.cpp b/dom/geolocation/nsGeolocation.cpp index 25504f3c2051..e29dd82a38a0 100644 --- a/dom/geolocation/nsGeolocation.cpp +++ b/dom/geolocation/nsGeolocation.cpp @@ -1456,7 +1456,7 @@ Geolocation::WatchPosition(PositionCallback& aCallback, const PositionOptions& aOptions, ErrorResult& aRv) { - int32_t ret; + int32_t ret = 0; GeoPositionCallback successCallback(&aCallback); GeoPositionErrorCallback errorCallback(aErrorCallback); diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 9b02eca3fa35..2febd8fbdd67 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -1289,7 +1289,7 @@ nsresult HTMLMediaElement::LoadResource() nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS; if (GetCORSMode() == CORS_USE_CREDENTIALS) { - securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS; + securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; } MOZ_ASSERT(IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video)); diff --git a/dom/interfaces/events/nsIDOMCustomEvent.idl b/dom/interfaces/events/nsIDOMCustomEvent.idl index 8d3b26e85da3..878052719f47 100644 --- a/dom/interfaces/events/nsIDOMCustomEvent.idl +++ b/dom/interfaces/events/nsIDOMCustomEvent.idl @@ -3,11 +3,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "nsIDOMEvent.idl" +#include "nsISupports.idl" interface nsIVariant; -[builtinclass, uuid(55fa3a13-4812-45a7-98b7-3be6cec2df43)] -interface nsIDOMCustomEvent : nsIDOMEvent +[builtinclass, uuid(5be16b03-36f9-4ca8-b2c5-0daadf3cd1b3)] +interface nsIDOMCustomEvent : nsISupports { readonly attribute nsIVariant detail; diff --git a/dom/ipc/Blob.cpp b/dom/ipc/Blob.cpp index b3653cf08ff6..965a07139c79 100644 --- a/dom/ipc/Blob.cpp +++ b/dom/ipc/Blob.cpp @@ -904,13 +904,17 @@ CreateBlobImpl(const nsTArray& aBlobDatas, MOZ_ASSERT(!isMutable); } + ErrorResult rv; RefPtr blobImpl; if (!hasRecursed && aMetadata.IsFile()) { - blobImpl = - new MultipartBlobImpl(blobImpls, aMetadata.mName, aMetadata.mContentType); + blobImpl = MultipartBlobImpl::Create(blobImpls, aMetadata.mName, + aMetadata.mContentType, rv); } else { - blobImpl = - new MultipartBlobImpl(blobImpls, aMetadata.mContentType); + blobImpl = MultipartBlobImpl::Create(blobImpls, aMetadata.mContentType, rv); + } + + if (NS_WARN_IF(rv.Failed())) { + return nullptr; } MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false))); @@ -1161,9 +1165,9 @@ RemoteInputStream::ReallyBlockAndWaitForStream() #ifdef DEBUG if (waited && mWeakSeekableStream) { int64_t position; - MOZ_ASSERT(NS_SUCCEEDED(mWeakSeekableStream->Tell(&position)), - "Failed to determine initial stream position!"); - MOZ_ASSERT(!position, "Stream not starting at 0!"); + if (NS_SUCCEEDED(mWeakSeekableStream->Tell(&position))) { + MOZ_ASSERT(!position, "Stream not starting at 0!"); + } } #endif } diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index d1d61dc8004e..0628bed4d2b2 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -26,8 +26,9 @@ using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h"; using struct gfxSize from "gfxPoint.h"; using CSSRect from "Units.h"; using CSSSize from "Units.h"; -using LayoutDeviceIntRect from "Units.h"; +using mozilla::LayoutDeviceIntRect from "Units.h"; using mozilla::LayoutDeviceIntPoint from "Units.h"; +using mozilla::ScreenIntPoint from "Units.h"; using ScreenIntSize from "Units.h"; using struct mozilla::layers::FrameMetrics from "FrameMetrics.h"; using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h"; @@ -490,11 +491,11 @@ parent: uint64_t aObserverId); SynthesizeNativeTouchPoint(uint32_t aPointerId, TouchPointerState aPointerState, - IntPoint aPointerScreenPoint, + ScreenIntPoint aPointerScreenPoint, double aPointerPressure, uint32_t aPointerOrientation, uint64_t aObserverId); - SynthesizeNativeTouchTap(IntPoint aPointerScreenPoint, + SynthesizeNativeTouchTap(ScreenIntPoint aPointerScreenPoint, bool aLongTap, uint64_t aObserverId); ClearNativeTouchSequence(uint64_t aObserverId); diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index c6e2146a6d82..8262561e5c56 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -1633,7 +1633,7 @@ TabParent::RecvSynthesizeNativeMouseScrollEvent(const LayoutDeviceIntPoint& aPoi bool TabParent::RecvSynthesizeNativeTouchPoint(const uint32_t& aPointerId, const TouchPointerState& aPointerState, - const nsIntPoint& aPointerScreenPoint, + const ScreenIntPoint& aPointerScreenPoint, const double& aPointerPressure, const uint32_t& aPointerOrientation, const uint64_t& aObserverId) @@ -1648,7 +1648,7 @@ TabParent::RecvSynthesizeNativeTouchPoint(const uint32_t& aPointerId, } bool -TabParent::RecvSynthesizeNativeTouchTap(const nsIntPoint& aPointerScreenPoint, +TabParent::RecvSynthesizeNativeTouchTap(const ScreenIntPoint& aPointerScreenPoint, const bool& aLongTap, const uint64_t& aObserverId) { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 8befdbaa68d2..2a74190b04c9 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -328,11 +328,11 @@ public: const uint64_t& aObserverId) override; virtual bool RecvSynthesizeNativeTouchPoint(const uint32_t& aPointerId, const TouchPointerState& aPointerState, - const nsIntPoint& aPointerScreenPoint, + const ScreenIntPoint& aPointerScreenPoint, const double& aPointerPressure, const uint32_t& aPointerOrientation, const uint64_t& aObserverId) override; - virtual bool RecvSynthesizeNativeTouchTap(const nsIntPoint& aPointerScreenPoint, + virtual bool RecvSynthesizeNativeTouchTap(const ScreenIntPoint& aPointerScreenPoint, const bool& aLongTap, const uint64_t& aObserverId) override; virtual bool RecvClearNativeTouchSequence(const uint64_t& aObserverId) override; diff --git a/dom/media/ADTSDecoder.cpp b/dom/media/ADTSDecoder.cpp new file mode 100644 index 000000000000..2cef6298c5b9 --- /dev/null +++ b/dom/media/ADTSDecoder.cpp @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ADTSDecoder.h" +#include "ADTSDemuxer.h" +#include "MediaDecoderStateMachine.h" +#include "MediaFormatReader.h" +#include "PDMFactory.h" + +namespace mozilla { + +MediaDecoder* +ADTSDecoder::Clone(MediaDecoderOwner* aOwner) +{ + if (!IsEnabled()) + return nullptr; + + return new ADTSDecoder(aOwner); +} + +MediaDecoderStateMachine* +ADTSDecoder::CreateStateMachine() +{ + RefPtr reader = + new MediaFormatReader(this, new ADTSDemuxer(GetResource())); + return new MediaDecoderStateMachine(this, reader); +} + +/* static */ bool +ADTSDecoder::IsEnabled() +{ + PDMFactory::Init(); + RefPtr platform = new PDMFactory(); + return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm")); +} + +/* static */ bool +ADTSDecoder::CanHandleMediaType(const nsACString& aType, + const nsAString& aCodecs) +{ + if (aType.EqualsASCII("audio/aac") || aType.EqualsASCII("audio/aacp")) { + return IsEnabled() && (aCodecs.IsEmpty() || aCodecs.EqualsASCII("aac")); + } + + return false; +} + +} // namespace mozilla diff --git a/dom/media/ADTSDecoder.h b/dom/media/ADTSDecoder.h new file mode 100644 index 000000000000..1b9f50142677 --- /dev/null +++ b/dom/media/ADTSDecoder.h @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ADTS_DECODER_H_ +#define ADTS_DECODER_H_ + +#include "MediaDecoder.h" + +namespace mozilla { + +class ADTSDecoder : public MediaDecoder { +public: + // MediaDecoder interface. + explicit ADTSDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {} + MediaDecoder* Clone(MediaDecoderOwner* aOwner) override; + MediaDecoderStateMachine* CreateStateMachine() override; + + // Returns true if the MP3 backend is pref'ed on, and we're running on a + // platform that is likely to have decoders for the format. + static bool IsEnabled(); + static bool CanHandleMediaType(const nsACString& aType, + const nsAString& aCodecs); +}; + +} // namespace mozilla + +#endif // !ADTS_DECODER_H_ diff --git a/dom/media/ADTSDemuxer.cpp b/dom/media/ADTSDemuxer.cpp new file mode 100644 index 000000000000..0699fd52c950 --- /dev/null +++ b/dom/media/ADTSDemuxer.cpp @@ -0,0 +1,877 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ADTSDemuxer.h" + +#include + +#include "VideoUtils.h" +#include "TimeUnits.h" +#include "prenv.h" + +#ifdef PR_LOGGING +mozilla::LazyLogModule gADTSDemuxerLog("ADTSDemuxer"); +#define ADTSLOG(msg, ...) \ + MOZ_LOG(gADTSDemuxerLog, LogLevel::Debug, ("ADTSDemuxer " msg, ##__VA_ARGS__)) +#define ADTSLOGV(msg, ...) \ + MOZ_LOG(gADTSDemuxerLog, LogLevel::Verbose, ("ADTSDemuxer " msg, ##__VA_ARGS__)) +#else +#define ADTSLOG(msg, ...) do {} while (false) +#define ADTSLOG(msg, ...) do {} while (false) +#endif + +namespace mozilla { +namespace adts { + +// adts::FrameHeader - Holds the ADTS frame header and its parsing +// state. +// +// ADTS Frame Structure +// +// 11111111 1111BCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP(QQQQQQQQ QQQQQQQQ) +// +// Header consists of 7 or 9 bytes(without or with CRC). +// Letter Length(bits) Description +// { sync } 12 syncword 0xFFF, all bits must be 1 +// B 1 MPEG Version: 0 for MPEG-4, 1 for MPEG-2 +// C 2 Layer: always 0 +// D 1 protection absent, Warning, set to 1 if there is no +// CRC and 0 if there is CRC +// E 2 profile, the MPEG-4 Audio Object Type minus 1 +// F 4 MPEG-4 Sampling Frequency Index (15 is forbidden) +// H 3 MPEG-4 Channel Configuration (in the case of 0, the +// channel configuration is sent via an in-band PCE) +// M 13 frame length, this value must include 7 or 9 bytes of +// header length: FrameLength = +// (ProtectionAbsent == 1 ? 7 : 9) + size(AACFrame) +// O 11 Buffer fullness +// P 2 Number of AAC frames(RDBs) in ADTS frame minus 1, for +// maximum compatibility always use 1 AAC frame per ADTS +// frame +// Q 16 CRC if protection absent is 0 +class FrameHeader { +public: + uint32_t mFrameLength; + uint32_t mSampleRate; + uint32_t mSamples; + uint32_t mChannels; + uint8_t mObjectType; + uint8_t mSamplingIndex; + uint8_t mChannelConfig; + uint8_t mNumAACFrames; + bool mHaveCrc; + + // Returns whether aPtr matches a valid ADTS header sync marker + static bool MatchesSync(const uint8_t* aPtr) { + return aPtr[0] == 0xFF && (aPtr[1] & 0xF6) == 0xF0; + } + + FrameHeader() { Reset(); } + + // Header size + size_t HeaderSize() const { return (mHaveCrc) ? 9 : 7; } + + bool IsValid() const { return mFrameLength > 0; } + + // Resets the state to allow for a new parsing session. + void Reset() { PodZero(this); } + + // Returns whether the byte creates a valid sequence up to this point. + bool Parse(const uint8_t* aPtr) { + const uint8_t* p = aPtr; + + if (!MatchesSync(p)) { + return false; + } + + // AAC has 1024 samples per frame per channel. + mSamples = 1024; + + mHaveCrc = !(p[1] & 0x01); + mObjectType = ((p[2] & 0xC0) >> 6) + 1; + mSamplingIndex = (p[2] & 0x3C) >> 2; + mChannelConfig = (p[2] & 0x01) << 2 | (p[3] & 0xC0) >> 6; + mFrameLength = (p[3] & 0x03) << 11 | (p[4] & 0xFF) << 3 | (p[5] & 0xE0) >> 5; + mNumAACFrames = (p[6] & 0x03) + 1; + + static const int32_t SAMPLE_RATES[16] = { + 96000, 88200, 64000, 48000, + 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, + 7350 + }; + mSampleRate = SAMPLE_RATES[mSamplingIndex]; + + MOZ_ASSERT(mChannelConfig < 8); + mChannels = (mChannelConfig == 7) ? 8 : mChannelConfig; + + return true; + } +}; + + +// adts::Frame - Frame meta container used to parse and hold a frame +// header and side info. +class Frame { +public: + Frame() : mOffset(0), mHeader() {} + + int64_t Offset() const { return mOffset; } + size_t Length() const { + // TODO: If fields are zero'd when invalid, this check wouldn't be necessary. + if (!mHeader.IsValid()) { + return 0; + } + + return mHeader.mFrameLength; + } + + // Returns the offset to the start of frame's raw data. + int64_t PayloadOffset() const { + return mOffset + mHeader.HeaderSize(); + } + + // Returns the length of the frame's raw data (excluding the header) in bytes. + size_t PayloadLength() const { + // TODO: If fields are zero'd when invalid, this check wouldn't be necessary. + if (!mHeader.IsValid()) { + return 0; + } + + return mHeader.mFrameLength - mHeader.HeaderSize(); + } + + // Returns the parsed frame header. + const FrameHeader& Header() const { + return mHeader; + } + + bool IsValid() const { + return mHeader.IsValid(); + } + + // Resets the frame header and data. + void Reset() { + mHeader.Reset(); + mOffset = 0; + } + + // Returns whether the valid + bool Parse(int64_t aOffset, uint8_t* aStart, uint8_t* aEnd) { + MOZ_ASSERT(aStart && aEnd); + + bool found = false; + uint8_t* ptr = aStart; + // Require at least 7 bytes of data at the end of the buffer for the minimum + // ADTS frame header. + while (ptr < aEnd - 7 && !found) { + found = mHeader.Parse(ptr); + ptr++; + } + + mOffset = aOffset + (ptr - aStart) - 1; + + return found; + } + +private: + // The offset to the start of the header. + int64_t mOffset; + + // The currently parsed frame header. + FrameHeader mHeader; +}; + + +class FrameParser { +public: + + // Returns the currently parsed frame. Reset via Reset or EndFrameSession. + const Frame& CurrentFrame() const { return mFrame; } + + +#ifdef ENABLE_TESTS + // Returns the previously parsed frame. Reset via Reset. + const Frame& PrevFrame() const { return mPrevFrame; } +#endif + + // Returns the first parsed frame. Reset via Reset. + const Frame& FirstFrame() const { return mFirstFrame; } + + // Resets the parser. Don't use between frames as first frame data is reset. + void Reset() { + EndFrameSession(); + mFirstFrame.Reset(); + } + + // Clear the last parsed frame to allow for next frame parsing, i.e.: + // - sets PrevFrame to CurrentFrame + // - resets the CurrentFrame + // - resets ID3Header if no valid header was parsed yet + void EndFrameSession() { +#ifdef ENABLE_TESTS + mPrevFrame = mFrame; +#endif + mFrame.Reset(); + } + + // Parses contents of given ByteReader for a valid frame header and returns true + // if one was found. After returning, the variable passed to 'aBytesToSkip' holds + // the amount of bytes to be skipped (if any) in order to jump across a large + // ID3v2 tag spanning multiple buffers. + bool Parse(int64_t aOffset, uint8_t* aStart, uint8_t* aEnd) { + const bool found = mFrame.Parse(aOffset, aStart, aEnd); + + if (mFrame.Length() && !mFirstFrame.Length()) { + mFirstFrame = mFrame; + } + + return found; + } + +private: + // We keep the first parsed frame around for static info access, the + // previously parsed frame for debugging and the currently parsed frame. + Frame mFirstFrame; + Frame mFrame; +#ifdef ENABLE_TESTS + Frame mPrevFrame; +#endif +}; + + +// Return the AAC Profile Level Indication based upon sample rate and channels +// Information based upon table 1.10 from ISO/IEC 14496-3:2005(E) +static int8_t +ProfileLevelIndication(const Frame& frame) +{ + const FrameHeader& header = frame.Header(); + MOZ_ASSERT(header.IsValid()); + + if (!header.IsValid()) { + return 0; + } + + const int channels = header.mChannels; + const int sampleRate = header.mSampleRate; + + if (channels <= 2) { + if (sampleRate <= 24000) { + // AAC Profile L1 + return 0x28; + } + else if (sampleRate <= 48000) { + // AAC Profile L2 + return 0x29; + } + } + else if (channels <= 5) { + if (sampleRate <= 48000) { + // AAC Profile L4 + return 0x2A; + } + else if (sampleRate <= 96000) { + // AAC Profile L5 + return 0x2B; + } + } + + // TODO: Should this be 0xFE for 'no audio profile specified'? + return 0; +} + + +// Initialize the AAC AudioSpecificConfig. +// Only handles two-byte version for AAC-LC. +static void +InitAudioSpecificConfig(const Frame& frame, + MediaByteBuffer* aBuffer) +{ + const FrameHeader& header = frame.Header(); + MOZ_ASSERT(header.IsValid()); + + int audioObjectType = header.mObjectType; + int samplingFrequencyIndex = header.mSamplingIndex; + int channelConfig = header.mChannelConfig; + + uint8_t asc[2]; + asc[0] = (audioObjectType & 0x1F) << 3 | (samplingFrequencyIndex & 0x0E) >> 1; + asc[1] = (samplingFrequencyIndex & 0x01) << 7 | (channelConfig & 0x0F) << 3; + + aBuffer->AppendElements(asc, 2); +} + +} // namespace adts + +// ADTSDemuxer + +ADTSDemuxer::ADTSDemuxer(MediaResource* aSource) + : mSource(aSource) +{} + +bool +ADTSDemuxer::InitInternal() +{ + if (!mTrackDemuxer) { + mTrackDemuxer = new ADTSTrackDemuxer(mSource); + } + return mTrackDemuxer->Init(); +} + +RefPtr +ADTSDemuxer::Init() +{ + if (!InitInternal()) { + ADTSLOG("Init() failure: waiting for data"); + + return InitPromise::CreateAndReject( + DemuxerFailureReason::DEMUXER_ERROR, __func__); + } + + ADTSLOG("Init() successful"); + return InitPromise::CreateAndResolve(NS_OK, __func__); +} + +bool +ADTSDemuxer::HasTrackType(TrackInfo::TrackType aType) const +{ + return aType == TrackInfo::kAudioTrack; +} + +uint32_t +ADTSDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const +{ + return (aType == TrackInfo::kAudioTrack) ? 1 : 0; +} + +already_AddRefed +ADTSDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber) +{ + if (!mTrackDemuxer) { + return nullptr; + } + + return RefPtr(mTrackDemuxer).forget(); +} + +bool +ADTSDemuxer::IsSeekable() const +{ + int64_t length = mSource->GetLength(); + if (length > -1) + return true; + return false; +} + + +// ADTSTrackDemuxer +ADTSTrackDemuxer::ADTSTrackDemuxer(MediaResource* aSource) + : mSource(aSource) + , mParser(new adts::FrameParser()) + , mOffset(0) + , mNumParsedFrames(0) + , mFrameIndex(0) + , mTotalFrameLen(0) + , mSamplesPerFrame(0) + , mSamplesPerSecond(0) + , mChannels(0) +{ + Reset(); +} + +ADTSTrackDemuxer::~ADTSTrackDemuxer() +{ + delete mParser; + mParser = nullptr; +} + +bool +ADTSTrackDemuxer::Init() +{ + + FastSeek(media::TimeUnit()); + // Read the first frame to fetch sample rate and other meta data. + RefPtr frame(GetNextFrame(FindNextFrame(true))); + + ADTSLOG("Init StreamLength()=%" PRId64 " first-frame-found=%d", + StreamLength(), !!frame); + + if (!frame) { + return false; + } + + // Rewind back to the stream begin to avoid dropping the first frame. + FastSeek(media::TimeUnit()); + + if (!mInfo) { + mInfo = MakeUnique(); + } + + mInfo->mRate = mSamplesPerSecond; + mInfo->mChannels = mChannels; + mInfo->mBitDepth = 16; + mInfo->mDuration = Duration().ToMicroseconds(); + + // AAC Specific information + mInfo->mMimeType = "audio/mp4a-latm"; + + // Configure AAC codec-specific values. + + // According to + // https://msdn.microsoft.com/en-us/library/windows/desktop/dd742784%28v=vs.85%29.aspx, + // wAudioProfileLevelIndication, which is passed mInfo->mProfile, is + // a value from Table 1.12 -- audioProfileLevelIndication values, ISO/IEC 14496-3. + mInfo->mProfile = ProfileLevelIndication(mParser->FirstFrame()); + // For AAC, mExtendedProfile contains the audioObjectType from Table + // 1.3 -- Audio Profile definition, ISO/IEC 14496-3. Eg. 2 == AAC LC + mInfo->mExtendedProfile = mParser->FirstFrame().Header().mObjectType; + InitAudioSpecificConfig(mParser->FirstFrame(), mInfo->mCodecSpecificConfig); + + ADTSLOG("Init mInfo={mRate=%u mChannels=%u mBitDepth=%u mDuration=%" PRId64 "}", + mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth, mInfo->mDuration); + + return mSamplesPerSecond && mChannels; +} + +#ifdef ENABLE_TESTS +const adts::Frame& +ADTSTrackDemuxer::LastFrame() const +{ + return mParser->PrevFrame(); +} + +RefPtr +ADTSTrackDemuxer::DemuxSample() +{ + return GetNextFrame(FindNextFrame()); +} + +media::TimeUnit +ADTSTrackDemuxer::SeekPosition() const +{ + return Duration(mFrameIndex); +} +#endif + +UniquePtr +ADTSTrackDemuxer::GetInfo() const +{ + return mInfo->Clone(); +} + +RefPtr +ADTSTrackDemuxer::Seek(media::TimeUnit aTime) +{ + // Efficiently seek to the position. + FastSeek(aTime); + // Correct seek position by scanning the next frames. + const media::TimeUnit seekTime = ScanUntil(aTime); + + return SeekPromise::CreateAndResolve(seekTime, __func__); +} + +media::TimeUnit +ADTSTrackDemuxer::FastSeek(const media::TimeUnit& aTime) +{ + ADTSLOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mOffset=%" PRIu64, + aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset); + + const int64_t firstFrameOffset = mParser->FirstFrame().Offset(); + if (!aTime.ToMicroseconds()) { + // Quick seek to the beginning of the stream. + mOffset = firstFrameOffset; + } else if (AverageFrameLength() > 0) { + mOffset = firstFrameOffset + FrameIndexFromTime(aTime) * + AverageFrameLength(); + } + + if (mOffset > firstFrameOffset && StreamLength() > 0) { + mOffset = std::min(StreamLength() - 1, mOffset); + } + + mFrameIndex = FrameIndexFromOffset(mOffset); + mParser->EndFrameSession(); + + ADTSLOG("FastSeek End avgFrameLen=%f mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mFirstFrameOffset=%llu mOffset=%" PRIu64 + " SL=%llu", + AverageFrameLength(), mNumParsedFrames, mFrameIndex, + firstFrameOffset, mOffset, StreamLength()); + + return Duration(mFrameIndex); +} + +media::TimeUnit +ADTSTrackDemuxer::ScanUntil(const media::TimeUnit& aTime) +{ + ADTSLOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mOffset=%" PRIu64, + aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset); + + if (!aTime.ToMicroseconds()) { + return FastSeek(aTime); + } + + if (Duration(mFrameIndex) > aTime) { + FastSeek(aTime); + } + + while (SkipNextFrame(FindNextFrame()) && Duration(mFrameIndex + 1) < aTime) { + ADTSLOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64, + aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, + mOffset, Duration(mFrameIndex + 1)); + } + + ADTSLOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mOffset=%" PRIu64, + aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset); + + return Duration(mFrameIndex); +} + +RefPtr +ADTSTrackDemuxer::GetSamples(int32_t aNumSamples) +{ + ADTSLOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d " + "mSamplesPerSecond=%d mChannels=%d", + aNumSamples, mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, + mSamplesPerFrame, mSamplesPerSecond, mChannels); + + if (!aNumSamples) { + return SamplesPromise::CreateAndReject( + DemuxerFailureReason::DEMUXER_ERROR, __func__); + } + + RefPtr frames = new SamplesHolder(); + + while (aNumSamples--) { + RefPtr frame(GetNextFrame(FindNextFrame())); + if (!frame) + break; + + frames->mSamples.AppendElement(frame); + } + + ADTSLOGV("GetSamples() End mSamples.Size()=%d aNumSamples=%d mOffset=%" PRIu64 + " mNumParsedFrames=%" PRIu64 " mFrameIndex=%" PRId64 + " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d " + "mChannels=%d", + frames->mSamples.Length(), aNumSamples, mOffset, mNumParsedFrames, + mFrameIndex, mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, + mChannels); + + if (frames->mSamples.IsEmpty()) { + return SamplesPromise::CreateAndReject( + DemuxerFailureReason::END_OF_STREAM, __func__); + } + + return SamplesPromise::CreateAndResolve(frames, __func__); +} + +void +ADTSTrackDemuxer::Reset() +{ + ADTSLOG("Reset()"); + MOZ_ASSERT(mParser); + if (mParser) { + mParser->Reset(); + } + FastSeek(media::TimeUnit()); +} + +RefPtr +ADTSTrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold) +{ + // Will not be called for audio-only resources. + return SkipAccessPointPromise::CreateAndReject( + SkipFailureHolder(DemuxerFailureReason::DEMUXER_ERROR, 0), __func__); +} + +int64_t +ADTSTrackDemuxer::GetResourceOffset() const +{ + return mOffset; +} + +media::TimeIntervals +ADTSTrackDemuxer::GetBuffered() +{ + media::TimeUnit duration = Duration(); + + if (duration <= media::TimeUnit()) { + return media::TimeIntervals(); + } + + AutoPinned stream(mSource.GetResource()); + return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds()); +} + +int64_t +ADTSTrackDemuxer::StreamLength() const +{ + return mSource.GetLength(); +} + +media::TimeUnit +ADTSTrackDemuxer::Duration() const +{ + if (!mNumParsedFrames) { + return media::TimeUnit::FromMicroseconds(-1); + } + + const int64_t streamLen = StreamLength(); + if (streamLen < 0) { + // Unknown length, we can't estimate duration. + return media::TimeUnit::FromMicroseconds(-1); + } + const int64_t firstFrameOffset = mParser->FirstFrame().Offset(); + int64_t numFrames = (streamLen - firstFrameOffset) / AverageFrameLength(); + return Duration(numFrames); +} + +media::TimeUnit +ADTSTrackDemuxer::Duration(int64_t aNumFrames) const +{ + if (!mSamplesPerSecond) { + return media::TimeUnit::FromMicroseconds(-1); + } + + const double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond; + return media::TimeUnit::FromMicroseconds(aNumFrames * usPerFrame); +} + +const adts::Frame& +ADTSTrackDemuxer::FindNextFrame(bool findFirstFrame /*= false*/) +{ + static const int BUFFER_SIZE = 4096; + static const int MAX_SKIPPED_BYTES = 10 * BUFFER_SIZE; + + ADTSLOGV("FindNext() Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 + " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d", + mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, + mSamplesPerFrame, mSamplesPerSecond, mChannels); + + uint8_t buffer[BUFFER_SIZE]; + int32_t read = 0; + + bool foundFrame = false; + int64_t frameHeaderOffset = mOffset; + + // Prepare the parser for the next frame parsing session. + mParser->EndFrameSession(); + + // Check whether we've found a valid ADTS frame. + while (!foundFrame) { + if ((read = Read(buffer, frameHeaderOffset, BUFFER_SIZE)) == 0) { + ADTSLOG("FindNext() EOS without a frame"); + break; + } + + if (frameHeaderOffset - mOffset > MAX_SKIPPED_BYTES) { + ADTSLOG("FindNext() exceeded MAX_SKIPPED_BYTES without a frame"); + break; + } + + const adts::Frame& currentFrame = mParser->CurrentFrame(); + foundFrame = mParser->Parse(frameHeaderOffset, buffer, buffer + read); + if (findFirstFrame && foundFrame) { + // Check for sync marker after the found frame, since it's + // possible to find sync marker in AAC data. If sync marker + // exists after the current frame then we've found a frame + // header. + int64_t nextFrameHeaderOffset = currentFrame.Offset() + currentFrame.Length(); + int32_t read = Read(buffer, nextFrameHeaderOffset, 2); + if (read != 2 || !adts::FrameHeader::MatchesSync(buffer)) { + frameHeaderOffset = currentFrame.Offset() + 1; + mParser->Reset(); + foundFrame = false; + continue; + } + } + + if (foundFrame) { + break; + } + + // Minimum header size is 7 bytes. + int64_t advance = read - 7; + + // Check for offset overflow. + if (frameHeaderOffset + advance <= frameHeaderOffset) { + break; + } + + frameHeaderOffset += advance; + } + + if (!foundFrame || !mParser->CurrentFrame().Length()) { + ADTSLOG("FindNext() Exit foundFrame=%d mParser->CurrentFrame().Length()=%d ", + foundFrame, mParser->CurrentFrame().Length()); + mParser->Reset(); + return mParser->CurrentFrame(); + } + + ADTSLOGV("FindNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " frameHeaderOffset=%d" + " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d" + " mChannels=%d", + mOffset, mNumParsedFrames, mFrameIndex, frameHeaderOffset, + mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, mChannels); + + return mParser->CurrentFrame(); +} + +bool +ADTSTrackDemuxer::SkipNextFrame(const adts::Frame& aFrame) +{ + if (!mNumParsedFrames || !aFrame.Length()) { + RefPtr frame(GetNextFrame(aFrame)); + return frame; + } + + UpdateState(aFrame); + + ADTSLOGV("SkipNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 + " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d", + mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, + mSamplesPerFrame, mSamplesPerSecond, mChannels); + + return true; +} + +already_AddRefed +ADTSTrackDemuxer::GetNextFrame(const adts::Frame& aFrame) +{ + ADTSLOG("GetNext() Begin({mOffset=%" PRId64 " HeaderSize()=%d Length()=%d})", + aFrame.Offset(), aFrame.Header().HeaderSize(), aFrame.PayloadLength()); + if (!aFrame.IsValid()) + return nullptr; + + const int64_t offset = aFrame.PayloadOffset(); + const uint32_t length = aFrame.PayloadLength(); + + RefPtr frame = new MediaRawData(); + frame->mOffset = offset; + + nsAutoPtr frameWriter(frame->CreateWriter()); + if (!frameWriter->SetSize(length)) { + ADTSLOG("GetNext() Exit failed to allocated media buffer"); + return nullptr; + } + + const uint32_t read = Read(frameWriter->Data(), offset, length); + if (read != length) { + ADTSLOG("GetNext() Exit read=%u frame->Size()=%u", read, frame->Size()); + return nullptr; + } + + UpdateState(aFrame); + + frame->mTime = Duration(mFrameIndex - 1).ToMicroseconds(); + frame->mDuration = Duration(1).ToMicroseconds(); + frame->mTimecode = frame->mTime; + frame->mKeyframe = true; + + MOZ_ASSERT(frame->mTime >= 0); + MOZ_ASSERT(frame->mDuration > 0); + + ADTSLOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 + " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d", + mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, + mSamplesPerFrame, mSamplesPerSecond, mChannels); + + return frame.forget(); +} + +int64_t +ADTSTrackDemuxer::FrameIndexFromOffset(int64_t aOffset) const +{ + int64_t frameIndex = 0; + + if (AverageFrameLength() > 0) { + frameIndex = (aOffset - mParser->FirstFrame().Offset()) / AverageFrameLength(); + } + + ADTSLOGV("FrameIndexFromOffset(%" PRId64 ") -> %" PRId64, aOffset, frameIndex); + return std::max(0, frameIndex); +} + +int64_t +ADTSTrackDemuxer::FrameIndexFromTime(const media::TimeUnit& aTime) const +{ + int64_t frameIndex = 0; + if (mSamplesPerSecond > 0 && mSamplesPerFrame > 0) { + frameIndex = aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerFrame - 1; + } + + ADTSLOGV("FrameIndexFromOffset(%fs) -> %" PRId64, aTime.ToSeconds(), frameIndex); + return std::max(0, frameIndex); +} + +void +ADTSTrackDemuxer::UpdateState(const adts::Frame& aFrame) +{ + int32_t frameLength = aFrame.Length(); + // Prevent overflow. + if (mTotalFrameLen + frameLength < mTotalFrameLen) { + // These variables have a linear dependency and are only used to derive the + // average frame length. + mTotalFrameLen /= 2; + mNumParsedFrames /= 2; + } + + // Full frame parsed, move offset to its end. + mOffset = aFrame.Offset() + frameLength; + mTotalFrameLen += frameLength; + + if (!mSamplesPerFrame) { + const adts::FrameHeader& header = aFrame.Header(); + mSamplesPerFrame = header.mSamples; + mSamplesPerSecond = header.mSampleRate; + mChannels = header.mChannels; + } + + ++mNumParsedFrames; + ++mFrameIndex; + MOZ_ASSERT(mFrameIndex > 0); +} + +int32_t +ADTSTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize) +{ + ADTSLOGV("ADTSTrackDemuxer::Read(%p %" PRId64 " %d)", aBuffer, aOffset, aSize); + + const int64_t streamLen = StreamLength(); + if (mInfo && streamLen > 0) { + // Prevent blocking reads after successful initialization. + aSize = std::min(aSize, streamLen - aOffset); + } + + uint32_t read = 0; + ADTSLOGV("ADTSTrackDemuxer::Read -> ReadAt(%d)", aSize); + const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast(aBuffer), + static_cast(aSize), &read); + NS_ENSURE_SUCCESS(rv, 0); + return static_cast(read); +} + +double +ADTSTrackDemuxer::AverageFrameLength() const +{ + if (mNumParsedFrames) { + return static_cast(mTotalFrameLen) / mNumParsedFrames; + } + + return 0.0; +} + +} // namespace mozilla diff --git a/dom/media/ADTSDemuxer.h b/dom/media/ADTSDemuxer.h new file mode 100644 index 000000000000..996cb143a41c --- /dev/null +++ b/dom/media/ADTSDemuxer.h @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ADTS_DEMUXER_H_ +#define ADTS_DEMUXER_H_ + +#include "mozilla/Attributes.h" +#include "mozilla/Maybe.h" +#include "MediaDataDemuxer.h" +#include "MediaResource.h" +#include "mp4_demuxer/ByteReader.h" + +namespace mozilla { + +namespace adts { +class Frame; +class FrameParser; +} + +class ADTSTrackDemuxer; + +class ADTSDemuxer : public MediaDataDemuxer { +public: + // MediaDataDemuxer interface. + explicit ADTSDemuxer(MediaResource* aSource); + RefPtr Init() override; + bool HasTrackType(TrackInfo::TrackType aType) const override; + uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override; + already_AddRefed GetTrackDemuxer( + TrackInfo::TrackType aType, uint32_t aTrackNumber) override; + bool IsSeekable() const override; + bool ShouldComputeStartTime() const override { return false; } + +private: + bool InitInternal(); + + RefPtr mSource; + RefPtr mTrackDemuxer; +}; + +class ADTSTrackDemuxer : public MediaTrackDemuxer { +public: + explicit ADTSTrackDemuxer(MediaResource* aSource); + + // Initializes the track demuxer by reading the first frame for meta data. + // Returns initialization success state. + bool Init(); + + // Returns the total stream length if known, -1 otherwise. + int64_t StreamLength() const; + + // Returns the estimated stream duration, or a 0-duration if unknown. + media::TimeUnit Duration() const; + + // Returns the estimated duration up to the given frame number, + // or a 0-duration if unknown. + media::TimeUnit Duration(int64_t aNumFrames) const; + +#ifdef ENABLE_TESTS + const adts::Frame& LastFrame() const; + RefPtr DemuxSample(); + media::TimeUnit SeekPosition() const; +#endif + + // MediaTrackDemuxer interface. + UniquePtr GetInfo() const override; + RefPtr Seek(media::TimeUnit aTime) override; + RefPtr GetSamples(int32_t aNumSamples = 1) override; + void Reset() override; + RefPtr SkipToNextRandomAccessPoint( + media::TimeUnit aTimeThreshold) override; + int64_t GetResourceOffset() const override; + media::TimeIntervals GetBuffered() override; + +private: + // Destructor. + ~ADTSTrackDemuxer(); + + // Fast approximate seeking to given time. + media::TimeUnit FastSeek(const media::TimeUnit& aTime); + + // Seeks by scanning the stream up to the given time for more accurate results. + media::TimeUnit ScanUntil(const media::TimeUnit& aTime); + + // Finds the next valid frame and returns its byte range. + const adts::Frame& FindNextFrame(bool findFirstFrame = false); + + // Skips the next frame given the provided byte range. + bool SkipNextFrame(const adts::Frame& aFrame); + + // Returns the next ADTS frame, if available. + already_AddRefed GetNextFrame(const adts::Frame& aFrame); + + // Updates post-read meta data. + void UpdateState(const adts::Frame& aFrame); + + // Returns the frame index for the given offset. + int64_t FrameIndexFromOffset(int64_t aOffset) const; + + // Returns the frame index for the given time. + int64_t FrameIndexFromTime(const media::TimeUnit& aTime) const; + + // Reads aSize bytes into aBuffer from the source starting at aOffset. + // Returns the actual size read. + int32_t Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize); + + // Returns the average frame length derived from the previously parsed frames. + double AverageFrameLength() const; + + // The (hopefully) ADTS resource. + MediaResourceIndex mSource; + + // ADTS frame parser used to detect frames and extract side info. + adts::FrameParser* mParser; + + // Current byte offset in the source stream. + int64_t mOffset; + + // Total parsed frames. + uint64_t mNumParsedFrames; + + // Current frame index. + int64_t mFrameIndex; + + // Sum of parsed frames' lengths in bytes. + uint64_t mTotalFrameLen; + + // Samples per frame metric derived from frame headers or 0 if none available. + uint32_t mSamplesPerFrame; + + // Samples per second metric derived from frame headers or 0 if none available. + uint32_t mSamplesPerSecond; + + // Channel count derived from frame headers or 0 if none available. + uint32_t mChannels; + + // Audio track config info. + UniquePtr mInfo; +}; + +} // mozilla + +#endif // !ADTS_DEMUXER_H_ diff --git a/dom/media/DecoderTraits.cpp b/dom/media/DecoderTraits.cpp index f607204a3b2d..3bcb45006522 100644 --- a/dom/media/DecoderTraits.cpp +++ b/dom/media/DecoderTraits.cpp @@ -57,6 +57,9 @@ #include "MP3Decoder.h" #include "MP3Demuxer.h" +#include "ADTSDecoder.h" +#include "ADTSDemuxer.h" + namespace mozilla { @@ -354,6 +357,13 @@ IsMP3SupportedType(const nsACString& aType, return MP3Decoder::CanHandleMediaType(aType, aCodecs); } +static bool +IsAACSupportedType(const nsACString& aType, + const nsAString& aCodecs = EmptyString()) +{ + return ADTSDecoder::CanHandleMediaType(aType, aCodecs); +} + /* static */ bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType) { @@ -414,6 +424,9 @@ DecoderTraits::CanHandleCodecsType(const char* aMIMEType, if (IsMP3SupportedType(nsDependentCString(aMIMEType), aRequestedCodecs)) { return CANPLAY_YES; } + if (IsAACSupportedType(nsDependentCString(aMIMEType), aRequestedCodecs)) { + return CANPLAY_YES; + } #ifdef MOZ_OMX_DECODER if (IsOmxSupportedType(nsDependentCString(aMIMEType))) { if (nsDependentCString(aMIMEType).EqualsASCII("audio/mpeg")) { @@ -499,6 +512,9 @@ DecoderTraits::CanHandleMediaType(const char* aMIMEType, if (IsMP3SupportedType(nsDependentCString(aMIMEType))) { return CANPLAY_MAYBE; } + if (IsAACSupportedType(nsDependentCString(aMIMEType))) { + return CANPLAY_MAYBE; + } #ifdef MOZ_GSTREAMER if (GStreamerDecoder::CanHandleMediaType(nsDependentCString(aMIMEType), aHaveRequestedCodecs ? &aRequestedCodecs : nullptr)) { @@ -547,6 +563,10 @@ InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner) decoder = new MP3Decoder(aOwner); return decoder.forget(); } + if (IsAACSupportedType(aType)) { + decoder = new ADTSDecoder(aOwner); + return decoder.forget(); + } #ifdef MOZ_GSTREAMER if (IsGStreamerSupportedType(aType)) { decoder = new GStreamerDecoder(aOwner); @@ -646,6 +666,9 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac if (IsMP3SupportedType(aType)) { decoderReader = new MediaFormatReader(aDecoder, new mp3::MP3Demuxer(aDecoder->GetResource())); } else + if (IsAACSupportedType(aType)) { + decoderReader = new MediaFormatReader(aDecoder, new ADTSDemuxer(aDecoder->GetResource())); + } else #ifdef MOZ_GSTREAMER if (IsGStreamerSupportedType(aType)) { decoderReader = new GStreamerReader(aDecoder); @@ -725,6 +748,7 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType) IsMP4SupportedType(aType) || #endif IsMP3SupportedType(aType) || + IsAACSupportedType(aType) || #ifdef MOZ_DIRECTSHOW IsDirectShowSupportedType(aType) || #endif diff --git a/dom/media/GraphDriver.cpp b/dom/media/GraphDriver.cpp index e3cdacad5bb0..a40def3afb14 100644 --- a/dom/media/GraphDriver.cpp +++ b/dom/media/GraphDriver.cpp @@ -4,6 +4,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include "mozilla/dom/AudioContext.h" #include "CubebUtils.h" #ifdef XP_MACOSX diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 82b8a334335e..097cfcffd6ac 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -1248,6 +1248,7 @@ MediaFormatReader::Flush(TrackType aTrack) auto& decoder = GetDecoderData(aTrack); if (!decoder.mDecoder) { + decoder.ResetState(); return; } diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index b6c91622535c..82d67210c8cf 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -1191,7 +1191,7 @@ MediaManager::SelectSettings( sources.Clear(); const char* badConstraint = nullptr; - if (IsOn(aConstraints.mVideo)) { + if (videos.Length() && IsOn(aConstraints.mVideo)) { badConstraint = MediaConstraintsHelper::SelectSettings( GetInvariant(aConstraints.mVideo), videos); for (auto& video : videos) { @@ -1427,6 +1427,8 @@ MediaManager::EnumerateRawDevices(uint64_t aWindowId, bool aFake, bool aFakeTracks) { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aVideoType != MediaSourceEnum::Other || + aAudioType != MediaSourceEnum::Other); RefPtr p = new PledgeSourceSet(); uint32_t id = mOutstandingPledges.Append(*p); @@ -1452,30 +1454,39 @@ MediaManager::EnumerateRawDevices(uint64_t aWindowId, videoLoopDev, aVideoType, aAudioType, aFake, aFakeTracks]() mutable { - RefPtr backend; - if (aFake) { - backend = new MediaEngineDefault(aFakeTracks); - } else { + // Only enumerate what's asked for, and only fake cams and mics. + bool hasVideo = aVideoType != MediaSourceEnum::Other; + bool hasAudio = aAudioType != MediaSourceEnum::Other; + bool fakeCams = aFake && aVideoType == MediaSourceEnum::Camera; + bool fakeMics = aFake && aAudioType == MediaSourceEnum::Microphone; + + RefPtr fakeBackend, realBackend; + if (fakeCams || fakeMics) { + fakeBackend = new MediaEngineDefault(aFakeTracks); + } + if ((!fakeCams && hasVideo) || (!fakeMics && hasAudio)) { RefPtr manager = MediaManager_GetInstance(); - backend = manager->GetBackend(aWindowId); + realBackend = manager->GetBackend(aWindowId); } ScopedDeletePtr result(new SourceSet); - nsTArray> videos; - GetSources(backend, aVideoType, &MediaEngine::EnumerateVideoDevices, videos, - videoLoopDev); - for (auto& source : videos) { - result->AppendElement(source); + if (hasVideo) { + nsTArray> videos; + GetSources(fakeCams? fakeBackend : realBackend, aVideoType, + &MediaEngine::EnumerateVideoDevices, videos, videoLoopDev); + for (auto& source : videos) { + result->AppendElement(source); + } } - - nsTArray> audios; - GetSources(backend, aAudioType, - &MediaEngine::EnumerateAudioDevices, audios, audioLoopDev); - for (auto& source : audios) { - result->AppendElement(source); + if (hasAudio) { + nsTArray> audios; + GetSources(fakeMics? fakeBackend : realBackend, aAudioType, + &MediaEngine::EnumerateAudioDevices, audios, audioLoopDev); + for (auto& source : audios) { + result->AppendElement(source); + } } - SourceSet* handoff = result.forget(); NS_DispatchToMainThread(do_AddRef(NewRunnableFrom([id, handoff]() mutable { ScopedDeletePtr result(handoff); // grab result @@ -1488,7 +1499,7 @@ MediaManager::EnumerateRawDevices(uint64_t aWindowId, p->Resolve(result.forget()); } return NS_OK; - }))); + }))); })); return p.forget(); } @@ -1867,8 +1878,8 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, c.mVideo.SetAsBoolean() = false; } - MediaSourceEnum videoType = dom::MediaSourceEnum::Camera; - MediaSourceEnum audioType = dom::MediaSourceEnum::Microphone; + MediaSourceEnum videoType = dom::MediaSourceEnum::Other; // none + MediaSourceEnum audioType = dom::MediaSourceEnum::Other; // none if (c.mVideo.IsMediaTrackConstraints()) { auto& vc = c.mVideo.GetAsMediaTrackConstraints(); @@ -1971,6 +1982,8 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, videoType == dom::MediaSourceEnum::Screen)) { privileged = false; } + } else if (IsOn(c.mVideo)) { + videoType = dom::MediaSourceEnum::Camera; } if (c.mAudio.IsMediaTrackConstraints()) { @@ -2025,7 +2038,10 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, } } } + } else if (IsOn(c.mAudio)) { + audioType = dom::MediaSourceEnum::Microphone; } + StreamListeners* listeners = AddWindowID(windowID); // Create a disabled listener to act as a placeholder @@ -2088,8 +2104,8 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, (!fake || Preferences::GetBool("media.navigator.permission.fake")); RefPtr p = EnumerateDevicesImpl(windowID, videoType, - audioType, fake, - fakeTracks); + audioType, fake, + fakeTracks); p->Then([this, onSuccess, onFailure, windowID, c, listener, askPermission, prefs, isHTTPS, callID, origin](SourceSet*& aDevices) mutable { @@ -2309,8 +2325,8 @@ MediaManager::EnumerateDevicesImpl(uint64_t aWindowId, RefPtr mgr = MediaManager_GetInstance(); RefPtr p = mgr->EnumerateRawDevices(aWindowId, - aVideoType, aAudioType, - aFake, aFakeTracks); + aVideoType, aAudioType, + aFake, aFakeTracks); p->Then([id, aWindowId, aOriginKey](SourceSet*& aDevices) mutable { ScopedDeletePtr devices(aDevices); // secondary result diff --git a/dom/media/mediasource/MediaSourceDemuxer.cpp b/dom/media/mediasource/MediaSourceDemuxer.cpp index 54a081cd8b2d..4011ce77339b 100644 --- a/dom/media/mediasource/MediaSourceDemuxer.cpp +++ b/dom/media/mediasource/MediaSourceDemuxer.cpp @@ -295,6 +295,7 @@ MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent, , mManager(aManager) , mType(aType) , mMonitor("MediaSourceTrackDemuxer") + , mLastSeek(Some(TimeUnit())) { } @@ -327,6 +328,7 @@ MediaSourceTrackDemuxer::Reset() RefPtr self = this; nsCOMPtr task = NS_NewRunnableFunction([self] () { + self->mLastSeek = Some(TimeUnit()); self->mManager->Seek(self->mType, TimeUnit(), TimeUnit()); { MonitorAutoLock mon(self->mMonitor); @@ -378,6 +380,7 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime) buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ); if (!buffered.Contains(aTime)) { + mLastSeek = Some(aTime); // We don't have the data to seek to. return SeekPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA, __func__); @@ -388,12 +391,26 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime) MonitorAutoLock mon(mMonitor); mNextRandomAccessPoint = mManager->GetNextRandomAccessPoint(mType); } + mLastSeek = Some(aTime); return SeekPromise::CreateAndResolve(seekTime, __func__); } RefPtr MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples) { + if (mLastSeek) { + // If a seek (or reset) was recently performed, we ensure that the data + // we are about to retrieve is still available. + TimeIntervals buffered = mManager->Buffered(mType); + buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ); + + if (!buffered.Contains(mLastSeek.ref())) { + return SamplesPromise::CreateAndReject( + mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM : + DemuxerFailureReason::WAITING_FOR_DATA, __func__); + } + mLastSeek.reset(); + } bool error; RefPtr sample = mManager->GetSample(mType, diff --git a/dom/media/mediasource/MediaSourceDemuxer.h b/dom/media/mediasource/MediaSourceDemuxer.h index 7bf02a345d7a..f32746210d51 100644 --- a/dom/media/mediasource/MediaSourceDemuxer.h +++ b/dom/media/mediasource/MediaSourceDemuxer.h @@ -129,6 +129,7 @@ private: // Monitor protecting members below accessed from multiple threads. Monitor mMonitor; media::TimeUnit mNextRandomAccessPoint; + Maybe mLastSeek; }; } // namespace mozilla diff --git a/dom/media/moz.build b/dom/media/moz.build index 031316436b8d..24393a69bf28 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -94,6 +94,8 @@ XPIDL_MODULE = 'dom_media' EXPORTS += [ 'AbstractMediaDecoder.h', + 'ADTSDecoder.h', + 'ADTSDemuxer.h', 'AudioBufferUtils.h', 'AudioChannelFormat.h', 'AudioCompactor.h', @@ -190,6 +192,8 @@ EXPORTS.mozilla.dom += [ ] UNIFIED_SOURCES += [ + 'ADTSDecoder.cpp', + 'ADTSDemuxer.cpp', 'AudioCaptureStream.cpp', 'AudioChannelFormat.cpp', 'AudioCompactor.cpp', diff --git a/dom/media/platforms/agnostic/BlankDecoderModule.cpp b/dom/media/platforms/agnostic/BlankDecoderModule.cpp index ed5d0d13ae3e..39f8d7f8a182 100644 --- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp +++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp @@ -12,6 +12,7 @@ #include "mozilla/RefPtr.h" #include "mozilla/TaskQueue.h" #include "mozilla/UniquePtr.h" +#include "mozilla/UniquePtrExtensions.h" #include "nsRect.h" #include "PlatformDecoderModule.h" #include "TimeUnits.h" @@ -188,8 +189,8 @@ public: frames.value() > (UINT32_MAX / mChannelCount)) { return nullptr; } - UniquePtr samples( - new (fallible) AudioDataValue[frames.value() * mChannelCount]); + auto samples = + MakeUniqueFallible(frames.value() * mChannelCount); if (!samples) { return nullptr; } diff --git a/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp b/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp index 60e5fc558552..8accc17c3e3c 100644 --- a/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp +++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp @@ -41,6 +41,59 @@ typedef android::MediaCodecProxy MediaCodecProxy; namespace mozilla { +class GonkTextureClientAllocationHelper : public layers::ITextureClientAllocationHelper +{ +public: + GonkTextureClientAllocationHelper(uint32_t aGrallocFormat, + gfx::IntSize aSize) + : ITextureClientAllocationHelper(gfx::SurfaceFormat::UNKNOWN, + aSize, + BackendSelector::Content, + TextureFlags::DEALLOCATE_CLIENT, + ALLOC_DISALLOW_BUFFERTEXTURECLIENT) + , mGrallocFormat(aGrallocFormat) + {} + + already_AddRefed Allocate(CompositableForwarder* aAllocator) override + { + uint32_t usage = android::GraphicBuffer::USAGE_SW_READ_OFTEN | + android::GraphicBuffer::USAGE_SW_WRITE_OFTEN | + android::GraphicBuffer::USAGE_HW_TEXTURE; + + GrallocTextureData* texData = GrallocTextureData::Create(mSize, mGrallocFormat, + gfx::BackendType::NONE, + usage, aAllocator); + if (!texData) { + return nullptr; + } + sp graphicBuffer = texData->GetGraphicBuffer(); + if (!graphicBuffer.get()) { + return nullptr; + } + RefPtr textureClient = + TextureClient::CreateWithData(texData, TextureFlags::DEALLOCATE_CLIENT, aAllocator); + return textureClient.forget(); + } + + bool IsCompatible(TextureClient* aTextureClient) override + { + if (!aTextureClient) { + return false; + } + sp graphicBuffer = + static_cast(aTextureClient->GetInternalData())->GetGraphicBuffer(); + if (!graphicBuffer.get() || + static_cast(graphicBuffer->getPixelFormat()) != mGrallocFormat || + aTextureClient->GetSize() != mSize) { + return false; + } + return true; + } + +private: + uint32_t mGrallocFormat; +}; + GonkVideoDecoderManager::GonkVideoDecoderManager( mozilla::layers::ImageContainer* aImageContainer, const VideoInfo& aConfig) @@ -269,6 +322,36 @@ Align(int aX, int aAlign) return (aX + aAlign - 1) & ~(aAlign - 1); } +// Venus formats are doucmented in kernel/include/media/msm_media_info.h: +// * Y_Stride : Width aligned to 128 +// * UV_Stride : Width aligned to 128 +// * Y_Scanlines: Height aligned to 32 +// * UV_Scanlines: Height/2 aligned to 16 +// * Total size = align((Y_Stride * Y_Scanlines +// * + UV_Stride * UV_Scanlines + 4096), 4096) +static void +CopyVenus(uint8_t* aSrc, uint8_t* aDest, uint32_t aWidth, uint32_t aHeight) +{ + size_t yStride = Align(aWidth, 128); + uint8_t* s = aSrc; + uint8_t* d = aDest; + for (size_t i = 0; i < aHeight; i++) { + memcpy(d, s, aWidth); + s += yStride; + d += yStride; + } + size_t uvStride = yStride; + size_t uvLines = (aHeight + 1) / 2; + size_t ySize = yStride * Align(aHeight, 32); + s = aSrc + ySize; + d = aDest + ySize; + for (size_t i = 0; i < uvLines; i++) { + memcpy(d, s, aWidth); + s += uvStride; + d += uvStride; + } +} + static void CopyGraphicBuffer(sp& aSource, sp& aDestination) { @@ -281,7 +364,7 @@ CopyGraphicBuffer(sp& aSource, sp& aDestination) // Build PlanarYCbCrData for source buffer. PlanarYCbCrData srcData; switch (aSource->getPixelFormat()) { - case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_YV12: { // Android YV12 format is defined in system/core/include/system/graphics.h srcData.mYChannel = static_cast(srcPtr); srcData.mYSkip = 0; @@ -297,46 +380,37 @@ CopyGraphicBuffer(sp& aSource, sp& aDestination) srcData.mCrSkip = 0; srcData.mCbChannel = srcData.mCrChannel + (srcData.mCbCrStride * srcData.mCbCrSize.height); srcData.mCbSkip = 0; + + // Build PlanarYCbCrData for destination buffer. + PlanarYCbCrData destData; + destData.mYChannel = static_cast(destPtr); + destData.mYSkip = 0; + destData.mYSize.width = aDestination->getWidth(); + destData.mYSize.height = aDestination->getHeight(); + destData.mYStride = aDestination->getStride(); + // 4:2:0. + destData.mCbCrSize.width = destData.mYSize.width / 2; + destData.mCbCrSize.height = destData.mYSize.height / 2; + destData.mCrChannel = destData.mYChannel + (destData.mYStride * destData.mYSize.height); + // Aligned to 16 bytes boundary. + destData.mCbCrStride = Align(destData.mYStride / 2, 16); + destData.mCrSkip = 0; + destData.mCbChannel = destData.mCrChannel + (destData.mCbCrStride * destData.mCbCrSize.height); + destData.mCbSkip = 0; + + CopyYUV(srcData, destData); break; + } case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS: - // Venus formats are doucmented in kernel/include/media/msm_media_info.h: - srcData.mYChannel = static_cast(srcPtr); - srcData.mYSkip = 0; - srcData.mYSize.width = aSource->getWidth(); - srcData.mYSize.height = aSource->getHeight(); - // - Y & UV Width aligned to 128 - srcData.mYStride = aSource->getStride(); - srcData.mCbCrSize.width = (srcData.mYSize.width + 1) / 2; - srcData.mCbCrSize.height = (srcData.mYSize.height + 1) / 2; - // - Y height aligned to 32 - srcData.mCbChannel = srcData.mYChannel + (srcData.mYStride * Align(srcData.mYSize.height, 32)); - // Interleaved VU plane. - srcData.mCbSkip = 1; - srcData.mCrChannel = srcData.mCbChannel + 1; - srcData.mCrSkip = 1; - srcData.mCbCrStride = srcData.mYStride; + CopyVenus(static_cast(srcPtr), + static_cast(destPtr), + aSource->getWidth(), + aSource->getHeight()); break; default: NS_ERROR("Unsupported input gralloc image type. Should never be here."); } - // Build PlanarYCbCrData for destination buffer. - PlanarYCbCrData destData; - destData.mYChannel = static_cast(destPtr); - destData.mYSkip = 0; - destData.mYSize.width = aDestination->getWidth(); - destData.mYSize.height = aDestination->getHeight(); - destData.mYStride = aDestination->getStride(); - // 4:2:0. - destData.mCbCrSize.width = destData.mYSize.width / 2; - destData.mCbCrSize.height = destData.mYSize.height / 2; - destData.mCrChannel = destData.mYChannel + (destData.mYStride * destData.mYSize.height); - // Aligned to 16 bytes boundary. - destData.mCbCrStride = Align(destData.mYStride / 2, 16); - destData.mCrSkip = 0; - destData.mCbChannel = destData.mCrChannel + (destData.mCbCrStride * destData.mCbCrSize.height); - destData.mCbSkip = 0; - CopyYUV(srcData, destData); aSource->unlock(); aDestination->unlock(); @@ -359,19 +433,13 @@ GonkVideoDecoderManager::CreateVideoDataFromGraphicBuffer(MediaBuffer* aSource, return nullptr; } - gfx::IntSize size(Align(aPicture.width, 2) , Align(aPicture.height, 2)); - textureClient = - mCopyAllocator->CreateOrRecycle(gfx::SurfaceFormat::YUV, size, - BackendSelector::Content, - TextureFlags::DEFAULT, - ALLOC_DISALLOW_BUFFERTEXTURECLIENT); + gfx::IntSize size(srcBuffer->getWidth(), srcBuffer->getHeight()); + GonkTextureClientAllocationHelper helper(srcBuffer->getPixelFormat(), size); + textureClient = mCopyAllocator->CreateOrRecycle(helper); if (!textureClient) { GVDM_LOG("Copy buffer allocation failed!"); return nullptr; } - // Update size to match buffer's. - aPicture.width = size.width; - aPicture.height = size.height; sp destBuffer = static_cast(textureClient->GetInternalData())->GetGraphicBuffer(); diff --git a/dom/media/test/test_can_play_type_mpeg.html b/dom/media/test/test_can_play_type_mpeg.html index 591cb33c6108..ee6dd610d3f6 100644 --- a/dom/media/test/test_can_play_type_mpeg.html +++ b/dom/media/test/test_can_play_type_mpeg.html @@ -33,9 +33,9 @@ function check_mp4(v, enabled) { // Not the MIME type that other browsers respond to, so we won't either. check("audio/m4a", ""); check("video/m4v", ""); - // Only Safari responds affirmatively to "audio/aac", - // so we'll let x-m4a cover aac support. - check("audio/aac", ""); + + check("audio/aac", "maybe"); + check("audio/aacp", "maybe"); // H.264 Constrained Baseline Profile Level 3.0, AAC-LC check("video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"", "probably"); diff --git a/dom/media/webrtc/MediaTrackConstraints.h b/dom/media/webrtc/MediaTrackConstraints.h index d00ee3a72246..267b73163501 100644 --- a/dom/media/webrtc/MediaTrackConstraints.h +++ b/dom/media/webrtc/MediaTrackConstraints.h @@ -117,18 +117,19 @@ protected: template static bool - AreUnfitSettings(const dom::MediaTrackConstraints &aConstraints, - nsTArray>& aSources) + SomeSettingsFit(const dom::MediaTrackConstraints &aConstraints, + nsTArray>& aSources) { nsTArray aggregateConstraints; aggregateConstraints.AppendElement(&aConstraints); + MOZ_ASSERT(aSources.Length()); for (auto& source : aSources) { if (source->GetBestFitnessDistance(aggregateConstraints) != UINT32_MAX) { - return false; + return true; } } - return true; + return false; } public: @@ -168,38 +169,42 @@ public: // of the sources. Unfortunately, this is a bit laborious to find out, and // requires updating as new constraints are added! + if (!unsatisfactory.Length() || + !SomeSettingsFit(dom::MediaTrackConstraints(), unsatisfactory)) { + return ""; + } if (c.mDeviceId.IsConstrainDOMStringParameters()) { dom::MediaTrackConstraints fresh; fresh.mDeviceId = c.mDeviceId; - if (AreUnfitSettings(fresh, unsatisfactory)) { + if (!SomeSettingsFit(fresh, unsatisfactory)) { return "deviceId"; } } if (c.mWidth.IsConstrainLongRange()) { dom::MediaTrackConstraints fresh; fresh.mWidth = c.mWidth; - if (AreUnfitSettings(fresh, unsatisfactory)) { + if (!SomeSettingsFit(fresh, unsatisfactory)) { return "width"; } } if (c.mHeight.IsConstrainLongRange()) { dom::MediaTrackConstraints fresh; fresh.mHeight = c.mHeight; - if (AreUnfitSettings(fresh, unsatisfactory)) { + if (!SomeSettingsFit(fresh, unsatisfactory)) { return "height"; } } if (c.mFrameRate.IsConstrainDoubleRange()) { dom::MediaTrackConstraints fresh; fresh.mFrameRate = c.mFrameRate; - if (AreUnfitSettings(fresh, unsatisfactory)) { + if (!SomeSettingsFit(fresh, unsatisfactory)) { return "frameRate"; } } if (c.mFacingMode.IsConstrainDOMStringParameters()) { dom::MediaTrackConstraints fresh; fresh.mFacingMode = c.mFacingMode; - if (AreUnfitSettings(fresh, unsatisfactory)) { + if (!SomeSettingsFit(fresh, unsatisfactory)) { return "facingMode"; } } diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index c2b5c102974d..49f6b333e393 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -3384,7 +3384,8 @@ nsresult nsPluginHost::NewPluginURLStream(const nsString& aURL, nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, nsIContentPolicy::TYPE_OBJECT_SUBREQUEST, nullptr, // aLoadGroup - listenerPeer); + listenerPeer, + nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI); NS_ENSURE_SUCCESS(rv, rv); if (doc) { diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp index c74830d01dd6..a73d7d9a3035 100644 --- a/dom/security/nsContentSecurityManager.cpp +++ b/dom/security/nsContentSecurityManager.cpp @@ -8,7 +8,9 @@ #include "mozilla/dom/Element.h" -NS_IMPL_ISUPPORTS(nsContentSecurityManager, nsIContentSecurityManager) +NS_IMPL_ISUPPORTS(nsContentSecurityManager, + nsIContentSecurityManager, + nsIChannelEventSink) static nsresult ValidateSecurityFlags(nsILoadInfo* aLoadInfo) @@ -24,12 +26,6 @@ ValidateSecurityFlags(nsILoadInfo* aLoadInfo) return NS_ERROR_FAILURE; } - // make sure that cors-with-credentials is only used in combination with CORS. - if (aLoadInfo->GetRequireCorsWithCredentials() && - securityMode != nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) { - MOZ_ASSERT(false, "can not use cors-with-credentials without cors"); - return NS_ERROR_FAILURE; - } // all good, found the right security flags return NS_OK; } @@ -89,7 +85,7 @@ URIHasFlags(nsIURI* aURI, uint32_t aURIFlags) } static nsresult -DoSOPChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo) +DoSOPChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo, nsIChannel* aChannel) { if (aLoadInfo->GetAllowChrome() && (URIHasFlags(aURI, nsIProtocolHandler::URI_IS_UI_RESOURCE) || @@ -98,19 +94,10 @@ DoSOPChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo) return DoCheckLoadURIChecks(aURI, aLoadInfo); } - nsIPrincipal* loadingPrincipal = aLoadInfo->LoadingPrincipal(); - bool sameOriginDataInherits = - aLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS; + NS_ENSURE_FALSE(NS_HasBeenCrossOrigin(aChannel, true), + NS_ERROR_DOM_BAD_URI); - if (sameOriginDataInherits && - aLoadInfo->GetAboutBlankInherits() && - NS_IsAboutBlank(aURI)) { - return NS_OK; - } - - return loadingPrincipal->CheckMayLoad(aURI, - true, // report to console - sameOriginDataInherits); + return NS_OK; } static nsresult @@ -122,7 +109,8 @@ DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo, RefPtr corsListener = new nsCORSListenerProxy(aInAndOutListener, loadingPrincipal, - aLoadInfo->GetRequireCorsWithCredentials()); + aLoadInfo->GetCookiePolicy() == + nsILoadInfo::SEC_COOKIES_INCLUDE); // XXX: @arg: DataURIHandling::Allow // lets use DataURIHandling::Allow for now and then decide on callsite basis. see also: // http://mxr.mozilla.org/mozilla-central/source/dom/security/nsCORSListenerProxy.h#33 @@ -342,22 +330,17 @@ nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel, return NS_ERROR_UNEXPECTED; } + // if dealing with a redirected channel then we have already installed + // streamlistener and redirect proxies and so we are done. + if (loadInfo->GetInitialSecurityCheckDone()) { + return NS_OK; + } + // make sure that only one of the five security flags is set in the loadinfo // e.g. do not require same origin and allow cross origin at the same time nsresult rv = ValidateSecurityFlags(loadInfo); NS_ENSURE_SUCCESS(rv, rv); - // lets store the initialSecurityCheckDone flag which indicates whether the channel - // was initialy evaluated by the contentSecurityManager. Once the inital - // asyncOpen() of the channel went through the contentSecurityManager then - // redirects do not have perform all the security checks, e.g. no reason - // to setup CORS again. - bool initialSecurityCheckDone = loadInfo->GetInitialSecurityCheckDone(); - - // now lets set the initalSecurityFlag for subsequent calls - rv = loadInfo->SetInitialSecurityCheckDone(true); - NS_ENSURE_SUCCESS(rv, rv); - // since aChannel was openend using asyncOpen2() we have to make sure // that redirects of that channel also get openend using asyncOpen2() // please note that some implementations of ::AsyncOpen2 might already @@ -366,48 +349,145 @@ nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel, rv = loadInfo->SetEnforceSecurity(true); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr finalChannelURI; - rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI)); - NS_ENSURE_SUCCESS(rv, rv); - - nsSecurityFlags securityMode = loadInfo->GetSecurityMode(); - - // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply - if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) || - (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) { - rv = DoSOPChecks(finalChannelURI, loadInfo); - NS_ENSURE_SUCCESS(rv, rv); - } - - // if dealing with a redirected channel then we only enforce SOP - // and can return at this point. - if (initialSecurityCheckDone) { - return NS_OK; - } - - if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) || - (securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) { - // Please note that DoCheckLoadURIChecks should only be enforced for - // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set - // within the loadInfo, then then CheckLoadURIWithPrincipal is performed - // within nsCorsListenerProxy - rv = DoCheckLoadURIChecks(finalChannelURI, loadInfo); - NS_ENSURE_SUCCESS(rv, rv); - } - - if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) { + if (loadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) { rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener); NS_ENSURE_SUCCESS(rv, rv); } + rv = CheckChannel(aChannel); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr finalChannelURI; + rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI)); + NS_ENSURE_SUCCESS(rv, rv); + // Perform all ContentPolicy checks (MixedContent, CSP, ...) rv = DoContentSecurityChecks(finalChannelURI, loadInfo); NS_ENSURE_SUCCESS(rv, rv); + // now lets set the initalSecurityFlag for subsequent calls + rv = loadInfo->SetInitialSecurityCheckDone(true); + NS_ENSURE_SUCCESS(rv, rv); + // all security checks passed - lets allow the load return NS_OK; } +NS_IMETHODIMP +nsContentSecurityManager::AsyncOnChannelRedirect(nsIChannel* aOldChannel, + nsIChannel* aNewChannel, + uint32_t aRedirFlags, + nsIAsyncVerifyRedirectCallback *aCb) +{ + nsCOMPtr loadInfo = aOldChannel->GetLoadInfo(); + // Are we enforcing security using LoadInfo? + if (loadInfo && loadInfo->GetEnforceSecurity()) { + nsresult rv = CheckChannel(aNewChannel); + if (NS_FAILED(rv)) { + aOldChannel->Cancel(rv); + return rv; + } + } + + // Also verify that the redirecting server is allowed to redirect to the + // given URI + nsCOMPtr oldPrincipal; + nsContentUtils::GetSecurityManager()-> + GetChannelResultPrincipal(aOldChannel, getter_AddRefs(oldPrincipal)); + + nsCOMPtr newURI; + aNewChannel->GetURI(getter_AddRefs(newURI)); + nsCOMPtr newOriginalURI; + aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI)); + + NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI); + + const uint32_t flags = + nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT | + nsIScriptSecurityManager::DISALLOW_SCRIPT; + nsresult rv = nsContentUtils::GetSecurityManager()-> + CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags); + if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) { + rv = nsContentUtils::GetSecurityManager()-> + CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags); + } + NS_ENSURE_SUCCESS(rv, rv); + + aCb->OnRedirectVerifyCallback(NS_OK); + return NS_OK; +} + +static void +AddLoadFlags(nsIRequest *aRequest, nsLoadFlags aNewFlags) +{ + nsLoadFlags flags; + aRequest->GetLoadFlags(&flags); + flags |= aNewFlags; + aRequest->SetLoadFlags(flags); +} + +/* + * Check that this channel passes all security checks. Returns an error code + * if this requesst should not be permitted. + */ +nsresult +nsContentSecurityManager::CheckChannel(nsIChannel* aChannel) +{ + nsCOMPtr loadInfo = aChannel->GetLoadInfo(); + MOZ_ASSERT(loadInfo); + + nsCOMPtr uri; + nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, rv); + + // Handle cookie policies + uint32_t cookiePolicy = loadInfo->GetCookiePolicy(); + if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) { + nsIPrincipal* loadingPrincipal = loadInfo->LoadingPrincipal(); + + // It doesn't matter what we pass for the third, data-inherits, argument. + // Any protocol which inherits won't pay attention to cookies anyway. + rv = loadingPrincipal->CheckMayLoad(uri, false, false); + if (NS_FAILED(rv)) { + AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS); + } + } + else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_OMIT) { + AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS); + } + + nsSecurityFlags securityMode = loadInfo->GetSecurityMode(); + + // CORS mode is handled by nsCORSListenerProxy + if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) { + if (NS_HasBeenCrossOrigin(aChannel)) { + loadInfo->MaybeIncreaseTainting(LoadTainting::CORS); + } + return NS_OK; + } + + // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply + if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) || + (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) { + rv = DoSOPChecks(uri, loadInfo, aChannel); + NS_ENSURE_SUCCESS(rv, rv); + } + + if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) || + (securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) { + if (NS_HasBeenCrossOrigin(aChannel)) { + loadInfo->MaybeIncreaseTainting(LoadTainting::Opaque); + } + // Please note that DoCheckLoadURIChecks should only be enforced for + // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set + // within the loadInfo, then then CheckLoadURIWithPrincipal is performed + // within nsCorsListenerProxy + rv = DoCheckLoadURIChecks(uri, loadInfo); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} // ==== nsIContentSecurityManager implementation ===== diff --git a/dom/security/nsContentSecurityManager.h b/dom/security/nsContentSecurityManager.h index 49fef2ac92ae..912c0e89f110 100644 --- a/dom/security/nsContentSecurityManager.h +++ b/dom/security/nsContentSecurityManager.h @@ -9,6 +9,7 @@ #include "nsIContentSecurityManager.h" #include "nsIChannel.h" +#include "nsIChannelEventSink.h" class nsIStreamListener; @@ -19,10 +20,12 @@ class nsIStreamListener; { 0xa2, 0x94, 0xa6, 0x51, 0xfa, 0x35, 0x22, 0x7f } } class nsContentSecurityManager : public nsIContentSecurityManager + , public nsIChannelEventSink { public: NS_DECL_ISUPPORTS NS_DECL_NSICONTENTSECURITYMANAGER + NS_DECL_NSICHANNELEVENTSINK nsContentSecurityManager() {} @@ -30,6 +33,8 @@ public: nsCOMPtr& aInAndOutListener); private: + static nsresult CheckChannel(nsIChannel* aChannel); + virtual ~nsContentSecurityManager() {} }; diff --git a/dom/security/test/cors/file_CrossSiteXHR_server.sjs b/dom/security/test/cors/file_CrossSiteXHR_server.sjs index 90141c31b93e..14fd4c3b27a9 100644 --- a/dom/security/test/cors/file_CrossSiteXHR_server.sjs +++ b/dom/security/test/cors/file_CrossSiteXHR_server.sjs @@ -27,7 +27,9 @@ function handleRequest(request, response) var curHop = hops[query.hop - 1]; query.allowOrigin = curHop.allowOrigin; query.allowHeaders = curHop.allowHeaders; + query.allowMethods = curHop.allowMethods; query.allowCred = curHop.allowCred; + query.noAllowPreflight = curHop.noAllowPreflight; if (curHop.setCookie) { query.setCookie = unescape(curHop.setCookie); } diff --git a/dom/security/test/cors/test_CrossSiteXHR.html b/dom/security/test/cors/test_CrossSiteXHR.html index 76b16f0264f9..28d1b005d17f 100644 --- a/dom/security/test/cors/test_CrossSiteXHR.html +++ b/dom/security/test/cors/test_CrossSiteXHR.html @@ -1084,7 +1084,7 @@ function runTest() { }, ], }, - { pass: 0, + { pass: 1, method: "POST", body: "hi there", headers: { "Content-Type": "text/plain", @@ -1099,11 +1099,133 @@ function runTest() { ], }, { pass: 0, + method: "POST", + body: "hi there", + headers: { "Content-Type": "text/plain", + "my-header": "myValue", + }, + hops: [{ server: "http://example.org", + }, + { server: "http://example.com", + allowOrigin: origin, + allowHeaders: "my-header", + }, + { server: "http://sub1.test1.example.org", + allowOrigin: origin, + allowHeaders: "my-header", + }, + ], + }, + { pass: 0, + method: "POST", + body: "hi there", + headers: { "Content-Type": "text/plain", + "my-header": "myValue", + }, + hops: [{ server: "http://example.org", + }, + { server: "http://example.com", + allowOrigin: origin, + allowHeaders: "my-header", + }, + { server: "http://example.com", + allowOrigin: origin, + allowHeaders: "my-header", + }, + ], + }, + { pass: 0, + method: "POST", + body: "hi there", + headers: { "Content-Type": "text/plain", + "my-header": "myValue", + }, + hops: [{ server: "http://example.org", + }, + { server: "http://example.com", + allowOrigin: origin, + allowHeaders: "my-header", + }, + { server: "http://example.org", + allowOrigin: origin, + allowHeaders: "my-header", + }, + ], + }, + { pass: 0, + method: "POST", + body: "hi there", + headers: { "Content-Type": "text/plain", + "my-header": "myValue", + }, + hops: [{ server: "http://example.org", + }, + { server: "http://example.com", + allowOrigin: origin, + noAllowPreflight: 1, + }, + ], + }, + { pass: 1, method: "DELETE", hops: [{ server: "http://example.org", }, { server: "http://example.com", allowOrigin: origin, + allowMethods: "DELETE", + }, + ], + }, + { pass: 0, + method: "DELETE", + hops: [{ server: "http://example.org", + }, + { server: "http://example.com", + allowOrigin: origin, + allowMethods: "DELETE", + }, + { server: "http://sub1.test1.example.org", + allowOrigin: origin, + allowMethods: "DELETE", + }, + ], + }, + { pass: 0, + method: "DELETE", + hops: [{ server: "http://example.org", + }, + { server: "http://example.com", + allowOrigin: origin, + allowMethods: "DELETE", + }, + { server: "http://example.com", + allowOrigin: origin, + allowMethods: "DELETE", + }, + ], + }, + { pass: 0, + method: "DELETE", + hops: [{ server: "http://example.org", + }, + { server: "http://example.com", + allowOrigin: origin, + allowMethods: "DELETE", + }, + { server: "http://example.org", + allowOrigin: origin, + allowMethods: "DELETE", + }, + ], + }, + { pass: 0, + method: "DELETE", + hops: [{ server: "http://example.org", + }, + { server: "http://example.com", + allowOrigin: origin, + allowMethods: "DELETE", + noAllowPreflight: 1, }, ], }, diff --git a/dom/security/test/csp/file_redirects_main.html b/dom/security/test/csp/file_redirects_main.html index 5c9affea04ba..d05af88fe808 100644 --- a/dom/security/test/csp/file_redirects_main.html +++ b/dom/security/test/csp/file_redirects_main.html @@ -18,7 +18,6 @@ var tests = { "font-src": thisSite+page+"?testid=font-src", "object-src": thisSite+page+"?testid=object-src", "script-src": thisSite+page+"?testid=script-src", "style-src": thisSite+page+"?testid=style-src", - "worker": thisSite+page+"?testid=worker", "xhr-src": thisSite+page+"?testid=xhr-src", "from-worker": thisSite+page+"?testid=from-worker", "from-blob-worker": thisSite+page+"?testid=from-blob-worker", diff --git a/dom/security/test/csp/file_redirects_page.sjs b/dom/security/test/csp/file_redirects_page.sjs index 9e3c0d0350d4..ced2d0787b90 100644 --- a/dom/security/test/csp/file_redirects_page.sjs +++ b/dom/security/test/csp/file_redirects_page.sjs @@ -14,13 +14,8 @@ function handleRequest(request, response) var resource = "/tests/dom/security/test/csp/file_redirects_resource.sjs"; // CSP header value - var additional = "" - if (query['testid'] == "worker") { - additional = "; script-src 'self' 'unsafe-inline'"; - } response.setHeader("Content-Security-Policy", - "default-src 'self' blob: ; style-src 'self' 'unsafe-inline'" + additional, - false); + "default-src 'self' blob: ; style-src 'self' 'unsafe-inline'", false); // downloadable font that redirects to another site if (query["testid"] == "font-src") { @@ -69,12 +64,6 @@ function handleRequest(request, response) return; } - // worker script resource that redirects to another site - if (query["testid"] == "worker") { - response.write(''); - return; - } - // script that XHR's to a resource that redirects to another site if (query["testid"] == "xhr-src") { response.write(''); diff --git a/dom/security/test/csp/file_redirects_resource.sjs b/dom/security/test/csp/file_redirects_resource.sjs index d281f19d8ba5..da138630fdcd 100644 --- a/dom/security/test/csp/file_redirects_resource.sjs +++ b/dom/security/test/csp/file_redirects_resource.sjs @@ -85,13 +85,6 @@ function handleRequest(request, response) return; } - // web worker resource - if (query["res"] == "worker") { - response.setHeader("Content-Type", "application/javascript", false); - response.write("worker script data..."); - return; - } - // internal stylesheet that loads an image from an external site if (query["res"] == "cssLoader") { let bgURL = thisSite + resource + '?redir=other&res=image&id=' + query["id"]; diff --git a/dom/security/test/csp/test_redirects.html b/dom/security/test/csp/test_redirects.html index 4a588dcbe111..df01e3b41738 100644 --- a/dom/security/test/csp/test_redirects.html +++ b/dom/security/test/csp/test_redirects.html @@ -82,14 +82,12 @@ var testExpectedResults = { "font-src": true, "script-src-redir": false, "style-src": true, "style-src-redir": false, - "worker": true, - "worker-redir": false, "xhr-src": true, "xhr-src-redir": false, "from-worker": true, - "script-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */ - "xhr-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */ - "fetch-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */ + "script-src-redir-from-worker": true, // redir is allowed since policy isn't inherited + "xhr-src-redir-from-worker": true, // redir is allowed since policy isn't inherited + "fetch-src-redir-from-worker": true, // redir is allowed since policy isn't inherited "from-blob-worker": true, "script-src-redir-from-blob-worker": false, "xhr-src-redir-from-blob-worker": false, diff --git a/dom/svg/DOMSVGPathSegList.cpp b/dom/svg/DOMSVGPathSegList.cpp index 52c5b85d55f0..edacdfc93e9c 100644 --- a/dom/svg/DOMSVGPathSegList.cpp +++ b/dom/svg/DOMSVGPathSegList.cpp @@ -385,6 +385,7 @@ DOMSVGPathSegList::InsertItemBefore(DOMSVGPathSeg& aNewItem, if (AnimListMirrorsBaseList()) { DOMSVGPathSegList *animVal = GetDOMWrapperIfExists(InternalAList().GetAnimValKey()); + MOZ_ASSERT(animVal, "animVal should be a valid pointer"); if (!animVal->mItems.SetCapacity( animVal->mItems.Length() + 1, fallible)) { aError.Throw(NS_ERROR_OUT_OF_MEMORY); diff --git a/dom/svg/DOMSVGPointList.cpp b/dom/svg/DOMSVGPointList.cpp index 6efe2c217177..966ef476e739 100644 --- a/dom/svg/DOMSVGPointList.cpp +++ b/dom/svg/DOMSVGPointList.cpp @@ -321,6 +321,7 @@ DOMSVGPointList::InsertItemBefore(nsISVGPoint& aNewItem, uint32_t aIndex, if (AnimListMirrorsBaseList()) { DOMSVGPointList *animVal = GetDOMWrapperIfExists(InternalAList().GetAnimValKey()); + MOZ_ASSERT(animVal, "animVal must be a valid pointer"); if (!animVal->mItems.SetCapacity( animVal->mItems.Length() + 1, fallible)) { aError.Throw(NS_ERROR_OUT_OF_MEMORY); diff --git a/dom/tests/mochitest/fetch/test_fetch_cors.js b/dom/tests/mochitest/fetch/test_fetch_cors.js index e07d2cd28148..e57a28fd3824 100644 --- a/dom/tests/mochitest/fetch/test_fetch_cors.js +++ b/dom/tests/mochitest/fetch/test_fetch_cors.js @@ -1283,6 +1283,20 @@ function testCORSRedirects() { }, ], }, + { pass: 1, + method: "POST", + body: "hi there", + headers: { "Content-Type": "text/plain", + "my-header": "myValue", + }, + hops: [{ server: "http://mochi.test:8888", + }, + { server: "http://example.com", + allowOrigin: origin, + allowHeaders: "my-header", + }, + ], + }, { pass: 0, method: "POST", body: "hi there", @@ -1294,6 +1308,7 @@ function testCORSRedirects() { { server: "http://example.com", allowOrigin: origin, allowHeaders: "my-header", + noAllowPreflight: 1, }, ], }, @@ -1315,12 +1330,24 @@ function testCORSRedirects() { } ], }, + { pass: 1, + method: "DELETE", + hops: [{ server: "http://mochi.test:8888", + }, + { server: "http://example.com", + allowOrigin: origin, + allowMethods: "DELETE", + }, + ], + }, { pass: 0, method: "DELETE", hops: [{ server: "http://mochi.test:8888", }, { server: "http://example.com", allowOrigin: origin, + allowMethods: "DELETE", + noAllowPreflight: 1, }, ], }, @@ -1330,9 +1357,11 @@ function testCORSRedirects() { }, { server: "http://test1.example.com", allowOrigin: origin, + allowMethods: "DELETE", }, { server: "http://test2.example.com", allowOrigin: origin, + allowMethods: "DELETE", }, ], }, @@ -1354,9 +1383,11 @@ function testCORSRedirects() { method: "DELETE", hops: [{ server: "http://example.com", allowOrigin: origin, + allowMethods: "DELETE", }, { server: "http://sub1.test1.mochi.test:8888", allowOrigin: origin, + allowMethods: "DELETE", }, ], }, diff --git a/dom/webidl/FormData.webidl b/dom/webidl/FormData.webidl index f5902c4efe00..455ea505da9d 100644 --- a/dom/webidl/FormData.webidl +++ b/dom/webidl/FormData.webidl @@ -12,13 +12,17 @@ typedef (File or USVString) FormDataEntryValue; [Constructor(optional HTMLFormElement form), Exposed=(Window,Worker)] interface FormData { + [Throws] void append(USVString name, Blob value, optional USVString filename); + [Throws] void append(USVString name, USVString value); void delete(USVString name); FormDataEntryValue? get(USVString name); sequence getAll(USVString name); boolean has(USVString name); + [Throws] void set(USVString name, Blob value, optional USVString filename); + [Throws] void set(USVString name, USVString value); iterable; }; diff --git a/dom/webidl/NamedNodeMap.webidl b/dom/webidl/NamedNodeMap.webidl index 71d61181ce9b..82a873019553 100644 --- a/dom/webidl/NamedNodeMap.webidl +++ b/dom/webidl/NamedNodeMap.webidl @@ -5,7 +5,7 @@ interface NamedNodeMap { getter Attr? getNamedItem(DOMString name); - [Throws] + [Throws, BinaryName="setNamedItemNS"] Attr? setNamedItem(Attr arg); [Throws] Attr removeNamedItem(DOMString name); diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index c944eb4cd889..c7b855a1e5c7 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -1079,7 +1079,7 @@ public: // Step 8 "Queue a task..." for updatefound. nsCOMPtr upr = - NS_NewRunnableMethodWithArg( + NS_NewRunnableMethodWithArg>( swm, &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations, mRegistration); @@ -1767,9 +1767,8 @@ ServiceWorkerRegistrationInfo::Activate() // "Queue a task to fire a simple event named controllerchange..." nsCOMPtr controllerChangeRunnable = - NS_NewRunnableMethodWithArg(swm, - &ServiceWorkerManager::FireControllerChange, - this); + NS_NewRunnableMethodWithArg>( + swm, &ServiceWorkerManager::FireControllerChange, this); NS_DispatchToMainThread(controllerChangeRunnable); nsCOMPtr failRunnable = @@ -2652,7 +2651,8 @@ ServiceWorkerManager::HandleError(JSContext* aCx, void ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess) { - if (mPendingUninstall || !mActiveWorker) { + if (mPendingUninstall || !mActiveWorker || + mActiveWorker->State() != ServiceWorkerState::Activating) { return; } diff --git a/dom/xml/nsXMLPrettyPrinter.cpp b/dom/xml/nsXMLPrettyPrinter.cpp index 9bda8c7a4a8f..0f49eaf30486 100644 --- a/dom/xml/nsXMLPrettyPrinter.cpp +++ b/dom/xml/nsXMLPrettyPrinter.cpp @@ -171,7 +171,7 @@ nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument, NS_ENSURE_SUCCESS(rv, rv); event->SetTrusted(true); bool dummy; - rv = rootCont->DispatchEvent(static_cast(event), &dummy); + rv = rootCont->DispatchEvent(event, &dummy); NS_ENSURE_SUCCESS(rv, rv); // Observe the document so we know when to switch to "normal" view diff --git a/dom/xslt/base/txURIUtils.cpp b/dom/xslt/base/txURIUtils.cpp index 33f7e919aa06..201f5d395477 100644 --- a/dom/xslt/base/txURIUtils.cpp +++ b/dom/xslt/base/txURIUtils.cpp @@ -17,30 +17,6 @@ using mozilla::LoadInfo; * A set of utilities for handling URIs **/ -/** - * Resolves the given href argument, using the given documentBase - * if necessary. - * The new resolved href will be appended to the given dest String -**/ -void URIUtils::resolveHref(const nsAString& href, const nsAString& base, - nsAString& dest) { - if (base.IsEmpty()) { - dest.Append(href); - return; - } - if (href.IsEmpty()) { - dest.Append(base); - return; - } - nsCOMPtr pURL; - nsAutoString resultHref; - nsresult result = NS_NewURI(getter_AddRefs(pURL), base); - if (NS_SUCCEEDED(result)) { - NS_MakeAbsoluteURI(resultHref, href, pURL); - dest.Append(resultHref); - } -} //-- resolveHref - // static void URIUtils::ResetWithSource(nsIDocument *aNewDoc, nsIDOMNode *aSourceNode) diff --git a/dom/xslt/base/txURIUtils.h b/dom/xslt/base/txURIUtils.h index ca38538a8b82..188f7224df58 100644 --- a/dom/xslt/base/txURIUtils.h +++ b/dom/xslt/base/txURIUtils.h @@ -23,14 +23,6 @@ public: * Reset the given document with the document of the source node */ static void ResetWithSource(nsIDocument *aNewDoc, nsIDOMNode *aSourceNode); - - /** - * Resolves the given href argument, using the given documentBase - * if necessary. - * The new resolved href will be appended to the given dest String - **/ - static void resolveHref(const nsAString& href, const nsAString& base, - nsAString& dest); }; //-- URIUtils /* */ diff --git a/dom/xslt/nsIXSLTProcessorPrivate.idl b/dom/xslt/nsIXSLTProcessorPrivate.idl index d88320cef25c..9212f70fd392 100644 --- a/dom/xslt/nsIXSLTProcessorPrivate.idl +++ b/dom/xslt/nsIXSLTProcessorPrivate.idl @@ -5,9 +5,16 @@ #include "nsISupports.idl" -[scriptable, uuid(b8d727f7-67f4-4dc1-a318-ec0c87280816)] +[scriptable, uuid(75d14f5d-293d-4872-8a26-e79268de592f)] interface nsIXSLTProcessorPrivate : nsISupports { + /** + * This needs to be called if the XSLTProcessor is instantiated + * through the XPCOM registry (i.e. using do_createInstance) and the + * stylesheet uses xsl:import/xsl:include or the document() xpath function. + */ + void init(in nsISupports global); + /** * Disables all loading of external documents, such as from * and document() diff --git a/dom/xslt/tests/mochitest/file_bug1222624.xml b/dom/xslt/tests/mochitest/file_bug1222624.xml new file mode 100644 index 000000000000..c8290a33808d --- /dev/null +++ b/dom/xslt/tests/mochitest/file_bug1222624.xml @@ -0,0 +1,4 @@ + + + file_bug1222624_data2.xml + diff --git a/dom/xslt/tests/mochitest/file_bug1222624.xsl b/dom/xslt/tests/mochitest/file_bug1222624.xsl new file mode 100644 index 000000000000..cf954c4fc81b --- /dev/null +++ b/dom/xslt/tests/mochitest/file_bug1222624.xsl @@ -0,0 +1,12 @@ + + + + + + + + ! + + + + diff --git a/dom/xslt/tests/mochitest/file_bug1222624_data1.xml b/dom/xslt/tests/mochitest/file_bug1222624_data1.xml new file mode 100644 index 000000000000..f50fdbc1cbfb --- /dev/null +++ b/dom/xslt/tests/mochitest/file_bug1222624_data1.xml @@ -0,0 +1 @@ +doc1 diff --git a/dom/xslt/tests/mochitest/file_bug1222624_data2.xml b/dom/xslt/tests/mochitest/file_bug1222624_data2.xml new file mode 100644 index 000000000000..e6228590c19c --- /dev/null +++ b/dom/xslt/tests/mochitest/file_bug1222624_data2.xml @@ -0,0 +1 @@ +doc2 diff --git a/dom/xslt/tests/mochitest/file_bug1222624_sub.xsl b/dom/xslt/tests/mochitest/file_bug1222624_sub.xsl new file mode 100644 index 000000000000..189031a1f364 --- /dev/null +++ b/dom/xslt/tests/mochitest/file_bug1222624_sub.xsl @@ -0,0 +1,4 @@ + + + + diff --git a/dom/xslt/tests/mochitest/file_bug1222624_sub_sub.xsl b/dom/xslt/tests/mochitest/file_bug1222624_sub_sub.xsl new file mode 100644 index 000000000000..881e4c55bcd3 --- /dev/null +++ b/dom/xslt/tests/mochitest/file_bug1222624_sub_sub.xsl @@ -0,0 +1,6 @@ + + + + + + diff --git a/dom/xslt/tests/mochitest/mochitest.ini b/dom/xslt/tests/mochitest/mochitest.ini index 53a6d001cd6b..08ae18980889 100644 --- a/dom/xslt/tests/mochitest/mochitest.ini +++ b/dom/xslt/tests/mochitest/mochitest.ini @@ -16,5 +16,7 @@ [test_bug667315.html] [test_bug1135764.html] support-files = file_bug1135764.xml file_bug1135764.xsl +[test_bug1222624.html] +support-files = file_bug1222624.xml file_bug1222624.xsl file_bug1222624_sub.xsl file_bug1222624_sub_sub.xsl file_bug1222624_data1.xml file_bug1222624_data2.xml [test_exslt_regex.html] [test_parameter.html] diff --git a/dom/xslt/tests/mochitest/test_bug1222624.html b/dom/xslt/tests/mochitest/test_bug1222624.html new file mode 100644 index 000000000000..46e47a6e0f44 --- /dev/null +++ b/dom/xslt/tests/mochitest/test_bug1222624.html @@ -0,0 +1,53 @@ + + + + + Test for Bug 1222624 + + + + +Mozilla Bug 1222624 +

+ +
+
+
+ + diff --git a/dom/xslt/xml/txXMLParser.cpp b/dom/xslt/xml/txXMLParser.cpp index 0cf281f2cff5..8882cdfbf8b7 100644 --- a/dom/xslt/xml/txXMLParser.cpp +++ b/dom/xslt/xml/txXMLParser.cpp @@ -15,20 +15,14 @@ #include "nsIPrincipal.h" nsresult -txParseDocumentFromURI(const nsAString& aHref, - const txXPathNode& aLoader, +txParseDocumentFromURI(nsIURI* aUri, + nsIDocument* aLoadingDocument, nsAString& aErrMsg, txXPathNode** aResult) { - NS_ENSURE_ARG_POINTER(aResult); *aResult = nullptr; - nsCOMPtr documentURI; - nsresult rv = NS_NewURI(getter_AddRefs(documentURI), aHref); - NS_ENSURE_SUCCESS(rv, rv); - nsIDocument* loaderDocument = txXPathNativeNode::getDocument(aLoader); - - nsCOMPtr loadGroup = loaderDocument->GetDocumentLoadGroup(); + nsCOMPtr loadGroup = aLoadingDocument->GetDocumentLoadGroup(); // For the system principal loaderUri will be null here, which is good // since that means that chrome documents can load any uri. @@ -36,20 +30,24 @@ txParseDocumentFromURI(const nsAString& aHref, // Raw pointer, we want the resulting txXPathNode to hold a reference to // the document. nsIDOMDocument* theDocument = nullptr; - nsAutoSyncOperation sync(loaderDocument); - rv = nsSyncLoadService::LoadDocument(documentURI, - nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST, - loaderDocument->NodePrincipal(), - nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, - loadGroup, true, - loaderDocument->GetReferrerPolicy(), - &theDocument); + nsAutoSyncOperation sync(aLoadingDocument); + nsresult rv = + nsSyncLoadService::LoadDocument(aUri, + nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST, + aLoadingDocument->NodePrincipal(), + nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, + loadGroup, + true, + aLoadingDocument->GetReferrerPolicy(), + &theDocument); if (NS_FAILED(rv)) { aErrMsg.AppendLiteral("Document load of "); - aErrMsg.Append(aHref); + nsAutoCString spec; + aUri->GetSpec(spec); + aErrMsg.Append(NS_ConvertUTF8toUTF16(spec)); aErrMsg.AppendLiteral(" failed."); - return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE; + return rv; } *aResult = txXPathNativeNode::createXPathNode(theDocument); diff --git a/dom/xslt/xml/txXMLParser.h b/dom/xslt/xml/txXMLParser.h index fea9defe34b2..2153d90dc112 100644 --- a/dom/xslt/xml/txXMLParser.h +++ b/dom/xslt/xml/txXMLParser.h @@ -9,6 +9,8 @@ #include "txCore.h" class txXPathNode; +class nsIURI; +class nsIDocument; /** * API to load XML files into DOM datastructures. @@ -20,7 +22,9 @@ class txXPathNode; * of the document aLoader. */ extern "C" nsresult -txParseDocumentFromURI(const nsAString& aHref, const txXPathNode& aLoader, - nsAString& aErrMsg, txXPathNode** aResult); +txParseDocumentFromURI(nsIURI* aUri, + nsIDocument* aLoadingDocument, + nsAString& aErrMsg, + txXPathNode** aResult); #endif diff --git a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp index c0338ec0e6b7..231b7ea465ba 100644 --- a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp +++ b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp @@ -563,9 +563,9 @@ txXPathNodeUtils::getXSLTId(const txXPathNode& aNode, /* static */ void -txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsAString& aURI) +txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsIURI** aUri) { - aNode.mNode->GetBaseURI(aURI); + *aUri = aNode.mNode->GetBaseURI().take(); } /* static */ diff --git a/dom/xslt/xpath/txXPathTreeWalker.h b/dom/xslt/xpath/txXPathTreeWalker.h index 26cb42ddd420..1c66f2c8ba70 100644 --- a/dom/xslt/xpath/txXPathTreeWalker.h +++ b/dom/xslt/xpath/txXPathTreeWalker.h @@ -93,7 +93,7 @@ public: static nsresult getXSLTId(const txXPathNode& aNode, const txXPathNode& aBase, nsAString& aResult); static void release(txXPathNode* aNode); - static void getBaseURI(const txXPathNode& aNode, nsAString& aURI); + static void getBaseURI(const txXPathNode& aNode, nsIURI** aURI); static int comparePosition(const txXPathNode& aNode, const txXPathNode& aOtherNode); static bool localNameEquals(const txXPathNode& aNode, diff --git a/dom/xslt/xslt/txDocumentFunctionCall.cpp b/dom/xslt/xslt/txDocumentFunctionCall.cpp index 3ea7a83b24d3..1de5cf75c8cc 100644 --- a/dom/xslt/xslt/txDocumentFunctionCall.cpp +++ b/dom/xslt/xslt/txDocumentFunctionCall.cpp @@ -13,46 +13,33 @@ #include "txXSLTFunctions.h" #include "txExecutionState.h" #include "txURIUtils.h" - -/* - * Creates a new DocumentFunctionCall. - */ -DocumentFunctionCall::DocumentFunctionCall(const nsAString& aBaseURI) - : mBaseURI(aBaseURI) -{ -} +#include "nsIURI.h" +#include "nsNetUtil.h" static void -retrieveNode(txExecutionState* aExecutionState, const nsAString& aUri, - const nsAString& aBaseUri, txNodeSet* aNodeSet) +retrieveNode(txExecutionState* aExecutionState, + const nsAString& aUri, + nsIURI* aBaseUri, + txNodeSet* aNodeSet) { - nsAutoString absUrl; - URIUtils::resolveHref(aUri, aBaseUri, absUrl); - - int32_t hash = absUrl.RFindChar(char16_t('#')); - uint32_t urlEnd, fragStart, fragEnd; - if (hash == kNotFound) { - urlEnd = absUrl.Length(); - fragStart = 0; - fragEnd = 0; - } - else { - urlEnd = hash; - fragStart = hash + 1; - fragEnd = absUrl.Length(); + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri, nullptr, aBaseUri); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; } - nsDependentSubstring docUrl(absUrl, 0, urlEnd); - nsDependentSubstring frag(absUrl, fragStart, fragEnd); + nsAutoCString frag; + uri->GetRef(frag); + uri->SetRef(EmptyCString()); - const txXPathNode* loadNode = aExecutionState->retrieveDocument(docUrl); + const txXPathNode* loadNode = aExecutionState->retrieveDocument(uri); if (loadNode) { if (frag.IsEmpty()) { aNodeSet->add(*loadNode); } else { txXPathTreeWalker walker(*loadNode); - if (walker.moveToElementById(frag)) { + if (walker.moveToElementById(NS_ConvertUTF8toUTF16(frag))) { aNodeSet->add(walker.getCurrentPosition()); } } @@ -87,7 +74,7 @@ DocumentFunctionCall::evaluate(txIEvalContext* aContext, rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult1)); NS_ENSURE_SUCCESS(rv, rv); - nsAutoString baseURI; + nsCOMPtr baseURI; bool baseURISet = false; if (mParams.Length() == 2) { @@ -104,7 +91,8 @@ DocumentFunctionCall::evaluate(txIEvalContext* aContext, baseURISet = true; if (!nodeSet2->isEmpty()) { - txXPathNodeUtils::getBaseURI(nodeSet2->get(0), baseURI); + txXPathNodeUtils::getBaseURI(nodeSet2->get(0), + getter_AddRefs(baseURI)); } } @@ -121,7 +109,7 @@ DocumentFunctionCall::evaluate(txIEvalContext* aContext, if (!baseURISet) { // if the second argument wasn't specified, use // the baseUri of node itself - txXPathNodeUtils::getBaseURI(node, baseURI); + txXPathNodeUtils::getBaseURI(node, getter_AddRefs(baseURI)); } retrieveNode(es, uriStr, baseURI, nodeSet); } @@ -134,8 +122,8 @@ DocumentFunctionCall::evaluate(txIEvalContext* aContext, // The first argument is not a NodeSet nsAutoString uriStr; exprResult1->stringValue(uriStr); - const nsAString* base = baseURISet ? &baseURI : &mBaseURI; - retrieveNode(es, uriStr, *base, nodeSet); + nsIURI* base = baseURISet ? baseURI.get() : mBaseURI.get(); + retrieveNode(es, uriStr, base, nodeSet); NS_ADDREF(*aResult = nodeSet); diff --git a/dom/xslt/xslt/txExecutionState.cpp b/dom/xslt/xslt/txExecutionState.cpp index 7bd3bdc07b34..9389f63e0432 100644 --- a/dom/xslt/xslt/txExecutionState.cpp +++ b/dom/xslt/xslt/txExecutionState.cpp @@ -21,27 +21,28 @@ txLoadedDocumentsHash::init(txXPathNode* aSourceDocument) { mSourceDocument = aSourceDocument; - nsAutoString baseURI; - txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI); + nsCOMPtr baseURI; + txXPathNodeUtils::getBaseURI(*mSourceDocument, getter_AddRefs(baseURI)); - PutEntry(baseURI)->mDocument = mSourceDocument; + LookupOrAdd(baseURI)->mDocument = mSourceDocument; } txLoadedDocumentsHash::~txLoadedDocumentsHash() { if (mSourceDocument) { - nsAutoString baseURI; - txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI); + nsCOMPtr baseURI; + txXPathNodeUtils::getBaseURI(*mSourceDocument, getter_AddRefs(baseURI)); - txLoadedDocumentEntry* entry = GetEntry(baseURI); - if (entry) { - delete entry->mDocument.forget(); + txLoadedDocumentInfo* info = Get(baseURI); + if (info) { + delete info->mDocument.forget(); } } } txExecutionState::txExecutionState(txStylesheet* aStylesheet, - bool aDisableLoads) + bool aDisableLoads, + nsIDocument* aLoadingDocument) : mOutputHandler(nullptr), mResultHandler(nullptr), mStylesheet(aStylesheet), @@ -51,6 +52,7 @@ txExecutionState::txExecutionState(txStylesheet* aStylesheet, mEvalContext(nullptr), mInitialEvalContext(nullptr), mGlobalParams(nullptr), + mLoadingDocument(aLoadingDocument), mKeyHash(aStylesheet->getKeyMap()), mDisableLoads(aDisableLoads) { @@ -372,41 +374,48 @@ txExecutionState::getEvalContext() } const txXPathNode* -txExecutionState::retrieveDocument(const nsAString& aUri) +txExecutionState::retrieveDocument(nsIURI* aUri) { - NS_ASSERTION(!aUri.Contains(char16_t('#')), - "Remove the fragment."); +#ifdef DEBUG + { + bool hasFrag; + aUri->GetHasRef(&hasFrag); + MOZ_ASSERT(!hasFrag, "Remove the fragment"); + } +#endif - if (mDisableLoads) { + if (mDisableLoads || !mLoadingDocument) { return nullptr; } - MOZ_LOG(txLog::xslt, LogLevel::Debug, - ("Retrieve Document %s", NS_LossyConvertUTF16toASCII(aUri).get())); + if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Debug)) { + nsAutoCString spec; + aUri->GetSpec(spec); + MOZ_LOG(txLog::xslt, LogLevel::Debug, + ("Retrieve Document %s", spec.get())); + } // try to get already loaded document - txLoadedDocumentEntry *entry = mLoadedDocuments.PutEntry(aUri); - if (!entry) { - return nullptr; - } + txLoadedDocumentInfo* info = mLoadedDocuments.LookupOrAdd(aUri); - if (!entry->mDocument && !entry->LoadingFailed()) { + if (!info->mDocument && !info->LoadingFailed()) { // open URI nsAutoString errMsg; - // XXX we should get the loader from the actual node - // triggering the load, but this will do for the time being - entry->mLoadResult = - txParseDocumentFromURI(aUri, *mLoadedDocuments.mSourceDocument, - errMsg, getter_Transfers(entry->mDocument)); + info->mLoadResult = + txParseDocumentFromURI(aUri, mLoadingDocument, + errMsg, getter_Transfers(info->mDocument)); - if (entry->LoadingFailed()) { + if (info->LoadingFailed()) { + nsAutoCString spec; + aUri->GetSpec(spec); receiveError(NS_LITERAL_STRING("Couldn't load document '") + - aUri + NS_LITERAL_STRING("': ") + errMsg, - entry->mLoadResult); + NS_ConvertUTF8toUTF16(spec) + + NS_LITERAL_STRING("': ") + errMsg, + info->mLoadResult); } } - return entry->mDocument; + return info->mDocument; } nsresult diff --git a/dom/xslt/xslt/txExecutionState.h b/dom/xslt/xslt/txExecutionState.h index b5525c59b049..f57a516ddaab 100644 --- a/dom/xslt/xslt/txExecutionState.h +++ b/dom/xslt/xslt/txExecutionState.h @@ -17,24 +17,19 @@ #include "txStylesheet.h" #include "txXPathTreeWalker.h" #include "nsTArray.h" +#include "nsURIHashKey.h" class txAOutputHandlerFactory; class txAXMLEventHandler; class txInstruction; -class txLoadedDocumentEntry : public nsStringHashKey +class txLoadedDocumentInfo { public: - explicit txLoadedDocumentEntry(KeyTypePointer aStr) : nsStringHashKey(aStr), - mLoadResult(NS_OK) + explicit txLoadedDocumentInfo() : mLoadResult(NS_OK) { } - txLoadedDocumentEntry(const txLoadedDocumentEntry& aToCopy) - : nsStringHashKey(aToCopy) - { - NS_ERROR("We're horked."); - } - ~txLoadedDocumentEntry() + ~txLoadedDocumentInfo() { if (mDocument) { txXPathNodeUtils::release(mDocument); @@ -52,11 +47,11 @@ public: nsresult mLoadResult; }; -class txLoadedDocumentsHash : public nsTHashtable +class txLoadedDocumentsHash : public nsClassHashtable { public: txLoadedDocumentsHash() - : nsTHashtable(4), + : nsClassHashtable(4), mSourceDocument(nullptr) { } @@ -72,7 +67,8 @@ private: class txExecutionState : public txIMatchContext { public: - txExecutionState(txStylesheet* aStylesheet, bool aDisableLoads); + txExecutionState(txStylesheet* aStylesheet, bool aDisableLoads, + nsIDocument* aLoaderDocument); ~txExecutionState(); nsresult init(const txXPathNode& aNode, txOwningExpandedNameMap* aGlobalParams); @@ -107,7 +103,7 @@ public: // state-getting functions txIEvalContext* getEvalContext(); - const txXPathNode* retrieveDocument(const nsAString& aUri); + const txXPathNode* retrieveDocument(nsIURI* aUri); nsresult getKeyNodes(const txExpandedName& aKeyName, const txXPathNode& aRoot, const nsAString& aKeyValue, bool aIndexIfNotFound, @@ -161,6 +157,7 @@ private: //Document* mRTFDocument; txOwningExpandedNameMap* mGlobalParams; + nsCOMPtr mLoadingDocument; txLoadedDocumentsHash mLoadedDocuments; txKeyHash mKeyHash; RefPtr mRecycler; diff --git a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp index 3df5d21802eb..379ba4f33daa 100644 --- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp +++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp @@ -256,6 +256,16 @@ txStylesheetSink::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) nsCOMPtr channel = do_QueryInterface(aRequest); + nsCOMPtr channelPrincipal; + nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( + channel, getter_AddRefs(channelPrincipal)); + mCompiler->setPrincipal(channelPrincipal); + + nsCOMPtr baseURI; + nsresult rv = NS_GetFinalChannelURI(channel, getter_AddRefs(baseURI)); + NS_ENSURE_SUCCESS(rv, rv); + mCompiler->setBaseURI(baseURI); + // check channel's charset... nsAutoCString charsetVal; nsAutoCString charset; @@ -374,9 +384,8 @@ public: TX_DECL_ACOMPILEOBSERVER NS_INLINE_DECL_REFCOUNTING(txCompileObserver) - nsresult startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler, - nsIPrincipal* aSourcePrincipal, - ReferrerPolicy aReferrerPolicy); + nsresult startLoad(nsIURI* aUri, nsIPrincipal* aSourcePrincipal, + txStylesheetCompiler* aCompiler); private: RefPtr mProcessor; @@ -399,30 +408,15 @@ txCompileObserver::txCompileObserver(txMozillaXSLTProcessor* aProcessor, } nsresult -txCompileObserver::loadURI(const nsAString& aUri, - const nsAString& aReferrerUri, - ReferrerPolicy aReferrerPolicy, +txCompileObserver::loadURI(nsIURI* aUri, + nsIPrincipal* aReferrerPrincipal, txStylesheetCompiler* aCompiler) { - if (mProcessor->IsLoadDisabled()) { + if (mProcessor->IsLoadDisabled() || !mLoaderDocument) { return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR; } - nsCOMPtr uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr referrerUri; - rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr referrerPrincipal; - rv = nsContentUtils::GetSecurityManager()-> - GetSimpleCodebasePrincipal(referrerUri, - getter_AddRefs(referrerPrincipal)); - NS_ENSURE_SUCCESS(rv, rv); - - return startLoad(uri, aCompiler, referrerPrincipal, aReferrerPolicy); + return startLoad(aUri, aReferrerPrincipal, aCompiler); } void @@ -440,10 +434,11 @@ txCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler, } nsresult -txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler, - nsIPrincipal* aReferrerPrincipal, - ReferrerPolicy aReferrerPolicy) +txCompileObserver::startLoad(nsIURI* aUri, nsIPrincipal* aReferrerPrincipal, + txStylesheetCompiler* aCompiler) { + MOZ_ASSERT(aReferrerPrincipal); + nsCOMPtr loadGroup = mLoaderDocument->GetDocumentLoadGroup(); if (!loadGroup) { return NS_ERROR_FAILURE; @@ -472,7 +467,8 @@ txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler, nsCOMPtr referrerURI; aReferrerPrincipal->GetURI(getter_AddRefs(referrerURI)); if (referrerURI) { - httpChannel->SetReferrerWithPolicy(referrerURI, aReferrerPolicy); + httpChannel->SetReferrerWithPolicy(referrerURI, + mLoaderDocument->GetReferrerPolicy()); } } @@ -480,7 +476,6 @@ txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler, NS_ENSURE_SUCCESS(rv, rv); RefPtr sink = new txStylesheetSink(aCompiler, parser); - NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY); channel->SetNotificationCallbacks(sink); @@ -493,24 +488,26 @@ txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler, nsresult TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor, - nsIDocument* aLoaderDocument, ReferrerPolicy aReferrerPolicy) + nsIDocument* aLoaderDocument) { - nsIPrincipal* principal = aLoaderDocument->NodePrincipal(); - - nsAutoCString spec; - aUri->GetSpec(spec); - MOZ_LOG(txLog::xslt, LogLevel::Info, ("TX_LoadSheet: %s\n", spec.get())); + if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) { + nsAutoCString spec; + aUri->GetSpec(spec); + MOZ_LOG(txLog::xslt, LogLevel::Info, + ("TX_LoadSheet: %s\n", spec.get())); + } RefPtr observer = new txCompileObserver(aProcessor, aLoaderDocument); - NS_ENSURE_TRUE(observer, NS_ERROR_OUT_OF_MEMORY); + + nsAutoCString fragment; + aUri->GetRef(fragment); RefPtr compiler = - new txStylesheetCompiler(NS_ConvertUTF8toUTF16(spec), aReferrerPolicy, - observer); - NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY); + new txStylesheetCompiler(NS_ConvertUTF8toUTF16(fragment), observer); - return observer->startLoad(aUri, compiler, principal, aReferrerPolicy); + return observer->startLoad(aUri, aLoaderDocument->NodePrincipal(), + compiler); } /** @@ -584,7 +581,11 @@ handleNode(nsINode* aNode, txStylesheetCompiler* aCompiler) class txSyncCompileObserver final : public txACompileObserver { public: - explicit txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor); + explicit txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor, + nsIDocument* aLoaderDocument) + : mProcessor(aProcessor), + mLoaderDocument(aLoaderDocument) + {} TX_DECL_ACOMPILEOBSERVER NS_INLINE_DECL_REFCOUNTING(txSyncCompileObserver) @@ -596,60 +597,43 @@ private: } RefPtr mProcessor; + nsCOMPtr mLoaderDocument; }; -txSyncCompileObserver::txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor) - : mProcessor(aProcessor) -{ -} - nsresult -txSyncCompileObserver::loadURI(const nsAString& aUri, - const nsAString& aReferrerUri, - ReferrerPolicy aReferrerPolicy, +txSyncCompileObserver::loadURI(nsIURI* aUri, + nsIPrincipal* aReferrerPrincipal, txStylesheetCompiler* aCompiler) { - if (mProcessor->IsLoadDisabled()) { + MOZ_ASSERT(aReferrerPrincipal); + + if (mProcessor->IsLoadDisabled() || !mLoaderDocument) { return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR; } - nsCOMPtr uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr referrerUri; - rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr referrerPrincipal; - rv = nsContentUtils::GetSecurityManager()-> - GetSimpleCodebasePrincipal(referrerUri, - getter_AddRefs(referrerPrincipal)); - NS_ENSURE_SUCCESS(rv, rv); - - // This is probably called by js, a loadGroup for the channel doesn't - // make sense. - nsCOMPtr source; - if (mProcessor) { - source = - do_QueryInterface(mProcessor->GetSourceContentModel()); - } - nsAutoSyncOperation sync(source ? source->OwnerDoc() : nullptr); + nsAutoSyncOperation sync(mLoaderDocument); nsCOMPtr document; - rv = nsSyncLoadService::LoadDocument(uri, nsIContentPolicy::TYPE_XSLT, - referrerPrincipal, - nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, - nullptr, false, - aReferrerPolicy, - getter_AddRefs(document)); + nsCOMPtr loadGroup = mLoaderDocument->GetDocumentLoadGroup(); + nsresult rv = + nsSyncLoadService::LoadDocument(aUri, + nsIContentPolicy::TYPE_XSLT, + aReferrerPrincipal, + nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, + loadGroup, + false, + mLoaderDocument->GetReferrerPolicy(), + getter_AddRefs(document)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr doc = do_QueryInterface(document); + nsCOMPtr baseURI = doc->GetBaseURI(); + aCompiler->setBaseURI(baseURI); + aCompiler->setPrincipal(doc->NodePrincipal()); rv = handleNode(doc, aCompiler); if (NS_FAILED(rv)) { nsAutoCString spec; - uri->GetSpec(spec); + aUri->GetSpec(spec); aCompiler->cancel(rv, nullptr, NS_ConvertUTF8toUTF16(spec).get()); return rv; } @@ -666,46 +650,20 @@ void txSyncCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler, } nsresult -TX_CompileStylesheet(nsINode* aNode, txMozillaXSLTProcessor* aProcessor, +TX_CompileStylesheet(nsINode* aNode, + nsIDocument* aLoaderDocument, + txMozillaXSLTProcessor* aProcessor, txStylesheet** aStylesheet) { - // If we move GetBaseURI to nsINode this can be simplified. - nsCOMPtr doc = aNode->OwnerDoc(); - - nsCOMPtr uri; - if (aNode->IsNodeOfType(nsINode::eCONTENT)) { - uri = static_cast(aNode)->GetBaseURI(); - } - else { - NS_ASSERTION(aNode->IsNodeOfType(nsINode::eDOCUMENT), "not a doc"); - uri = static_cast(aNode)->GetBaseURI(); - } - NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); - - nsAutoCString spec; - uri->GetSpec(spec); - NS_ConvertUTF8toUTF16 baseURI(spec); - - nsIURI* docUri = doc->GetDocumentURI(); - NS_ENSURE_TRUE(docUri, NS_ERROR_FAILURE); - - // We need to remove the ref, a URI with a ref would mean that we have an - // embedded stylesheet. - docUri->CloneIgnoringRef(getter_AddRefs(uri)); - NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); - - uri->GetSpec(spec); - NS_ConvertUTF8toUTF16 stylesheetURI(spec); - RefPtr obs = - new txSyncCompileObserver(aProcessor); - NS_ENSURE_TRUE(obs, NS_ERROR_OUT_OF_MEMORY); + new txSyncCompileObserver(aProcessor, aLoaderDocument); RefPtr compiler = - new txStylesheetCompiler(stylesheetURI, doc->GetReferrerPolicy(), obs); - NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY); + new txStylesheetCompiler(EmptyString(), obs); + nsCOMPtr baseURI = aNode->GetBaseURI(); compiler->setBaseURI(baseURI); + compiler->setPrincipal(aNode->NodePrincipal()); nsresult rv = handleNode(aNode, compiler); if (NS_FAILED(rv)) { diff --git a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp index 6e0c0945b58c..dc4cf4647c63 100644 --- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp +++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp @@ -361,6 +361,20 @@ txMozillaXSLTProcessor::txMozillaXSLTProcessor(nsISupports* aOwner) { } +NS_IMETHODIMP +txMozillaXSLTProcessor::Init(nsISupports* aOwner) +{ + mOwner = aOwner; + if (nsCOMPtr win = do_QueryInterface(aOwner)) { + if (win->IsOuterWindow()) { + // Must be bound to inner window, innerize if necessary. + mOwner = win->GetCurrentInnerWindow(); + } + } + + return NS_OK; +} + txMozillaXSLTProcessor::~txMozillaXSLTProcessor() { if (mStylesheetDocument) { @@ -604,7 +618,7 @@ txMozillaXSLTProcessor::ImportStylesheet(nsIDOMNode *aStyle) styleNode->IsNodeOfType(nsINode::eDOCUMENT)), NS_ERROR_INVALID_ARG); - nsresult rv = TX_CompileStylesheet(styleNode, this, + nsresult rv = TX_CompileStylesheet(styleNode, getLoaderDoc(), this, getter_AddRefs(mStylesheet)); // XXX set up exception context, bug 204658 NS_ENSURE_SUCCESS(rv, rv); @@ -659,7 +673,7 @@ txMozillaXSLTProcessor::TransformToDoc(nsIDOMDocument **aResult, sourceDOMDocument = do_QueryInterface(mSource); } - txExecutionState es(mStylesheet, IsLoadDisabled()); + txExecutionState es(mStylesheet, IsLoadDisabled(), getLoaderDoc()); // XXX Need to add error observers @@ -727,7 +741,7 @@ txMozillaXSLTProcessor::TransformToFragment(nsIDOMNode *aSource, return NS_ERROR_OUT_OF_MEMORY; } - txExecutionState es(mStylesheet, IsLoadDisabled()); + txExecutionState es(mStylesheet, IsLoadDisabled(), getLoaderDoc()); // XXX Need to add error observers @@ -1036,12 +1050,7 @@ NS_IMETHODIMP txMozillaXSLTProcessor::LoadStyleSheet(nsIURI* aUri, nsIDocument* aLoaderDocument) { - mozilla::net::ReferrerPolicy refpol = mozilla::net::RP_Default; - if (mStylesheetDocument) { - refpol = mStylesheetDocument->GetReferrerPolicy(); - } - - nsresult rv = TX_LoadSheet(aUri, this, aLoaderDocument, refpol); + nsresult rv = TX_LoadSheet(aUri, this, aLoaderDocument); if (NS_FAILED(rv) && mObserver) { // This is most likely a network or security error, just // use the uri as context. @@ -1198,6 +1207,24 @@ txMozillaXSLTProcessor::notifyError() mObserver->OnTransformDone(mTransformResult, document); } +nsIDocument* +txMozillaXSLTProcessor::getLoaderDoc() +{ + if (mOwner) { + nsCOMPtr win = do_QueryInterface(mOwner); + if (win) { + return win->GetExtantDoc(); + } + } + + if (mSource) { + nsCOMPtr node = do_QueryInterface(mSource); + return node->OwnerDoc(); + } + + return nullptr; +} + nsresult txMozillaXSLTProcessor::ensureStylesheet() { @@ -1212,7 +1239,8 @@ txMozillaXSLTProcessor::ensureStylesheet() style = mStylesheetDocument; } - return TX_CompileStylesheet(style, this, getter_AddRefs(mStylesheet)); + return TX_CompileStylesheet(style, getLoaderDoc(), this, + getter_AddRefs(mStylesheet)); } void diff --git a/dom/xslt/xslt/txMozillaXSLTProcessor.h b/dom/xslt/xslt/txMozillaXSLTProcessor.h index 6fa2ab1338c2..1cf381a873b6 100644 --- a/dom/xslt/xslt/txMozillaXSLTProcessor.h +++ b/dom/xslt/xslt/txMozillaXSLTProcessor.h @@ -166,6 +166,8 @@ private: void notifyError(); nsresult ensureStylesheet(); + nsIDocument* getLoaderDoc(); + nsCOMPtr mOwner; RefPtr mStylesheet; @@ -185,10 +187,10 @@ private: }; extern nsresult TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor, - nsIDocument* aLoaderDocument, - mozilla::net::ReferrerPolicy aReferrerPolicy); + nsIDocument* aLoaderDocument); extern nsresult TX_CompileStylesheet(nsINode* aNode, + nsIDocument* aLoaderDocument, txMozillaXSLTProcessor* aProcessor, txStylesheet** aStylesheet); diff --git a/dom/xslt/xslt/txStylesheetCompileHandlers.cpp b/dom/xslt/xslt/txStylesheetCompileHandlers.cpp index f2aa10a796d3..295604cd1197 100644 --- a/dom/xslt/xslt/txStylesheetCompileHandlers.cpp +++ b/dom/xslt/xslt/txStylesheetCompileHandlers.cpp @@ -20,6 +20,7 @@ #include "txNamespaceMap.h" #include "txURIUtils.h" #include "txXSLTFunctions.h" +#include "nsNetUtil.h" using namespace mozilla; @@ -756,10 +757,12 @@ txFnStartImport(int32_t aNamespaceID, nsGkAtoms::href, true, &attr); NS_ENSURE_SUCCESS(rv, rv); - nsAutoString absUri; - URIUtils::resolveHref(attr->mValue, aState.mElementContext->mBaseURI, - absUri); - rv = aState.loadImportedStylesheet(absUri, importPtr->mFrame); + nsCOMPtr uri; + rv = NS_NewURI(getter_AddRefs(uri), attr->mValue, nullptr, + aState.mElementContext->mBaseURI); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aState.loadImportedStylesheet(uri, importPtr->mFrame); NS_ENSURE_SUCCESS(rv, rv); return aState.pushHandlerTable(gTxIgnoreHandler); @@ -787,10 +790,12 @@ txFnStartInclude(int32_t aNamespaceID, nsGkAtoms::href, true, &attr); NS_ENSURE_SUCCESS(rv, rv); - nsAutoString absUri; - URIUtils::resolveHref(attr->mValue, aState.mElementContext->mBaseURI, - absUri); - rv = aState.loadIncludedStylesheet(absUri); + nsCOMPtr uri; + rv = NS_NewURI(getter_AddRefs(uri), attr->mValue, nullptr, + aState.mElementContext->mBaseURI); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aState.loadIncludedStylesheet(uri); NS_ENSURE_SUCCESS(rv, rv); return aState.pushHandlerTable(gTxIgnoreHandler); diff --git a/dom/xslt/xslt/txStylesheetCompiler.cpp b/dom/xslt/xslt/txStylesheetCompiler.cpp index e01b37b41d33..b086f44bfbf3 100644 --- a/dom/xslt/xslt/txStylesheetCompiler.cpp +++ b/dom/xslt/xslt/txStylesheetCompiler.cpp @@ -22,31 +22,29 @@ #include "nsICategoryManager.h" #include "nsServiceManagerUtils.h" #include "nsTArray.h" +#include "nsIURI.h" using namespace mozilla; using mozilla::net::ReferrerPolicy; -txStylesheetCompiler::txStylesheetCompiler(const nsAString& aStylesheetURI, - ReferrerPolicy aReferrerPolicy, +txStylesheetCompiler::txStylesheetCompiler(const nsString& aFragment, txACompileObserver* aObserver) : txStylesheetCompilerState(aObserver) { - mStatus = init(aStylesheetURI, aReferrerPolicy, nullptr, nullptr); + mStatus = init(aFragment, nullptr, nullptr); } -txStylesheetCompiler::txStylesheetCompiler(const nsAString& aStylesheetURI, +txStylesheetCompiler::txStylesheetCompiler(const nsString& aFragment, txStylesheet* aStylesheet, txListIterator* aInsertPosition, - ReferrerPolicy aReferrerPolicy, txACompileObserver* aObserver) : txStylesheetCompilerState(aObserver) { - mStatus = init(aStylesheetURI, aReferrerPolicy, aStylesheet, - aInsertPosition); + mStatus = init(aFragment, aStylesheet, aInsertPosition); } void -txStylesheetCompiler::setBaseURI(const nsString& aBaseURI) +txStylesheetCompiler::setBaseURI(nsIURI* aBaseURI) { NS_ASSERTION(mObjectStack.size() == 1 && !mObjectStack.peek(), "Execution already started"); @@ -58,6 +56,16 @@ txStylesheetCompiler::setBaseURI(const nsString& aBaseURI) mElementContext->mBaseURI = aBaseURI; } +void +txStylesheetCompiler::setPrincipal(nsIPrincipal* aPrincipal) +{ + if (NS_FAILED(mStatus)) { + return; + } + + mStylesheetPrincipal = aPrincipal; +} + nsresult txStylesheetCompiler::startElement(int32_t aNamespaceID, nsIAtom* aLocalName, nsIAtom* aPrefix, @@ -216,9 +224,11 @@ txStylesheetCompiler::startElementInternal(int32_t aNamespaceID, rv = ensureNewElementContext(); NS_ENSURE_SUCCESS(rv, rv); - nsAutoString uri; - URIUtils::resolveHref(attr->mValue, mElementContext->mBaseURI, uri); - mElementContext->mBaseURI = uri; + nsCOMPtr uri; + rv = NS_NewURI(getter_AddRefs(uri), attr->mValue, + nullptr, mElementContext->mBaseURI); + NS_ENSURE_SUCCESS(rv, rv); + mElementContext->mBaseURI = uri.forget(); } // extension-element-prefixes @@ -370,9 +380,15 @@ txStylesheetCompiler::characters(const nsAString& aStr) nsresult txStylesheetCompiler::doneLoading() { - MOZ_LOG(txLog::xslt, LogLevel::Info, - ("Compiler::doneLoading: %s\n", - NS_LossyConvertUTF16toASCII(mStylesheetURI).get())); + if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) { + nsCOMPtr uri; + mStylesheetPrincipal->GetURI(getter_AddRefs(uri)); + nsAutoCString spec; + uri->GetSpec(spec); + MOZ_LOG(txLog::xslt, LogLevel::Info, + ("Compiler::doneLoading: %s\n", + spec.get())); + } if (NS_FAILED(mStatus)) { return mStatus; } @@ -386,11 +402,17 @@ void txStylesheetCompiler::cancel(nsresult aError, const char16_t *aErrorText, const char16_t *aParam) { - MOZ_LOG(txLog::xslt, LogLevel::Info, - ("Compiler::cancel: %s, module: %d, code %d\n", - NS_LossyConvertUTF16toASCII(mStylesheetURI).get(), - NS_ERROR_GET_MODULE(aError), - NS_ERROR_GET_CODE(aError))); + if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) { + nsCOMPtr uri; + mStylesheetPrincipal->GetURI(getter_AddRefs(uri)); + nsAutoCString spec; + uri->GetSpec(spec); + MOZ_LOG(txLog::xslt, LogLevel::Info, + ("Compiler::cancel: %s, module: %d, code %d\n", + spec.get(), + NS_ERROR_GET_MODULE(aError), + NS_ERROR_GET_CODE(aError))); + } if (NS_SUCCEEDED(mStatus)) { mStatus = aError; } @@ -410,20 +432,30 @@ txStylesheetCompiler::getStylesheet() } nsresult -txStylesheetCompiler::loadURI(const nsAString& aUri, - const nsAString& aReferrerUri, - ReferrerPolicy aReferrerPolicy, +txStylesheetCompiler::loadURI(nsIURI* aUri, + nsIPrincipal* aReferrerPrincipal, txStylesheetCompiler* aCompiler) { - MOZ_LOG(txLog::xslt, LogLevel::Info, - ("Compiler::loadURI forwards %s thru %s\n", - NS_LossyConvertUTF16toASCII(aUri).get(), - NS_LossyConvertUTF16toASCII(mStylesheetURI).get())); - if (mStylesheetURI.Equals(aUri)) { + nsCOMPtr stylesheetURI; + mStylesheetPrincipal->GetURI(getter_AddRefs(stylesheetURI)); + + if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) { + nsAutoCString stylesheetSpec; + stylesheetURI->GetSpec(stylesheetSpec); + nsAutoCString uriSpec; + aUri->GetSpec(uriSpec); + MOZ_LOG(txLog::xslt, LogLevel::Info, + ("Compiler::loadURI forwards %s thru %s\n", + uriSpec.get(), + stylesheetSpec.get())); + } + + bool equals; + if (NS_FAILED(stylesheetURI->Equals(aUri, &equals)) || equals) { return NS_ERROR_XSLT_LOAD_RECURSION; } return mObserver ? - mObserver->loadURI(aUri, aReferrerUri, aReferrerPolicy, aCompiler) : + mObserver->loadURI(aUri, aReferrerPrincipal, aCompiler) : NS_ERROR_FAILURE; } @@ -533,27 +565,20 @@ txStylesheetCompilerState::txStylesheetCompilerState(txACompileObserver* aObserv } nsresult -txStylesheetCompilerState::init(const nsAString& aStylesheetURI, - ReferrerPolicy aReferrerPolicy, +txStylesheetCompilerState::init(const nsString& aFragment, txStylesheet* aStylesheet, txListIterator* aInsertPosition) { NS_ASSERTION(!aStylesheet || aInsertPosition, "must provide insertposition if loading subsheet"); - mStylesheetURI = aStylesheetURI; - mReferrerPolicy = aReferrerPolicy; + // Check for fragment identifier of an embedded stylesheet. - int32_t fragment = aStylesheetURI.FindChar('#') + 1; - if (fragment > 0) { - int32_t fragmentLength = aStylesheetURI.Length() - fragment; - if (fragmentLength > 0) { - // This is really an embedded stylesheet, not just a - // "url#". We may want to unescape the fragment. - mTarget = Substring(aStylesheetURI, (uint32_t)fragment, - fragmentLength); - mEmbedStatus = eNeedEmbed; - mHandlerTable = gTxEmbedHandler; - } + if (!aFragment.IsEmpty()) { + // This is really an embedded stylesheet, not just a + // "url#". We may want to unescape the fragment. + mTarget = aFragment; + mEmbedStatus = eNeedEmbed; + mHandlerTable = gTxEmbedHandler; } nsresult rv = NS_OK; if (aStylesheet) { @@ -572,8 +597,7 @@ txStylesheetCompilerState::init(const nsAString& aStylesheetURI, mIsTopCompiler = true; } - mElementContext = new txElementContext(aStylesheetURI); - NS_ENSURE_TRUE(mElementContext->mMappings, NS_ERROR_OUT_OF_MEMORY); + mElementContext = new txElementContext(); // Push the "old" txElementContext rv = pushObject(0); @@ -736,18 +760,25 @@ txStylesheetCompilerState::addInstruction(nsAutoPtr&& aInstructio } nsresult -txStylesheetCompilerState::loadIncludedStylesheet(const nsAString& aURI) +txStylesheetCompilerState::loadIncludedStylesheet(nsIURI* aURI) { - MOZ_LOG(txLog::xslt, LogLevel::Info, - ("CompilerState::loadIncludedStylesheet: %s\n", - NS_LossyConvertUTF16toASCII(aURI).get())); - if (mStylesheetURI.Equals(aURI)) { + if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) { + nsAutoCString spec; + aURI->GetSpec(spec); + MOZ_LOG(txLog::xslt, LogLevel::Info, + ("CompilerState::loadIncludedStylesheet: %s\n", + spec.get())); + } + + nsCOMPtr stylesheetURI; + mStylesheetPrincipal->GetURI(getter_AddRefs(stylesheetURI)); + bool equals; + if (NS_FAILED(stylesheetURI->Equals(aURI, &equals)) || equals) { return NS_ERROR_XSLT_LOAD_RECURSION; } NS_ENSURE_TRUE(mObserver, NS_ERROR_NOT_IMPLEMENTED); nsAutoPtr item(new txDummyItem); - NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY); nsresult rv = mToplevelIterator.addBefore(item); NS_ENSURE_SUCCESS(rv, rv); @@ -759,19 +790,21 @@ txStylesheetCompilerState::loadIncludedStylesheet(const nsAString& aURI) txACompileObserver* observer = static_cast(this); + nsAutoCString fragment; + aURI->GetRef(fragment); + RefPtr compiler = - new txStylesheetCompiler(aURI, mStylesheet, &mToplevelIterator, - mReferrerPolicy, observer); - NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY); + new txStylesheetCompiler(NS_ConvertUTF8toUTF16(fragment), + mStylesheet, + &mToplevelIterator, + observer); // step forward before calling the observer in case of syncronous loading mToplevelIterator.next(); - if (mChildCompilerList.AppendElement(compiler) == nullptr) { - return NS_ERROR_OUT_OF_MEMORY; - } + mChildCompilerList.AppendElement(compiler); - rv = mObserver->loadURI(aURI, mStylesheetURI, mReferrerPolicy, compiler); + rv = mObserver->loadURI(aURI, mStylesheetPrincipal, compiler); if (NS_FAILED(rv)) { mChildCompilerList.RemoveElement(compiler); } @@ -780,13 +813,21 @@ txStylesheetCompilerState::loadIncludedStylesheet(const nsAString& aURI) } nsresult -txStylesheetCompilerState::loadImportedStylesheet(const nsAString& aURI, +txStylesheetCompilerState::loadImportedStylesheet(nsIURI* aURI, txStylesheet::ImportFrame* aFrame) { - MOZ_LOG(txLog::xslt, LogLevel::Info, - ("CompilerState::loadImportedStylesheet: %s\n", - NS_LossyConvertUTF16toASCII(aURI).get())); - if (mStylesheetURI.Equals(aURI)) { + if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) { + nsAutoCString spec; + aURI->GetSpec(spec); + MOZ_LOG(txLog::xslt, LogLevel::Info, + ("CompilerState::loadImportedStylesheet: %s\n", + spec.get())); + } + + nsCOMPtr stylesheetURI; + mStylesheetPrincipal->GetURI(getter_AddRefs(stylesheetURI)); + bool equals; + if (NS_FAILED(stylesheetURI->Equals(aURI, &equals)) || equals) { return NS_ERROR_XSLT_LOAD_RECURSION; } NS_ENSURE_TRUE(mObserver, NS_ERROR_NOT_IMPLEMENTED); @@ -796,17 +837,18 @@ txStylesheetCompilerState::loadImportedStylesheet(const nsAString& aURI, txACompileObserver* observer = static_cast(this); + nsAutoCString fragment; + aURI->GetRef(fragment); + RefPtr compiler = - new txStylesheetCompiler(aURI, mStylesheet, &iter, mReferrerPolicy, + new txStylesheetCompiler(NS_ConvertUTF8toUTF16(fragment), + mStylesheet, + &iter, observer); - NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY); - if (mChildCompilerList.AppendElement(compiler) == nullptr) { - return NS_ERROR_OUT_OF_MEMORY; - } + mChildCompilerList.AppendElement(compiler); - nsresult rv = mObserver->loadURI(aURI, mStylesheetURI, mReferrerPolicy, - compiler); + nsresult rv = mObserver->loadURI(aURI, mStylesheetPrincipal, compiler); if (NS_FAILED(rv)) { mChildCompilerList.RemoveElement(compiler); } @@ -1067,9 +1109,7 @@ extern bool TX_XSLTFunctionAvailable(nsIAtom* aName, int32_t aNameSpaceID) { RefPtr compiler = - new txStylesheetCompiler(EmptyString(), - mozilla::net::RP_Default, nullptr); - NS_ENSURE_TRUE(compiler, false); + new txStylesheetCompiler(EmptyString(), nullptr); nsAutoPtr fnCall; @@ -1113,10 +1153,9 @@ txStylesheetCompilerState::shutdown() sXPCOMFunctionMappings = nullptr; } -txElementContext::txElementContext(const nsAString& aBaseURI) +txElementContext::txElementContext() : mPreserveWhitespace(false), mForwardsCompatibleParsing(true), - mBaseURI(aBaseURI), mMappings(new txNamespaceMap), mDepth(0) { diff --git a/dom/xslt/xslt/txStylesheetCompiler.h b/dom/xslt/xslt/txStylesheetCompiler.h index 7fb0f05d6061..b15272160ca0 100644 --- a/dom/xslt/xslt/txStylesheetCompiler.h +++ b/dom/xslt/xslt/txStylesheetCompiler.h @@ -32,12 +32,12 @@ class txInScopeVariable; class txElementContext : public txObject { public: - explicit txElementContext(const nsAString& aBaseURI); + explicit txElementContext(); txElementContext(const txElementContext& aOther); bool mPreserveWhitespace; bool mForwardsCompatibleParsing; - nsString mBaseURI; + nsCOMPtr mBaseURI; RefPtr mMappings; nsTArray mInstructionNamespaces; int32_t mDepth; @@ -49,9 +49,8 @@ public: NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0; NS_IMETHOD_(MozExternalRefCountType) Release() = 0; - virtual nsresult loadURI(const nsAString& aUri, - const nsAString& aReferrerUri, - mozilla::net::ReferrerPolicy aReferrerPolicy, + virtual nsresult loadURI(nsIURI* aUri, + nsIPrincipal* aReferrerPrincipal, txStylesheetCompiler* aCompiler) = 0; virtual void onDoneCompiling(txStylesheetCompiler* aCompiler, nsresult aResult, @@ -60,8 +59,8 @@ public: }; #define TX_DECL_ACOMPILEOBSERVER \ - nsresult loadURI(const nsAString& aUri, const nsAString& aReferrerUri, \ - mozilla::net::ReferrerPolicy aReferrerPolicy, \ + nsresult loadURI(nsIURI* aUri, \ + nsIPrincipal* aReferrerPrincipal, \ txStylesheetCompiler* aCompiler); \ void onDoneCompiling(txStylesheetCompiler* aCompiler, nsresult aResult, \ const char16_t *aErrorText = nullptr, \ @@ -73,9 +72,8 @@ public: explicit txStylesheetCompilerState(txACompileObserver* aObserver); ~txStylesheetCompilerState(); - nsresult init(const nsAString& aStylesheetURI, - mozilla::net::ReferrerPolicy aReferrerPolicy, - txStylesheet* aStylesheet, txListIterator* aInsertPosition); + nsresult init(const nsString& aFragment, txStylesheet* aStylesheet, + txListIterator* aInsertPosition); // Embedded stylesheets state bool handleEmbeddedSheet() @@ -116,8 +114,8 @@ public: nsresult openInstructionContainer(txInstructionContainer* aContainer); void closeInstructionContainer(); nsresult addInstruction(nsAutoPtr&& aInstruction); - nsresult loadIncludedStylesheet(const nsAString& aURI); - nsresult loadImportedStylesheet(const nsAString& aURI, + nsresult loadIncludedStylesheet(nsIURI* aURI); + nsresult loadImportedStylesheet(nsIURI* aURI, txStylesheet::ImportFrame* aFrame); // misc @@ -166,6 +164,7 @@ public: uint16_t mDisAllowed; protected: + nsCOMPtr mStylesheetPrincipal; RefPtr mObserver; nsTArray mInScopeVariables; nsTArray mChildCompilerList; @@ -178,7 +177,6 @@ protected: eInEmbed, eHasEmbed } mEmbedStatus; - nsString mStylesheetURI; bool mIsTopCompiler; bool mDoneWithThisStylesheet; txStack mObjectStack; @@ -189,7 +187,6 @@ private: txInstruction** mNextInstrPtr; txListIterator mToplevelIterator; nsTArray mGotoTargetPointers; - mozilla::net::ReferrerPolicy mReferrerPolicy; }; struct txStylesheetAttr @@ -207,16 +204,15 @@ public: friend class txStylesheetCompilerState; friend bool TX_XSLTFunctionAvailable(nsIAtom* aName, int32_t aNameSpaceID); - txStylesheetCompiler(const nsAString& aStylesheetURI, - mozilla::net::ReferrerPolicy aReferrerPolicy, + txStylesheetCompiler(const nsString& aFragment, txACompileObserver* aObserver); - txStylesheetCompiler(const nsAString& aStylesheetURI, + txStylesheetCompiler(const nsString& aFragment, txStylesheet* aStylesheet, txListIterator* aInsertPosition, - mozilla::net::ReferrerPolicy aReferrerPolicy, txACompileObserver* aObserver); - void setBaseURI(const nsString& aBaseURI); + void setBaseURI(nsIURI* aBaseURI); + void setPrincipal(nsIPrincipal* aPrincipal); nsresult startElement(int32_t aNamespaceID, nsIAtom* aLocalName, nsIAtom* aPrefix, txStylesheetAttr* aAttributes, diff --git a/dom/xslt/xslt/txXSLTFunctions.h b/dom/xslt/xslt/txXSLTFunctions.h index 1bc0f9961eb0..3ef80f09507e 100644 --- a/dom/xslt/xslt/txXSLTFunctions.h +++ b/dom/xslt/xslt/txXSLTFunctions.h @@ -23,12 +23,14 @@ public: /** * Creates a new document() function call **/ - explicit DocumentFunctionCall(const nsAString& aBaseURI); + explicit DocumentFunctionCall(nsIURI* aBaseURI) + : mBaseURI(aBaseURI) + {} TX_DECL_FUNCTION private: - nsString mBaseURI; + nsCOMPtr mBaseURI; }; /* diff --git a/embedding/browser/nsWebBrowser.cpp b/embedding/browser/nsWebBrowser.cpp index b9dafeeed746..878fc84105db 100644 --- a/embedding/browser/nsWebBrowser.cpp +++ b/embedding/browser/nsWebBrowser.cpp @@ -1729,7 +1729,7 @@ nsWebBrowser::WindowLowered(nsIWidget* aWidget) } bool -nsWebBrowser::PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) +nsWebBrowser::PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion aRegion) { LayerManager* layerManager = aWidget->GetLayerManager(); NS_ASSERTION(layerManager, "Must be in paint event"); @@ -1737,7 +1737,7 @@ nsWebBrowser::PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) layerManager->BeginTransaction(); RefPtr root = layerManager->CreatePaintedLayer(); if (root) { - nsIntRect dirtyRect = aRegion.GetBounds(); + nsIntRect dirtyRect = aRegion.GetBounds().ToUnknownRect(); root->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(dirtyRect)); layerManager->SetRoot(root); } diff --git a/embedding/browser/nsWebBrowser.h b/embedding/browser/nsWebBrowser.h index 4eb60881a494..867f058886f1 100644 --- a/embedding/browser/nsWebBrowser.h +++ b/embedding/browser/nsWebBrowser.h @@ -122,7 +122,8 @@ protected: // nsIWidgetListener virtual void WindowRaised(nsIWidget* aWidget); virtual void WindowLowered(nsIWidget* aWidget); - virtual bool PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) override; + virtual bool PaintWindow(nsIWidget* aWidget, + mozilla::LayoutDeviceIntRegion aRegion) override; protected: RefPtr mDocShellTreeOwner; diff --git a/extensions/cookie/test/test_app_uninstall_cookies.html b/extensions/cookie/test/test_app_uninstall_cookies.html index 165bd27942d7..f5724f563b53 100644 --- a/extensions/cookie/test/test_app_uninstall_cookies.html +++ b/extensions/cookie/test/test_app_uninstall_cookies.html @@ -28,7 +28,7 @@ const Cu = Components.utils; SimpleTest.waitForExplicitFinish(); Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserver = new HttpServer(); var cookieSetPath = "/setcookie"; @@ -94,15 +94,8 @@ function cookieCheckHandler(metadata, response) { } function setupChannel(path) { - var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - var chan = ios.newChannel2("http://localhost:4444" + path, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var url = NetUtil.newURI("http://localhost:4444" + path); + var chan = NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}); chan.notificationCallbacks = cookies[counter].loadContext; chan.QueryInterface(Ci.nsIHttpChannel); return chan; @@ -111,7 +104,7 @@ function setupChannel(path) { function setCookie() { var channel = setupChannel(cookieSetPath); channel.setRequestHeader("foo-set-cookie", cookies[counter].cookieName, false); - channel.asyncOpen(new ChannelListener(setNextCookie, null), null); + channel.asyncOpen2(new ChannelListener(setNextCookie, null)); } function setNextCookie(request, data, context) { diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index 8ef835010f46..8ce1773ab8b0 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -694,6 +694,7 @@ public: DrawTarget() : mTransformDirty(false), mPermitSubpixelAA(false) {} virtual ~DrawTarget() {} + virtual bool IsValid() const { return true; }; virtual DrawTargetType GetType() const = 0; virtual BackendType GetBackendType() const = 0; diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp index 2b12034efc51..8e69bf128945 100644 --- a/gfx/2d/DrawTargetCairo.cpp +++ b/gfx/2d/DrawTargetCairo.cpp @@ -222,6 +222,7 @@ CopyToImageSurface(unsigned char *aData, // investigation hasn't been done to determine the underlying cause. We // will just handle the failure to allocate the surface to avoid a crash. if (cairo_surface_status(surf)) { + gfxWarning() << "Invalid surface DTC " << cairo_surface_status(surf); return nullptr; } @@ -600,10 +601,17 @@ DrawTargetCairo::~DrawTargetCairo() cairo_destroy(mContext); if (mSurface) { cairo_surface_destroy(mSurface); + mSurface = nullptr; } MOZ_ASSERT(!mLockedBits); } +bool +DrawTargetCairo::IsValid() const +{ + return mSurface && !cairo_surface_status(mSurface); +} + DrawTargetType DrawTargetCairo::GetType() const { @@ -681,6 +689,10 @@ GfxFormatForCairoSurface(cairo_surface_t* surface) already_AddRefed DrawTargetCairo::Snapshot() { + if (!IsValid()) { + gfxCriticalNote << "DrawTargetCairo::Snapshot with bad surface " << cairo_surface_status(mSurface); + return nullptr; + } if (mSnapshot) { RefPtr snapshot(mSnapshot); return snapshot.forget(); @@ -780,6 +792,11 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface, return; } + if (!IsValid()) { + gfxCriticalNote << "DrawSurface with bad surface " << cairo_surface_status(mSurface); + return; + } + AutoPrepareForDrawing prep(this, mContext); AutoClearDeviceOffset clear(aSurface); @@ -1031,7 +1048,7 @@ DrawTargetCairo::CopySurfaceInternal(cairo_surface_t* aSurface, const IntPoint &aDest) { if (cairo_surface_status(aSurface)) { - gfxWarning() << "Invalid surface"; + gfxWarning() << "Invalid surface" << cairo_surface_status(aSurface); return; } @@ -1234,6 +1251,11 @@ DrawTargetCairo::FillGlyphs(ScaledFont *aFont, return; } + if (!IsValid()) { + gfxDebug() << "FillGlyphs bad surface " << cairo_surface_status(mSurface); + return; + } + AutoPrepareForDrawing prep(this, mContext); AutoClearDeviceOffset clear(aPattern); @@ -1266,6 +1288,10 @@ DrawTargetCairo::FillGlyphs(ScaledFont *aFont, } cairo_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs); + + if (mSurface && cairo_surface_status(mSurface)) { + gfxDebug() << "Ending FillGlyphs with a bad surface " << cairo_surface_status(mSurface); + } } void @@ -1627,7 +1653,7 @@ bool DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat) { if (cairo_surface_status(aSurface)) { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) + gfxCriticalNote << "Attempt to create DrawTarget for invalid surface. " << aSize << " Cairo Status: " << cairo_surface_status(aSurface); cairo_surface_destroy(aSurface); diff --git a/gfx/2d/DrawTargetCairo.h b/gfx/2d/DrawTargetCairo.h index 216e1fd19050..4fdd25de91ad 100644 --- a/gfx/2d/DrawTargetCairo.h +++ b/gfx/2d/DrawTargetCairo.h @@ -59,6 +59,7 @@ public: DrawTargetCairo(); virtual ~DrawTargetCairo(); + virtual bool IsValid() const override; virtual DrawTargetType GetType() const override; virtual BackendType GetBackendType() const override { return BackendType::CAIRO; } virtual already_AddRefed Snapshot() override; diff --git a/gfx/layers/TextureDIB.cpp b/gfx/layers/TextureDIB.cpp index 7dcaf3c21439..d021cfa2e951 100644 --- a/gfx/layers/TextureDIB.cpp +++ b/gfx/layers/TextureDIB.cpp @@ -366,6 +366,11 @@ DIBTextureHost::UpdatedInternal(const nsIntRegion* aRegion) mTextureSource = mCompositor->CreateDataTextureSource(mFlags); } + if (mSurface->CairoStatus()) { + gfxWarning() << "Bad Cairo surface internal update " << mSurface->CairoStatus(); + mTextureSource = nullptr; + return; + } RefPtr imgSurf = mSurface->GetAsImageSurface(); RefPtr surf = Factory::CreateWrappingDataSourceSurface(imgSurf->Data(), imgSurf->Stride(), mSize, mFormat); diff --git a/gfx/layers/apz/src/AsyncPanZoomAnimation.h b/gfx/layers/apz/src/AsyncPanZoomAnimation.h index 27caa20da912..baa3643cf3cf 100644 --- a/gfx/layers/apz/src/AsyncPanZoomAnimation.h +++ b/gfx/layers/apz/src/AsyncPanZoomAnimation.h @@ -43,14 +43,11 @@ public: } /** - * Get the deferred tasks in |mDeferredTasks|. See |mDeferredTasks| - * for more information. - * Clears |mDeferredTasks|. + * Get the deferred tasks in |mDeferredTasks| and place them in |aTasks|. See + * |mDeferredTasks| for more information. Clears |mDeferredTasks|. */ - Vector TakeDeferredTasks() { - Vector result; - mDeferredTasks.swap(result); - return result; + void TakeDeferredTasks(Vector& aTasks) { + mDeferredTasks.swap(aTasks); } /** diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 9b72f217d6f6..ad816df735b6 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -2880,7 +2880,7 @@ bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime, if (mAnimation) { bool continueAnimation = mAnimation->Sample(mFrameMetrics, sampleTimeDelta); - *aOutDeferredTasks = mAnimation->TakeDeferredTasks(); + mAnimation->TakeDeferredTasks(*aOutDeferredTasks); if (continueAnimation) { if (mPaintThrottler->TimeSinceLastRequest(aSampleTime) > mAnimation->mRepaintInterval) { diff --git a/gfx/layers/basic/BasicPaintedLayer.cpp b/gfx/layers/basic/BasicPaintedLayer.cpp index 81acd0b630fa..13e7461aaa7b 100644 --- a/gfx/layers/basic/BasicPaintedLayer.cpp +++ b/gfx/layers/basic/BasicPaintedLayer.cpp @@ -162,7 +162,8 @@ BasicPaintedLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback, mContentClient->BeginPaintBuffer(this, flags); mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate); - if (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state)) { + DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state); + if (target && target->IsValid()) { // The area that became invalid and is visible needs to be repainted // (this could be the whole visible area if our buffer switched // from RGB to RGBA, because we might need to repaint with @@ -183,16 +184,22 @@ BasicPaintedLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback, Mutated(); ctx = nullptr; mContentClient->ReturnDrawTargetToBuffer(target); + target = nullptr; RenderTraceInvalidateEnd(this, "FFFF00"); } else { + if (target) { + mContentClient->ReturnDrawTargetToBuffer(target); + target = nullptr; + } + // It's possible that state.mRegionToInvalidate is nonempty here, // if we are shrinking the valid region to nothing. So use mRegionToDraw // instead. NS_WARN_IF_FALSE(state.mRegionToDraw.IsEmpty(), "No context when we have something to draw, resource exhaustion?"); } - + for (uint32_t i = 0; i < readbackUpdates.Length(); ++i) { ReadbackProcessor::Update& update = readbackUpdates[i]; nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset(); diff --git a/gfx/layers/basic/GrallocTextureHostBasic.cpp b/gfx/layers/basic/GrallocTextureHostBasic.cpp index ed3607d18887..553d1fb9fc85 100644 --- a/gfx/layers/basic/GrallocTextureHostBasic.cpp +++ b/gfx/layers/basic/GrallocTextureHostBasic.cpp @@ -82,7 +82,7 @@ NeedsConvertFromYUVtoRGB565(int aHalFormat) GrallocTextureHostBasic::GrallocTextureHostBasic( TextureFlags aFlags, - const NewSurfaceDescriptorGralloc& aDescriptor) + const SurfaceDescriptorGralloc& aDescriptor) : TextureHost(aFlags) , mGrallocHandle(aDescriptor) , mSize(0, 0) diff --git a/gfx/layers/basic/GrallocTextureHostBasic.h b/gfx/layers/basic/GrallocTextureHostBasic.h index b1bcece152c0..d72f67e86a3b 100644 --- a/gfx/layers/basic/GrallocTextureHostBasic.h +++ b/gfx/layers/basic/GrallocTextureHostBasic.h @@ -24,7 +24,7 @@ class GrallocTextureHostBasic : public TextureHost { public: GrallocTextureHostBasic(TextureFlags aFlags, - const NewSurfaceDescriptorGralloc& aDescriptor); + const SurfaceDescriptorGralloc& aDescriptor); virtual void SetCompositor(Compositor* aCompositor) override; @@ -66,7 +66,7 @@ public: protected: RefPtr mCompositor; RefPtr mTextureSource; - NewSurfaceDescriptorGralloc mGrallocHandle; + SurfaceDescriptorGralloc mGrallocHandle; // gralloc buffer size. gfx::IntSize mSize; // Size reported by TextureClient, can be different in some cases (video?), diff --git a/gfx/layers/basic/TextureHostBasic.cpp b/gfx/layers/basic/TextureHostBasic.cpp index 07949f70a022..3207ffa781d5 100644 --- a/gfx/layers/basic/TextureHostBasic.cpp +++ b/gfx/layers/basic/TextureHostBasic.cpp @@ -30,9 +30,9 @@ CreateTextureHostBasic(const SurfaceDescriptor& aDesc, } #endif #ifdef MOZ_WIDGET_GONK - if (aDesc.type() == SurfaceDescriptor::TNewSurfaceDescriptorGralloc) { - const NewSurfaceDescriptorGralloc& desc = - aDesc.get_NewSurfaceDescriptorGralloc(); + if (aDesc.type() == SurfaceDescriptor::TSurfaceDescriptorGralloc) { + const SurfaceDescriptorGralloc& desc = + aDesc.get_SurfaceDescriptorGralloc(); return MakeAndAddRef(aFlags, desc); } #endif diff --git a/gfx/layers/client/ClientPaintedLayer.cpp b/gfx/layers/client/ClientPaintedLayer.cpp index 8192d93cec22..1c590eeeaf2e 100644 --- a/gfx/layers/client/ClientPaintedLayer.cpp +++ b/gfx/layers/client/ClientPaintedLayer.cpp @@ -80,6 +80,13 @@ ClientPaintedLayer::PaintThebes() bool didUpdate = false; RotatedContentBuffer::DrawIterator iter; while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) { + if (!target || !target->IsValid()) { + if (target) { + mContentClient->ReturnDrawTargetToBuffer(target); + } + continue; + } + SetAntialiasingFlags(this, target); RefPtr ctx = gfxContext::ContextForDrawTarget(target); diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index a661c5ecd2e9..3aeb3df51e0b 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -586,15 +586,21 @@ ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw) // Restrict the DrawTargets and frontBuffer to a scope to make // sure there is no more external references to the DrawTargets // when we Unlock the TextureClients. - RefPtr surf = mFrontClient->BorrowDrawTarget()->Snapshot(); - RefPtr surfOnWhite = mFrontClientOnWhite - ? mFrontClientOnWhite->BorrowDrawTarget()->Snapshot() - : nullptr; - SourceRotatedBuffer frontBuffer(surf, - surfOnWhite, - mFrontBufferRect, - mFrontBufferRotation); - UpdateDestinationFrom(frontBuffer, updateRegion); + gfx::DrawTarget* dt = mFrontClient->BorrowDrawTarget(); + gfx::DrawTarget* dtw = mFrontClientOnWhite ? mFrontClientOnWhite->BorrowDrawTarget() : nullptr; + if (dt && dt->IsValid()) { + RefPtr surf = dt->Snapshot(); + RefPtr surfOnWhite = dtw ? dtw->Snapshot() : nullptr; + SourceRotatedBuffer frontBuffer(surf, + surfOnWhite, + mFrontBufferRect, + mFrontBufferRotation); + UpdateDestinationFrom(frontBuffer, updateRegion); + } else { + // We know this can happen, but we want to track it somewhat, in case it leads + // to other problems. + gfxCriticalNote << "Invalid draw target(s) " << hexa(dt) << " and " << hexa(dtw); + } } void diff --git a/gfx/layers/client/TextureClientRecycleAllocator.cpp b/gfx/layers/client/TextureClientRecycleAllocator.cpp index f4c04d488881..99c7b5909f56 100644 --- a/gfx/layers/client/TextureClientRecycleAllocator.cpp +++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp @@ -32,6 +32,45 @@ protected: RefPtr mTextureClient; }; +class DefaultTextureClientAllocationHelper : public ITextureClientAllocationHelper +{ +public: + DefaultTextureClientAllocationHelper(TextureClientRecycleAllocator* aAllocator, + gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocationFlags) + : ITextureClientAllocationHelper(aFormat, + aSize, + aSelector, + aTextureFlags, + aAllocationFlags) + , mAllocator(aAllocator) + {} + + bool IsCompatible(TextureClient* aTextureClient) override + { + if (aTextureClient->GetFormat() != mFormat || + aTextureClient->GetSize() != mSize) { + return false; + } + return true; + } + + already_AddRefed Allocate(CompositableForwarder* aAllocator) override + { + return mAllocator->Allocate(mFormat, + mSize, + mSelector, + mTextureFlags, + mAllocationFlags); + } + +protected: + TextureClientRecycleAllocator* mAllocator; +}; + TextureClientRecycleAllocator::TextureClientRecycleAllocator(CompositableForwarder* aAllocator) : mSurfaceAllocator(aAllocator) , mMaxPooledSize(kMaxPooledSized) @@ -78,14 +117,26 @@ TextureClientRecycleAllocator::CreateOrRecycle(gfx::SurfaceFormat aFormat, BackendSelector aSelector, TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags) +{ + MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE)); + DefaultTextureClientAllocationHelper helper(this, + aFormat, + aSize, + aSelector, + aTextureFlags, + aAllocFlags); + return CreateOrRecycle(helper); +} + +already_AddRefed +TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper) { // TextureAllocationFlags is actually used only by ContentClient. // This class does not handle ContentClient's TextureClient allocation. - MOZ_ASSERT(aAllocFlags == TextureAllocationFlags::ALLOC_DEFAULT || - aAllocFlags == TextureAllocationFlags::ALLOC_DISALLOW_BUFFERTEXTURECLIENT || - aAllocFlags == TextureAllocationFlags::ALLOC_FOR_OUT_OF_BAND_CONTENT); - MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE)); - aTextureFlags = aTextureFlags | TextureFlags::RECYCLE; // Set recycle flag + MOZ_ASSERT(aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_DEFAULT || + aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_DISALLOW_BUFFERTEXTURECLIENT || + aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_FOR_OUT_OF_BAND_CONTENT); + MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE); RefPtr textureHolder; @@ -96,14 +147,13 @@ TextureClientRecycleAllocator::CreateOrRecycle(gfx::SurfaceFormat aFormat, mPooledClients.pop(); Task* task = nullptr; // If a pooled TextureClient is not compatible, release it. - if (textureHolder->GetTextureClient()->GetFormat() != aFormat || - textureHolder->GetTextureClient()->GetSize() != aSize) { + if (!aHelper.IsCompatible(textureHolder->GetTextureClient())) { // Release TextureClient. task = new TextureClientReleaseTask(textureHolder->GetTextureClient()); textureHolder->ClearTextureClient(); textureHolder = nullptr; } else { - task = new TextureClientRecycleTask(textureHolder->GetTextureClient(), aTextureFlags); + task = new TextureClientRecycleTask(textureHolder->GetTextureClient(), aHelper.mTextureFlags); } mSurfaceAllocator->GetMessageLoop()->PostTask(FROM_HERE, task); } @@ -111,7 +161,7 @@ TextureClientRecycleAllocator::CreateOrRecycle(gfx::SurfaceFormat aFormat, if (!textureHolder) { // Allocate new TextureClient - RefPtr texture = Allocate(aFormat, aSize, aSelector, aTextureFlags, aAllocFlags); + RefPtr texture = aHelper.Allocate(mSurfaceAllocator); if (!texture) { return nullptr; } diff --git a/gfx/layers/client/TextureClientRecycleAllocator.h b/gfx/layers/client/TextureClientRecycleAllocator.h index 4fd35851dee3..6035181a011d 100644 --- a/gfx/layers/client/TextureClientRecycleAllocator.h +++ b/gfx/layers/client/TextureClientRecycleAllocator.h @@ -16,9 +16,32 @@ namespace mozilla { namespace layers { -class ISurfaceAllocator; class TextureClientHolder; +class ITextureClientAllocationHelper +{ +public: + ITextureClientAllocationHelper(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocationFlags) + : mFormat(aFormat) + , mSize(aSize) + , mSelector(aSelector) + , mTextureFlags(aTextureFlags | TextureFlags::RECYCLE) // Set recycle flag + , mAllocationFlags(aAllocationFlags) + {} + + virtual already_AddRefed Allocate(CompositableForwarder* aAllocator) = 0; + virtual bool IsCompatible(TextureClient* aTextureClient) = 0; + + const gfx::SurfaceFormat mFormat; + const gfx::IntSize mSize; + const BackendSelector mSelector; + const TextureFlags mTextureFlags; + const TextureAllocationFlags mAllocationFlags; +}; /** * TextureClientRecycleAllocator provides TextureClients allocation and @@ -49,6 +72,9 @@ public: TextureFlags aTextureFlags, TextureAllocationFlags flags = ALLOC_DEFAULT); + already_AddRefed + CreateOrRecycle(ITextureClientAllocationHelper& aHelper); + protected: virtual already_AddRefed Allocate(gfx::SurfaceFormat aFormat, @@ -61,6 +87,7 @@ protected: private: friend class TextureClient; + friend class DefaultTextureClientAllocationHelper; void RecycleTextureClient(TextureClient* aClient); static const uint32_t kMaxPooledSized = 2; diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp index 6aa539282d8d..1d2240fba505 100644 --- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -213,7 +213,7 @@ TextureHost::Create(const SurfaceDescriptor& aDesc, case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture: return CreateTextureHostOGL(aDesc, aDeallocator, aFlags); - case SurfaceDescriptor::TNewSurfaceDescriptorGralloc: + case SurfaceDescriptor::TSurfaceDescriptorGralloc: case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: if (aBackend == LayersBackend::LAYERS_OPENGL) { return CreateTextureHostOGL(aDesc, aDeallocator, aFlags); diff --git a/gfx/layers/d3d11/CompositorD3D11.cpp b/gfx/layers/d3d11/CompositorD3D11.cpp index f66af622529c..c4e7ee8f69ba 100644 --- a/gfx/layers/d3d11/CompositorD3D11.cpp +++ b/gfx/layers/d3d11/CompositorD3D11.cpp @@ -546,9 +546,7 @@ CompositorD3D11::CreateRenderTargetFromSource(const gfx::IntRect &aRect, const IntSize& srcSize = sourceD3D11->GetSize(); MOZ_ASSERT(srcSize.width >= 0 && srcSize.height >= 0, "render targets should have nonnegative sizes"); - if (srcBox.left >= 0 && - srcBox.top >= 0 && - srcBox.left < srcBox.right && + if (srcBox.left < srcBox.right && srcBox.top < srcBox.bottom && srcBox.right <= static_cast(srcSize.width) && srcBox.bottom <= static_cast(srcSize.height)) { diff --git a/gfx/layers/ipc/LayersSurfaces.ipdlh b/gfx/layers/ipc/LayersSurfaces.ipdlh index 3a07984ed701..4d0f5203359b 100644 --- a/gfx/layers/ipc/LayersSurfaces.ipdlh +++ b/gfx/layers/ipc/LayersSurfaces.ipdlh @@ -90,7 +90,7 @@ struct SurfaceDescriptorSharedGLTexture { bool hasAlpha; }; -struct NewSurfaceDescriptorGralloc { +struct SurfaceDescriptorGralloc { MaybeMagicGrallocBufferHandle buffer; bool isOpaque; }; @@ -123,7 +123,7 @@ union SurfaceDescriptor { SurfaceTextureDescriptor; EGLImageDescriptor; SurfaceDescriptorMacIOSurface; - NewSurfaceDescriptorGralloc; + SurfaceDescriptorGralloc; SurfaceDescriptorSharedGLTexture; null_t; }; diff --git a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp index 33530ff36d75..1e3ff8ea6780 100644 --- a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp +++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp @@ -231,8 +231,8 @@ android::sp GetGraphicBufferFromDesc(SurfaceDescriptor aDesc) { MaybeMagicGrallocBufferHandle handle; - if (aDesc.type() == SurfaceDescriptor::TNewSurfaceDescriptorGralloc) { - handle = aDesc.get_NewSurfaceDescriptorGralloc().buffer(); + if (aDesc.type() == SurfaceDescriptor::TSurfaceDescriptorGralloc) { + handle = aDesc.get_SurfaceDescriptorGralloc().buffer(); } return GetGraphicBufferFrom(handle); } diff --git a/gfx/layers/ipc/SharedBufferManagerParent.cpp b/gfx/layers/ipc/SharedBufferManagerParent.cpp index ab1fa0ba80bc..fd10629dfb9c 100644 --- a/gfx/layers/ipc/SharedBufferManagerParent.cpp +++ b/gfx/layers/ipc/SharedBufferManagerParent.cpp @@ -279,7 +279,7 @@ void SharedBufferManagerParent::DropGrallocBufferSync(SharedBufferManagerParent* /*static*/ void SharedBufferManagerParent::DropGrallocBuffer(ProcessId id, mozilla::layers::SurfaceDescriptor aDesc) { - if (aDesc.type() != SurfaceDescriptor::TNewSurfaceDescriptorGralloc) { + if (aDesc.type() != SurfaceDescriptor::TSurfaceDescriptorGralloc) { return; } @@ -312,8 +312,8 @@ void SharedBufferManagerParent::DropGrallocBufferImpl(mozilla::layers::SurfaceDe #ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC int64_t key = -1; MaybeMagicGrallocBufferHandle handle; - if (aDesc.type() == SurfaceDescriptor::TNewSurfaceDescriptorGralloc) { - handle = aDesc.get_NewSurfaceDescriptorGralloc().buffer(); + if (aDesc.type() == SurfaceDescriptor::TSurfaceDescriptorGralloc) { + handle = aDesc.get_SurfaceDescriptorGralloc().buffer(); } else { return; } diff --git a/gfx/layers/opengl/GrallocTextureClient.cpp b/gfx/layers/opengl/GrallocTextureClient.cpp index 43f6666b88e5..d645b9a8f300 100644 --- a/gfx/layers/opengl/GrallocTextureClient.cpp +++ b/gfx/layers/opengl/GrallocTextureClient.cpp @@ -134,7 +134,7 @@ GrallocTextureData::Forget(ISurfaceAllocator* aAllocator) bool GrallocTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) { - aOutDescriptor = NewSurfaceDescriptorGralloc(mGrallocHandle, gfx::IsOpaque(mFormat)); + aOutDescriptor = SurfaceDescriptorGralloc(mGrallocHandle, gfx::IsOpaque(mFormat)); return true; } diff --git a/gfx/layers/opengl/GrallocTextureHost.cpp b/gfx/layers/opengl/GrallocTextureHost.cpp index 28cea35c5c1c..17ea0e717783 100644 --- a/gfx/layers/opengl/GrallocTextureHost.cpp +++ b/gfx/layers/opengl/GrallocTextureHost.cpp @@ -99,7 +99,7 @@ TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat) } GrallocTextureHostOGL::GrallocTextureHostOGL(TextureFlags aFlags, - const NewSurfaceDescriptorGralloc& aDescriptor) + const SurfaceDescriptorGralloc& aDescriptor) : TextureHost(aFlags) , mGrallocHandle(aDescriptor) , mSize(0, 0) diff --git a/gfx/layers/opengl/GrallocTextureHost.h b/gfx/layers/opengl/GrallocTextureHost.h index a258a1e427f2..e3a7d08cf8fb 100644 --- a/gfx/layers/opengl/GrallocTextureHost.h +++ b/gfx/layers/opengl/GrallocTextureHost.h @@ -20,7 +20,7 @@ class GrallocTextureHostOGL : public TextureHost friend class GrallocBufferActor; public: GrallocTextureHostOGL(TextureFlags aFlags, - const NewSurfaceDescriptorGralloc& aDescriptor); + const SurfaceDescriptorGralloc& aDescriptor); virtual ~GrallocTextureHostOGL(); @@ -63,7 +63,7 @@ public: private: void DestroyEGLImage(); - NewSurfaceDescriptorGralloc mGrallocHandle; + SurfaceDescriptorGralloc mGrallocHandle; RefPtr mGLTextureSource; RefPtr mCompositor; // Size reported by the GraphicBuffer diff --git a/gfx/layers/opengl/TextureHostOGL.cpp b/gfx/layers/opengl/TextureHostOGL.cpp index b0bff91adfdc..f41113cc41af 100644 --- a/gfx/layers/opengl/TextureHostOGL.cpp +++ b/gfx/layers/opengl/TextureHostOGL.cpp @@ -89,9 +89,9 @@ CreateTextureHostOGL(const SurfaceDescriptor& aDesc, #endif #ifdef MOZ_WIDGET_GONK - case SurfaceDescriptor::TNewSurfaceDescriptorGralloc: { - const NewSurfaceDescriptorGralloc& desc = - aDesc.get_NewSurfaceDescriptorGralloc(); + case SurfaceDescriptor::TSurfaceDescriptorGralloc: { + const SurfaceDescriptorGralloc& desc = + aDesc.get_SurfaceDescriptorGralloc(); result = new GrallocTextureHostOGL(aFlags, desc); break; } diff --git a/gfx/src/nsRect.cpp b/gfx/src/nsRect.cpp index b94d844e43e3..562eb67cbc33 100644 --- a/gfx/src/nsRect.cpp +++ b/gfx/src/nsRect.cpp @@ -15,15 +15,6 @@ static_assert((int(NS_SIDE_TOP) == 0) && (int(NS_SIDE_LEFT) == 3), "The mozilla::css::Side sequence must match the nsMargin nscoord sequence"); -nsRect -ToAppUnits(const mozilla::gfx::IntRect& aRect, nscoord aAppUnitsPerPixel) -{ - return nsRect(NSIntPixelsToAppUnits(aRect.x, aAppUnitsPerPixel), - NSIntPixelsToAppUnits(aRect.y, aAppUnitsPerPixel), - NSIntPixelsToAppUnits(aRect.width, aAppUnitsPerPixel), - NSIntPixelsToAppUnits(aRect.height, aAppUnitsPerPixel)); -} - const mozilla::gfx::IntRect& GetMaxSizedIntRect() { static const mozilla::gfx::IntRect r(0, 0, INT32_MAX, INT32_MAX); return r; diff --git a/gfx/src/nsRect.h b/gfx/src/nsRect.h index 38a41f964f3c..22481774d06a 100644 --- a/gfx/src/nsRect.h +++ b/gfx/src/nsRect.h @@ -303,8 +303,15 @@ nsRect::RemoveResolution(const float aResolution) const const mozilla::gfx::IntRect& GetMaxSizedIntRect(); // app units are integer multiples of pixels, so no rounding needed +template nsRect -ToAppUnits(const mozilla::gfx::IntRect& aRect, nscoord aAppUnitsPerPixel); +ToAppUnits(const mozilla::gfx::IntRectTyped& aRect, nscoord aAppUnitsPerPixel) +{ + return nsRect(NSIntPixelsToAppUnits(aRect.x, aAppUnitsPerPixel), + NSIntPixelsToAppUnits(aRect.y, aAppUnitsPerPixel), + NSIntPixelsToAppUnits(aRect.width, aAppUnitsPerPixel), + NSIntPixelsToAppUnits(aRect.height, aAppUnitsPerPixel)); +} #ifdef DEBUG // Diagnostics diff --git a/gfx/thebes/gfxBlur.cpp b/gfx/thebes/gfxBlur.cpp index 97d90a67a60a..119af1532d1e 100644 --- a/gfx/thebes/gfxBlur.cpp +++ b/gfx/thebes/gfxBlur.cpp @@ -919,6 +919,8 @@ gfxAlphaBoxBlur::GetInsetBlur(IntMargin& aExtendDestBy, const RectCornerRadii& aInnerClipRadii, const Color& aShadowColor, const bool& aHasBorderRadius, + const Point aShadowOffset, + bool& aMovedOffset, gfxContext* aDestinationCtx) { if (!gBlurCache) { @@ -933,6 +935,19 @@ gfxAlphaBoxBlur::GetInsetBlur(IntMargin& aExtendDestBy, aShadowClipRect, aHasBorderRadius, aInnerClipRadii); + // If we have a shadow offset larger than the min rect, + // there's no clean way we can properly create a min rect with the offset + // in the correct place and still render correctly. In those cases, + // fallback to just rendering the dest rect as is. + bool useDestRect = (std::abs(aShadowOffset.x) > aSlice.left) || + (std::abs(aShadowOffset.y) > aSlice.top); + aMovedOffset = false; + if (useDestRect) { + aDestinationRect.ToIntRect(&outerRect); + aShadowClipRect.ToIntRect(&innerRect); + aMovedOffset = true; + } + DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget(); BlurCacheData* cached = gBlurCache->LookupInsetBoxShadow(outerRect.Size(), innerRect.Size(), @@ -940,7 +955,7 @@ gfxAlphaBoxBlur::GetInsetBlur(IntMargin& aExtendDestBy, &aInnerClipRadii, aShadowColor, aHasBorderRadius, destDrawTarget->GetBackendType()); - if (cached) { + if (cached && !useDestRect) { aExtendDestBy = cached->mExtendDest; // Need to extend it twice: once for the outer rect and once for the inner rect. aSlice += aExtendDestBy; @@ -985,14 +1000,22 @@ gfxAlphaBoxBlur::GetInsetBlur(IntMargin& aExtendDestBy, IntRect blurRect(topLeft, minInsetBlur->GetSize()); aExtendDestBy = blurRect - outerRect; - aSlice += aExtendDestBy; - aSlice += aExtendDestBy; - CacheInsetBlur(outerRect.Size(), innerRect.Size(), + if (useDestRect) { + // Since we're just going to paint the actual rect to the destination + aSlice.SizeTo(0, 0, 0, 0); + } else { + aSlice += aExtendDestBy; + aSlice += aExtendDestBy; + + CacheInsetBlur(outerRect.Size(), innerRect.Size(), aBlurRadius, aSpreadRadius, &aInnerClipRadii, aShadowColor, aHasBorderRadius, destDrawTarget->GetBackendType(), aExtendDestBy, minInsetBlur); + + } + return minInsetBlur.forget(); } @@ -1027,11 +1050,13 @@ gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx, IntMargin extendDest; IntMargin slice; + bool didMoveOffset; RefPtr minInsetBlur = GetInsetBlur(extendDest, slice, aDestinationRect, aShadowClipRect, aBlurRadius, aSpreadRadius, aInnerClipRadii, aShadowColor, - aHasBorderRadius, aDestinationCtx); + aHasBorderRadius, aShadowOffset, + didMoveOffset, aDestinationCtx); if (!minInsetBlur) { return; } @@ -1041,13 +1066,15 @@ gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx, srcInner.Deflate(Margin(slice)); Rect dstOuter(aDestinationRect); - dstOuter.MoveBy(aShadowOffset); + if (!didMoveOffset) { + dstOuter.MoveBy(aShadowOffset); + } dstOuter.Inflate(Margin(extendDest)); Rect dstInner = dstOuter; dstInner.Deflate(Margin(slice)); DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget(); - if (srcOuter.IsEqualInterior(srcInner)) { + if (dstOuter.Size() == srcOuter.Size()) { destDrawTarget->DrawSurface(minInsetBlur, dstOuter, srcOuter); } else { DrawBoxShadows(*destDrawTarget, minInsetBlur, diff --git a/gfx/thebes/gfxBlur.h b/gfx/thebes/gfxBlur.h index 1c2dc6bf6bdc..27c293b44621 100644 --- a/gfx/thebes/gfxBlur.h +++ b/gfx/thebes/gfxBlur.h @@ -174,6 +174,8 @@ protected: const RectCornerRadii& aInnerClipRadii, const mozilla::gfx::Color& aShadowColor, const bool& aHasBorderRadius, + const mozilla::gfx::Point aShadowOffset, + bool& aMovedOffset, gfxContext* aDestinationCtx); /** diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp index 8b6e89dca523..fa8d8edd0d13 100644 --- a/gfx/thebes/gfxContext.cpp +++ b/gfx/thebes/gfxContext.cpp @@ -85,6 +85,11 @@ gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset) /* static */ already_AddRefed gfxContext::ContextForDrawTarget(DrawTarget* aTarget) { + if (!aTarget || !aTarget->IsValid()) { + gfxWarning() << "Invalid target in gfxContext::ContextForDrawTarget"; + return nullptr; + } + Matrix transform = aTarget->GetTransform(); RefPtr result = new gfxContext(aTarget); result->SetMatrix(ThebesMatrix(transform)); @@ -100,10 +105,6 @@ gfxContext::~gfxContext() for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) { mDT->PopClip(); } - - if (mStateStack[i].clipWasReset) { - break; - } } mDT->Flush(); MOZ_COUNT_DTOR(gfxContext); @@ -157,7 +158,6 @@ gfxContext::Save() { CurrentState().transform = mTransform; mStateStack.AppendElement(AzureState(CurrentState())); - CurrentState().clipWasReset = false; CurrentState().pushedClips.Clear(); } @@ -168,11 +168,6 @@ gfxContext::Restore() mDT->PopClip(); } - if (CurrentState().clipWasReset && - CurrentState().drawTarget == mStateStack[mStateStack.Length() - 2].drawTarget) { - PushClipsToDT(mDT); - } - mStateStack.RemoveElementAt(mStateStack.Length() - 1); mDT = CurrentState().drawTarget; @@ -537,18 +532,6 @@ gfxContext::CurrentMiterLimit() const return CurrentState().strokeOptions.mMiterLimit; } -void -gfxContext::SetFillRule(FillRule rule) -{ - CurrentState().fillRule = rule; -} - -FillRule -gfxContext::CurrentFillRule() const -{ - return CurrentState().fillRule; -} - // clipping void gfxContext::Clip(const Rect& rect) @@ -625,9 +608,6 @@ gfxContext::HasComplexClip() const return true; } } - if (mStateStack[i].clipWasReset) { - break; - } } return false; } @@ -635,15 +615,7 @@ gfxContext::HasComplexClip() const bool gfxContext::ExportClip(ClipExporter& aExporter) { - unsigned int lastReset = 0; - for (int i = mStateStack.Length() - 1; i > 0; i--) { - if (mStateStack[i].clipWasReset) { - lastReset = i; - break; - } - } - - for (unsigned int i = lastReset; i < mStateStack.Length(); i++) { + for (unsigned int i = 0; i < mStateStack.Length(); i++) { for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) { AzureState::PushedClip &clip = mStateStack[i].pushedClips[c]; gfx::Matrix transform = clip.transform; @@ -669,20 +641,12 @@ gfxContext::ExportClip(ClipExporter& aExporter) bool gfxContext::ClipContainsRect(const gfxRect& aRect) { - unsigned int lastReset = 0; - for (int i = mStateStack.Length() - 2; i > 0; i--) { - if (mStateStack[i].clipWasReset) { - lastReset = i; - break; - } - } - // Since we always return false when the clip list contains a // non-rectangular clip or a non-rectilinear transform, our 'total' clip // is always a rectangle if we hit the end of this function. Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height)); - for (unsigned int i = lastReset; i < mStateStack.Length(); i++) { + for (unsigned int i = 0; i < mStateStack.Length(); i++) { for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) { AzureState::PushedClip &clip = mStateStack[i].pushedClips[c]; if (clip.path || !clip.transform.IsRectilinear()) { @@ -1039,18 +1003,18 @@ gfxContext::EnsurePath() Matrix mat = mTransform; mat.Invert(); mat = mPathTransform * mat; - mPathBuilder = mPath->TransformedCopyToBuilder(mat, CurrentState().fillRule); + mPathBuilder = mPath->TransformedCopyToBuilder(mat); mPath = mPathBuilder->Finish(); mPathBuilder = nullptr; mTransformChanged = false; } - if (CurrentState().fillRule == mPath->GetFillRule()) { + if (FillRule::FILL_WINDING == mPath->GetFillRule()) { return; } - mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule); + mPathBuilder = mPath->CopyToBuilder(); mPath = mPathBuilder->Finish(); mPathBuilder = nullptr; @@ -1071,13 +1035,13 @@ gfxContext::EnsurePathBuilder() if (mPath) { if (!mTransformChanged) { - mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule); + mPathBuilder = mPath->CopyToBuilder(); mPath = nullptr; } else { Matrix invTransform = mTransform; invTransform.Invert(); Matrix toNewUS = mPathTransform * invTransform; - mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule); + mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS); } return; } @@ -1085,7 +1049,7 @@ gfxContext::EnsurePathBuilder() DebugOnly oldPath = mPathBuilder.get(); if (!mPathBuilder) { - mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule); + mPathBuilder = mDT->CreatePathBuilder(FillRule::FILL_WINDING); if (mPathIsRect) { mPathBuilder->MoveTo(mRect.TopLeft()); @@ -1108,7 +1072,7 @@ gfxContext::EnsurePathBuilder() Matrix toNewUS = mPathTransform * invTransform; RefPtr path = mPathBuilder->Finish(); - mPathBuilder = path->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule); + mPathBuilder = path->TransformedCopyToBuilder(toNewUS); } mPathIsRect = false; @@ -1140,22 +1104,10 @@ gfxContext::FillAzure(const Pattern& aPattern, Float aOpacity) void gfxContext::PushClipsToDT(DrawTarget *aDT) { - // Tricky, we have to restore all clips -since the last time- the clip - // was reset. If we didn't reset the clip, just popping the clips we - // added was fine. - unsigned int lastReset = 0; - for (int i = mStateStack.Length() - 2; i > 0; i--) { - if (mStateStack[i].clipWasReset) { - lastReset = i; - break; - } - } - // Don't need to save the old transform, we'll be setting a new one soon! - // Push all clips from the last state on the stack where the clip was - // reset to the clip before ours. - for (unsigned int i = lastReset; i < mStateStack.Length() - 1; i++) { + // Push all clips from the bottom of the stack to the clip before ours. + for (unsigned int i = 0; i < mStateStack.Length() - 1; i++) { for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) { aDT->SetTransform(mStateStack[i].pushedClips[c].transform * GetDeviceTransform()); if (mStateStack[i].pushedClips[c].path) { @@ -1229,8 +1181,8 @@ gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransfo mRect = toNewUS.TransformBounds(mRect); mRect.NudgeToIntegers(); } else { - mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule); - + mPathBuilder = mDT->CreatePathBuilder(FillRule::FILL_WINDING); + mPathBuilder->MoveTo(toNewUS * mRect.TopLeft()); mPathBuilder->LineTo(toNewUS * mRect.TopRight()); mPathBuilder->LineTo(toNewUS * mRect.BottomRight()); @@ -1255,17 +1207,9 @@ gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransfo Rect gfxContext::GetAzureDeviceSpaceClipBounds() { - unsigned int lastReset = 0; - for (int i = mStateStack.Length() - 1; i > 0; i--) { - if (mStateStack[i].clipWasReset) { - lastReset = i; - break; - } - } - Rect rect(CurrentState().deviceOffset.x, CurrentState().deviceOffset.y, Float(mDT->GetSize().width), Float(mDT->GetSize().height)); - for (unsigned int i = lastReset; i < mStateStack.Length(); i++) { + for (unsigned int i = 0; i < mStateStack.Length(); i++) { for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) { AzureState::PushedClip &clip = mStateStack[i].pushedClips[c]; if (clip.path) { diff --git a/gfx/thebes/gfxContext.h b/gfx/thebes/gfxContext.h index f08858122805..d709a0e597e5 100644 --- a/gfx/thebes/gfxContext.h +++ b/gfx/thebes/gfxContext.h @@ -366,13 +366,6 @@ public: void SetMiterLimit(gfxFloat limit); gfxFloat CurrentMiterLimit() const; - /** - ** Fill Properties - **/ - - void SetFillRule(FillRule rule); - FillRule CurrentFillRule() const; - /** * Sets the operator used for all further drawing. The operator affects * how drawing something will modify the destination. For example, the @@ -496,8 +489,6 @@ private: AzureState() : op(mozilla::gfx::CompositionOp::OP_OVER) , color(0, 0, 0, 1.0f) - , clipWasReset(false) - , fillRule(mozilla::gfx::FillRule::FILL_WINDING) , aaMode(mozilla::gfx::AntialiasMode::SUBPIXEL) , patternTransformChanged(false) {} @@ -517,11 +508,8 @@ private: }; nsTArray pushedClips; nsTArray dashPattern; - bool clipWasReset; - mozilla::gfx::FillRule fillRule; StrokeOptions strokeOptions; RefPtr drawTarget; - RefPtr parentTarget; mozilla::gfx::AntialiasMode aaMode; bool patternTransformChanged; Matrix patternTransform; diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 0100aabade57..f1906a2e1fce 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -2886,8 +2886,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT; } if (boundary != ' ' || - !aTextRun->SetSpaceGlyphIfSimple(this, aContext, - aRunStart + i, ch, + !aTextRun->SetSpaceGlyphIfSimple(this, aRunStart + i, ch, orientation)) { // Currently, the only "boundary" characters we recognize are // space and no-break space, which are both 8-bit, so we force diff --git a/gfx/thebes/gfxGraphiteShaper.cpp b/gfx/thebes/gfxGraphiteShaper.cpp index 11e24c60155f..fe4744eb8ccf 100644 --- a/gfx/thebes/gfxGraphiteShaper.cpp +++ b/gfx/thebes/gfxGraphiteShaper.cpp @@ -34,7 +34,6 @@ gfxGraphiteShaper::gfxGraphiteShaper(gfxFont *aFont) mGrFont(nullptr), mFallbackToSmallCaps(false) { mCallbackData.mFont = aFont; - mCallbackData.mShaper = this; } gfxGraphiteShaper::~gfxGraphiteShaper() @@ -50,8 +49,7 @@ gfxGraphiteShaper::GrGetAdvance(const void* appFontHandle, uint16_t glyphid) { const CallbackData *cb = static_cast(appFontHandle); - return FixedToFloat(cb->mFont->GetGlyphWidth(*cb->mContext->GetDrawTarget(), - glyphid)); + return FixedToFloat(cb->mFont->GetGlyphWidth(*cb->mDrawTarget, glyphid)); } static inline uint32_t @@ -97,7 +95,7 @@ gfxGraphiteShaper::ShapeText(gfxContext *aContext, return false; } - mCallbackData.mContext = aContext; + mCallbackData.mDrawTarget = aContext->GetDrawTarget(); const gfxFontStyle *style = mFont->GetStyle(); diff --git a/gfx/thebes/gfxGraphiteShaper.h b/gfx/thebes/gfxGraphiteShaper.h index 8127a8405635..1075e9d1b802 100644 --- a/gfx/thebes/gfxGraphiteShaper.h +++ b/gfx/thebes/gfxGraphiteShaper.h @@ -8,6 +8,8 @@ #include "gfxFont.h" +#include "mozilla/gfx/2D.h" + struct gr_face; struct gr_font; struct gr_segment; @@ -42,9 +44,8 @@ protected: gr_font *mGrFont; // owned by the shaper itself struct CallbackData { - gfxFont *mFont; - gfxGraphiteShaper *mShaper; - gfxContext *mContext; + gfxFont* mFont; + mozilla::gfx::DrawTarget* mDrawTarget; }; CallbackData mCallbackData; diff --git a/gfx/thebes/gfxHarfBuzzShaper.cpp b/gfx/thebes/gfxHarfBuzzShaper.cpp index 92d7c5f96ada..ea2be0ac7b5a 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.cpp +++ b/gfx/thebes/gfxHarfBuzzShaper.cpp @@ -335,7 +335,7 @@ gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t *font, void *font_data, static_cast(font_data); gfxFont *gfxfont = fcd->mShaper->GetFont(); if (gfxfont->ProvidesGlyphWidths()) { - return gfxfont->GetGlyphWidth(*fcd->mContext->GetDrawTarget(), glyph); + return gfxfont->GetGlyphWidth(*fcd->mDrawTarget, glyph); } return fcd->mShaper->GetGlyphHAdvance(glyph); } @@ -1475,7 +1475,7 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext, return false; } - mCallbackData.mContext = aContext; + mCallbackData.mDrawTarget = aContext->GetDrawTarget(); mUseVerticalPresentationForms = false; if (!Initialize()) { diff --git a/gfx/thebes/gfxHarfBuzzShaper.h b/gfx/thebes/gfxHarfBuzzShaper.h index 643e75b20466..1e8417ba4b00 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.h +++ b/gfx/thebes/gfxHarfBuzzShaper.h @@ -10,6 +10,7 @@ #include "harfbuzz/hb.h" #include "nsUnicodeProperties.h" +#include "mozilla/gfx/2D.h" class gfxHarfBuzzShaper : public gfxFontShaper { public: @@ -21,8 +22,8 @@ public: * FontCallbackData struct */ struct FontCallbackData { - gfxHarfBuzzShaper *mShaper; - gfxContext *mContext; + gfxHarfBuzzShaper* mShaper; + mozilla::gfx::DrawTarget* mDrawTarget; }; bool Initialize(); diff --git a/gfx/thebes/gfxImageSurface.cpp b/gfx/thebes/gfxImageSurface.cpp index be9176cfc62d..63fa0efec2e2 100644 --- a/gfx/thebes/gfxImageSurface.cpp +++ b/gfx/thebes/gfxImageSurface.cpp @@ -29,6 +29,11 @@ gfxImageSurface::gfxImageSurface() void gfxImageSurface::InitFromSurface(cairo_surface_t *csurf) { + if (!csurf || cairo_surface_status(csurf)) { + MakeInvalid(); + return; + } + mSize.width = cairo_image_surface_get_width(csurf); mSize.height = cairo_image_surface_get_height(csurf); mData = cairo_image_surface_get_data(csurf); diff --git a/gfx/thebes/gfxImageSurface.h b/gfx/thebes/gfxImageSurface.h index d7d495d02ccb..829fa421c8e8 100644 --- a/gfx/thebes/gfxImageSurface.h +++ b/gfx/thebes/gfxImageSurface.h @@ -77,8 +77,18 @@ public: gfxImageFormat Format() const { return mFormat; } virtual const mozilla::gfx::IntSize GetSize() const override { return mSize; } - int32_t Width() const { return mSize.width; } - int32_t Height() const { return mSize.height; } + int32_t Width() const { + if (mSize.width < 0) { + return 0; + } + return mSize.width; + } + int32_t Height() const { + if (mSize.height < 0) { + return 0; + } + return mSize.height; + } /** * Distance in bytes between the start of a line and the start of the @@ -93,7 +103,12 @@ public: /** * Returns the total size of the image data. */ - int32_t GetDataSize() const { return mStride*mSize.height; } + int32_t GetDataSize() const { + if (mStride < 0 || mSize.height < 0) { + return 0; + } + return mStride*mSize.height; + } /* Fast copy from another image surface; returns TRUE if successful, FALSE otherwise */ bool CopyFrom (gfxImageSurface *other); @@ -144,8 +159,12 @@ protected: void AllocateAndInit(long aStride, int32_t aMinimalAllocation, bool aClear); void InitFromSurface(cairo_surface_t *csurf); - long ComputeStride() const { return ComputeStride(mSize, mFormat); } - + long ComputeStride() const { + if (mSize.height < 0 || mSize.width < 0) { + return 0; + } + return ComputeStride(mSize, mFormat); + } void MakeInvalid(); diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp index b6809394e2f7..40d71b9d811c 100644 --- a/gfx/thebes/gfxTextRun.cpp +++ b/gfx/thebes/gfxTextRun.cpp @@ -1321,8 +1321,7 @@ void gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, uint32_t aCharIndex, uint16_t aOrientation) { - if (SetSpaceGlyphIfSimple(aFont, aContext, aCharIndex, ' ', - aOrientation)) { + if (SetSpaceGlyphIfSimple(aFont, aCharIndex, ' ', aOrientation)) { return; } @@ -1350,9 +1349,8 @@ gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, } bool -gfxTextRun::SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext, - uint32_t aCharIndex, char16_t aSpaceChar, - uint16_t aOrientation) +gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex, + char16_t aSpaceChar, uint16_t aOrientation) { uint32_t spaceGlyph = aFont->GetSpaceGlyph(); if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) { diff --git a/gfx/thebes/gfxTextRun.h b/gfx/thebes/gfxTextRun.h index fa804106d325..8042393a2650 100644 --- a/gfx/thebes/gfxTextRun.h +++ b/gfx/thebes/gfxTextRun.h @@ -514,9 +514,8 @@ public: // Returns true if it was able to set simple glyph data for the space; // if it returns false, the caller needs to fall back to some other // means to create the necessary (detailed) glyph data. - bool SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext, - uint32_t aCharIndex, char16_t aSpaceChar, - uint16_t aOrientation); + bool SetSpaceGlyphIfSimple(gfxFont *aFont, uint32_t aCharIndex, + char16_t aSpaceChar, uint16_t aOrientation); // Record the positions of specific characters that layout may need to // detect in the textrun, even though it doesn't have an explicit copy diff --git a/image/test/unit/async_load_tests.js b/image/test/unit/async_load_tests.js index a1705f243cb4..fafb26f75805 100644 --- a/image/test/unit/async_load_tests.js +++ b/image/test/unit/async_load_tests.js @@ -12,7 +12,7 @@ var Cu = Components.utils; var Cr = Components.results; Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var server = new HttpServer(); server.registerDirectory("/", do_get_file('')); @@ -125,16 +125,9 @@ function getChannelLoadImageStopCallback(streamlistener, next) function checkSecondChannelLoad() { do_test_pending(); - - var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - var channel = ioService.newChannelFromURI2(uri, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}); var channellistener = new ChannelListener(); - channel.asyncOpen(channellistener, null); + channel.asyncOpen2(channellistener); var listener = new ImageListener(null, getChannelLoadImageStopCallback(channellistener, @@ -154,16 +147,9 @@ function run_loadImageWithChannel_tests() gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader); do_test_pending(); - - var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - var channel = ioService.newChannelFromURI2(uri, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}); var channellistener = new ChannelListener(); - channel.asyncOpen(channellistener, null); + channel.asyncOpen2(channellistener); var listener = new ImageListener(null, getChannelLoadImageStopCallback(channellistener, diff --git a/image/test/unit/test_private_channel.js b/image/test/unit/test_private_channel.js index da72d1bb1f8a..cb8df49f3675 100644 --- a/image/test/unit/test_private_channel.js +++ b/image/test/unit/test_private_channel.js @@ -4,6 +4,7 @@ var Cr = Components.results; var Cu = Components.utils; Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://testing-common/httpd.js"); var server = new HttpServer(); @@ -53,16 +54,11 @@ NotificationCallbacks.prototype = { var gImgPath = 'http://localhost:' + server.identity.primaryPort + '/image.png'; function setup_chan(path, isPrivate, callback) { - var uri = gIoService.newURI(gImgPath, null, null); - var chan = gIoService.newChannelFromURI2(uri, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var uri = NetUtil.newURI(gImgPath); + var chan = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}); chan.notificationCallbacks = new NotificationCallbacks(isPrivate); var channelListener = new ChannelListener(); - chan.asyncOpen(channelListener, null); + chan.asyncOpen2(channelListener); var listener = new ImageListener(null, callback); var outlistener = {}; diff --git a/ipc/glue/BackgroundUtils.cpp b/ipc/glue/BackgroundUtils.cpp index 85e6a312e2fc..054935792ade 100644 --- a/ipc/glue/BackgroundUtils.cpp +++ b/ipc/glue/BackgroundUtils.cpp @@ -242,6 +242,7 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo, triggeringPrincipalInfo, aLoadInfo->GetSecurityFlags(), aLoadInfo->InternalContentPolicyType(), + static_cast(aLoadInfo->GetTainting()), aLoadInfo->GetUpgradeInsecureRequests(), aLoadInfo->GetUpgradeInsecurePreloads(), aLoadInfo->GetInnerWindowID(), @@ -252,7 +253,10 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo, aLoadInfo->GetIsInThirdPartyContext(), aLoadInfo->GetOriginAttributes(), redirectChainIncludingInternalRedirects, - redirectChain); + redirectChain, + aLoadInfo->CorsUnsafeHeaders(), + aLoadInfo->GetForcePreflight(), + aLoadInfo->GetIsPreflight()); return NS_OK; } @@ -298,6 +302,7 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs, triggeringPrincipal, loadInfoArgs.securityFlags(), loadInfoArgs.contentPolicyType(), + static_cast(loadInfoArgs.tainting()), loadInfoArgs.upgradeInsecureRequests(), loadInfoArgs.upgradeInsecurePreloads(), loadInfoArgs.innerWindowID(), @@ -308,7 +313,10 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs, loadInfoArgs.isInThirdPartyContext(), loadInfoArgs.originAttributes(), redirectChainIncludingInternalRedirects, - redirectChain); + redirectChain, + loadInfoArgs.corsUnsafeHeaders(), + loadInfoArgs.forcePreflight(), + loadInfoArgs.isPreflight()); loadInfo.forget(outLoadInfo); return NS_OK; diff --git a/js/public/Class.h b/js/public/Class.h index cedd60bd2d9f..ceb9eb69d40b 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -719,7 +719,7 @@ struct JSClass { // application. #define JSCLASS_GLOBAL_APPLICATION_SLOTS 5 #define JSCLASS_GLOBAL_SLOT_COUNT \ - (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 32) + (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 35) #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \ (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n))) #define JSCLASS_GLOBAL_FLAGS \ diff --git a/js/public/TraceableVector.h b/js/public/TraceableVector.h index 7382b08ec043..79bade658e8c 100644 --- a/js/public/TraceableVector.h +++ b/js/public/TraceableVector.h @@ -33,26 +33,96 @@ template > -class TraceableVector - : public mozilla::VectorBase>, - public JS::Traceable +class TraceableVector : public JS::Traceable { - using Base = mozilla::VectorBase; + mozilla::Vector vector; public: - explicit TraceableVector(AllocPolicy alloc = AllocPolicy()) : Base(alloc) {} - TraceableVector(TraceableVector&& vec) : Base(mozilla::Forward(vec)) {} + explicit TraceableVector(AllocPolicy alloc = AllocPolicy()) + : vector(alloc) + {} + + TraceableVector(TraceableVector&& vec) + : vector(mozilla::Move(vec.vector)) + {} + TraceableVector& operator=(TraceableVector&& vec) { - return Base::operator=(mozilla::Forward(vec)); + vector = mozilla::Move(vec.vector); + return *this; + } + + size_t length() const { return vector.length(); } + bool empty() const { return vector.empty(); } + size_t capacity() const { return vector.capacity(); } + + T* begin() { return vector.begin(); } + const T* begin() const { return vector.begin(); } + + T* end() { return vector.end(); } + const T* end() const { return vector.end(); } + + T& operator[](size_t i) { return vector[i]; } + const T& operator[](size_t i) const { return vector[i]; } + + T& back() { return vector.back(); } + const T& back() const { return vector.back(); } + + bool initCapacity(size_t cap) { return vector.initCapacity(cap); } + bool reserve(size_t req) { return vector.reserve(req); } + void shrinkBy(size_t amount) { return vector.shrinkBy(amount); } + bool growBy(size_t amount) { return vector.growBy(amount); } + bool resize(size_t newLen) { return vector.resize(newLen); } + + void clear() { return vector.clear(); } + + template bool append(U&& item) { return vector.append(mozilla::Forward(item)); } + + template + bool + emplaceBack(Args&&... args) { + return vector.emplaceBack(mozilla::Forward(args)...); + } + + template + void infallibleAppend(U&& aU) { + return vector.infallibleAppend(mozilla::Forward(aU)); + } + void infallibleAppendN(const T& aT, size_t aN) { + return vector.infallibleAppendN(aT, aN); + } + template void + infallibleAppend(const U* aBegin, const U* aEnd) { + return vector.infallibleAppend(aBegin, aEnd); + } + template void infallibleAppend(const U* aBegin, size_t aLength) { + return vector.infallibleAppend(aBegin, aLength); + } + + bool appendN(const T& val, size_t count) { return vector.appendN(val, count); } + + template bool append(const U* aBegin, const U* aEnd) { + return vector.append(aBegin, aEnd); + } + template bool append(const U* aBegin, size_t aLength) { + return vector.append(aBegin, aLength); + } + + void popBack() { return vector.popBack(); } + T popCopy() { return vector.popCopy(); } + + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { + return vector.sizeOfExcludingThis(mallocSizeOf); + } + + size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { + return vector.sizeOfIncludingThis(mallocSizeOf); } static void trace(TraceableVector* vec, JSTracer* trc) { vec->trace(trc); } + void trace(JSTracer* trc) { - for (size_t i = 0; i < this->length(); ++i) - GCPolicy::trace(trc, &Base::operator[](i), "vector element"); + for (auto& elem : vector) + GCPolicy::trace(trc, &elem, "vector element"); } }; @@ -114,8 +184,8 @@ class MutableTraceableVectorOperations template bool emplaceBack(Args&&... aArgs) { return vec().emplaceBack(mozilla::Forward(aArgs...)); } - template - bool appendAll(const mozilla::VectorBase& aU) { return vec().appendAll(aU); } + template + bool appendAll(const mozilla::Vector& aU) { return vec().appendAll(aU); } bool appendN(const T& aT, size_t aN) { return vec().appendN(aT, aN); } template bool append(const U* aBegin, const U* aEnd) { return vec().append(aBegin, aEnd); diff --git a/js/public/Vector.h b/js/public/Vector.h index 7789816a1c94..07f2240f47ff 100644 --- a/js/public/Vector.h +++ b/js/public/Vector.h @@ -19,47 +19,32 @@ namespace js { class TempAllocPolicy; -// If we had C++11 template aliases, we could just use this: -// -// template -// using Vector = mozilla::Vector; -// -// ...and get rid of all the CRTP madness in mozilla::Vector(Base). But we -// can't because compiler support's not up to snuff. (Template aliases are in -// gcc 4.7 and clang 3.0 and are expected to be in MSVC 2013.) Instead, have a -// completely separate class inheriting from mozilla::Vector, and throw CRTP at -// the problem til things work. -// -// This workaround presents a couple issues. First, because js::Vector is a -// distinct type from mozilla::Vector, overload resolution, method calls, etc. -// are affected. *Hopefully* this won't be too bad in practice. (A bunch of -// places had to be fixed when mozilla::Vector was introduced, but it wasn't a -// crazy number.) Second, mozilla::Vector's interface has to be made subclass- -// ready via CRTP -- or rather, via mozilla::VectorBase, which basically no one -// should use. :-) Third, we have to redefine the constructors and the non- -// inherited operators. Blech. Happily there aren't too many of these, so it -// isn't the end of the world. +namespace detail { + +template +struct TypeIsGCThing : mozilla::FalseType +{}; + +// Uncomment this once we actually can assert it: +//template <> +//struct TypeIsGCThing : mozilla::TrueType +//{}; + +} // namespace detail template -class Vector - : public mozilla::VectorBase > -{ - typedef typename mozilla::VectorBase Base; - - public: - explicit Vector(AllocPolicy alloc = AllocPolicy()) : Base(alloc) {} - Vector(Vector&& vec) : Base(mozilla::Move(vec)) {} - Vector& operator=(Vector&& vec) { - return Base::operator=(mozilla::Move(vec)); - } -}; + class AllocPolicy = TempAllocPolicy +// 1800 is MSVC2013. Optimistically assume MSVC2015 (1900) is fixed. +// If you're porting to MSVC2015 and this doesn't work, extend the +// condition to encompass that additional version (but *do* keep the +// version-check so we know when MSVC's fixed). +#if !defined(_MSC_VER) || (1800 <= _MSC_VER && _MSC_VER <= 1800) + // Don't use this with JS::Value! Use JS::AutoValueVector instead. + , typename = typename mozilla::EnableIf::value>::Type +#endif + > +using Vector = mozilla::Vector; } // namespace js diff --git a/js/src/asmjs/AsmJSModule.cpp b/js/src/asmjs/AsmJSModule.cpp index 87a3b83d9fcb..350827042ee1 100644 --- a/js/src/asmjs/AsmJSModule.cpp +++ b/js/src/asmjs/AsmJSModule.cpp @@ -23,6 +23,7 @@ #include "mozilla/EnumeratedRange.h" #include "mozilla/PodOperations.h" #include "mozilla/TaggedAnonymousMemory.h" +#include "mozilla/Vector.h" #include "jslibmath.h" #include "jsmath.h" @@ -1132,7 +1133,7 @@ AsmJSModule::Name::clone(ExclusiveContext* cx, Name* out) const template size_t -SerializedVectorSize(const Vector& vec) +SerializedVectorSize(const mozilla::Vector& vec) { size_t size = sizeof(uint32_t); for (size_t i = 0; i < vec.length(); i++) @@ -1142,7 +1143,7 @@ SerializedVectorSize(const Vector& vec) template uint8_t* -SerializeVector(uint8_t* cursor, const Vector& vec) +SerializeVector(uint8_t* cursor, const mozilla::Vector& vec) { cursor = WriteScalar(cursor, vec.length()); for (size_t i = 0; i < vec.length(); i++) @@ -1152,7 +1153,8 @@ SerializeVector(uint8_t* cursor, const Vector& vec) template const uint8_t* -DeserializeVector(ExclusiveContext* cx, const uint8_t* cursor, Vector* vec) +DeserializeVector(ExclusiveContext* cx, const uint8_t* cursor, + mozilla::Vector* vec) { uint32_t length; cursor = ReadScalar(cursor, &length); @@ -1167,8 +1169,8 @@ DeserializeVector(ExclusiveContext* cx, const uint8_t* cursor, Vector bool -CloneVector(ExclusiveContext* cx, const Vector& in, - Vector* out) +CloneVector(ExclusiveContext* cx, const mozilla::Vector& in, + mozilla::Vector* out) { if (!out->resize(in.length())) return false; @@ -1179,27 +1181,27 @@ CloneVector(ExclusiveContext* cx, const Vector& in, return true; } -template +template size_t -SerializedPodVectorSize(const mozilla::VectorBase& vec) +SerializedPodVectorSize(const mozilla::Vector& vec) { return sizeof(uint32_t) + vec.length() * sizeof(T); } -template +template uint8_t* -SerializePodVector(uint8_t* cursor, const mozilla::VectorBase& vec) +SerializePodVector(uint8_t* cursor, const mozilla::Vector& vec) { cursor = WriteScalar(cursor, vec.length()); cursor = WriteBytes(cursor, vec.begin(), vec.length() * sizeof(T)); return cursor; } -template +template const uint8_t* DeserializePodVector(ExclusiveContext* cx, const uint8_t* cursor, - mozilla::VectorBase* vec) + mozilla::Vector* vec) { uint32_t length; cursor = ReadScalar(cursor, &length); @@ -1211,8 +1213,8 @@ DeserializePodVector(ExclusiveContext* cx, const uint8_t* cursor, template bool -ClonePodVector(ExclusiveContext* cx, const Vector& in, - Vector* out) +ClonePodVector(ExclusiveContext* cx, const mozilla::Vector& in, + mozilla::Vector* out) { if (!out->resize(in.length())) return false; diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index 1aaa9f4a57d5..5e82aeea2528 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -221,7 +221,7 @@ MapIteratorObject::next(JSContext* cx, Handle mapIterator, const Class MapObject::class_ = { "Map", - JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Map), nullptr, // addProperty nullptr, // delProperty @@ -412,21 +412,20 @@ MapObject::set(JSContext* cx, HandleObject obj, HandleValue k, HandleValue v) } MapObject* -MapObject::create(JSContext* cx) +MapObject::create(JSContext* cx, HandleObject proto /* = nullptr */) { - Rooted obj(cx, NewBuiltinClassInstance(cx)); - if (!obj) - return nullptr; - - ValueMap* map = cx->new_(cx->runtime()); + auto map = cx->make_unique(cx->runtime()); if (!map || !map->init()) { - js_delete(map); ReportOutOfMemory(cx); return nullptr; } - obj->setPrivate(map); - return obj; + MapObject* mapObj = NewObjectWithClassProto(cx, proto); + if (!mapObj) + return nullptr; + + mapObj->setPrivate(map.release()); + return mapObj; } void @@ -444,7 +443,12 @@ MapObject::construct(JSContext* cx, unsigned argc, Value* vp) if (!ThrowIfNotConstructing(cx, args, "Map")) return false; - Rooted obj(cx, MapObject::create(cx)); + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + Rooted obj(cx, MapObject::create(cx, proto)); if (!obj) return false; @@ -1064,19 +1068,19 @@ SetObject::add(JSContext* cx, HandleObject obj, HandleValue k) } SetObject* -SetObject::create(JSContext* cx) +SetObject::create(JSContext* cx, HandleObject proto /* = nullptr */) { - SetObject* obj = NewBuiltinClassInstance(cx); - if (!obj) - return nullptr; - - ValueSet* set = cx->new_(cx->runtime()); + auto set = cx->make_unique(cx->runtime()); if (!set || !set->init()) { - js_delete(set); ReportOutOfMemory(cx); return nullptr; } - obj->setPrivate(set); + + SetObject* obj = NewObjectWithClassProto(cx, proto); + if (!obj) + return nullptr; + + obj->setPrivate(set.release()); return obj; } @@ -1106,7 +1110,12 @@ SetObject::construct(JSContext* cx, unsigned argc, Value* vp) if (!ThrowIfNotConstructing(cx, args, "Set")) return false; - Rooted obj(cx, SetObject::create(cx)); + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + Rooted obj(cx, SetObject::create(cx, proto)); if (!obj) return false; diff --git a/js/src/builtin/MapObject.h b/js/src/builtin/MapObject.h index 835905594d8c..1118675f8736 100644 --- a/js/src/builtin/MapObject.h +++ b/js/src/builtin/MapObject.h @@ -92,7 +92,7 @@ class MapObject : public NativeObject { JS::AutoValueVector* entries); static bool entries(JSContext* cx, unsigned argc, Value* vp); static bool has(JSContext* cx, unsigned argc, Value* vp); - static MapObject* create(JSContext* cx); + static MapObject* create(JSContext* cx, HandleObject proto = nullptr); // Publicly exposed Map calls for JSAPI access (webidl maplike/setlike // interfaces, etc.) @@ -181,7 +181,7 @@ class SetObject : public NativeObject { // Publicly exposed Set calls for JSAPI access (webidl maplike/setlike // interfaces, etc.) - static SetObject* create(JSContext *cx); + static SetObject* create(JSContext *cx, HandleObject proto = nullptr); static uint32_t size(JSContext *cx, HandleObject obj); static bool has(JSContext *cx, HandleObject obj, HandleValue key, bool* rval); static bool clear(JSContext *cx, HandleObject obj); diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index 3ba3d8c3c9d9..33d4e344d580 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -72,7 +72,6 @@ ModuleValueGetter(JSContext* cx, unsigned argc, Value* vp) ImportEntryObject::class_ = { "ImportEntry", JSCLASS_HAS_RESERVED_SLOTS(ImportEntryObject::SlotCount) | - JSCLASS_HAS_CACHED_PROTO(JSProto_ImportEntry) | JSCLASS_IS_ANONYMOUS }; @@ -90,8 +89,8 @@ ImportEntryObject::isInstance(HandleValue value) return value.isObject() && value.toObject().is(); } -/* static */ JSObject* -ImportEntryObject::initClass(JSContext* cx, HandleObject obj) +/* static */ bool +GlobalObject::initImportEntryProto(JSContext* cx, Handle global) { static const JSPropertySpec protoAccessors[] = { JS_PSG("moduleRequest", ImportEntryObject_moduleRequestGetter, 0), @@ -100,22 +99,15 @@ ImportEntryObject::initClass(JSContext* cx, HandleObject obj) JS_PS_END }; - Rooted global(cx, &obj->as()); RootedObject proto(cx, global->createBlankPrototype(cx)); if (!proto) - return nullptr; + return false; if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr)) - return nullptr; + return false; - global->setPrototype(JSProto_ImportEntry, ObjectValue(*proto)); - return proto; -} - -JSObject* -js::InitImportEntryClass(JSContext* cx, HandleObject obj) -{ - return ImportEntryObject::initClass(cx, obj); + global->setReservedSlot(IMPORT_ENTRY_PROTO, ObjectValue(*proto)); + return true; } /* static */ ImportEntryObject* @@ -124,9 +116,12 @@ ImportEntryObject::create(JSContext* cx, HandleAtom importName, HandleAtom localName) { - RootedImportEntry self(cx, NewBuiltinClassInstance(cx)); - if (!self) + RootedObject proto(cx, cx->global()->getImportEntryPrototype()); + RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto)); + if (!obj) return nullptr; + + RootedImportEntry self(cx, &obj->as()); self->initReservedSlot(ModuleRequestSlot, StringValue(moduleRequest)); self->initReservedSlot(ImportNameSlot, StringValue(importName)); self->initReservedSlot(LocalNameSlot, StringValue(localName)); @@ -140,7 +135,6 @@ ImportEntryObject::create(JSContext* cx, ExportEntryObject::class_ = { "ExportEntry", JSCLASS_HAS_RESERVED_SLOTS(ExportEntryObject::SlotCount) | - JSCLASS_HAS_CACHED_PROTO(JSProto_ExportEntry) | JSCLASS_IS_ANONYMOUS }; @@ -160,8 +154,8 @@ ExportEntryObject::isInstance(HandleValue value) return value.isObject() && value.toObject().is(); } -/* static */ JSObject* -ExportEntryObject::initClass(JSContext* cx, HandleObject obj) +/* static */ bool +GlobalObject::initExportEntryProto(JSContext* cx, Handle global) { static const JSPropertySpec protoAccessors[] = { JS_PSG("exportName", ExportEntryObject_exportNameGetter, 0), @@ -171,22 +165,15 @@ ExportEntryObject::initClass(JSContext* cx, HandleObject obj) JS_PS_END }; - Rooted global(cx, &obj->as()); RootedObject proto(cx, global->createBlankPrototype(cx)); if (!proto) - return nullptr; + return false; if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr)) - return nullptr; + return false; - global->setPrototype(JSProto_ExportEntry, ObjectValue(*proto)); - return proto; -} - -JSObject* -js::InitExportEntryClass(JSContext* cx, HandleObject obj) -{ - return ExportEntryObject::initClass(cx, obj); + global->setReservedSlot(EXPORT_ENTRY_PROTO, ObjectValue(*proto)); + return true; } static Value @@ -202,9 +189,12 @@ ExportEntryObject::create(JSContext* cx, HandleAtom maybeImportName, HandleAtom maybeLocalName) { - RootedExportEntry self(cx, NewBuiltinClassInstance(cx)); - if (!self) + RootedObject proto(cx, cx->global()->getExportEntryPrototype()); + RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto)); + if (!obj) return nullptr; + + RootedExportEntry self(cx, &obj->as()); self->initReservedSlot(ExportNameSlot, StringOrNullValue(maybeExportName)); self->initReservedSlot(ModuleRequestSlot, StringOrNullValue(maybeModuleRequest)); self->initReservedSlot(ImportNameSlot, StringOrNullValue(maybeImportName)); @@ -545,7 +535,6 @@ void FunctionDeclaration::trace(JSTracer* trc) ModuleObject::class_ = { "Module", JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Module) | JSCLASS_IS_ANONYMOUS, nullptr, /* addProperty */ nullptr, /* delProperty */ @@ -554,7 +543,7 @@ ModuleObject::class_ = { nullptr, /* enumerate */ nullptr, /* resolve */ nullptr, /* mayResolve */ - nullptr, /* finalize */ + ModuleObject::finalize, nullptr, /* call */ nullptr, /* hasInstance */ nullptr, /* construct */ @@ -583,10 +572,12 @@ ModuleObject::isInstance(HandleValue value) /* static */ ModuleObject* ModuleObject::create(ExclusiveContext* cx, HandleObject enclosingStaticScope) { - Rooted self(cx, NewBuiltinClassInstance(cx, TenuredObject)); - if (!self) + RootedObject proto(cx, cx->global()->getModulePrototype()); + RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto)); + if (!obj) return nullptr; + RootedModuleObject self(cx, &obj->as()); self->initReservedSlot(StaticScopeSlot, ObjectOrNullValue(enclosingStaticScope)); Zone* zone = cx->zone(); @@ -856,8 +847,8 @@ DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries, LocalExportEntriesSlot DEFINE_GETTER_FUNCTIONS(ModuleObject, indirectExportEntries, IndirectExportEntriesSlot) DEFINE_GETTER_FUNCTIONS(ModuleObject, starExportEntries, StarExportEntriesSlot) -JSObject* -js::InitModuleClass(JSContext* cx, HandleObject obj) +/* static */ bool +GlobalObject::initModuleProto(JSContext* cx, Handle global) { static const JSPropertySpec protoAccessors[] = { JS_PSG("namespace", ModuleObject_namespace_Getter, 0), @@ -878,17 +869,24 @@ js::InitModuleClass(JSContext* cx, HandleObject obj) JS_FS_END }; - Rooted global(cx, &obj->as()); - RootedObject proto(cx, global->createBlankPrototype(cx)); if (!proto) - return nullptr; + return false; if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, protoFunctions)) - return nullptr; + return false; - global->setPrototype(JSProto_Module, ObjectValue(*proto)); - return proto; + global->setReservedSlot(MODULE_PROTO, ObjectValue(*proto)); + return true; +} + +bool +js::InitModuleClasses(JSContext* cx, HandleObject obj) +{ + Rooted global(cx, &obj->as()); + return GlobalObject::initModuleProto(cx, global) && + GlobalObject::initImportEntryProto(cx, global) && + GlobalObject::initExportEntryProto(cx, global); } #undef DEFINE_GETTER_FUNCTIONS diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h index 36fe427f4998..cdcf4e5a2a15 100644 --- a/js/src/builtin/ModuleObject.h +++ b/js/src/builtin/ModuleObject.h @@ -311,9 +311,7 @@ class MOZ_STACK_CLASS ModuleBuilder ArrayObject* createArray(const TraceableVector& vector); }; -JSObject* InitModuleClass(JSContext* cx, HandleObject obj); -JSObject* InitImportEntryClass(JSContext* cx, HandleObject obj); -JSObject* InitExportEntryClass(JSContext* cx, HandleObject obj); +bool InitModuleClasses(JSContext* cx, HandleObject obj); } // namespace js diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 542d22716ab5..cdbd9f76b345 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -33,7 +33,12 @@ js::obj_construct(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); RootedObject obj(cx, nullptr); - if (args.length() > 0 && !args[0].isNullOrUndefined()) { + if (args.isConstructing() && (&args.newTarget().toObject() != &args.callee())) { + RootedObject newTarget(cx, &args.newTarget().toObject()); + obj = CreateThis(cx, &PlainObject::class_, newTarget); + if (!obj) + return false; + } else if (args.length() > 0 && !args[0].isNullOrUndefined()) { obj = ToObject(cx, args[0]); if (!obj) return false; diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 3076dff7db0d..abe869be44fa 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2495,6 +2495,7 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) } case PNK_FOR: + case PNK_COMPREHENSIONFOR: { MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); @@ -2759,12 +2760,12 @@ ASTSerializer::comprehension(ParseNode* pn, MutableHandleValue dst) // They have slightly different parse trees and scoping. bool isLegacy = pn->isKind(PNK_LEXICALSCOPE); ParseNode* next = isLegacy ? pn->pn_expr : pn; - LOCAL_ASSERT(next->isKind(PNK_FOR)); + LOCAL_ASSERT(next->isKind(PNK_COMPREHENSIONFOR)); NodeVector blocks(cx); RootedValue filter(cx, MagicValue(JS_SERIALIZE_NO_NODE)); while (true) { - if (next->isKind(PNK_FOR)) { + if (next->isKind(PNK_COMPREHENSIONFOR)) { RootedValue block(cx); if (!comprehensionBlock(next, &block) || !blocks.append(block)) return false; @@ -2802,12 +2803,12 @@ ASTSerializer::generatorExpression(ParseNode* pn, MutableHandleValue dst) // expression. bool isLegacy = pn->isKind(PNK_LEXICALSCOPE); ParseNode* next = isLegacy ? pn->pn_expr : pn; - LOCAL_ASSERT(next->isKind(PNK_FOR)); + LOCAL_ASSERT(next->isKind(PNK_COMPREHENSIONFOR)); NodeVector blocks(cx); RootedValue filter(cx, MagicValue(JS_SERIALIZE_NO_NODE)); while (true) { - if (next->isKind(PNK_FOR)) { + if (next->isKind(PNK_COMPREHENSIONFOR)) { RootedValue block(cx); if (!comprehensionBlock(next, &block) || !blocks.append(block)) return false; diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 0ce8d2843d99..8d6c4a1b7c31 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -185,7 +185,7 @@ RegExpInitialize(JSContext* cx, Handle obj, HandleValue patternVa } /* Steps 11-15. */ - if (!InitializeRegExp(cx, obj, pattern, flags)) + if (!RegExpObject::initFromAtom(cx, obj, pattern, flags)) return false; /* Step 16. */ @@ -268,7 +268,7 @@ regexp_compile_impl(JSContext* cx, const CallArgs& args) } // Step 5. - if (!InitializeRegExp(cx, regexp, sourceAtom, flags)) + if (!RegExpObject::initFromAtom(cx, regexp, sourceAtom, flags)) return false; args.rval().setObject(*regexp); @@ -307,11 +307,11 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp) if (!IsRegExp(cx, args.get(0), &patternIsRegExp)) return false; - if (args.isConstructing()) { - // XXX Step 3! - } else { - // XXX Step 4a + // We can delay step 3 and step 4a until later, during + // GetPrototypeFromCallableConstructor calls. Accessing the new.target + // and the callee from the stack is unobservable. + if (!args.isConstructing()) { // Step 4b. if (patternIsRegExp && !args.hasDefined(1)) { RootedObject patternObj(cx, &args[0].toObject()); @@ -341,6 +341,7 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp) // don't reuse the RegExpShared below. RootedObject patternObj(cx, &patternValue.toObject()); + // Step 5 RootedAtom sourceAtom(cx); RegExpFlag flags; { @@ -353,27 +354,30 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp) if (!args.hasDefined(1)) { // Step 5b. flags = g->getFlags(); - } else { - // Step 5c. - // XXX We shouldn't be converting to string yet! This must - // come *after* the .constructor access in step 8. - flags = RegExpFlag(0); - RootedString flagStr(cx, ToString(cx, args[1])); - if (!flagStr) - return false; - if (!ParseRegExpFlags(cx, flagStr, &flags)) - return false; } } // Steps 8-9. - // XXX Note bug in step 5c, with respect to step 8. - Rooted regexp(cx, RegExpAlloc(cx)); + RootedObject proto(cx); + if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) + return false; + + Rooted regexp(cx, RegExpAlloc(cx, proto)); if (!regexp) return false; // Step 10. - if (!InitializeRegExp(cx, regexp, sourceAtom, flags)) + if (args.hasDefined(1)) { + // Step 5c / 21.2.3.2.2 RegExpInitialize step 5. + flags = RegExpFlag(0); + RootedString flagStr(cx, ToString(cx, args[1])); + if (!flagStr) + return false; + if (!ParseRegExpFlags(cx, flagStr, &flags)) + return false; + } + + if (!RegExpObject::initFromAtom(cx, regexp, sourceAtom, flags)) return false; args.rval().setObject(*regexp); @@ -404,7 +408,11 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp) } // Steps 8-9. - Rooted regexp(cx, RegExpAlloc(cx)); + RootedObject proto(cx); + if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) + return false; + + Rooted regexp(cx, RegExpAlloc(cx, proto)); if (!regexp) return false; @@ -699,7 +707,9 @@ js::CreateRegExpPrototype(JSContext* cx, JSProtoKey key) proto->NativeObject::setPrivate(nullptr); RootedAtom source(cx, cx->names().empty); - return InitializeRegExp(cx, proto, source, RegExpFlag(0)); + if (!RegExpObject::initFromAtom(cx, proto, source, RegExpFlag(0))) + return nullptr; + return proto; } static bool diff --git a/js/src/builtin/WeakMapObject.cpp b/js/src/builtin/WeakMapObject.cpp index 240398b71be5..f94872853883 100644 --- a/js/src/builtin/WeakMapObject.cpp +++ b/js/src/builtin/WeakMapObject.cpp @@ -316,7 +316,8 @@ WeakMap_construct(JSContext* cx, unsigned argc, Value* vp) if (!ThrowIfNotConstructing(cx, args, "WeakMap")) return false; - RootedObject obj(cx, NewBuiltinClassInstance(cx, &WeakMapObject::class_)); + RootedObject newTarget(cx, &args.newTarget().toObject()); + RootedObject obj(cx, CreateThis(cx, &WeakMapObject::class_, newTarget)); if (!obj) return false; diff --git a/js/src/builtin/WeakSetObject.cpp b/js/src/builtin/WeakSetObject.cpp index 35fb216e144d..6ae866c236da 100644 --- a/js/src/builtin/WeakSetObject.cpp +++ b/js/src/builtin/WeakSetObject.cpp @@ -61,14 +61,14 @@ WeakSetObject::initClass(JSContext* cx, JSObject* obj) } WeakSetObject* -WeakSetObject::create(JSContext* cx) +WeakSetObject::create(JSContext* cx, HandleObject proto /* = nullptr */) { - Rooted obj(cx, NewBuiltinClassInstance(cx)); - if (!obj) + RootedObject map(cx, NewBuiltinClassInstance(cx)); + if (!map) return nullptr; - RootedObject map(cx, JS::NewWeakMapObject(cx)); - if (!map) + WeakSetObject* obj = NewObjectWithClassProto(cx, proto); + if (!obj) return nullptr; obj->setReservedSlot(WEAKSET_MAP_SLOT, ObjectValue(*map)); @@ -78,16 +78,21 @@ WeakSetObject::create(JSContext* cx) bool WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp) { - Rooted obj(cx, WeakSetObject::create(cx)); - if (!obj) - return false; - // Based on our "Set" implementation instead of the more general ES6 steps. CallArgs args = CallArgsFromVp(argc, vp); if (!ThrowIfNotConstructing(cx, args, "WeakSet")) return false; + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + Rooted obj(cx, WeakSetObject::create(cx, proto)); + if (!obj) + return false; + if (!args.get(0).isNullOrUndefined()) { RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject()); diff --git a/js/src/builtin/WeakSetObject.h b/js/src/builtin/WeakSetObject.h index 308b67e67fc4..9aa4fc51d63d 100644 --- a/js/src/builtin/WeakSetObject.h +++ b/js/src/builtin/WeakSetObject.h @@ -23,7 +23,7 @@ class WeakSetObject : public NativeObject static const JSPropertySpec properties[]; static const JSFunctionSpec methods[]; - static WeakSetObject* create(JSContext* cx); + static WeakSetObject* create(JSContext* cx, HandleObject proto = nullptr); static bool construct(JSContext* cx, unsigned argc, Value* vp); }; diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index b15d02dee743..d9489f0bad28 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -9,6 +9,7 @@ #include "mozilla/FloatingPoint.h" #include "mozilla/MemoryReporting.h" #include "mozilla/NumericLimits.h" +#include "mozilla/Vector.h" #include #include @@ -40,6 +41,7 @@ #include "builtin/TypedObject.h" #include "ctypes/Library.h" #include "gc/Zone.h" +#include "js/Vector.h" #include "jsatominlines.h" #include "jsobjinlines.h" @@ -2658,7 +2660,7 @@ jsvalToPtrExplicit(JSContext* cx, Value val, uintptr_t* result) template void -IntegerToString(IntegerType i, int radix, Vector& result) +IntegerToString(IntegerType i, int radix, mozilla::Vector& result) { JS_STATIC_ASSERT(NumericLimits::is_exact); @@ -3654,7 +3656,7 @@ BuildTypeSource(JSContext* cx, const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj); size_t length = fields->count(); - Array fieldsArray; + Vector fieldsArray; if (!fieldsArray.resize(length)) break; @@ -3812,7 +3814,7 @@ BuildDataSource(JSContext* cx, // be able to ImplicitConvert successfully. const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj); size_t length = fields->count(); - Array fieldsArray; + Vector fieldsArray; if (!fieldsArray.resize(length)) return false; @@ -6510,7 +6512,7 @@ FunctionType::ConstructData(JSContext* cx, return JS_FreezeObject(cx, dataObj); } -typedef Array AutoValueAutoArray; +typedef Vector AutoValueAutoArray; static bool ConvertArgument(JSContext* cx, @@ -6932,7 +6934,12 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) ArgClosure argClosure(cif, result, args, static_cast(userData)); JSRuntime* rt = argClosure.cinfo->rt; RootedObject fun(rt, argClosure.cinfo->jsfnObj); - (void) js::PrepareScriptEnvironmentAndInvoke(rt, fun, argClosure); + + // Arbitrarily choose a cx in which to run this code. This is bad, as + // JSContexts are stateful and have options. The hope is to eliminate + // JSContexts (see bug 650361). + js::PrepareScriptEnvironmentAndInvoke(rt->contextList.getFirst(), fun, + argClosure); } bool CClosure::ArgClosure::operator()(JSContext* cx) @@ -7025,11 +7032,14 @@ bool CClosure::ArgClosure::operator()(JSContext* cx) size_t copySize = CType::GetSize(fninfo->mReturnType); MOZ_ASSERT(copySize <= rvSize); memcpy(result, cinfo->errResult, copySize); + + // We still want to return false here, so that + // PrepareScriptEnvironmentAndInvoke will report the exception. } else { // Bad case: not much we can do here. The rv is already zeroed out, so we // just return and hope for the best. - return false; } + return false; } // Small integer types must be returned as a word-sized ffi_arg. Coerce it diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index 6ff6ccf17c8d..728baf9c6c8e 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -7,6 +7,7 @@ #define ctypes_CTypes_h #include "mozilla/UniquePtr.h" +#include "mozilla/Vector.h" #include "ffi.h" #include "jsalloc.h" @@ -25,14 +26,6 @@ namespace ctypes { ** Utility classes *******************************************************************************/ -// Container class for Vector, using SystemAllocPolicy. -template -class Array : public Vector -{ - static_assert(!mozilla::IsSame::value, - "use JS::AutoValueVector instead"); -}; - // String and AutoString classes, based on Vector. typedef Vector String; typedef Vector AutoString; @@ -42,7 +35,7 @@ typedef Vector AutoCString; // Convenience functions to append, insert, and compare Strings. template void -AppendString(Vector& v, const char (&array)[ArrayLength]) +AppendString(mozilla::Vector& v, const char (&array)[ArrayLength]) { // Don't include the trailing '\0'. size_t alen = ArrayLength - 1; @@ -56,7 +49,7 @@ AppendString(Vector& v, const char (&array)[ArrayLength]) template void -AppendChars(Vector& v, const char c, size_t count) +AppendChars(mozilla::Vector& v, const char c, size_t count) { size_t vlen = v.length(); if (!v.resize(vlen + count)) @@ -68,7 +61,7 @@ AppendChars(Vector& v, const char c, size_t count) template void -AppendUInt(Vector& v, unsigned n) +AppendUInt(mozilla::Vector& v, unsigned n) { char array[16]; size_t alen = JS_snprintf(array, 16, "%u", n); @@ -82,14 +75,14 @@ AppendUInt(Vector& v, unsigned n) template void -AppendString(Vector& v, Vector& w) +AppendString(mozilla::Vector& v, mozilla::Vector& w) { v.append(w.begin(), w.length()); } template void -AppendString(Vector& v, JSString* str) +AppendString(mozilla::Vector& v, JSString* str) { MOZ_ASSERT(str); JSLinearString* linear = str->ensureLinear(nullptr); @@ -104,7 +97,7 @@ AppendString(Vector& v, JSString* str) template void -AppendString(Vector& v, JSString* str) +AppendString(mozilla::Vector& v, JSString* str) { MOZ_ASSERT(str); size_t vlen = v.length(); @@ -130,7 +123,7 @@ AppendString(Vector& v, JSString* str) template void -PrependString(Vector& v, const char (&array)[ArrayLength]) +PrependString(mozilla::Vector& v, const char (&array)[ArrayLength]) { // Don't include the trailing '\0'. size_t alen = ArrayLength - 1; @@ -148,7 +141,7 @@ PrependString(Vector& v, const char (&array)[ArrayLength]) template void -PrependString(Vector& v, JSString* str) +PrependString(mozilla::Vector& v, JSString* str) { MOZ_ASSERT(str); size_t vlen = v.length(); @@ -306,12 +299,12 @@ struct FunctionInfo // A fixed array of known parameter types, excluding any variadic // parameters (if mIsVariadic). - Array > mArgTypes; + Vector, 0, SystemAllocPolicy> mArgTypes; // A variable array of ffi_type*s corresponding to both known parameter // types and dynamic (variadic) parameter types. Longer than mArgTypes // only if mIsVariadic. - Array mFFITypes; + Vector mFFITypes; // Flag indicating whether the function behaves like a C function with // ... as the final formal parameter. diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 88288a14db93..b3f7a3aaa4bc 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2189,6 +2189,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case PNK_DOWHILE: case PNK_WHILE: case PNK_FOR: + case PNK_COMPREHENSIONFOR: MOZ_ASSERT(pn->isArity(PN_BINARY)); *answer = true; return true; @@ -2347,9 +2348,9 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) *answer = true; return true; - case PNK_FORIN: // by PNK_FOR - case PNK_FOROF: // by PNK_FOR - case PNK_FORHEAD: // by PNK_FOR + case PNK_FORIN: // by PNK_FOR/PNK_COMPREHENSIONFOR + case PNK_FOROF: // by PNK_FOR/PNK_COMPREHENSIONFOR + case PNK_FORHEAD: // by PNK_FOR/PNK_COMPREHENSIONFOR case PNK_CLASSMETHOD: // by PNK_CLASS case PNK_CLASSNAMES: // by PNK_CLASS case PNK_CLASSMETHODLIST: // by PNK_CLASS @@ -5381,8 +5382,10 @@ BytecodeEmitter::emitIterator() } bool -BytecodeEmitter::emitForInOrOfVariables(ParseNode* pn, bool* letDecl) +BytecodeEmitter::emitForInOrOfVariables(ParseNode* pn) { + MOZ_ASSERT(pn->isKind(PNK_VAR) || pn->isKind(PNK_LET)); + // ES6 specifies that loop variables get a fresh binding in each iteration. // This is currently implemented for C-style for(;;) loops, but not // for-in/of loops, though a similar approach should work. See bug 449811. @@ -5391,32 +5394,23 @@ BytecodeEmitter::emitForInOrOfVariables(ParseNode* pn, bool* letDecl) // scope containing an uninitialized `x`. If EXPR accesses `x`, we should // get a ReferenceError due to the TDZ violation. This is not yet // implemented. See bug 1069480. - - *letDecl = pn->isKind(PNK_LEXICALSCOPE); - MOZ_ASSERT_IF(*letDecl, pn->isLexical()); - - // If the left part is 'var x', emit code to define x if necessary using a - // prologue opcode, but do not emit a pop. If it is 'let x', enterBlockScope - // will initialize let bindings in emitForOf and emitForIn with - // undefineds. // - // Due to the horror of legacy comprehensions, there is a third case where - // we have PNK_LET without a lexical scope, because those expressions are - // parsed with single lexical scope for the entire comprehension. In this - // case we must initialize the lets to not trigger dead zone checks via - // InitializeVars. - if (!*letDecl) { - emittingForInit = true; - if (pn->isKind(PNK_VAR)) { - if (!emitVariables(pn, DefineVars)) - return false; - } else { - MOZ_ASSERT(pn->isKind(PNK_LET)); - if (!emitVariables(pn, InitializeVars)) - return false; - } - emittingForInit = false; + // If the left part is 'var x', emit code to define x if necessary using a + // prologue opcode, but do not emit a pop. If it's 'let x', we initialize + // the lets to not trigger dead zone checks, via InitializeVars. (The + // frontend currently assumes use of a 'let', dominated by its declaration, + // needs no TDZ check, so we can't just fix the TDZ bug above by not + // initializing here.) + emittingForInit = true; + if (pn->isKind(PNK_VAR)) { + if (!emitVariables(pn, DefineVars)) + return false; + } else { + MOZ_ASSERT(pn->isKind(PNK_LET)); + if (!emitVariables(pn, InitializeVars)) + return false; } + emittingForInit = false; return true; } @@ -5425,8 +5419,14 @@ bool BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn) { MOZ_ASSERT(type == StmtType::FOR_OF_LOOP || type == StmtType::SPREAD); - MOZ_ASSERT_IF(type == StmtType::FOR_OF_LOOP, pn && pn->pn_left->isKind(PNK_FOROF)); - MOZ_ASSERT_IF(type == StmtType::SPREAD, !pn); +#ifdef DEBUG + if (type == StmtType::FOR_OF_LOOP) { + MOZ_ASSERT(pn); + MOZ_ASSERT(pn->pn_left->isKind(PNK_FOROF)); + } else { + MOZ_ASSERT(!pn); + } +#endif ptrdiff_t top = offset(); ParseNode* forHead = pn ? pn->pn_left : nullptr; @@ -5434,8 +5434,7 @@ BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn) ParseNode* forBody = pn ? pn->pn_right : nullptr; ParseNode* loopDecl = forHead ? forHead->pn_kid1 : nullptr; - bool letDecl = false; - if (loopDecl && !emitForInOrOfVariables(loopDecl, &letDecl)) + if (loopDecl && !emitForInOrOfVariables(loopDecl)) return false; if (type == StmtType::FOR_OF_LOOP) { @@ -5453,15 +5452,6 @@ BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn) return false; } - // Enter the block before the loop body, after evaluating the obj. - // Initialize let bindings with undefined when entering, as the name - // assigned to is a plain assignment. - StmtInfoBCE letStmt(cx); - if (letDecl) { - if (!enterBlockScope(&letStmt, loopDecl->pn_objbox, JSOP_UNDEFINED, 0)) - return false; - } - LoopStmtInfo stmtInfo(cx); pushLoopStatement(&stmtInfo, type, top); @@ -5559,11 +5549,6 @@ BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn) if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top, offset())) return false; - if (letDecl) { - if (!leaveNestedScope(&letStmt)) - return false; - } - if (type == StmtType::SPREAD) { if (!emit2(JSOP_PICK, 3)) // ARR I RESULT ITER return false; @@ -5581,8 +5566,7 @@ BytecodeEmitter::emitForIn(ParseNode* pn) ParseNode* forBody = pn->pn_right; ParseNode* loopDecl = forHead->pn_kid1; - bool letDecl = false; - if (loopDecl && !emitForInOrOfVariables(loopDecl, &letDecl)) + if (loopDecl && !emitForInOrOfVariables(loopDecl)) return false; /* Compile the object expression to the right of 'in'. */ @@ -5603,15 +5587,6 @@ BytecodeEmitter::emitForIn(ParseNode* pn) if (!emit1(JSOP_UNDEFINED)) return false; - // Enter the block before the loop body, after evaluating the obj. - // Initialize let bindings with undefined when entering, as the name - // assigned to is a plain assignment. - StmtInfoBCE letStmt(cx); - if (letDecl) { - if (!enterBlockScope(&letStmt, loopDecl->pn_objbox, JSOP_UNDEFINED, 0)) - return false; - } - LoopStmtInfo stmtInfo(cx); pushLoopStatement(&stmtInfo, StmtType::FOR_IN_LOOP, top); @@ -5687,11 +5662,6 @@ BytecodeEmitter::emitForIn(ParseNode* pn) if (!emit1(JSOP_ENDITER)) return false; - if (letDecl) { - if (!leaveNestedScope(&letStmt)) - return false; - } - return true; } @@ -5874,6 +5844,8 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn) bool BytecodeEmitter::emitFor(ParseNode* pn) { + MOZ_ASSERT(pn->isKind(PNK_FOR)); + if (pn->pn_left->isKind(PNK_FORHEAD)) return emitCStyleFor(pn); @@ -5887,6 +5859,308 @@ BytecodeEmitter::emitFor(ParseNode* pn) return emitForOf(StmtType::FOR_OF_LOOP, pn); } +bool +BytecodeEmitter::emitComprehensionForInOrOfVariables(ParseNode* pn, bool* letBlockScope) +{ + // ES6 specifies that lexical for-loop variables get a fresh binding each + // iteration, and that evaluation of the expression looped over occurs with + // these variables uninitialized. But these rules only apply to *standard* + // for-in/of loops, and we haven't extended these requirements to + // comprehension syntax. + + *letBlockScope = pn->isKind(PNK_LEXICALSCOPE); + if (*letBlockScope) { + // This is initially-ES7-tracked syntax, now with considerably + // murkier outlook. The |enterBlockScope()| precipitated by the + // outparam-set here initializes the let-binding in + // |emitComprehensionFor{In,Of}| with |undefined|, so there's nothing + // to do here. + MOZ_ASSERT(pn->isLexical()); + } else { + // This is legacy comprehension syntax. We'll have PNK_LET here, using + // a lexical scope provided by/for the entire comprehension. Name + // analysis assumes declarations initialize lets, but as we're handling + // this declaration manually, we must also initialize manually to avoid + // triggering dead zone checks. + MOZ_ASSERT(pn->isKind(PNK_LET)); + MOZ_ASSERT(pn->pn_count == 1); + + emittingForInit = true; + if (!emitVariables(pn, InitializeVars)) + return false; + emittingForInit = false; + } + + return true; +} + +bool +BytecodeEmitter::emitComprehensionForOf(ParseNode* pn) +{ + MOZ_ASSERT(pn->isKind(PNK_COMPREHENSIONFOR)); + + ParseNode* forHead = pn->pn_left; + MOZ_ASSERT(forHead->isKind(PNK_FOROF)); + + ParseNode* forHeadExpr = forHead->pn_kid3; + ParseNode* forBody = pn->pn_right; + + ptrdiff_t top = offset(); + + ParseNode* loopDecl = forHead->pn_kid1; + bool letBlockScope = false; + if (loopDecl && !emitComprehensionForInOrOfVariables(loopDecl, &letBlockScope)) + return false; + + // For-of loops run with two values on the stack: the iterator and the + // current result object. + + // Compile the object expression to the right of 'of'. + if (!emitTree(forHeadExpr)) // EXPR + return false; + if (!emitIterator()) // ITER + return false; + + // Push a dummy result so that we properly enter iteration midstream. + if (!emit1(JSOP_UNDEFINED)) // ITER RESULT + return false; + + // Enter the block before the loop body, after evaluating the obj. + // Initialize let bindings with undefined when entering, as the name + // assigned to is a plain assignment. + StmtInfoBCE letStmt(cx); + if (letBlockScope) { + if (!enterBlockScope(&letStmt, loopDecl->pn_objbox, JSOP_UNDEFINED, 0)) + return false; + } + + LoopStmtInfo stmtInfo(cx); + pushLoopStatement(&stmtInfo, StmtType::FOR_OF_LOOP, top); + + // Jump down to the loop condition to minimize overhead assuming at least + // one iteration, as the other loop forms do. Annotate so IonMonkey can + // find the loop-closing jump. + unsigned noteIndex; + if (!newSrcNote(SRC_FOR_OF, ¬eIndex)) + return false; + ptrdiff_t jmp; + if (!emitJump(JSOP_GOTO, 0, &jmp)) + return false; + + top = offset(); + stmtInfo.setTop(top); + if (!emitLoopHead(nullptr)) + return false; + +#ifdef DEBUG + int loopDepth = this->stackDepth; +#endif + + // Emit code to assign result.value to the iteration variable. + if (!emit1(JSOP_DUP)) // ITER RESULT RESULT + return false; + if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER RESULT VALUE + return false; + if (!emitAssignment(forHead->pn_kid2, JSOP_NOP, nullptr)) // ITER RESULT VALUE + return false; + if (!emit1(JSOP_POP)) // ITER RESULT + return false; + + // The stack should be balanced around the assignment opcode sequence. + MOZ_ASSERT(this->stackDepth == loopDepth); + + // Emit code for the loop body. + if (!emitTree(forBody)) + return false; + + // Set loop and enclosing "update" offsets, for continue. + StmtInfoBCE* stmt = &stmtInfo; + do { + stmt->update = offset(); + } while ((stmt = stmt->enclosing) != nullptr && stmt->type == StmtType::LABEL); + + // COME FROM the beginning of the loop to here. + setJumpOffsetAt(jmp); + if (!emitLoopEntry(forHeadExpr)) + return false; + + if (!emit1(JSOP_POP)) // ITER + return false; + if (!emit1(JSOP_DUP)) // ITER ITER + return false; + if (!emitIteratorNext(forHead)) // ITER RESULT + return false; + if (!emit1(JSOP_DUP)) // ITER RESULT RESULT + return false; + if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE? + return false; + + ptrdiff_t beq; + if (!emitJump(JSOP_IFEQ, top - offset(), &beq)) // ITER RESULT + return false; + + MOZ_ASSERT(this->stackDepth == loopDepth); + + // Let Ion know where the closing jump of this loop is. + if (!setSrcNoteOffset(noteIndex, 0, beq - jmp)) + return false; + + // Fixup breaks and continues. + popStatement(); + + if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top, offset())) + return false; + + if (letBlockScope) { + if (!leaveNestedScope(&letStmt)) + return false; + } + + // Pop the result and the iter. + return emitUint16Operand(JSOP_POPN, 2); // +} + +bool +BytecodeEmitter::emitComprehensionForIn(ParseNode* pn) +{ + MOZ_ASSERT(pn->isKind(PNK_COMPREHENSIONFOR)); + + ptrdiff_t top = offset(); + + ParseNode* forHead = pn->pn_left; + MOZ_ASSERT(forHead->isKind(PNK_FORIN)); + + ParseNode* forBody = pn->pn_right; + + ParseNode* loopDecl = forHead->pn_kid1; + bool letBlockScope = false; + if (loopDecl && !emitComprehensionForInOrOfVariables(loopDecl, &letBlockScope)) + return false; + + /* Compile the object expression to the right of 'in'. */ + if (!emitTree(forHead->pn_kid3)) + return false; + + /* + * Emit a bytecode to convert top of stack value to the iterator + * object depending on the loop variant (for-in, for-each-in, or + * destructuring for-in). + */ + MOZ_ASSERT(pn->isOp(JSOP_ITER)); + if (!emit2(JSOP_ITER, (uint8_t) pn->pn_iflags)) + return false; + + // For-in loops have both the iterator and the value on the stack. Push + // undefined to balance the stack. + if (!emit1(JSOP_UNDEFINED)) + return false; + + // Enter the block before the loop body, after evaluating the obj. + // Initialize let bindings with undefined when entering, as the name + // assigned to is a plain assignment. + StmtInfoBCE letStmt(cx); + if (letBlockScope) { + if (!enterBlockScope(&letStmt, loopDecl->pn_objbox, JSOP_UNDEFINED, 0)) + return false; + } + + LoopStmtInfo stmtInfo(cx); + pushLoopStatement(&stmtInfo, StmtType::FOR_IN_LOOP, top); + + /* Annotate so IonMonkey can find the loop-closing jump. */ + unsigned noteIndex; + if (!newSrcNote(SRC_FOR_IN, ¬eIndex)) + return false; + + /* + * Jump down to the loop condition to minimize overhead assuming at + * least one iteration, as the other loop forms do. + */ + ptrdiff_t jmp; + if (!emitJump(JSOP_GOTO, 0, &jmp)) + return false; + + top = offset(); + stmtInfo.setTop(top); + if (!emitLoopHead(nullptr)) + return false; + +#ifdef DEBUG + int loopDepth = this->stackDepth; +#endif + + // Emit code to assign the enumeration value to the left hand side, but + // also leave it on the stack. + if (!emitAssignment(forHead->pn_kid2, JSOP_NOP, nullptr)) + return false; + + /* The stack should be balanced around the assignment opcode sequence. */ + MOZ_ASSERT(this->stackDepth == loopDepth); + + /* Emit code for the loop body. */ + if (!emitTree(forBody)) + return false; + + /* Set loop and enclosing "update" offsets, for continue. */ + StmtInfoBCE* stmt = &stmtInfo; + do { + stmt->update = offset(); + } while ((stmt = stmt->enclosing) != nullptr && stmt->type == StmtType::LABEL); + + /* + * Fixup the goto that starts the loop to jump down to JSOP_MOREITER. + */ + setJumpOffsetAt(jmp); + if (!emitLoopEntry(nullptr)) + return false; + if (!emit1(JSOP_POP)) + return false; + if (!emit1(JSOP_MOREITER)) + return false; + if (!emit1(JSOP_ISNOITER)) + return false; + ptrdiff_t beq; + if (!emitJump(JSOP_IFEQ, top - offset(), &beq)) + return false; + + /* Set the srcnote offset so we can find the closing jump. */ + if (!setSrcNoteOffset(noteIndex, 0, beq - jmp)) + return false; + + // Fix up breaks and continues. + popStatement(); + + // Pop the enumeration value. + if (!emit1(JSOP_POP)) + return false; + + if (!tryNoteList.append(JSTRY_FOR_IN, this->stackDepth, top, offset())) + return false; + if (!emit1(JSOP_ENDITER)) + return false; + + if (letBlockScope) { + if (!leaveNestedScope(&letStmt)) + return false; + } + + return true; +} + +bool +BytecodeEmitter::emitComprehensionFor(ParseNode* compFor) +{ + MOZ_ASSERT(compFor->pn_left->isKind(PNK_FORIN) || + compFor->pn_left->isKind(PNK_FOROF)); + + if (!updateLineNumberNotes(compFor->pn_pos.begin)) + return false; + + return compFor->pn_left->isKind(PNK_FORIN) + ? emitComprehensionForIn(compFor) + : emitComprehensionForOf(compFor); +} + MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) { @@ -7978,6 +8252,11 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) return false; break; + case PNK_COMPREHENSIONFOR: + if (!emitComprehensionFor(pn)) + return false; + break; + case PNK_BREAK: if (!emitBreak(pn->as().label())) return false; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 9704240cc67f..125a59d08ec6 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -601,10 +601,15 @@ struct BytecodeEmitter bool emitSelfHostedResumeGenerator(ParseNode* pn); bool emitSelfHostedForceInterpreter(ParseNode* pn); + bool emitComprehensionFor(ParseNode* compFor); + bool emitComprehensionForIn(ParseNode* pn); + bool emitComprehensionForInOrOfVariables(ParseNode* pn, bool* letBlockScope); + bool emitComprehensionForOf(ParseNode* pn); + bool emitDo(ParseNode* pn); bool emitFor(ParseNode* pn); bool emitForIn(ParseNode* pn); - bool emitForInOrOfVariables(ParseNode* pn, bool* letDecl); + bool emitForInOrOfVariables(ParseNode* pn); bool emitCStyleFor(ParseNode* pn); bool emitWhile(ParseNode* pn); diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index ab00cb5e9c2e..f226310c209a 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -241,7 +241,8 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) case PNK_CASE: return ContainsHoistedDeclaration(cx, node->as().statementList(), result); - case PNK_FOR: { + case PNK_FOR: + case PNK_COMPREHENSIONFOR: { MOZ_ASSERT(node->isArity(PN_BINARY)); ParseNode* loopHead = node->pn_left; @@ -1890,6 +1891,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo case PNK_SHORTHAND: case PNK_LETBLOCK: case PNK_FOR: + case PNK_COMPREHENSIONFOR: case PNK_CLASSMETHOD: case PNK_IMPORT_SPEC: case PNK_EXPORT_SPEC: diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 3e12658d384c..031f78ec7836 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -565,6 +565,19 @@ class FullParseHandler return pn; } + ParseNode* newComprehensionFor(uint32_t begin, ParseNode* forHead, ParseNode* body) { + // A PNK_COMPREHENSIONFOR node is binary: left is loop control, right + // is the body. + MOZ_ASSERT(forHead->isKind(PNK_FORIN) || forHead->isKind(PNK_FOROF)); + JSOp op = forHead->isKind(PNK_FORIN) ? JSOP_ITER : JSOP_NOP; + BinaryNode* pn = new_(PNK_COMPREHENSIONFOR, op, + TokenPos(begin, body->pn_pos.end), forHead, body); + if (!pn) + return null(); + pn->pn_iflags = JSOP_ITER; + return pn; + } + ParseNode* newForHead(ParseNodeKind kind, ParseNode* pn1, ParseNode* pn2, ParseNode* pn3, const TokenPos& pos) { diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index 88d297c57437..cbc6578b1eb5 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -448,6 +448,7 @@ class NameResolver case PNK_SWITCH: case PNK_LETBLOCK: case PNK_FOR: + case PNK_COMPREHENSIONFOR: case PNK_CLASSMETHOD: case PNK_SETTHIS: MOZ_ASSERT(cur->isArity(PN_BINARY)); @@ -687,13 +688,15 @@ class NameResolver } break; - // Array comprehension nodes are lists with a single child -- PNK_FOR for - // comprehensions, PNK_LEXICALSCOPE for legacy comprehensions. Probably - // this should be a non-list eventually. + // Array comprehension nodes are lists with a single child: + // PNK_COMPREHENSIONFOR for comprehensions, PNK_LEXICALSCOPE for + // legacy comprehensions. Probably this should be a non-list + // eventually. case PNK_ARRAYCOMP: MOZ_ASSERT(cur->isArity(PN_LIST)); MOZ_ASSERT(cur->pn_count == 1); - MOZ_ASSERT(cur->pn_head->isKind(PNK_LEXICALSCOPE) || cur->pn_head->isKind(PNK_FOR)); + MOZ_ASSERT(cur->pn_head->isKind(PNK_LEXICALSCOPE) || + cur->pn_head->isKind(PNK_COMPREHENSIONFOR)); if (!resolve(cur->pn_head, prefix)) return false; break; diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index ec0ea01a2b57..245831c077f1 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -282,7 +282,8 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_CLASSMETHOD: case PNK_NEWTARGET: case PNK_SETTHIS: - case PNK_FOR: { + case PNK_FOR: + case PNK_COMPREHENSIONFOR: { MOZ_ASSERT(pn->isArity(PN_BINARY)); stack->push(pn->pn_left); stack->push(pn->pn_right); @@ -493,15 +494,16 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_CLASSMETHODLIST: return PushListNodeChildren(pn, stack); - // Array comprehension nodes are lists with a single child -- PNK_FOR for - // comprehensions, PNK_LEXICALSCOPE for legacy comprehensions. Probably - // this should be a non-list eventually. + // Array comprehension nodes are lists with a single child: + // PNK_COMPREHENSIONFOR for comprehensions, PNK_LEXICALSCOPE for legacy + // comprehensions. Probably this should be a non-list eventually. case PNK_ARRAYCOMP: { #ifdef DEBUG MOZ_ASSERT(pn->isKind(PNK_ARRAYCOMP)); MOZ_ASSERT(pn->isArity(PN_LIST)); MOZ_ASSERT(pn->pn_count == 1); - MOZ_ASSERT(pn->pn_head->isKind(PNK_LEXICALSCOPE) || pn->pn_head->isKind(PNK_FOR)); + MOZ_ASSERT(pn->pn_head->isKind(PNK_LEXICALSCOPE) || + pn->pn_head->isKind(PNK_COMPREHENSIONFOR)); #endif return PushListNodeChildren(pn, stack); } diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index d98fbfd2ae70..d70a0689295c 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -126,6 +126,7 @@ class PackedScopeCoordinate F(WHILE) \ F(DOWHILE) \ F(FOR) \ + F(COMPREHENSIONFOR) \ F(BREAK) \ F(CONTINUE) \ F(VAR) \ @@ -312,6 +313,8 @@ IsDeleteKind(ParseNodeKind kind) * PNK_FOR binary pn_left: either PNK_FORIN (for-in statement), * PNK_FOROF (for-of) or PNK_FORHEAD (for(;;)) * pn_right: body + * PNK_COMPREHENSIONFOR pn_left: either PNK_FORIN or PNK_FOROF + * binary pn_right: body * PNK_FORIN ternary pn_kid1: PNK_VAR to left of 'in', or nullptr * pn_kid2: PNK_NAME or destructuring expr * to left of 'in'; if pn_kid1, then this @@ -619,7 +622,7 @@ class ParseNode ParseNode* left; ParseNode* right; union { - unsigned iflags; /* JSITER_* flags for PNK_FOR node */ + unsigned iflags; /* JSITER_* flags for PNK_{COMPREHENSION,}FOR node */ ObjectBox* objbox; /* only for PN_BINARY_OBJ */ bool isStatic; /* only for PNK_CLASSMETHOD */ uint32_t offset; /* for the emitter's use on PNK_CASE nodes */ @@ -836,7 +839,8 @@ class ParseNode ParseNode* callee = this->pn_head; ParseNode* body = callee->pn_body; MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST)); - MOZ_ASSERT(body->last()->isKind(PNK_LEXICALSCOPE) || body->last()->isKind(PNK_FOR)); + MOZ_ASSERT(body->last()->isKind(PNK_LEXICALSCOPE) || + body->last()->isKind(PNK_COMPREHENSIONFOR)); return body->last(); } #endif diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 34da9d563b84..093b379b5b11 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -7903,12 +7903,10 @@ Parser::legacyComprehensionTail(ParseNode* bodyExpr, unsigned JSMSG_ARRAY_INIT_TOO_BIG); while (true) { - /* - * FOR node is binary, left is loop control and right is body. Use - * index to count each block-local let-variable on the left-hand side - * of the in/of. - */ - ParseNode* pn2 = handler.new_(PNK_FOR, JSOP_ITER, pos(), + // PNK_COMPREHENSIONFOR is binary: left is loop control, right is body. + // Use index to count each block-local let-variable on the left-hand + // side of the in/of. + ParseNode* pn2 = handler.new_(PNK_COMPREHENSIONFOR, JSOP_ITER, pos(), nullptr, nullptr); if (!pn2) return null(); @@ -8357,7 +8355,7 @@ Parser::comprehensionFor(GeneratorKind comprehensionKind) if (!tail) return null(); - return handler.newForStatement(begin, head, tail, JSOP_ITER); + return handler.newComprehensionFor(begin, head, tail); } template diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index ecb29bd54dad..3785ffd181d1 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -339,6 +339,10 @@ class SyntaxParseHandler return NodeGeneric; } + Node newComprehensionFor(uint32_t begin, Node forHead, Node body) { + return NodeGeneric; + } + Node newForHead(ParseNodeKind kind, Node decls, Node lhs, Node rhs, const TokenPos& pos) { return NodeGeneric; } diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 7a0fe93a5abe..d7cf559b9874 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -537,7 +537,7 @@ struct Callback { }; template -class CallbackVector : public Vector, 4, SystemAllocPolicy> {}; +using CallbackVector = Vector, 4, SystemAllocPolicy>; template class ChainedIter diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 0a0882ed6d6f..2804fb3e22dc 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -432,6 +432,13 @@ js::TraceRoot(JSTracer* trc, T* thingp, const char* name) DispatchToTracer(trc, ConvertToBase(thingp), name); } +template +void +js::TraceRoot(JSTracer* trc, ReadBarriered* thingp, const char* name) +{ + TraceRoot(trc, thingp->unsafeGet(), name); +} + template void js::TraceNullableRoot(JSTracer* trc, T* thingp, const char* name) @@ -441,6 +448,13 @@ js::TraceNullableRoot(JSTracer* trc, T* thingp, const char* name) DispatchToTracer(trc, ConvertToBase(thingp), name); } +template +void +js::TraceNullableRoot(JSTracer* trc, ReadBarriered* thingp, const char* name) +{ + TraceNullableRoot(trc, thingp->unsafeGet(), name); +} + template void js::TraceRange(JSTracer* trc, size_t len, WriteBarrieredBase* vec, const char* name) @@ -473,7 +487,9 @@ js::TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name) template void js::TraceManuallyBarrieredEdge(JSTracer*, type*, const char*); \ template void js::TraceWeakEdge(JSTracer*, WeakRef*, const char*); \ template void js::TraceRoot(JSTracer*, type*, const char*); \ + template void js::TraceRoot(JSTracer*, ReadBarriered*, const char*); \ template void js::TraceNullableRoot(JSTracer*, type*, const char*); \ + template void js::TraceNullableRoot(JSTracer*, ReadBarriered*, const char*); \ template void js::TraceRange(JSTracer*, size_t, WriteBarrieredBase*, const char*); \ template void js::TraceRootRange(JSTracer*, size_t, type*, const char*); FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS) diff --git a/js/src/gc/Tracer.h b/js/src/gc/Tracer.h index 636133525d8c..33f0977dc84b 100644 --- a/js/src/gc/Tracer.h +++ b/js/src/gc/Tracer.h @@ -63,12 +63,20 @@ template void TraceRoot(JSTracer* trc, T* thingp, const char* name); +template +void +TraceRoot(JSTracer* trc, ReadBarriered* thingp, const char* name); + // Idential to TraceRoot, except that this variant will not crash if |*thingp| // is null. template void TraceNullableRoot(JSTracer* trc, T* thingp, const char* name); +template +void +TraceNullableRoot(JSTracer* trc, ReadBarriered* thingp, const char* name); + // Like TraceEdge, but for edges that do not use one of the automatic barrier // classes and, thus, must be treated specially for moving GC. This method is // separate from TraceEdge to make accidental use of such edges more obvious. diff --git a/js/src/jit-test/tests/TypedObject/bug976697.js b/js/src/jit-test/tests/TypedObject/bug976697.js deleted file mode 100644 index fa1a5de55172..000000000000 --- a/js/src/jit-test/tests/TypedObject/bug976697.js +++ /dev/null @@ -1,15 +0,0 @@ -// Test that instantiating a typed array on top of a neutered buffer -// doesn't trip any asserts. -// -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -x = new ArrayBuffer(); -neuter(x, "same-data"); -new Uint32Array(x); -gc(); - -x = new ArrayBuffer(); -neuter(x, "change-data"); -new Uint32Array(x); -gc(); diff --git a/js/src/jit-test/tests/baseline/arraySubclassPropertyLookup.js b/js/src/jit-test/tests/baseline/arraySubclassPropertyLookup.js new file mode 100644 index 000000000000..62b339f54f2d --- /dev/null +++ b/js/src/jit-test/tests/baseline/arraySubclassPropertyLookup.js @@ -0,0 +1,17 @@ +function f(v, expected) { + assertEq(v.prop, expected); +}; + +class SubArrayA extends Array { +} +class SubArrayB extends Array { +} +SubArrayA.prototype.prop = "A"; +SubArrayB.prototype.prop = "B"; + +var a = new SubArrayA(); +var b = new SubArrayB(); +for (let i = 0; i < 10; i++) { + f(a, "A"); + f(b, "B"); +} diff --git a/js/src/jit-test/tests/modules/namespace-import-compilation-2.js b/js/src/jit-test/tests/modules/namespace-import-compilation-2.js new file mode 100644 index 000000000000..ae322246dd33 --- /dev/null +++ b/js/src/jit-test/tests/modules/namespace-import-compilation-2.js @@ -0,0 +1,17 @@ +// |jit-test| module + +import * as ns from 'module1.js'; + +let other = ns; + +const iterations = 2000; + +let x = 0; +for (let i = 0; i < iterations; i++) { + if (i == iterations / 2) + other = 1; + x += other.a; +} + +assertEq(other.a, undefined); +assertEq(x, NaN); diff --git a/js/src/jit-test/tests/modules/namespace-import-compilation.js b/js/src/jit-test/tests/modules/namespace-import-compilation.js new file mode 100644 index 000000000000..42595b709572 --- /dev/null +++ b/js/src/jit-test/tests/modules/namespace-import-compilation.js @@ -0,0 +1,17 @@ +// |jit-test| module + +import * as ns from 'module1.js'; + +let other = ns; + +const iterations = 10000; + +let x = 0; +for (let i = 0; i < iterations; i++) { + if (i == iterations / 2) + other = { a: 0 }; + x += other.a; +} + +assertEq(other.a, 0); +assertEq(x, iterations / 2); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 108919ca602d..9dc368d07380 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -5679,7 +5679,7 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, if (native == StringConstructor) { RootedString emptyString(cx, cx->runtime()->emptyString); - res.set(StringObject::create(cx, emptyString, TenuredObject)); + res.set(StringObject::create(cx, emptyString, /* proto = */ nullptr, TenuredObject)); return !!res; } diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 4152cf6c37c5..2e329be96688 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -735,7 +735,7 @@ class MacroAssembler : public MacroAssemblerSpecific inline void sub32(Register src, Register dest) PER_SHARED_ARCH; inline void sub32(Imm32 imm, Register dest) PER_SHARED_ARCH; - inline void add64(Register64 src, Register64 dest) DEFINED_ON(x86, x64, arm, arm64, mips64); + inline void add64(Register64 src, Register64 dest) PER_ARCH; // =============================================================== // Shift functions diff --git a/js/src/jit/mips32/MacroAssembler-mips32-inl.h b/js/src/jit/mips32/MacroAssembler-mips32-inl.h index 7e79acdce4b5..d7fce94fbf20 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h +++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h @@ -75,6 +75,18 @@ MacroAssembler::xorPtr(Imm32 imm, Register dest) ma_xor(dest, imm); } +// =============================================================== +// Arithmetic functions + +void +MacroAssembler::add64(Register64 src, Register64 dest) +{ + as_addu(dest.low, dest.low, src.low); + as_sltu(ScratchRegister, dest.low, src.low); + as_addu(dest.high, dest.high, src.high); + as_addu(dest.high, dest.high, ScratchRegister); +} + // =============================================================== // Shift functions diff --git a/js/src/jit/mips32/MacroAssembler-mips32.cpp b/js/src/jit/mips32/MacroAssembler-mips32.cpp index dbeb91d7052b..462c1d0a873d 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32.cpp +++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp @@ -964,7 +964,7 @@ void MacroAssemblerMIPSCompat::load32(wasm::SymbolicAddress address, Register dest) { movePtr(address, ScratchRegister); - load32(Address(ScratchRegister, 0), address); + load32(Address(ScratchRegister, 0), dest); } void diff --git a/js/src/jsapi-tests/testTypedArrays.cpp b/js/src/jsapi-tests/testTypedArrays.cpp index f2aa96f56045..8a9599e48e03 100644 --- a/js/src/jsapi-tests/testTypedArrays.cpp +++ b/js/src/jsapi-tests/testTypedArrays.cpp @@ -39,23 +39,37 @@ BEGIN_TEST(testTypedArrays) bool isShared; CHECK_EQUAL(JS_GetArrayBufferByteLength(buffer), nbytes); memset(JS_GetArrayBufferData(buffer, &isShared, nogc), 1, nbytes); - MOZ_ASSERT(!isShared); // Because ArrayBuffer + CHECK(!isShared); // Because ArrayBuffer } ok = ok && - TestArrayFromBuffer(cx) && - TestArrayFromBuffer(cx) && - TestArrayFromBuffer(cx) && - TestArrayFromBuffer(cx) && - TestArrayFromBuffer(cx) && - TestArrayFromBuffer(cx) && - TestArrayFromBuffer(cx) && - TestArrayFromBuffer(cx) && - TestArrayFromBuffer(cx); + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx); + + ok = ok && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx) && + TestArrayFromBuffer(cx); return ok; } +// Shared memory can only be mapped by a TypedArray by creating the +// TypedArray with a SharedArrayBuffer explicitly, so no tests here. + template @@ -82,7 +96,7 @@ TestPlainTypedArray(JSContext* cx) Element* data; bool isShared; CHECK(data = GetData(array, &isShared, nogc)); - MOZ_ASSERT(!isShared); // Because ArrayBuffer + CHECK(!isShared); // Because ArrayBuffer *data = 13; } RootedValue v(cx); @@ -95,18 +109,22 @@ TestPlainTypedArray(JSContext* cx) template bool TestArrayFromBuffer(JSContext* cx) { size_t elts = 8; size_t nbytes = elts * sizeof(Element); - RootedObject buffer(cx, JS_NewArrayBuffer(cx, nbytes)); + RootedObject buffer(cx, Shared ? JS_NewSharedArrayBuffer(cx, nbytes) + : JS_NewArrayBuffer(cx, nbytes)); { JS::AutoCheckCannotGC nogc; bool isShared; - memset(JS_GetArrayBufferData(buffer, &isShared, nogc), 1, nbytes); - MOZ_ASSERT(!isShared); // Because ArrayBuffer + void* data = Shared ? JS_GetSharedArrayBufferData(buffer, &isShared, nogc) + : JS_GetArrayBufferData(buffer, &isShared, nogc); + CHECK_EQUAL(Shared, isShared); + memset(data, 1, nbytes); } { @@ -121,18 +139,22 @@ TestArrayFromBuffer(JSContext* cx) { bool isShared; CHECK_EQUAL(JS_GetArrayBufferViewBuffer(cx, array, &isShared), (JSObject*) buffer); + CHECK_EQUAL(Shared, isShared); } { JS::AutoCheckCannotGC nogc; Element* data; bool isShared; - CHECK(data = GetData(array, &isShared, nogc)); - CHECK_EQUAL((void*) data, (void*) JS_GetArrayBufferData(buffer, &isShared, nogc)); - MOZ_ASSERT(!isShared); // Because ArrayBuffer - CHECK_EQUAL(*reinterpret_cast(JS_GetArrayBufferData(buffer, &isShared, nogc)), 1u); - MOZ_ASSERT(!isShared); // Because ArrayBuffer + CHECK(data = GetData(array, &isShared, nogc)); + CHECK_EQUAL(Shared, isShared); + + CHECK_EQUAL((void*) data, + Shared ? (void*) JS_GetSharedArrayBufferData(buffer, &isShared, nogc) + : (void*) JS_GetArrayBufferData(buffer, &isShared, nogc)); + CHECK_EQUAL(Shared, isShared); + CHECK_EQUAL(*reinterpret_cast(data), 1u); } @@ -159,7 +181,7 @@ TestArrayFromBuffer(JSContext* cx) Element* data; bool isShared; CHECK(data = GetData(array, &isShared, nogc)); - MOZ_ASSERT(!isShared); // Because ArrayBuffer + CHECK_EQUAL(Shared, isShared); CHECK_EQUAL(long(v.toInt32()), long(reinterpret_cast(data)[0])); } @@ -174,7 +196,7 @@ TestArrayFromBuffer(JSContext* cx) Element* data; bool isShared; CHECK(data = GetData(array, &isShared, nogc)); - MOZ_ASSERT(!isShared); // Because ArrayBuffer + CHECK_EQUAL(Shared, isShared); CHECK_EQUAL(long(v.toInt32()), long(reinterpret_cast(data)[elts / 2])); } @@ -189,7 +211,7 @@ TestArrayFromBuffer(JSContext* cx) Element* data; bool isShared; CHECK(data = GetData(array, &isShared, nogc)); - MOZ_ASSERT(!isShared); // Because ArrayBuffer + CHECK_EQUAL(Shared, isShared); CHECK_EQUAL(long(v.toInt32()), long(reinterpret_cast(data)[elts - 1])); } diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index fa2b8233c70a..d7367ec9b4bd 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1653,7 +1653,7 @@ MatchNumericComparator(JSContext* cx, const Value& v) return Match_None; JSFunction* fun = &obj.as(); - if (!fun->isInterpreted()) + if (!fun->isInterpreted() || fun->isClassConstructor()) return Match_None; JSScript* script = fun->getOrCreateScript(cx); @@ -3060,9 +3060,9 @@ IsArrayConstructor(const Value& v) } static bool -ArrayFromCallArgs(JSContext* cx, CallArgs& args) +ArrayFromCallArgs(JSContext* cx, CallArgs& args, HandleObject proto = nullptr) { - JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length()); + JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto); if (!obj) return false; @@ -3183,8 +3183,12 @@ js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); + RootedObject proto(cx); + if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) + return false; + if (args.length() != 1 || !args[0].isNumber()) - return ArrayFromCallArgs(cx, args); + return ArrayFromCallArgs(cx, args, proto); uint32_t length; if (args[0].isInt32()) { @@ -3203,7 +3207,7 @@ js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp) } } - JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length); + JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto); if (!obj) return false; @@ -3310,12 +3314,6 @@ EnsureNewArrayElements(ExclusiveContext* cx, ArrayObject* obj, uint32_t length) return true; } -static bool -NewArrayIsCachable(ExclusiveContext* cxArg, NewObjectKind newKind) -{ - return cxArg->isJSContext() && newKind == GenericObject; -} - template static MOZ_ALWAYS_INLINE ArrayObject* NewArray(ExclusiveContext* cxArg, uint32_t length, @@ -3325,13 +3323,18 @@ NewArray(ExclusiveContext* cxArg, uint32_t length, MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_)); allocKind = GetBackgroundAllocKind(allocKind); - bool isCachable = NewArrayIsCachable(cxArg, newKind); + RootedObject proto(cxArg, protoArg); + if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto)) + return nullptr; + + Rooted taggedProto(cxArg, TaggedProto(proto)); + bool isCachable = NewObjectWithTaggedProtoIsCachable(cxArg, taggedProto, newKind, &ArrayObject::class_); if (isCachable) { JSContext* cx = cxArg->asJSContext(); JSRuntime* rt = cx->runtime(); NewObjectCache& cache = rt->newObjectCache; NewObjectCache::EntryIndex entry = -1; - if (cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry)) { + if (cache.lookupProto(&ArrayObject::class_, proto, allocKind, &entry)) { gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_); AutoSetNewObjectMetadata metadata(cx); JSObject* obj = cache.newObjectFromHit(cx, entry, heap); @@ -3350,10 +3353,6 @@ NewArray(ExclusiveContext* cxArg, uint32_t length, } } - RootedObject proto(cxArg, protoArg); - if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto)) - return nullptr; - RootedObjectGroup group(cxArg, ObjectGroup::defaultNewGroup(cxArg, &ArrayObject::class_, TaggedProto(proto))); if (!group) @@ -3389,8 +3388,8 @@ NewArray(ExclusiveContext* cxArg, uint32_t length, if (isCachable) { NewObjectCache& cache = cxArg->asJSContext()->runtime()->newObjectCache; NewObjectCache::EntryIndex entry = -1; - cache.lookupGlobal(&ArrayObject::class_, cxArg->global(), allocKind, &entry); - cache.fillGlobal(entry, &ArrayObject::class_, cxArg->global(), allocKind, arr); + cache.lookupProto(&ArrayObject::class_, proto, allocKind, &entry); + cache.fillProto(entry, &ArrayObject::class_, taggedProto, allocKind, arr); } if (maxLength > 0 && !EnsureNewArrayElements(cxArg, arr, std::min(maxLength, length))) @@ -3490,7 +3489,9 @@ js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc } // Return a new boxed or unboxed array with the specified length and allocated -// capacity (up to maxLength), using the specified group if possible. +// capacity (up to maxLength), using the specified group if possible. If the +// specified group cannot be used, ensure that the created array at least has +// the given [[Prototype]]. template static inline JSObject* NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length, @@ -3504,14 +3505,14 @@ NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length if (group->shouldPreTenure() || group->maybePreliminaryObjects()) newKind = TenuredObject; + RootedObject proto(cx, group->proto().toObject()); if (group->maybeUnboxedLayout()) { if (length > UnboxedArrayObject::MaximumCapacity) - return NewArray(cx, length, nullptr, newKind); - + return NewArray(cx, length, proto, newKind); return UnboxedArrayObject::create(cx, group, length, newKind, maxLength); } - ArrayObject* res = NewArray(cx, length, nullptr, newKind); + ArrayObject* res = NewArray(cx, length, proto, newKind); if (!res) return nullptr; @@ -3590,9 +3591,9 @@ js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, } JSObject* -js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length) +js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto) { - RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto)); if (!group) return nullptr; return NewArrayTryUseGroup(cx, group, length); @@ -3652,9 +3653,10 @@ js::NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, } JSObject* -js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length) +js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length, + HandleObject proto /* = nullptr */) { - RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto)); if (!group) return nullptr; return NewCopiedArrayTryUseGroup(cx, group, vp, length); diff --git a/js/src/jsarray.h b/js/src/jsarray.h index 957c32ffaaee..160be920b6a3 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -101,7 +101,7 @@ NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, bool forceAnalyze = false); extern JSObject* -NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length); +NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto); enum class ShouldUpdateTypes { @@ -116,7 +116,8 @@ NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update); extern JSObject* -NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length); +NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length, + HandleObject proto = nullptr); /* * Determines whether a write to the given element on |obj| should fail because diff --git a/js/src/jsbool.cpp b/js/src/jsbool.cpp index b5cd8ad404cc..c8109b02c288 100644 --- a/js/src/jsbool.cpp +++ b/js/src/jsbool.cpp @@ -116,7 +116,13 @@ Boolean(JSContext* cx, unsigned argc, Value* vp) bool b = args.length() != 0 ? JS::ToBoolean(args[0]) : false; if (args.isConstructing()) { - JSObject* obj = BooleanObject::create(cx, b); + RootedObject newTarget (cx, &args.newTarget().toObject()); + RootedObject proto(cx); + + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + JSObject* obj = BooleanObject::create(cx, b, proto); if (!obj) return false; args.rval().setObject(*obj); diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 963f211593df..6c7859b426d8 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -3016,7 +3016,14 @@ static const JSFunctionSpec date_methods[] = { static bool NewDateObject(JSContext* cx, const CallArgs& args, ClippedTime t) { - JSObject* obj = NewDateObjectMsec(cx, t); + MOZ_ASSERT(args.isConstructing()); + + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + JSObject* obj = NewDateObjectMsec(cx, t, proto); if (!obj) return false; @@ -3260,9 +3267,9 @@ const Class DateObject::protoClass_ = { }; JSObject* -js::NewDateObjectMsec(JSContext* cx, ClippedTime t) +js::NewDateObjectMsec(JSContext* cx, ClippedTime t, HandleObject proto /* = nullptr */) { - JSObject* obj = NewBuiltinClassInstance(cx, &DateObject::class_); + JSObject* obj = NewObjectWithClassProto(cx, &DateObject::class_, proto); if (!obj) return nullptr; obj->as().setUTCTime(t); diff --git a/js/src/jsdate.h b/js/src/jsdate.h index 29bde83bb237..7dc62b471136 100644 --- a/js/src/jsdate.h +++ b/js/src/jsdate.h @@ -30,7 +30,7 @@ namespace js { * since the epoch. */ extern JSObject* -NewDateObjectMsec(JSContext* cx, JS::ClippedTime t); +NewDateObjectMsec(JSContext* cx, JS::ClippedTime t, JS::HandleObject proto = nullptr); /* * Construct a new Date Object from an exploded local time value. diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 46e10e6e8b99..43509f9706d2 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -338,6 +338,11 @@ Error(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); + // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString + RootedObject proto(cx); + if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) + return false; + /* Compute the error message, if any. */ RootedString message(cx, nullptr); if (args.hasDefined(0)) { @@ -389,7 +394,7 @@ Error(JSContext* cx, unsigned argc, Value* vp) JSExnType exnType = JSExnType(args.callee().as().getExtendedSlot(0).toInt32()); RootedObject obj(cx, ErrorObject::create(cx, exnType, stack, fileName, - lineNumber, columnNumber, nullptr, message)); + lineNumber, columnNumber, nullptr, message, proto)); if (!obj) return false; diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index bc394c69cb7f..a808545eb566 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1145,24 +1145,28 @@ js::detail::IdMatchesAtom(jsid id, JSAtom* atom) return id == INTERNED_STRING_TO_JSID(nullptr, atom); } -JS_FRIEND_API(bool) -js::PrepareScriptEnvironmentAndInvoke(JSRuntime* rt, HandleObject scope, ScriptEnvironmentPreparer::Closure& closure) +JS_FRIEND_API(void) +js::PrepareScriptEnvironmentAndInvoke(JSContext* cx, HandleObject scope, ScriptEnvironmentPreparer::Closure& closure) { - if (rt->scriptEnvironmentPreparer) - return rt->scriptEnvironmentPreparer->invoke(scope, closure); + MOZ_ASSERT(!cx->isExceptionPending()); + + if (cx->runtime()->scriptEnvironmentPreparer) { + cx->runtime()->scriptEnvironmentPreparer->invoke(scope, closure); + return; + } - MOZ_ASSERT(rt->contextList.getFirst() == rt->contextList.getLast()); - JSContext* cx = rt->contextList.getFirst(); JSAutoCompartment ac(cx, scope); bool ok = closure(cx); + MOZ_ASSERT_IF(ok, !cx->isExceptionPending()); + // NB: This does not affect Gecko, which has a prepareScriptEnvironment // callback. - if (JS_IsExceptionPending(cx)) { + if (!ok) { JS_ReportPendingException(cx); } - return ok; + MOZ_ASSERT(!cx->isExceptionPending()); } JS_FRIEND_API(void) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 6dd69cc3a222..fe3f3dfd577d 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -2577,11 +2577,16 @@ IdToValue(jsid id) * PrepareScriptEnvironmentAndInvoke will call the preparer's 'invoke' method * with the given |closure|, with the assumption that the preparer will set up * any state necessary to run script in |scope|, invoke |closure| with a valid - * JSContext*, and return. + * JSContext*, report any exceptions thrown from the closure, and return. * * If no preparer is registered, PrepareScriptEnvironmentAndInvoke will assert * that |rt| has exactly one JSContext associated with it, enter the compartment * of |scope| on that context, and invoke |closure|. + * + * In both cases, PrepareScriptEnvironmentAndInvoke will report any exceptions + * that are thrown by the closure. Consumers who want to propagate back + * whether the closure succeeded should do so via members of the closure + * itself. */ struct ScriptEnvironmentPreparer { @@ -2589,11 +2594,11 @@ struct ScriptEnvironmentPreparer { virtual bool operator()(JSContext* cx) = 0; }; - virtual bool invoke(JS::HandleObject scope, Closure& closure) = 0; + virtual void invoke(JS::HandleObject scope, Closure& closure) = 0; }; -extern JS_FRIEND_API(bool) -PrepareScriptEnvironmentAndInvoke(JSRuntime* rt, JS::HandleObject scope, +extern JS_FRIEND_API(void) +PrepareScriptEnvironmentAndInvoke(JSContext* cx, JS::HandleObject scope, ScriptEnvironmentPreparer::Closure& closure); JS_FRIEND_API(void) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index b11ca00e16e6..a4476d7c1131 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1592,6 +1592,22 @@ fun_isGenerator(JSContext* cx, unsigned argc, Value* vp) return true; } +static JSFunction* +NewNativeFunctionWithGivenProto(JSContext* cx, Native native, unsigned nargs, + HandleAtom atom, HandleObject proto) +{ + return NewFunctionWithProto(cx, native, nargs, JSFunction::NATIVE_FUN, nullptr, atom, proto, + AllocKind::FUNCTION, GenericObject, NewFunctionGivenProto); +} + +static JSFunction* +NewNativeConstructorWithGivenProto(JSContext* cx, Native native, unsigned nargs, + HandleAtom atom, HandleObject proto) +{ + return NewFunctionWithProto(cx, native, nargs, JSFunction::NATIVE_CTOR, nullptr, atom, proto, + AllocKind::FUNCTION, GenericObject, NewFunctionGivenProto); +} + // ES6 draft rev32 19.2.3.2 bool js::fun_bind(JSContext* cx, unsigned argc, Value* vp) @@ -1618,6 +1634,11 @@ js::fun_bind(JSContext* cx, unsigned argc, Value* vp) RootedValue thisArg(cx, args.length() >= 1 ? args[0] : UndefinedValue()); RootedObject target(cx, &thisv.toObject()); + // This is part of step 4, but we're delaying allocating the function object. + RootedObject proto(cx); + if (!GetPrototype(cx, target, &proto)) + return false; + double length = 0.0; // Try to avoid invoking the resolve hook. if (target->is() && !target->as().hasResolvedLength()) { @@ -1673,8 +1694,8 @@ js::fun_bind(JSContext* cx, unsigned argc, Value* vp) // Step 4. RootedFunction fun(cx, target->isConstructor() ? - NewNativeConstructor(cx, CallOrConstructBoundFunction, length, nameAtom) : - NewNativeFunction(cx, CallOrConstructBoundFunction, length, nameAtom)); + NewNativeConstructorWithGivenProto(cx, CallOrConstructBoundFunction, length, nameAtom, proto) : + NewNativeFunctionWithGivenProto(cx, CallOrConstructBoundFunction, length, nameAtom, proto)); if (!fun) return false; @@ -1841,7 +1862,11 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, global); if (!proto) return false; + } else { + if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) + return false; } + RootedObject globalLexical(cx, &global->lexicalScope()); RootedFunction fun(cx, NewFunctionWithProto(cx, nullptr, 0, JSFunction::INTERPRETED_LAMBDA, globalLexical, @@ -2013,7 +2038,8 @@ js::NewFunctionWithProto(ExclusiveContext* cx, Native native, unsigned nargs, JSFunction::Flags flags, HandleObject enclosingDynamicScope, HandleAtom atom, HandleObject proto, gc::AllocKind allocKind /* = AllocKind::FUNCTION */, - NewObjectKind newKind /* = GenericObject */) + NewObjectKind newKind /* = GenericObject */, + NewFunctionProtoHandling protoHandling /* = NewFunctionClassProto */) { MOZ_ASSERT(allocKind == AllocKind::FUNCTION || allocKind == AllocKind::FUNCTION_EXTENDED); MOZ_ASSERT_IF(native, !enclosingDynamicScope); @@ -2025,8 +2051,14 @@ js::NewFunctionWithProto(ExclusiveContext* cx, Native native, // isSingleton implies isInterpreted. if (native && !IsAsmJSModuleNative(native)) newKind = SingletonObject; - funobj = NewObjectWithClassProto(cx, &JSFunction::class_, proto, allocKind, - newKind); + + if (protoHandling == NewFunctionClassProto) { + funobj = NewObjectWithClassProto(cx, &JSFunction::class_, proto, allocKind, + newKind); + } else { + funobj = NewObjectWithGivenTaggedProto(cx, &JSFunction::class_, AsTaggedProto(proto), + allocKind, newKind); + } if (!funobj) return nullptr; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 8c45a0378e45..cfe85649198e 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -636,15 +636,23 @@ NewScriptedFunction(ExclusiveContext* cx, unsigned nargs, JSFunction::Flags flag NewObjectKind newKind = GenericObject, HandleObject enclosingDynamicScope = nullptr); -// If proto is nullptr, Function.prototype is used instead. If +// By default, if proto is nullptr, Function.prototype is used instead.i +// If protoHandling is NewFunctionExactProto, and proto is nullptr, the created +// function will use nullptr as its [[Prototype]] instead. If // enclosingDynamicScope is null, the function will have a null environment() // (yes, null, not the global). In all cases, the global will be used as the // parent. + +enum NewFunctionProtoHandling { + NewFunctionClassProto, + NewFunctionGivenProto +}; extern JSFunction* NewFunctionWithProto(ExclusiveContext* cx, JSNative native, unsigned nargs, JSFunction::Flags flags, HandleObject enclosingDynamicScope, HandleAtom atom, HandleObject proto, gc::AllocKind allocKind = gc::AllocKind::FUNCTION, - NewObjectKind newKind = GenericObject); + NewObjectKind newKind = GenericObject, + NewFunctionProtoHandling protoHandling = NewFunctionClassProto); extern JSAtom* IdToFunctionName(JSContext* cx, HandleId id); diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 1b90adb581fc..68c70a028a53 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -488,7 +488,11 @@ Number(JSContext* cx, unsigned argc, Value* vp) if (!isConstructing) return true; - JSObject* obj = NumberObject::create(cx, args.rval().toNumber()); + RootedObject newTarget(cx, &args.newTarget().toObject()); + RootedObject proto(cx); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + JSObject* obj = NumberObject::create(cx, args.rval().toNumber(), proto); if (!obj) return false; args.rval().setObject(*obj); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 3f3c0fc73aff..ee75835d552c 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -689,9 +689,9 @@ NewObjectCache::fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto return fill(entry, clasp, proto.raw(), kind, obj); } -static bool -NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle proto, - NewObjectKind newKind, const Class* clasp) +bool +js::NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle proto, + NewObjectKind newKind, const Class* clasp) { return cxArg->isJSContext() && proto.isObject() && @@ -883,11 +883,9 @@ js::NewObjectScriptedCall(JSContext* cx, MutableHandleObject pobj) JSObject* js::CreateThis(JSContext* cx, const Class* newclasp, HandleObject callee) { - RootedValue protov(cx); - if (!GetProperty(cx, callee, callee, cx->names().prototype, &protov)) + RootedObject proto(cx); + if (!GetPrototypeFromConstructor(cx, callee, &proto)) return nullptr; - - RootedObject proto(cx, protov.isObjectOrNull() ? protov.toObjectOrNull() : nullptr); gc::AllocKind kind = NewObjectGCKind(newclasp); return NewObjectWithClassProto(cx, newclasp, proto, kind); } @@ -989,16 +987,35 @@ js::CreateThisForFunctionWithProto(JSContext* cx, HandleObject callee, HandleObj return res; } +bool +js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget, MutableHandleObject proto) +{ + RootedValue protov(cx); + if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov)) + return false; + proto.set(protov.isObject() ? &protov.toObject() : nullptr); + return true; +} + +bool +js::GetPrototypeFromCallableConstructor(JSContext* cx, const CallArgs& args, MutableHandleObject proto) +{ + RootedObject newTarget(cx); + if (args.isConstructing()) + newTarget = &args.newTarget().toObject(); + else + newTarget = &args.callee(); + return GetPrototypeFromConstructor(cx, newTarget, proto); +} + JSObject* js::CreateThisForFunction(JSContext* cx, HandleObject callee, HandleObject newTarget, NewObjectKind newKind) { - RootedValue protov(cx); - if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov)) - return nullptr; RootedObject proto(cx); - if (protov.isObject()) - proto = &protov.toObject(); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return nullptr; + JSObject* obj = CreateThisForFunctionWithProto(cx, callee, newTarget, proto, newKind); if (obj && newKind == SingletonObject) { diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 076a646f3a1c..0ea0ec5fff28 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1089,6 +1089,17 @@ GetInitialHeap(NewObjectKind newKind, const Class* clasp) return gc::DefaultHeap; } +bool +NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle proto, + NewObjectKind newKind, const Class* clasp); + +// ES6 9.1.15 GetPrototypeFromConstructor. +extern bool +GetPrototypeFromConstructor(JSContext* cx, js::HandleObject newTarget, js::MutableHandleObject proto); + +extern bool +GetPrototypeFromCallableConstructor(JSContext* cx, const CallArgs& args, js::MutableHandleObject proto); + // Specialized call for constructing |this| with a known function callee, // and a known prototype. extern JSObject* diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 9067a2bca019..0b352e6b3cc0 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -687,6 +687,24 @@ NewObjectWithClassProto(ExclusiveContext* cx, const Class* clasp, HandleObject p return NewObjectWithClassProto(cx, clasp, proto, allocKind, newKind); } +template +inline T* +NewObjectWithClassProto(ExclusiveContext* cx, HandleObject proto, + NewObjectKind newKind = GenericObject) +{ + JSObject* obj = NewObjectWithClassProto(cx, &T::class_, proto, newKind); + return obj ? &obj->as() : nullptr; +} + +template +inline T* +NewObjectWithClassProto(ExclusiveContext* cx, HandleObject proto, gc::AllocKind allocKind, + NewObjectKind newKind = GenericObject) +{ + JSObject* obj = NewObjectWithClassProto(cx, &T::class_, proto, allocKind, newKind); + return obj ? &obj->as() : nullptr; +} + /* * Create a native instance of the given class with parent and proto set * according to the context's active global. diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h index f173876aa972..9e34e78df4df 100644 --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -102,9 +102,6 @@ IF_BDATA(real,imaginary)(SIMD, 41, InitSIMDClass, OCLASP(SI real(TypedArray, 43, InitViaClassSpec, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \ IF_SAB(real,imaginary)(Atomics, 44, InitAtomicsClass, OCLASP(Atomics)) \ real(SavedFrame, 45, InitViaClassSpec, &js::SavedFrame::class_) \ - real(Module, 46, InitModuleClass, OCLASP(Module)) \ - real(ImportEntry, 47, InitImportEntryClass, OCLASP(ImportEntry)) \ - real(ExportEntry, 48, InitExportEntryClass, OCLASP(ExportEntry)) \ #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro) diff --git a/js/src/jsscript.h b/js/src/jsscript.h index fe32926b602f..d4c901f1f003 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -9,6 +9,7 @@ #ifndef jsscript_h #define jsscript_h +#include "mozilla/Atomics.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PodOperations.h" #include "mozilla/UniquePtr.h" @@ -2369,7 +2370,7 @@ struct SharedScriptData { uint32_t length; uint32_t natoms; - bool marked; + mozilla::Atomic marked; jsbytecode data[1]; static SharedScriptData* new_(ExclusiveContext* cx, uint32_t codeLength, diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 626b16fb0698..5d434c685690 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3485,7 +3485,7 @@ LambdaIsGetElem(JSContext* cx, JSObject& lambda, MutableHandleNativeObject pobj) return true; RootedFunction fun(cx, &lambda.as()); - if (!fun->isInterpreted()) + if (!fun->isInterpreted() || fun->isClassConstructor()) return true; JSScript* script = fun->getOrCreateScript(cx); @@ -4083,7 +4083,12 @@ js::StringConstructor(JSContext* cx, unsigned argc, Value* vp) } if (args.isConstructing()) { - StringObject* strobj = StringObject::create(cx, str); + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + StringObject* strobj = StringObject::create(cx, str, proto); if (!strobj) return false; args.rval().setObject(*strobj); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 73c5b37b8867..3d265b829836 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -6078,6 +6078,9 @@ NewGlobalObject(JSContext* cx, JS::CompartmentOptions& options, /* Initialize FakeDOMObject.prototype */ InitDOMObject(domProto); + + if (!js::InitModuleClasses(cx, glob)) + return nullptr; } JS_FireOnNewGlobalObject(cx, glob); diff --git a/js/src/tests/ecma_6/Class/boundFunctionSubclassing.js b/js/src/tests/ecma_6/Class/boundFunctionSubclassing.js new file mode 100644 index 000000000000..fa5544f8d3e7 --- /dev/null +++ b/js/src/tests/ecma_6/Class/boundFunctionSubclassing.js @@ -0,0 +1,24 @@ +var test = ` + +class func extends Function { } +let inst = new func("x", "return this.bar + x"); + +// First, ensure that we get sane prototype chains for the bound instance +let bound = inst.bind({bar: 3}, 4); +assertEq(bound instanceof func, true); +assertEq(bound(), 7); + +// Check the corner case for Function.prototype.bind where the function has +// a null [[Prototype]] +Object.setPrototypeOf(inst, null); +bound = Function.prototype.bind.call(inst, {bar:1}, 3); +assertEq(Object.getPrototypeOf(bound), null); +assertEq(bound(), 4); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Class/bytecodePatternMatching.js b/js/src/tests/ecma_6/Class/bytecodePatternMatching.js new file mode 100644 index 000000000000..0c16069911f8 --- /dev/null +++ b/js/src/tests/ecma_6/Class/bytecodePatternMatching.js @@ -0,0 +1,34 @@ +// Constructors can't be called so we can't pattern match +// them in replace and sort. +var test = ` +function a() { + var b = {a: "A"}; + + class X { + constructor(a) { + return b[a] + } + }; + + assertThrowsInstanceOf(() => "a".replace(/a/, X), TypeError); +} + +function b() { + class X { + constructor(x, y) { + return x - y; + } + } + + assertThrowsInstanceOf(() => [1, 2, 3].sort(X), TypeError); +} + +a(); +b(); +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "OK"); diff --git a/js/src/tests/ecma_6/Class/extendBuiltinConstructors.js b/js/src/tests/ecma_6/Class/extendBuiltinConstructors.js new file mode 100644 index 000000000000..4715dc412d67 --- /dev/null +++ b/js/src/tests/ecma_6/Class/extendBuiltinConstructors.js @@ -0,0 +1,112 @@ +var test = ` + +function testBuiltinInstanceIsInstanceOf(instance, builtin, class_) { + assertEq(instance instanceof class_, true); + assertEq(instance instanceof builtin, true); + + if (builtin === Array) + assertEq(Array.isArray(instance), true); +} + +function testBuiltinInstance(builtin, ...args) { + class sub extends builtin { + constructor(...args) { + super(...args); + this.called = true; + } + } + + let instance = new sub(...args); + assertEq(instance.called, true); + testBuiltinInstanceIsInstanceOf(instance, builtin, sub); +} + +function testBuiltinMultipleSubclasses(builtin, ...args) { + function f(obj, prop) { + assertEq(obj.prop, prop); + } + + class sub1 extends builtin { }; + class sub2 extends builtin { }; + + const prop1 = "A"; + const prop2 = "B"; + + sub1.prototype.prop = prop1; + sub2.prototype.prop = prop2; + + let instance1 = new sub1(...args); + let instance2 = new sub2(...args); + + // Also make sure we get the properties we want with a default constructor + testBuiltinInstanceIsInstanceOf(instance1, builtin, sub1); + + for (let i = 0; i < 10; i++) { + f(instance1, prop1); + f(instance2, prop2); + } +} + +function testBuiltin(builtin, ...args) { + testBuiltinInstance(builtin, ...args); + testBuiltinMultipleSubclasses(builtin, ...args); +} + +function testBuiltinTypedArrays() { + let typedArrays = [Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array]; + + for (let array of typedArrays) { + testBuiltin(array); + testBuiltin(array, 5); + testBuiltin(array, new array()); + testBuiltin(array, new ArrayBuffer()); + } +} + +testBuiltin(Function); +testBuiltin(Object); +testBuiltin(Boolean); +testBuiltin(Error); +testBuiltin(EvalError); +testBuiltin(RangeError); +testBuiltin(ReferenceError); +testBuiltin(SyntaxError); +testBuiltin(TypeError); +testBuiltin(URIError); +testBuiltin(Number); +testBuiltin(Date); +testBuiltin(Date, 5); +testBuiltin(Date, 5, 10); +testBuiltin(RegExp); +testBuiltin(RegExp, /Regexp Argument/); +testBuiltin(RegExp, "String Argument"); +testBuiltin(Map); +testBuiltin(Set); +testBuiltin(WeakMap); +testBuiltin(WeakSet); +testBuiltin(ArrayBuffer); +testBuiltinTypedArrays(); +testBuiltin(DataView, new ArrayBuffer()); +testBuiltin(DataView, new (newGlobal().ArrayBuffer)()); +testBuiltin(String); +testBuiltin(Array); +testBuiltin(Array, 15); +testBuiltin(Array, 3.0); +testBuiltin(Array, "non-length one-arg"); +testBuiltin(Array, 5, 10, 15, "these are elements"); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Class/subclassedArrayUnboxed.js b/js/src/tests/ecma_6/Class/subclassedArrayUnboxed.js new file mode 100644 index 000000000000..00e812e94595 --- /dev/null +++ b/js/src/tests/ecma_6/Class/subclassedArrayUnboxed.js @@ -0,0 +1,29 @@ +var test = ` + +class foo extends Array { } + +function testArrs(arrs) { + for (let arr of arrs) { + assertEq(Object.getPrototypeOf(arr), foo.prototype); + } +} + +var arrs = []; +for (var i = 0; i < 25; i++) + arrs.push(new foo(1)); + +testArrs(arrs); + +arrs[0].nonIndexedProp = "uhoh"; + +arrs.push(new foo(1)); + +testArrs(arrs); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Class/superCallBaseInvoked.js b/js/src/tests/ecma_6/Class/superCallBaseInvoked.js index 807b132000be..59ef23650dc3 100644 --- a/js/src/tests/ecma_6/Class/superCallBaseInvoked.js +++ b/js/src/tests/ecma_6/Class/superCallBaseInvoked.js @@ -53,8 +53,6 @@ testBase(p); handler.construct = (target, args, nt) => Reflect.construct(target, args, nt); testBase(p); -// Object will have to wait for fixed builtins. - `; if (classesEnabled()) diff --git a/js/src/tests/ecma_6/DataView/detach-after-construction.js b/js/src/tests/ecma_6/DataView/detach-after-construction.js new file mode 100644 index 000000000000..6185b2a1d02b --- /dev/null +++ b/js/src/tests/ecma_6/DataView/detach-after-construction.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!xulRuntime.shell) + +for (var neuterArg of ['change-data', 'same-data']) { + var buf = new ArrayBuffer([1,2]); + var bufView = new DataView(buf); + + neuter(buf, neuterArg); + + assertThrowsInstanceOf(()=>bufView.getInt8(0), TypeError); +} + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/Map/iterable.js b/js/src/tests/ecma_6/Map/iterable.js index d064dd8143bc..cf6b7228abd3 100644 --- a/js/src/tests/ecma_6/Map/iterable.js +++ b/js/src/tests/ecma_6/Map/iterable.js @@ -7,9 +7,22 @@ let iterable = { next() { length = arguments.length; return {done: true}; } }; -let m = new Map(iterable); +new Map(iterable); // ensure no arguments are passed to next() during construction (Bug 1197095) assertEq(length, 0); +let typeofThis; +Object.defineProperty(Number.prototype, Symbol.iterator, { + value() { + "use strict"; + typeofThis = typeof this; + return { next() { return {done: true}; } }; + } +}); + +new Map(0); +// ensure that iterable objects retain their type (Bug 1197094) +assertEq(typeofThis, "number"); + if (typeof reportCompare === "function") reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Reflect/construct.js b/js/src/tests/ecma_6/Reflect/construct.js index 00473d591f67..32a8b712301e 100644 --- a/js/src/tests/ecma_6/Reflect/construct.js +++ b/js/src/tests/ecma_6/Reflect/construct.js @@ -101,9 +101,7 @@ for (var v of SOME_PRIMITIVE_VALUES.concat(nonConstructors)) { // creates a real array object. function someConstructor() {} var result = Reflect.construct(Array, [], someConstructor); -assertEq(Reflect.getPrototypeOf(result), - Array.prototype, // should be someConstructor.prototype, per ES6 22.1.1.1 Array() - "Congratulations on implementing Array subclassing! Fix this test for +1 karma point."); +assertEq(Reflect.getPrototypeOf(result), someConstructor.prototype); assertEq(result.length, 0); assertEq(Array.isArray(result), true); diff --git a/js/src/tests/ecma_6/RegExp/constructor-ordering-2.js b/js/src/tests/ecma_6/RegExp/constructor-ordering-2.js new file mode 100644 index 000000000000..21a6bbeca745 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/constructor-ordering-2.js @@ -0,0 +1,21 @@ +// Make sure that we don't ToString the second argument until /after/ doing +// the appropriate subclassing lookups + +var didLookup = false; + +var re = /a/; +var flags = { toString() { assertEq(didLookup, true); return "g"; } }; +var newRe = Reflect.construct(RegExp, [re, flags], + Object.defineProperty(function(){}.bind(null), "prototype", { + get() { + didLookup = true; + return RegExp.prototype; + } +})); + +assertEq(Object.getPrototypeOf(newRe), RegExp.prototype); +assertEq(didLookup, true); + + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/RegExp/constructor-ordering.js b/js/src/tests/ecma_6/RegExp/constructor-ordering.js new file mode 100644 index 000000000000..3e3a9b695b31 --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/constructor-ordering.js @@ -0,0 +1,16 @@ +// Make sure that we don't misorder subclassing accesses with respect to +// accessing regex arg internal slots +// +// Test credit André Bargull. + +var re = /a/; +var newRe = Reflect.construct(RegExp, [re], Object.defineProperty(function(){}.bind(null), "prototype", { + get() { + re.compile("b"); + return RegExp.prototype; + } +})); +assertEq(newRe.source, "a"); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/TypedArray/constructor-non-detached.js b/js/src/tests/ecma_6/TypedArray/constructor-non-detached.js new file mode 100644 index 000000000000..c3054256de8c --- /dev/null +++ b/js/src/tests/ecma_6/TypedArray/constructor-non-detached.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!xulRuntime.shell) + +const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array +]; + +for (var constructor of constructors) { + for (var neuterType of ["change-data", "same-data"]) { + var buf = new constructor(); + neuter(buf.buffer, neuterType); + assertThrowsInstanceOf(()=> new constructor(buf), TypeError); + + var buffer = new ArrayBuffer(); + neuter(buffer, neuterType); + assertThrowsInstanceOf(()=> new constructor(buffer), TypeError); + } +} + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/js1_5/Error/constructor-ordering.js b/js/src/tests/js1_5/Error/constructor-ordering.js new file mode 100644 index 000000000000..dbcc2e6048ac --- /dev/null +++ b/js/src/tests/js1_5/Error/constructor-ordering.js @@ -0,0 +1,17 @@ +var order = 0; +function assertOrdering(ordering) { + assertEq(order, ordering); + order++; +} + +// Spec mandates that the prototype is looked up /before/ we toString the +// argument. +var handler = { get() { assertOrdering(0); return Error.prototype } }; +var errorProxy = new Proxy(Error, handler); + +var toStringable = { toString() { assertOrdering(1); return "Argument"; } }; + +new errorProxy(toStringable); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 22c1306341c7..5cb626cdb2f1 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -476,7 +476,12 @@ ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp) return false; } - JSObject* bufobj = create(cx, uint32_t(nbytes)); + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + JSObject* bufobj = create(cx, uint32_t(nbytes), proto); if (!bufobj) return false; args.rval().setObject(*bufobj); @@ -793,6 +798,7 @@ ArrayBufferObject::setFlags(uint32_t flags) ArrayBufferObject* ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents contents, OwnsState ownsState /* = OwnsData */, + HandleObject proto /* = nullptr */, NewObjectKind newKind /* = GenericObject */) { MOZ_ASSERT_IF(contents.kind() == MAPPED, contents); @@ -833,7 +839,8 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content gc::AllocKind allocKind = GetGCObjectKind(nslots); AutoSetNewObjectMetadata metadata(cx); - Rooted obj(cx, NewBuiltinClassInstance(cx, allocKind, newKind)); + Rooted obj(cx, + NewObjectWithClassProto(cx, proto, allocKind, newKind)); if (!obj) { if (allocated) js_free(contents.data()); @@ -856,9 +863,11 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content ArrayBufferObject* ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, + HandleObject proto /* = nullptr */, NewObjectKind newKind /* = GenericObject */) { - return create(cx, nbytes, BufferContents::createPlain(nullptr)); + return create(cx, nbytes, BufferContents::createPlain(nullptr), + OwnsState::OwnsData, proto); } JSObject* @@ -890,21 +899,25 @@ ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args /* * This method is only called for |DataView(alienBuf, ...)| which calls - * this as |createDataViewForThis.call(alienBuf, ..., DataView.prototype)|, - * ergo there must be at least two arguments. + * this as |createDataViewForThis.call(alienBuf, byteOffset, byteLength, + * DataView.prototype)|, + * ergo there must be exactly 3 arguments. */ - MOZ_ASSERT(args.length() >= 2); + MOZ_ASSERT(args.length() == 3); - Rooted proto(cx, &args[args.length() - 1].toObject()); - - Rooted buffer(cx, &args.thisv().toObject()); + uint32_t byteOffset = args[0].toPrivateUint32(); + uint32_t byteLength = args[1].toPrivateUint32(); + Rooted buffer(cx, &args.thisv().toObject().as()); /* * Pop off the passed-along prototype and delegate to normal DataViewObject * construction. */ - CallArgs frobbedArgs = CallArgsFromVp(args.length() - 1, args.base()); - return DataViewObject::construct(cx, buffer, frobbedArgs, proto); + JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, &args[2].toObject()); + if (!obj) + return false; + args.rval().setObject(*obj); + return true; } bool @@ -1404,7 +1417,8 @@ JS_NewArrayBufferWithContents(JSContext* cx, size_t nbytes, void* data) MOZ_ASSERT_IF(!data, nbytes == 0); ArrayBufferObject::BufferContents contents = ArrayBufferObject::BufferContents::create(data); - return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData, TenuredObject); + return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData, + /* proto = */ nullptr, TenuredObject); } JS_FRIEND_API(bool) @@ -1469,7 +1483,8 @@ JS_NewMappedArrayBufferWithContents(JSContext* cx, size_t nbytes, void* data) MOZ_ASSERT(data); ArrayBufferObject::BufferContents contents = ArrayBufferObject::BufferContents::create(data); - return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData, TenuredObject); + return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData, + /* proto = */ nullptr, TenuredObject); } JS_PUBLIC_API(void*) diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index 546845c92a38..e7fc142ecd8b 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -213,8 +213,10 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes, BufferContents contents, OwnsState ownsState = OwnsData, + HandleObject proto = nullptr, NewObjectKind newKind = GenericObject); static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes, + HandleObject proto = nullptr, NewObjectKind newKind = GenericObject); static JSObject* createSlice(JSContext* cx, Handle arrayBuffer, diff --git a/js/src/vm/BooleanObject-inl.h b/js/src/vm/BooleanObject-inl.h index c8be7646e430..c5e0f75381fa 100644 --- a/js/src/vm/BooleanObject-inl.h +++ b/js/src/vm/BooleanObject-inl.h @@ -14,14 +14,13 @@ namespace js { inline BooleanObject* -BooleanObject::create(JSContext* cx, bool b) +BooleanObject::create(JSContext* cx, bool b, HandleObject proto /* = nullptr */) { - JSObject* obj = NewBuiltinClassInstance(cx, &class_); + BooleanObject* obj = NewObjectWithClassProto(cx, proto); if (!obj) return nullptr; - BooleanObject& boolobj = obj->as(); - boolobj.setPrimitiveValue(b); - return &boolobj; + obj->setPrimitiveValue(b); + return obj; } } // namespace js diff --git a/js/src/vm/BooleanObject.h b/js/src/vm/BooleanObject.h index 5472c50303d3..c3c74fb19837 100644 --- a/js/src/vm/BooleanObject.h +++ b/js/src/vm/BooleanObject.h @@ -24,10 +24,11 @@ class BooleanObject : public NativeObject static const Class class_; /* - * Creates a new Boolean object boxing the given primitive bool. The - * object's [[Prototype]] is determined from context. + * Creates a new Boolean object boxing the given primitive bool. + * If proto is nullptr, the [[Prototype]] will default to Boolean.prototype. */ - static inline BooleanObject* create(JSContext* cx, bool b); + static inline BooleanObject* create(JSContext* cx, bool b, + HandleObject proto = nullptr); bool unbox() const { return getFixedSlot(PRIMITIVE_VALUE_SLOT).toBoolean(); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 39e177bf289d..75c4e642b295 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -975,6 +975,26 @@ Debugger::unwrapPropertyDescriptor(JSContext* cx, HandleObject obj, return true; } +namespace { +class MOZ_STACK_CLASS ReportExceptionClosure : public ScriptEnvironmentPreparer::Closure +{ +public: + explicit ReportExceptionClosure(RootedValue& exn) + : exn_(exn) + { + } + + bool operator()(JSContext* cx) override + { + cx->setPendingException(exn_); + return false; + } + +private: + RootedValue& exn_; +}; +} // anonymous namespace + JSTrapStatus Debugger::handleUncaughtExceptionHelper(Maybe& ac, MutableHandleValue* vp, bool callHook) @@ -993,7 +1013,31 @@ Debugger::handleUncaughtExceptionHelper(Maybe& ac, } if (cx->isExceptionPending()) { - JS_ReportPendingException(cx); + /* + * We want to report the pending exception, but we want to let the + * embedding handle it however it wants to. So pretend like we're + * starting a new script execution on our current compartment (which + * is the debugger compartment, so reported errors won't get + * reported to various onerror handlers in debuggees) and as part of + * that "execution" simply throw our exception so the embedding can + * deal. + */ + RootedValue exn(cx); + if (cx->getPendingException(&exn)) { + /* + * Clear the exception, because + * PrepareScriptEnvironmentAndInvoke will assert that we don't + * have one. + */ + cx->clearPendingException(); + ReportExceptionClosure reportExn(exn); + PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn); + } + /* + * And if not, or if PrepareScriptEnvironmentAndInvoke somehow left + * an exception on cx (which it totally shouldn't do), just give + * up. + */ cx->clearPendingException(); } } diff --git a/js/src/vm/ErrorObject.cpp b/js/src/vm/ErrorObject.cpp index 0b141f2689d6..cdf07f3a04a4 100644 --- a/js/src/vm/ErrorObject.cpp +++ b/js/src/vm/ErrorObject.cpp @@ -84,13 +84,17 @@ js::ErrorObject::init(JSContext* cx, Handle obj, JSExnType type, /* static */ ErrorObject* js::ErrorObject::create(JSContext* cx, JSExnType errorType, HandleObject stack, HandleString fileName, uint32_t lineNumber, uint32_t columnNumber, - ScopedJSFreePtr* report, HandleString message) + ScopedJSFreePtr* report, HandleString message, + HandleObject protoArg /* = nullptr */) { AssertObjectIsSavedFrameOrWrapper(cx, stack); - Rooted proto(cx, GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(), errorType)); - if (!proto) - return nullptr; + RootedObject proto(cx, protoArg); + if (!proto) { + proto = GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(), errorType); + if (!proto) + return nullptr; + } Rooted errObject(cx); { diff --git a/js/src/vm/ErrorObject.h b/js/src/vm/ErrorObject.h index c4edd4038983..39e2c37f5dd6 100644 --- a/js/src/vm/ErrorObject.h +++ b/js/src/vm/ErrorObject.h @@ -72,7 +72,7 @@ class ErrorObject : public NativeObject static ErrorObject* create(JSContext* cx, JSExnType type, HandleObject stack, HandleString fileName, uint32_t lineNumber, uint32_t columnNumber, ScopedJSFreePtr* report, - HandleString message); + HandleString message, HandleObject proto = nullptr); /* * Assign the initial error shape to the empty object. (This shape does diff --git a/js/src/vm/ForOfIterator.cpp b/js/src/vm/ForOfIterator.cpp index ebe28dd20375..590327306aee 100644 --- a/js/src/vm/ForOfIterator.cpp +++ b/js/src/vm/ForOfIterator.cpp @@ -53,7 +53,7 @@ ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavio InvokeArgs args(cx); if (!args.init(0)) return false; - args.setThis(ObjectValue(*iterableObj)); + args.setThis(iterable); RootedValue callee(cx); RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)); diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 8b64c4ee662d..de2768d94f99 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -107,6 +107,9 @@ class GlobalObject : public NativeObject COLLATOR_PROTO, NUMBER_FORMAT_PROTO, DATE_TIME_FORMAT_PROTO, + MODULE_PROTO, + IMPORT_ENTRY_PROTO, + EXPORT_ENTRY_PROTO, REGEXP_STATICS, WARNED_ONCE_FLAGS, RUNTIME_CODEGEN_ENABLED, @@ -466,6 +469,18 @@ class GlobalObject : public NativeObject return getOrCreateObject(cx, DATE_TIME_FORMAT_PROTO, initDateTimeFormatProto); } + JSObject* getModulePrototype() { + return &getSlot(MODULE_PROTO).toObject(); + } + + JSObject* getImportEntryPrototype() { + return &getSlot(IMPORT_ENTRY_PROTO).toObject(); + } + + JSObject* getExportEntryPrototype() { + return &getSlot(EXPORT_ENTRY_PROTO).toObject(); + } + static JSFunction* getOrCreateTypedArrayConstructor(JSContext* cx, Handle global) { if (!ensureConstructor(cx, global, JSProto_TypedArray)) @@ -680,6 +695,11 @@ class GlobalObject : public NativeObject static bool initNumberFormatProto(JSContext* cx, Handle global); static bool initDateTimeFormatProto(JSContext* cx, Handle global); + // Implemented in builtin/ModuleObject.cpp + static bool initModuleProto(JSContext* cx, Handle global); + static bool initImportEntryProto(JSContext* cx, Handle global); + static bool initExportEntryProto(JSContext* cx, Handle global); + // Implemented in builtin/TypedObject.cpp static bool initTypedObjectModule(JSContext* cx, Handle global); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index c5509976a808..7676e5b0f775 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -4770,20 +4770,7 @@ js::DefaultClassConstructor(JSContext* cx, unsigned argc, Value* vp) } RootedObject newTarget(cx, &args.newTarget().toObject()); - RootedValue protoVal(cx); - - if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protoVal)) - return false; - - RootedObject proto(cx); - if (!protoVal.isObject()) { - if (!GetBuiltinPrototype(cx, JSProto_Object, &proto)) - return false; - } else { - proto = &protoVal.toObject(); - } - - JSObject* obj = NewObjectWithGivenProto(cx, &PlainObject::class_, proto); + JSObject* obj = CreateThis(cx, &PlainObject::class_, newTarget); if (!obj) return false; diff --git a/js/src/vm/NumberObject-inl.h b/js/src/vm/NumberObject-inl.h index 5d4ff0214327..7e0237b1c864 100644 --- a/js/src/vm/NumberObject-inl.h +++ b/js/src/vm/NumberObject-inl.h @@ -14,14 +14,13 @@ namespace js { inline NumberObject* -NumberObject::create(JSContext* cx, double d) +NumberObject::create(JSContext* cx, double d, HandleObject proto /* = nullptr */) { - JSObject* obj = NewBuiltinClassInstance(cx, &class_); + NumberObject* obj = NewObjectWithClassProto(cx, proto); if (!obj) return nullptr; - NumberObject& numobj = obj->as(); - numobj.setPrimitiveValue(d); - return &numobj; + obj->setPrimitiveValue(d); + return obj; } } // namespace js diff --git a/js/src/vm/NumberObject.h b/js/src/vm/NumberObject.h index c3523b8465ab..dd808309c782 100644 --- a/js/src/vm/NumberObject.h +++ b/js/src/vm/NumberObject.h @@ -22,10 +22,11 @@ class NumberObject : public NativeObject static const Class class_; /* - * Creates a new Number object boxing the given number. The object's - * [[Prototype]] is determined from context. + * Creates a new Number object boxing the given number. + * If proto is nullptr, then Number.prototype will be used instead. */ - static inline NumberObject* create(JSContext* cx, double d); + static inline NumberObject* create(JSContext* cx, double d, + HandleObject proto = nullptr); double unbox() const { return getFixedSlot(PRIMITIVE_VALUE_SLOT).toNumber(); diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index adf4932513a6..ba50057352ba 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -1345,40 +1345,69 @@ ObjectGroup::newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_ // ObjectGroupCompartment AllocationSiteTable ///////////////////////////////////////////////////////////////////// -struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher { - JSScript* script; +struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher, + public JS::Traceable { + ReadBarrieredScript script; uint32_t offset : 24; JSProtoKey kind : 8; + ReadBarrieredObject proto; + static const uint32_t OFFSET_LIMIT = (1 << 23); - AllocationSiteKey() { mozilla::PodZero(this); } + AllocationSiteKey(JSScript* script_, uint32_t offset_, JSProtoKey kind_, JSObject* proto_) + : script(script_), offset(offset_), kind(kind_), proto(proto_) + { + MOZ_ASSERT(offset_ < OFFSET_LIMIT); + } + + AllocationSiteKey(AllocationSiteKey&& key) + : script(mozilla::Move(key.script)), + offset(key.offset), + kind(key.kind), + proto(mozilla::Move(key.proto)) + { } + + AllocationSiteKey(const AllocationSiteKey& key) + : script(key.script), + offset(key.offset), + kind(key.kind), + proto(key.proto) + { } static inline uint32_t hash(AllocationSiteKey key) { - return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind); + return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind ^ + MovableCellHasher::hash(key.proto)); } static inline bool match(const AllocationSiteKey& a, const AllocationSiteKey& b) { - return a.script == b.script && a.offset == b.offset && a.kind == b.kind; + return DefaultHasher::match(a.script, b.script) && + a.offset == b.offset && + a.kind == b.kind && + MovableCellHasher::match(a.proto, b.proto); + } + + static void trace(AllocationSiteKey* key, JSTracer* trc) { + TraceRoot(trc, &key->script, "AllocationSiteKey script"); + TraceNullableRoot(trc, &key->proto, "AllocationSiteKey proto"); } }; /* static */ ObjectGroup* -ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc, - JSProtoKey kind) +ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode* pc, + JSProtoKey kind, HandleObject protoArg /* = nullptr */) { - MOZ_ASSERT(!useSingletonForAllocationSite(script, pc, kind)); + MOZ_ASSERT(!useSingletonForAllocationSite(scriptArg, pc, kind)); + MOZ_ASSERT_IF(protoArg, kind == JSProto_Array); - uint32_t offset = script->pcToOffset(pc); + uint32_t offset = scriptArg->pcToOffset(pc); - if (offset >= ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT) + if (offset >= ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT) { + if (protoArg) + return defaultNewGroup(cx, GetClassForProtoKey(kind), TaggedProto(protoArg)); return defaultNewGroup(cx, kind); - - ObjectGroupCompartment::AllocationSiteKey key; - key.script = script; - key.offset = offset; - key.kind = kind; + } ObjectGroupCompartment::AllocationSiteTable*& table = cx->compartment()->objectGroups.allocationSiteTable; @@ -1393,16 +1422,20 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc } } + RootedScript script(cx, scriptArg); + RootedObject proto(cx, protoArg); + if (!proto && kind != JSProto_Null && !GetBuiltinPrototype(cx, kind, &proto)) + return nullptr; + + Rooted key(cx, + ObjectGroupCompartment::AllocationSiteKey(script, offset, kind, proto)); + ObjectGroupCompartment::AllocationSiteTable::AddPtr p = table->lookupForAdd(key); if (p) return p->value(); AutoEnterAnalysis enter(cx); - RootedObject proto(cx); - if (kind != JSProto_Null && !GetBuiltinPrototype(cx, kind, &proto)) - return nullptr; - Rooted tagged(cx, TaggedProto(proto)); ObjectGroup* res = ObjectGroupCompartment::makeGroup(cx, GetClassForProtoKey(kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE); @@ -1447,10 +1480,7 @@ void ObjectGroupCompartment::replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc, JSProtoKey kind, ObjectGroup* group) { - AllocationSiteKey key; - key.script = script; - key.offset = script->pcToOffset(pc); - key.kind = kind; + AllocationSiteKey key(script, script->pcToOffset(pc), kind, group->proto().toObjectOrNull()); AllocationSiteTable::Ptr p = allocationSiteTable->lookup(key); MOZ_RELEASE_ASSERT(p); @@ -1463,12 +1493,16 @@ ObjectGroupCompartment::replaceAllocationSiteGroup(JSScript* script, jsbytecode* } /* static */ ObjectGroup* -ObjectGroup::callingAllocationSiteGroup(JSContext* cx, JSProtoKey key) +ObjectGroup::callingAllocationSiteGroup(JSContext* cx, JSProtoKey key, HandleObject proto) { + MOZ_ASSERT_IF(proto, key == JSProto_Array); + jsbytecode* pc; RootedScript script(cx, cx->currentScript(&pc)); if (script) - return allocationSiteGroup(cx, script, pc, key); + return allocationSiteGroup(cx, script, pc, key, proto); + if (proto) + return defaultNewGroup(cx, GetClassForProtoKey(key), TaggedProto(proto)); return defaultNewGroup(cx, key); } @@ -1753,13 +1787,11 @@ ObjectGroupCompartment::sweep(FreeOp* fop) if (allocationSiteTable) { for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) { - AllocationSiteKey key = e.front().key(); - bool keyDying = IsAboutToBeFinalizedUnbarriered(&key.script); + bool keyDying = IsAboutToBeFinalized(&e.front().mutableKey().script) || + (e.front().key().proto && IsAboutToBeFinalized(&e.front().mutableKey().proto)); bool valDying = IsAboutToBeFinalized(&e.front().value()); if (keyDying || valDying) e.removeFront(); - else if (key.script != e.front().key().script) - e.rekeyFront(key); } } diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index a83fe0071ed0..a1b09dc356d8 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -510,10 +510,11 @@ class ObjectGroup : public gc::TenuredCell // Get a non-singleton group to use for objects created at the specified // allocation site. static ObjectGroup* allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc, - JSProtoKey key); + JSProtoKey key, HandleObject proto = nullptr); // Get a non-singleton group to use for objects created in a JSNative call. - static ObjectGroup* callingAllocationSiteGroup(JSContext* cx, JSProtoKey key); + static ObjectGroup* callingAllocationSiteGroup(JSContext* cx, JSProtoKey key, + HandleObject proto = nullptr); // Set the group or singleton-ness of an object created for an allocation site. static bool diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 88b8b3143dae..89b3bf6ac345 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -41,25 +41,17 @@ JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE); JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY); RegExpObject* -js::RegExpAlloc(ExclusiveContext* cx) +js::RegExpAlloc(ExclusiveContext* cx, HandleObject proto /* = nullptr */) { // Note: RegExp objects are always allocated in the tenured heap. This is // not strictly required, but simplifies embedding them in jitcode. - RegExpObject* regexp = NewBuiltinClassInstance(cx, TenuredObject); + RegExpObject* regexp = NewObjectWithClassProto(cx, proto, TenuredObject); if (!regexp) return nullptr; - regexp->initPrivate(nullptr); return regexp; } -RegExpObject* -js::InitializeRegExp(ExclusiveContext* cx, Handle regexp, HandleAtom source, - RegExpFlag flags) -{ - return regexp->init(cx, source, flags) ? regexp : nullptr; -} - /* MatchPairs */ bool @@ -155,6 +147,13 @@ RegExpObject::trace(JSTracer* trc, JSObject* obj) } } +/* static */ bool +RegExpObject::initFromAtom(ExclusiveContext* cx, Handle regexp, HandleAtom source, + RegExpFlag flags) +{ + return regexp->init(cx, source, flags); +} + const Class RegExpObject::class_ = { js_RegExp_str, JSCLASS_HAS_PRIVATE | @@ -224,7 +223,10 @@ RegExpObject::createNoStatics(ExclusiveContext* cx, HandleAtom source, RegExpFla if (!regexp) return nullptr; - return InitializeRegExp(cx, regexp, source, flags); + if (!RegExpObject::initFromAtom(cx, regexp, source, flags)) + return nullptr; + + return regexp; } bool @@ -894,7 +896,10 @@ js::CloneRegExpObject(JSContext* cx, JSObject* obj_) if (!clone) return nullptr; - return InitializeRegExp(cx, clone, source, RegExpFlag(origFlags | staticsFlags)); + if (!RegExpObject::initFromAtom(cx, clone, source, RegExpFlag(origFlags | staticsFlags))) + return nullptr; + + return clone; } // Otherwise, the clone can use |regexp|'s RegExpShared. @@ -911,7 +916,7 @@ js::CloneRegExpObject(JSContext* cx, JSObject* obj_) if (!regex->getShared(cx, &g)) return nullptr; - if (!InitializeRegExp(cx, clone, source, g->getFlags())) + if (!RegExpObject::initFromAtom(cx, clone, source, g->getFlags())) return nullptr; clone->setShared(*g.re()); diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index 02972e314315..ad118914d9d0 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -64,11 +64,7 @@ enum RegExpRunStatus }; extern RegExpObject* -RegExpAlloc(ExclusiveContext* cx); - -extern RegExpObject* -InitializeRegExp(ExclusiveContext* cx, Handle regexp, HandleAtom source, - RegExpFlag flags); +RegExpAlloc(ExclusiveContext* cx, HandleObject proto = nullptr); // |regexp| is under-typed because this function's used in the JIT. extern JSObject* @@ -447,11 +443,10 @@ class RegExpObject : public NativeObject static void trace(JSTracer* trc, JSObject* obj); - private: - friend RegExpObject* - InitializeRegExp(ExclusiveContext* cx, Handle regexp, HandleAtom source, - RegExpFlag flags); + static bool initFromAtom(ExclusiveContext* cx, Handle regexp, HandleAtom source, + RegExpFlag flags); + private: bool init(ExclusiveContext* cx, HandleAtom source, RegExpFlag flags); /* diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index b61b545c3325..36726cb7c1a9 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1207,10 +1207,21 @@ intrinsic_ConstructorForTypedArray(JSContext* cx, unsigned argc, Value* vp) RootedObject object(cx, &args[0].toObject()); JSProtoKey protoKey = StandardProtoKeyOrNull(object); MOZ_ASSERT(protoKey); - RootedValue ctor(cx, cx->global()->getConstructor(protoKey)); - MOZ_ASSERT(ctor.isObject()); - args.rval().set(ctor); + // While it may seem like an invariant that in any compartment, + // seeing a typed array object implies that the TypedArray constructor + // for that type is initialized on the compartment's global, this is not + // the case. When we construct a typed array given a cross-compartment + // ArrayBuffer, we put the constructed TypedArray in the same compartment + // as the ArrayBuffer. Since we use the prototype from the initial + // compartment, and never call the constructor in the ArrayBuffer's + // compartment from script, we are not guaranteed to have initialized + // the constructor. + RootedObject ctor(cx); + if (!GetBuiltinConstructor(cx, protoKey, &ctor)) + return false; + + args.rval().setObject(*ctor); return true; } diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp index ca0a01383195..21e446efa2c2 100644 --- a/js/src/vm/SharedArrayObject.cpp +++ b/js/src/vm/SharedArrayObject.cpp @@ -101,34 +101,41 @@ SharedArrayRawBuffer::New(JSContext* cx, uint32_t length) if (allocSize <= length) return nullptr; #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB) - // Test >= to guard against the case where multiple extant runtimes - // race to allocate. - if (++numLive >= maxLive) { - JSRuntime* rt = cx->runtime(); - if (rt->largeAllocationFailureCallback) - rt->largeAllocationFailureCallback(rt->largeAllocationFailureCallbackData); - if (numLive >= maxLive) { + void* p = nullptr; + if (!IsValidAsmJSHeapLength(length)) { + p = MapMemory(allocSize, true); + if (!p) + return nullptr; + } else { + // Test >= to guard against the case where multiple extant runtimes + // race to allocate. + if (++numLive >= maxLive) { + JSRuntime* rt = cx->runtime(); + if (rt->largeAllocationFailureCallback) + rt->largeAllocationFailureCallback(rt->largeAllocationFailureCallbackData); + if (numLive >= maxLive) { + numLive--; + return nullptr; + } + } + // Get the entire reserved region (with all pages inaccessible) + p = MapMemory(SharedArrayMappedSize, false); + if (!p) { numLive--; return nullptr; } - } - // Get the entire reserved region (with all pages inaccessible) - void* p = MapMemory(SharedArrayMappedSize, false); - if (!p) { - numLive--; - return nullptr; - } - if (!MarkValidRegion(p, allocSize)) { - UnmapMemory(p, SharedArrayMappedSize); - numLive--; - return nullptr; - } + if (!MarkValidRegion(p, allocSize)) { + UnmapMemory(p, SharedArrayMappedSize); + numLive--; + return nullptr; + } # if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE) - // Tell Valgrind/Memcheck to not report accesses in the inaccessible region. - VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)p + allocSize, - SharedArrayMappedSize - allocSize); + // Tell Valgrind/Memcheck to not report accesses in the inaccessible region. + VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)p + allocSize, + SharedArrayMappedSize - allocSize); # endif + } #else void* p = MapMemory(allocSize, true); if (!p) @@ -136,7 +143,9 @@ SharedArrayRawBuffer::New(JSContext* cx, uint32_t length) #endif uint8_t* buffer = reinterpret_cast(p) + AsmJSPageSize; uint8_t* base = buffer - sizeof(SharedArrayRawBuffer); - return new (base) SharedArrayRawBuffer(buffer, length); + SharedArrayRawBuffer* rawbuf = new (base) SharedArrayRawBuffer(buffer, length); + MOZ_ASSERT(rawbuf->length == length); // Deallocation needs this + return rawbuf; } void @@ -159,18 +168,23 @@ SharedArrayRawBuffer::dropReference() MOZ_ASSERT(p.asValue() % AsmJSPageSize == 0); uint8_t* address = p.unwrap(/*safe - only reference*/); + uint32_t allocSize = (this->length + 2*AsmJSPageSize - 1) & ~(AsmJSPageSize - 1); #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB) - numLive--; - UnmapMemory(address, SharedArrayMappedSize); + if (!IsValidAsmJSHeapLength(allocSize)) { + UnmapMemory(address, allocSize); + } else { + numLive--; + UnmapMemory(address, SharedArrayMappedSize); # if defined(MOZ_VALGRIND) \ && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE) - // Tell Valgrind/Memcheck to recommence reporting accesses in the - // previously-inaccessible region. - VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(address, - SharedArrayMappedSize); + // Tell Valgrind/Memcheck to recommence reporting accesses in the + // previously-inaccessible region. + VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(address, + SharedArrayMappedSize); # endif + } #else - UnmapMemory(address, this->length + AsmJSPageSize); + UnmapMemory(address, allocSize); #endif } } diff --git a/js/src/vm/StringObject-inl.h b/js/src/vm/StringObject-inl.h index de3a9714f759..5fc1656f691e 100644 --- a/js/src/vm/StringObject-inl.h +++ b/js/src/vm/StringObject-inl.h @@ -33,9 +33,9 @@ StringObject::init(JSContext* cx, HandleString str) } inline StringObject* -StringObject::create(JSContext* cx, HandleString str, NewObjectKind newKind) +StringObject::create(JSContext* cx, HandleString str, HandleObject proto, NewObjectKind newKind) { - JSObject* obj = NewBuiltinClassInstance(cx, &class_, newKind); + JSObject* obj = NewObjectWithClassProto(cx, &class_, proto, newKind); if (!obj) return nullptr; Rooted strobj(cx, &obj->as()); diff --git a/js/src/vm/StringObject.h b/js/src/vm/StringObject.h index 57adbc80a50f..119e3d9fa6d0 100644 --- a/js/src/vm/StringObject.h +++ b/js/src/vm/StringObject.h @@ -29,6 +29,7 @@ class StringObject : public NativeObject * [[Prototype]] is determined from context. */ static inline StringObject* create(JSContext* cx, HandleString str, + HandleObject proto = nullptr, NewObjectKind newKind = GenericObject); /* diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index cd1128ef45c5..393e0d64f74b 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -111,6 +111,12 @@ AnyTypedArrayByteLength(const JSObject* obj) return obj->as().byteLength(); } +inline bool +AnyTypedArrayIsDetached(const JSObject* obj) +{ + return obj->as().isNeutered(); +} + inline bool IsAnyTypedArrayClass(const Class* clasp) { diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 7fd303d45de0..d156918ea403 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -180,6 +180,20 @@ NewArray(JSContext* cx, uint32_t nelements); namespace { +// We allow nullptr for newTarget for all the creation methods, to allow for +// JSFriendAPI functions that don't care about subclassing +static bool +GetPrototypeForInstance(JSContext* cx, HandleObject newTarget, MutableHandleObject proto) +{ + if (newTarget) { + if (!GetPrototypeFromConstructor(cx, newTarget, proto)) + return false; + } else { + proto.set(nullptr); + } + return true; +} + template class TypedArrayObjectTemplate : public TypedArrayObject { @@ -282,17 +296,8 @@ class TypedArrayObjectTemplate : public TypedArrayObject { MOZ_ASSERT(proto); - RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind)); - if (!obj) - return nullptr; - - ObjectGroup* group = ObjectGroup::defaultNewGroup(cx, obj->getClass(), - TaggedProto(proto.get())); - if (!group) - return nullptr; - obj->setGroup(group); - - return &obj->as(); + JSObject* obj = NewObjectWithClassProto(cx, instanceClass(), proto, allocKind); + return obj ? &obj->as() : nullptr; } static TypedArrayObject* @@ -334,9 +339,16 @@ class TypedArrayObjectTemplate : public TypedArrayObject ? GetGCObjectKind(instanceClass()) : AllocKindForLazyBuffer(len * sizeof(NativeType)); + // Subclassing mandates that we hand in the proto every time. Most of + // the time, though, that [[Prototype]] will not be interesting. If + // it isn't, we can do some more TI optimizations. + RootedObject checkProto(cx); + if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &checkProto)) + return nullptr; + AutoSetNewObjectMetadata metadata(cx); Rooted obj(cx); - if (proto) + if (proto && proto != checkProto) obj = makeProtoInstance(cx, proto, allocKind); else obj = makeTypedInstance(cx, len, allocKind); @@ -429,10 +441,13 @@ class TypedArrayObjectTemplate : public TypedArrayObject static JSObject* create(JSContext* cx, const CallArgs& args) { + MOZ_ASSERT(args.isConstructing()); + RootedObject newTarget(cx, &args.newTarget().toObject()); + /* () or (number) */ uint32_t len = 0; if (args.length() == 0 || ValueIsLength(args[0], &len)) - return fromLength(cx, len); + return fromLength(cx, len, newTarget); /* (not an object) */ if (!args[0].isObject()) { @@ -453,9 +468,13 @@ class TypedArrayObjectTemplate : public TypedArrayObject * shared array's values are copied here. */ if (!UncheckedUnwrap(dataObj)->is()) - return fromArray(cx, dataObj); + return fromArray(cx, dataObj, newTarget); /* (ArrayBuffer, [byteOffset, [length]]) */ + RootedObject proto(cx); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return nullptr; + int32_t byteOffset = 0; int32_t length = -1; @@ -479,7 +498,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject } } - return fromBuffer(cx, dataObj, byteOffset, length); + return fromBufferWithProto(cx, dataObj, byteOffset, length, proto); } public: @@ -533,9 +552,11 @@ class TypedArrayObjectTemplate : public TypedArrayObject * don't have to do anything *uniquely* crazy here. */ - Rooted proto(cx); - if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &proto)) - return nullptr; + RootedObject protoRoot(cx, proto); + if (!protoRoot) { + if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &protoRoot)) + return nullptr; + } InvokeArgs args(cx); if (!args.init(3)) @@ -545,7 +566,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject args.setThis(ObjectValue(*bufobj)); args[0].setNumber(byteOffset); args[1].setInt32(lengthInt); - args[2].setObject(*proto); + args[2].setObject(*protoRoot); if (!Invoke(cx, args)) return nullptr; @@ -559,10 +580,17 @@ class TypedArrayObjectTemplate : public TypedArrayObject } Rooted buffer(cx); - if (IsArrayBuffer(bufobj)) - buffer = static_cast(&AsArrayBuffer(bufobj)); - else + if (IsArrayBuffer(bufobj)) { + ArrayBufferObject& buf = AsArrayBuffer(bufobj); + if (buf.isNeutered()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return nullptr; + } + + buffer = static_cast(&buf); + } else { buffer = static_cast(&AsSharedArrayBuffer(bufobj)); + } if (byteOffset > buffer->byteLength() || byteOffset % sizeof(NativeType) != 0) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); @@ -622,16 +650,21 @@ class TypedArrayObjectTemplate : public TypedArrayObject } static JSObject* - fromLength(JSContext* cx, uint32_t nelements) + fromLength(JSContext* cx, uint32_t nelements, HandleObject newTarget = nullptr) { + RootedObject proto(cx); + if (!GetPrototypeForInstance(cx, newTarget, &proto)) + return nullptr; + Rooted buffer(cx); if (!maybeCreateArrayBuffer(cx, nelements, &buffer)) return nullptr; - return makeInstance(cx, buffer, 0, nelements); + + return makeInstance(cx, buffer, 0, nelements, proto); } static JSObject* - fromArray(JSContext* cx, HandleObject other); + fromArray(JSContext* cx, HandleObject other, HandleObject newTarget = nullptr); static const NativeType getIndex(JSObject* obj, uint32_t index) @@ -671,20 +704,35 @@ struct TypedArrayObject::OfType template /* static */ JSObject* -TypedArrayObjectTemplate::fromArray(JSContext* cx, HandleObject other) +TypedArrayObjectTemplate::fromArray(JSContext* cx, HandleObject other, + HandleObject newTarget /* = nullptr */) { + // Allow nullptr newTarget for FriendAPI methods, which don't care about + // subclassing. + RootedObject proto(cx); + uint32_t len; if (IsAnyTypedArray(other)) { + if (!GetPrototypeForInstance(cx, newTarget, &proto)) + return nullptr; + + if (AnyTypedArrayIsDetached(other)) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return nullptr; + } len = AnyTypedArrayLength(other); - } else if (!GetLengthProperty(cx, other, &len)) { - return nullptr; + } else { + if (!GetLengthProperty(cx, other, &len)) + return nullptr; + if (!GetPrototypeForInstance(cx, newTarget, &proto)) + return nullptr; } Rooted buffer(cx); if (!maybeCreateArrayBuffer(cx, len, &buffer)) return nullptr; - Rooted obj(cx, makeInstance(cx, buffer, 0, len)); + Rooted obj(cx, makeInstance(cx, buffer, 0, len, proto)); if (!obj || !TypedArrayMethods::setFromArrayLike(cx, obj, other, len)) return nullptr; return obj; @@ -970,7 +1018,7 @@ DataViewNewObjectKind(JSContext* cx, uint32_t byteLength, JSObject* proto) return GenericObject; } -inline DataViewObject* +DataViewObject* DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, Handle arrayBuffer, JSObject* protoArg) { @@ -983,24 +1031,21 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, RootedObject obj(cx); NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto); - obj = NewBuiltinClassInstance(cx, &class_, newKind); + obj = NewObjectWithClassProto(cx, &class_, proto, newKind); if (!obj) return nullptr; - if (proto) { - ObjectGroup* group = ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(proto)); - if (!group) - return nullptr; - obj->setGroup(group); - } else if (byteLength >= TypedArrayObject::SINGLETON_BYTE_LENGTH) { - MOZ_ASSERT(obj->isSingleton()); - } else { - jsbytecode* pc; - RootedScript script(cx, cx->currentScript(&pc)); - if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, - newKind == SingletonObject)) - { - return nullptr; + if (!proto) { + if (byteLength >= TypedArrayObject::SINGLETON_BYTE_LENGTH) { + MOZ_ASSERT(obj->isSingleton()); + } else { + jsbytecode* pc; + RootedScript script(cx, cx->currentScript(&pc)); + if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, + newKind == SingletonObject)) + { + return nullptr; + } } } @@ -1031,7 +1076,8 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, } bool -DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args, HandleObject proto) +DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args, + uint32_t* byteOffsetPtr, uint32_t* byteLengthPtr) { if (!IsArrayBuffer(bufobj)) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, @@ -1047,29 +1093,40 @@ DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args, if (!ToUint32(cx, args[1], &byteOffset)) return false; if (byteOffset > INT32_MAX) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, - JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); + return false; + } + } + + if (buffer->isNeutered()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + + if (args.length() > 1) { + if (byteOffset > byteLength) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); return false; } - if (!args.get(2).isUndefined()) { + if (args.get(2).isUndefined()) { + byteLength -= byteOffset; + } else { if (!ToUint32(cx, args[2], &byteLength)) return false; if (byteLength > INT32_MAX) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, - JSMSG_ARG_INDEX_OUT_OF_RANGE, "2"); + JSMSG_ARG_INDEX_OUT_OF_RANGE, "2"); return false; } - } else { - uint32_t bufferLength = buffer->byteLength(); - if (byteOffset > bufferLength) { + MOZ_ASSERT(byteOffset + byteLength >= byteOffset, + "can't overflow: both numbers are less than INT32_MAX"); + if (byteOffset + byteLength > buffer->byteLength()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); return false; } - - byteLength = bufferLength - byteOffset; } } @@ -1077,11 +1134,29 @@ DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args, MOZ_ASSERT(byteOffset <= INT32_MAX); MOZ_ASSERT(byteLength <= INT32_MAX); - if (byteOffset + byteLength > buffer->byteLength()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); - return false; - } + *byteOffsetPtr = byteOffset; + *byteLengthPtr = byteLength; + + return true; +} + +bool +DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args) +{ + MOZ_ASSERT(args.isConstructing()); + assertSameCompartment(cx, bufobj); + + uint32_t byteOffset, byteLength; + if (!getAndCheckConstructorArgs(cx, bufobj, args, &byteOffset, &byteLength)) + return false; + + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + Rooted buffer(cx, &AsArrayBuffer(bufobj)); JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto); if (!obj) return false; @@ -1089,6 +1164,70 @@ DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args, return true; } +// Create a DataView object in another compartment. +// +// ES6 supports creating a DataView in global A (using global A's DataView +// constructor) backed by an ArrayBuffer created in global B. +// +// Our DataViewObject implementation doesn't support a DataView in +// compartment A backed by an ArrayBuffer in compartment B. So in this case, +// we create the DataView in B (!) and return a cross-compartment wrapper. +// +// Extra twist: the spec says the new DataView's [[Prototype]] must be +// A's DataView.prototype. So even though we're creating the DataView in B, +// its [[Prototype]] must be (a cross-compartment wrapper for) the +// DataView.prototype in A. +// +// As if this were not confusing enough, the way we actually do this is also +// tricky. We call compartment A's createDataViewForThis method, passing it +// bufobj as `this`. That calls ArrayBufferObject::createDataViewForThis(), +// which uses CallNonGenericMethod to switch to compartment B so that +// the new DataView is created there. +bool +DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args) +{ + MOZ_ASSERT(args.isConstructing()); + MOZ_ASSERT(bufobj->is()); + + JSObject* unwrapped = CheckedUnwrap(bufobj); + if (!unwrapped) { + JS_ReportError(cx, "Permission denied to access object"); + return false; + } + + // NB: This entails the IsArrayBuffer check + uint32_t byteOffset, byteLength; + if (!getAndCheckConstructorArgs(cx, unwrapped, args, &byteOffset, &byteLength)) + return false; + + // Make sure to get the [[Prototype]] for the created view from this + // compartment. + RootedObject proto(cx); + RootedObject newTarget(cx, &args.newTarget().toObject()); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + Rooted global(cx, cx->compartment()->maybeGlobal()); + if (!proto) { + proto = global->getOrCreateDataViewPrototype(cx); + if (!proto) + return false; + } + + InvokeArgs args2(cx); + if (!args2.init(3)) + return false; + args2.setCallee(global->createDataViewForThis()); + args2.setThis(ObjectValue(*bufobj)); + args2[0].set(PrivateUint32Value(byteOffset)); + args2[1].set(PrivateUint32Value(byteLength)); + args2[2].setObject(*proto); + if (!Invoke(cx, args2)) + return false; + args.rval().set(args2.rval()); + return true; +} + bool DataViewObject::class_constructor(JSContext* cx, unsigned argc, Value* vp) { @@ -1101,26 +1240,9 @@ DataViewObject::class_constructor(JSContext* cx, unsigned argc, Value* vp) if (!GetFirstArgumentAsObject(cx, args, "DataView constructor", &bufobj)) return false; - if (bufobj->is() && IsArrayBuffer(UncheckedUnwrap(bufobj))) { - Rooted global(cx, cx->compartment()->maybeGlobal()); - Rooted proto(cx, global->getOrCreateDataViewPrototype(cx)); - if (!proto) - return false; - - InvokeArgs args2(cx); - if (!args2.init(args.length() + 1)) - return false; - args2.setCallee(global->createDataViewForThis()); - args2.setThis(ObjectValue(*bufobj)); - PodCopy(args2.array(), args.array(), args.length()); - args2[args.length()].setObject(*proto); - if (!Invoke(cx, args2)) - return false; - args.rval().set(args2.rval()); - return true; - } - - return construct(cx, bufobj, args, nullptr); + if (bufobj->is()) + return constructWrapped(cx, bufobj, args); + return constructSameCompartment(cx, bufobj, args); } template @@ -1227,6 +1349,11 @@ DataViewObject::read(JSContext* cx, Handle obj, bool fromLittleEndian = args.length() >= 2 && ToBoolean(args[1]); + if (obj->arrayBuffer().isNeutered()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + uint8_t* data = DataViewObject::getDataPointer(cx, obj, offset); if (!data) return false; @@ -1288,6 +1415,11 @@ DataViewObject::write(JSContext* cx, Handle obj, bool toLittleEndian = args.length() >= 3 && ToBoolean(args[2]); + if (obj->arrayBuffer().isNeutered()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + uint8_t* data = DataViewObject::getDataPointer(cx, obj, offset); if (!data) return false; @@ -1708,15 +1840,15 @@ TypedArrayObject::setElement(TypedArrayObject& obj, uint32_t index, double d) */ #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType) \ - JS_FRIEND_API(JSObject*) JS_New ## Name ## Array(JSContext* cx, uint32_t nelements) \ + JS_FRIEND_API(JSObject*) JS_New ## Name ## Array(JSContext* cx, uint32_t nelements) \ { \ return TypedArrayObjectTemplate::fromLength(cx, nelements); \ } \ - JS_FRIEND_API(JSObject*) JS_New ## Name ## ArrayFromArray(JSContext* cx, HandleObject other) \ + JS_FRIEND_API(JSObject*) JS_New ## Name ## ArrayFromArray(JSContext* cx, HandleObject other) \ { \ return TypedArrayObjectTemplate::fromArray(cx, other); \ } \ - JS_FRIEND_API(JSObject*) JS_New ## Name ## ArrayWithBuffer(JSContext* cx, \ + JS_FRIEND_API(JSObject*) JS_New ## Name ## ArrayWithBuffer(JSContext* cx, \ HandleObject arrayBuffer, uint32_t byteOffset, int32_t length) \ { \ return TypedArrayObjectTemplate::fromBuffer(cx, arrayBuffer, byteOffset, \ @@ -1728,8 +1860,8 @@ TypedArrayObject::setElement(TypedArrayObject& obj, uint32_t index, double d) return false; \ const Class* clasp = obj->getClass(); \ return clasp == TypedArrayObjectTemplate::instanceClass(); \ - } \ - JS_FRIEND_API(JSObject*) js::Unwrap ## Name ## Array(JSObject* obj) \ + } \ + JS_FRIEND_API(JSObject*) js::Unwrap ## Name ## Array(JSObject* obj) \ { \ obj = CheckedUnwrap(obj); \ if (!obj) \ @@ -1738,7 +1870,7 @@ TypedArrayObject::setElement(TypedArrayObject& obj, uint32_t index, double d) if (clasp == TypedArrayObjectTemplate::instanceClass()) \ return obj; \ return nullptr; \ - } \ + } \ const js::Class* const js::detail::Name ## ArrayClassPtr = \ &js::TypedArrayObject::classes[TypedArrayObjectTemplate::ArrayTypeID()]; diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index 9fe812cd9a28..018060140b15 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -198,7 +198,7 @@ class TypedArrayObject : public NativeObject } bool isNeutered() const { - return !isSharedMemory() && (!bufferUnshared() || bufferUnshared()->isNeutered()); + return !isSharedMemory() && bufferUnshared() && bufferUnshared()->isNeutered(); } private: @@ -384,6 +384,16 @@ class DataViewObject : public NativeObject static bool defineGetter(JSContext* cx, PropertyName* name, HandleNativeObject proto); + static bool getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args, + uint32_t *byteOffset, uint32_t* byteLength); + static bool constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args); + static bool constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args); + + friend bool ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args); + static DataViewObject* + create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, + Handle arrayBuffer, JSObject* proto); + public: static const Class class_; @@ -420,13 +430,6 @@ class DataViewObject : public NativeObject } static bool class_constructor(JSContext* cx, unsigned argc, Value* vp); - static bool constructWithProto(JSContext* cx, unsigned argc, Value* vp); - static bool construct(JSContext* cx, JSObject* bufobj, const CallArgs& args, - HandleObject proto); - - static inline DataViewObject* - create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, - Handle arrayBuffer, JSObject* proto); static bool getInt8Impl(JSContext* cx, const CallArgs& args); static bool fun_getInt8(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index c5e0f9e54446..c02c4fe6fc0d 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1328,16 +1328,25 @@ xpc::SimulateActivityCallback(bool aActive) XPCJSRuntime::ActivityCallback(XPCJSRuntime::Get(), aActive); } -// static -bool +void XPCJSRuntime::EnvironmentPreparer::invoke(HandleObject scope, js::ScriptEnvironmentPreparer::Closure& closure) { MOZ_ASSERT(NS_IsMainThread()); nsIGlobalObject* global = NativeGlobal(scope); - NS_ENSURE_TRUE(global && global->GetGlobalJSObject(), false); + + // Not much we can do if we simply don't have a usable global here... + NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject()); AutoEntryScript aes(global, "JS-engine-initiated execution"); aes.TakeOwnershipOfErrorReporting(); - return closure(aes.cx()); + + MOZ_ASSERT(!JS_IsExceptionPending(aes.cx())); + + DebugOnly ok = closure(aes.cx()); + + MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(aes.cx())); + + // The AutoEntryScript will check for JS_IsExceptionPending on the + // JSContext and report it as needed as it comes off the stack. } // static diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index d863f953fe07..d210885d4dec 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -592,7 +592,7 @@ public: void RemoveGCCallback(xpcGCCallback cb); struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer { - bool invoke(JS::HandleObject scope, Closure& closure) override; + void invoke(JS::HandleObject scope, Closure& closure) override; }; EnvironmentPreparer mEnvironmentPreparer; diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 9c1f62f820dd..aa8aff6ae109 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -947,7 +947,7 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, void nsCSSRendering::PaintFocus(nsPresContext* aPresContext, - nsRenderingContext& aRenderingContext, + DrawTarget* aDrawTarget, const nsRect& aFocusRect, nscolor aColor) { @@ -972,8 +972,6 @@ nsCSSRendering::PaintFocus(nsPresContext* aPresContext, NS_STYLE_BORDER_STYLE_DOTTED }; nscolor focusColors[4] = { aColor, aColor, aColor, aColor }; - gfxContext *ctx = aRenderingContext.ThebesContext(); - // Because this renders a dotted border, the background color // should not be used. Therefore, we provide a value that will // be blatantly wrong if it ever does get used. (If this becomes @@ -981,7 +979,7 @@ nsCSSRendering::PaintFocus(nsPresContext* aPresContext, // to a style context and can use the same logic that PaintBorder // and PaintOutline do.) nsCSSBorderRenderer br(aPresContext->Type(), - ctx->GetDrawTarget(), + aDrawTarget, focusRect, focusStyles, focusWidths, @@ -3737,7 +3735,7 @@ static void SetPoly(const Rect& aRect, Point* poly) } static void -DrawDashedSegment(nsRenderingContext& aContext, +DrawDashedSegment(DrawTarget& aDrawTarget, nsRect aRect, nscoord aDashLength, nscolor aColor, @@ -3745,8 +3743,6 @@ DrawDashedSegment(nsRenderingContext& aContext, nscoord aTwipsPerPixel, bool aHorizontal) { - DrawTarget* drawTarget = aContext.GetDrawTarget(); - ColorPattern color(ToDeviceColor(aColor)); DrawOptions drawOptions(1.f, CompositionOp::OP_OVER, AntialiasMode::NONE); StrokeOptions strokeOptions; @@ -3763,20 +3759,20 @@ DrawDashedSegment(nsRenderingContext& aContext, nsPoint right = (aRect.TopRight() + aRect.BottomRight()) / 2; strokeOptions.mLineWidth = Float(aRect.height) / aAppUnitsPerDevPixel; StrokeLineWithSnapping(left, right, - aAppUnitsPerDevPixel, *drawTarget, + aAppUnitsPerDevPixel, aDrawTarget, color, strokeOptions, drawOptions); } else { nsPoint top = (aRect.TopLeft() + aRect.TopRight()) / 2; nsPoint bottom = (aRect.BottomLeft() + aRect.BottomRight()) / 2; strokeOptions.mLineWidth = Float(aRect.width) / aAppUnitsPerDevPixel; StrokeLineWithSnapping(top, bottom, - aAppUnitsPerDevPixel, *drawTarget, + aAppUnitsPerDevPixel, aDrawTarget, color, strokeOptions, drawOptions); } } static void -DrawSolidBorderSegment(nsRenderingContext& aContext, +DrawSolidBorderSegment(DrawTarget& aDrawTarget, nsRect aRect, nscolor aColor, int32_t aAppUnitsPerDevPixel, @@ -3786,8 +3782,6 @@ DrawSolidBorderSegment(nsRenderingContext& aContext, uint8_t aEndBevelSide = 0, nscoord aEndBevelOffset = 0) { - DrawTarget* drawTarget = aContext.GetDrawTarget(); - ColorPattern color(ToDeviceColor(aColor)); DrawOptions drawOptions(1.f, CompositionOp::OP_OVER, AntialiasMode::NONE); @@ -3795,14 +3789,14 @@ DrawSolidBorderSegment(nsRenderingContext& aContext, if ((aRect.width == aTwipsPerPixel) || (aRect.height == aTwipsPerPixel) || ((0 == aStartBevelOffset) && (0 == aEndBevelOffset))) { // simple rectangle - drawTarget->FillRect(NSRectToSnappedRect(aRect, aAppUnitsPerDevPixel, - *drawTarget), + aDrawTarget.FillRect(NSRectToSnappedRect(aRect, aAppUnitsPerDevPixel, + aDrawTarget), color, drawOptions); } else { // polygon with beveling Point poly[4]; - SetPoly(NSRectToSnappedRect(aRect, aAppUnitsPerDevPixel, *drawTarget), + SetPoly(NSRectToSnappedRect(aRect, aAppUnitsPerDevPixel, aDrawTarget), poly); Float startBevelOffset = @@ -3837,14 +3831,14 @@ DrawSolidBorderSegment(nsRenderingContext& aContext, poly[3].y -= endBevelOffset; } - RefPtr builder = drawTarget->CreatePathBuilder(); + RefPtr builder = aDrawTarget.CreatePathBuilder(); builder->MoveTo(poly[0]); builder->LineTo(poly[1]); builder->LineTo(poly[2]); builder->LineTo(poly[3]); builder->Close(); RefPtr path = builder->Finish(); - drawTarget->Fill(path, color, drawOptions); + aDrawTarget.Fill(path, color, drawOptions); } } @@ -3902,6 +3896,8 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext, ctx->SetColor(Color::FromABGR(aBorderColor)); + DrawTarget& drawTarget = *aContext.GetDrawTarget(); + switch (aBorderStyle) { case NS_STYLE_BORDER_STYLE_NONE: case NS_STYLE_BORDER_STYLE_HIDDEN: @@ -3924,36 +3920,36 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext, GetDashInfo(aBorder.width, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength); nsRect rect(aBorder.x, aBorder.y, startDashLength, aBorder.height); - DrawSolidBorderSegment(aContext, rect, aBorderColor, + DrawSolidBorderSegment(drawTarget, rect, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel); rect.x += startDashLength + dashLength; rect.width = aBorder.width - (startDashLength + endDashLength + dashLength); - DrawDashedSegment(aContext, rect, dashLength, aBorderColor, + DrawDashedSegment(drawTarget, rect, dashLength, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel, horizontal); rect.x += rect.width; rect.width = endDashLength; - DrawSolidBorderSegment(aContext, rect, aBorderColor, + DrawSolidBorderSegment(drawTarget, rect, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel); } else { GetDashInfo(aBorder.height, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength); nsRect rect(aBorder.x, aBorder.y, aBorder.width, startDashLength); - DrawSolidBorderSegment(aContext, rect, aBorderColor, + DrawSolidBorderSegment(drawTarget, rect, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel); rect.y += rect.height + dashLength; rect.height = aBorder.height - (startDashLength + endDashLength + dashLength); - DrawDashedSegment(aContext, rect, dashLength, aBorderColor, + DrawDashedSegment(drawTarget, rect, dashLength, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel, horizontal); rect.y += rect.height; rect.height = endDashLength; - DrawSolidBorderSegment(aContext, rect, aBorderColor, + DrawSolidBorderSegment(drawTarget, rect, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel); } } @@ -3964,7 +3960,8 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext, if ((horizontal && (twipsPerPixel >= aBorder.height)) || (!horizontal && (twipsPerPixel >= aBorder.width))) { // a one pixel border - DrawSolidBorderSegment(aContext, aBorder, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel, + DrawSolidBorderSegment(drawTarget, aBorder, aBorderColor, + aAppUnitsPerDevPixel, twipsPerPixel, aStartBevelSide, aStartBevelOffset, aEndBevelSide, aEndBevelOffset); } @@ -3993,8 +3990,10 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext, if (NS_SIDE_TOP == aEndBevelSide) { rect.width -= endBevel; } - DrawSolidBorderSegment(aContext, rect, bevelColor, aAppUnitsPerDevPixel, twipsPerPixel, aStartBevelSide, - startBevel, aEndBevelSide, endBevel); + DrawSolidBorderSegment(drawTarget, rect, bevelColor, + aAppUnitsPerDevPixel, twipsPerPixel, + aStartBevelSide, startBevel, aEndBevelSide, + endBevel); } else { // left, right half = RoundFloatToPixel(0.5f * (float)aBorder.width, twipsPerPixel); @@ -4006,8 +4005,10 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext, if (NS_SIDE_LEFT == aEndBevelSide) { rect.height -= endBevel; } - DrawSolidBorderSegment(aContext, rect, bevelColor, aAppUnitsPerDevPixel, twipsPerPixel, aStartBevelSide, - startBevel, aEndBevelSide, endBevel); + DrawSolidBorderSegment(drawTarget, rect, bevelColor, + aAppUnitsPerDevPixel, twipsPerPixel, + aStartBevelSide, startBevel, aEndBevelSide, + endBevel); } rect = aBorder; @@ -4028,8 +4029,10 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext, if (NS_SIDE_BOTTOM == aEndBevelSide) { rect.width -= endBevel; } - DrawSolidBorderSegment(aContext, rect, bevelColor, aAppUnitsPerDevPixel, twipsPerPixel, aStartBevelSide, - startBevel, aEndBevelSide, endBevel); + DrawSolidBorderSegment(drawTarget, rect, bevelColor, + aAppUnitsPerDevPixel, twipsPerPixel, + aStartBevelSide, startBevel, aEndBevelSide, + endBevel); } else { rect.x = rect.x + half; @@ -4041,8 +4044,10 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext, if (NS_SIDE_RIGHT == aEndBevelSide) { rect.height -= endBevel; } - DrawSolidBorderSegment(aContext, rect, bevelColor, aAppUnitsPerDevPixel, twipsPerPixel, aStartBevelSide, - startBevel, aEndBevelSide, endBevel); + DrawSolidBorderSegment(drawTarget, rect, bevelColor, + aAppUnitsPerDevPixel, twipsPerPixel, + aStartBevelSide, startBevel, aEndBevelSide, + endBevel); } } break; @@ -4068,8 +4073,10 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext, if (NS_SIDE_TOP == aEndBevelSide) { topRect.width -= aEndBevelOffset - endBevel; } - DrawSolidBorderSegment(aContext, topRect, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel, aStartBevelSide, - startBevel, aEndBevelSide, endBevel); + DrawSolidBorderSegment(drawTarget, topRect, aBorderColor, + aAppUnitsPerDevPixel, twipsPerPixel, + aStartBevelSide, startBevel, aEndBevelSide, + endBevel); // draw the botom line or rect nscoord heightOffset = aBorder.height - thirdHeight; @@ -4081,8 +4088,10 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext, if (NS_SIDE_BOTTOM == aEndBevelSide) { bottomRect.width -= aEndBevelOffset - endBevel; } - DrawSolidBorderSegment(aContext, bottomRect, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel, aStartBevelSide, - startBevel, aEndBevelSide, endBevel); + DrawSolidBorderSegment(drawTarget, bottomRect, aBorderColor, + aAppUnitsPerDevPixel, twipsPerPixel, + aStartBevelSide, startBevel, aEndBevelSide, + endBevel); } else { // left, right nscoord thirdWidth = RoundFloatToPixel(0.333333f * (float)aBorder.width, twipsPerPixel); @@ -4095,8 +4104,10 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext, if (NS_SIDE_LEFT == aEndBevelSide) { leftRect.height -= aEndBevelOffset - endBevel; } - DrawSolidBorderSegment(aContext, leftRect, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel, aStartBevelSide, - startBevel, aEndBevelSide, endBevel); + DrawSolidBorderSegment(drawTarget, leftRect, aBorderColor, + aAppUnitsPerDevPixel, twipsPerPixel, + aStartBevelSide, startBevel, aEndBevelSide, + endBevel); nscoord widthOffset = aBorder.width - thirdWidth; nsRect rightRect(aBorder.x + widthOffset, aBorder.y, aBorder.width - widthOffset, aBorder.height); @@ -4107,14 +4118,17 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext, if (NS_SIDE_RIGHT == aEndBevelSide) { rightRect.height -= aEndBevelOffset - endBevel; } - DrawSolidBorderSegment(aContext, rightRect, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel, aStartBevelSide, - startBevel, aEndBevelSide, endBevel); + DrawSolidBorderSegment(drawTarget, rightRect, aBorderColor, + aAppUnitsPerDevPixel, twipsPerPixel, + aStartBevelSide, startBevel, aEndBevelSide, + endBevel); } break; } // else fall through to solid case NS_STYLE_BORDER_STYLE_SOLID: - DrawSolidBorderSegment(aContext, aBorder, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel, aStartBevelSide, + DrawSolidBorderSegment(drawTarget, aBorder, aBorderColor, + aAppUnitsPerDevPixel, twipsPerPixel, aStartBevelSide, aStartBevelOffset, aEndBevelSide, aEndBevelOffset); break; case NS_STYLE_BORDER_STYLE_OUTSET: diff --git a/layout/base/nsCSSRendering.h b/layout/base/nsCSSRendering.h index 274feb874e34..d3b2d9e2fe3f 100644 --- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -432,7 +432,7 @@ struct nsCSSRendering { * Not used for controls, because the native theme may differ. */ static void PaintFocus(nsPresContext* aPresContext, - nsRenderingContext& aRenderingContext, + DrawTarget* aDrawTarget, const nsRect& aFocusRect, nscolor aColor); diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 6b63d84a47c6..14de3c90f6ae 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1889,9 +1889,11 @@ void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, if (aState->mInPreserves3D) { // Collect leaves of the current 3D rendering context. for (item = GetBottom(); item; item = item->GetAbove()) { - MOZ_ASSERT(item->GetType() == nsDisplayTransform::TYPE_TRANSFORM); + MOZ_ASSERT(item->GetType() == nsDisplayTransform::TYPE_TRANSFORM || + item->GetType() == nsDisplayTransform::TYPE_PERSPECTIVE); if (item->Frame()->Extend3DContext() && - !static_cast(item)->IsTransformSeparator()) { + (item->GetType() == nsDisplayTransform::TYPE_PERSPECTIVE || + !static_cast(item)->IsTransformSeparator())) { item->HitTest(aBuilder, aRect, aState, aOutFrames); } else { // One of leaves in the current 3D rendering context. diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index e890f77b357f..2bed6269a4e9 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -5363,7 +5363,7 @@ nsLayoutUtils::MinISizeFromInline(nsIFrame* aFrame, nsIFrame::InlineMinISizeData data; DISPLAY_MIN_WIDTH(aFrame, data.prevLines); aFrame->AddInlineMinISize(aRenderingContext, &data); - data.ForceBreak(aRenderingContext); + data.ForceBreak(); return data.prevLines; } @@ -5377,7 +5377,7 @@ nsLayoutUtils::PrefISizeFromInline(nsIFrame* aFrame, nsIFrame::InlinePrefISizeData data; DISPLAY_PREF_WIDTH(aFrame, data.prevLines); aFrame->AddInlinePrefISize(aRenderingContext, &data); - data.ForceBreak(aRenderingContext); + data.ForceBreak(); return data.prevLines; } diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 8310b1c54871..94d58fc70986 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -1285,7 +1285,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { NS_PARENTPROCESSMESSAGEMANAGER_CONTRACTID, &kNS_PARENTPROCESSMESSAGEMANAGER_CID }, { NS_CHILDPROCESSMESSAGEMANAGER_CONTRACTID, &kNS_CHILDPROCESSMESSAGEMANAGER_CID }, { NS_SCRIPTSECURITYMANAGER_CONTRACTID, &kNS_SCRIPTSECURITYMANAGER_CID }, - { NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID, &kNS_SCRIPTSECURITYMANAGER_CID }, { NS_PRINCIPAL_CONTRACTID, &kNS_PRINCIPAL_CID }, { NS_SYSTEMPRINCIPAL_CONTRACTID, &kNS_SYSTEMPRINCIPAL_CID }, { NS_NULLPRINCIPAL_CONTRACTID, &kNS_NULLPRINCIPAL_CID }, diff --git a/layout/forms/nsComboboxControlFrame.cpp b/layout/forms/nsComboboxControlFrame.cpp index f0a22853c4bd..e4d1ce0f854a 100644 --- a/layout/forms/nsComboboxControlFrame.cpp +++ b/layout/forms/nsComboboxControlFrame.cpp @@ -1557,8 +1557,7 @@ nsComboboxControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, DisplaySelectionOverlay(aBuilder, aLists.Content()); } -void nsComboboxControlFrame::PaintFocus(DrawTarget& aDrawTarget, - nsPoint aPt) +void nsComboboxControlFrame::PaintFocus(DrawTarget& aDrawTarget, nsPoint aPt) { /* Do we need to do anything? */ EventStates eventStates = mContent->AsElement()->State(); diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp index eecfcc624232..17b322a40b5c 100644 --- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -182,7 +182,7 @@ nsListControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, * @param aPt the offset of this frame, relative to the rendering reference * frame */ -void nsListControlFrame::PaintFocus(nsRenderingContext& aRC, nsPoint aPt) +void nsListControlFrame::PaintFocus(DrawTarget* aDrawTarget, nsPoint aPt) { if (mFocused != this) return; @@ -232,7 +232,7 @@ void nsListControlFrame::PaintFocus(nsRenderingContext& aRC, nsPoint aPt) LookAndFeel::eColorID_WidgetSelectForeground : LookAndFeel::eColorID_WidgetSelectBackground); - nsCSSRendering::PaintFocus(presContext, aRC, fRect, color); + nsCSSRendering::PaintFocus(presContext, aDrawTarget, fRect, color); } void diff --git a/layout/forms/nsListControlFrame.h b/layout/forms/nsListControlFrame.h index c2afcb0c43f4..bfd6a8d915ce 100644 --- a/layout/forms/nsListControlFrame.h +++ b/layout/forms/nsListControlFrame.h @@ -193,7 +193,8 @@ public: * @param aPt the offset of this frame, relative to the rendering reference * frame */ - void PaintFocus(nsRenderingContext& aRC, nsPoint aPt); + void PaintFocus(mozilla::gfx::DrawTarget* aDrawTarget, nsPoint aPt); + /** * If this frame IsFocused(), invalidates an area that includes anything * that PaintFocus will or could have painted --- basically the whole diff --git a/layout/forms/nsSelectsAreaFrame.cpp b/layout/forms/nsSelectsAreaFrame.cpp index c1bd774c4339..d9800b50863c 100644 --- a/layout/forms/nsSelectsAreaFrame.cpp +++ b/layout/forms/nsSelectsAreaFrame.cpp @@ -118,7 +118,8 @@ public: nsRenderingContext* aCtx) override { nsListControlFrame* listFrame = GetEnclosingListFrame(Frame()); // listFrame must be non-null or we wouldn't get called. - listFrame->PaintFocus(*aCtx, aBuilder->ToReferenceFrame(listFrame)); + listFrame->PaintFocus(aCtx->GetDrawTarget(), + aBuilder->ToReferenceFrame(listFrame)); } NS_DISPLAY_DECL_NAME("ListFocus", TYPE_LIST_FOCUS) }; diff --git a/layout/generic/nsBRFrame.cpp b/layout/generic/nsBRFrame.cpp index 875de0fc6f0f..505e5ba531b3 100644 --- a/layout/generic/nsBRFrame.cpp +++ b/layout/generic/nsBRFrame.cpp @@ -170,7 +170,7 @@ BRFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext, nsIFrame::InlineMinISizeData *aData) { if (!GetParent()->StyleContext()->ShouldSuppressLineBreak()) { - aData->ForceBreak(aRenderingContext); + aData->ForceBreak(); } } @@ -179,7 +179,7 @@ BRFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext, nsIFrame::InlinePrefISizeData *aData) { if (!GetParent()->StyleContext()->ShouldSuppressLineBreak()) { - aData->ForceBreak(aRenderingContext); + aData->ForceBreak(); } } diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 4ffbef11b6c6..949a7f8f40c5 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -714,10 +714,10 @@ nsBlockFrame::GetMinISize(nsRenderingContext *aRenderingContext) AutoNoisyIndenter lineindent(gNoisyIntrinsic); #endif if (line->IsBlock()) { - data.ForceBreak(aRenderingContext); + data.ForceBreak(); data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, line->mFirstChild, nsLayoutUtils::MIN_ISIZE); - data.ForceBreak(aRenderingContext); + data.ForceBreak(); } else { if (!curFrame->GetPrevContinuation() && line == curFrame->begin_lines()) { @@ -747,7 +747,7 @@ nsBlockFrame::GetMinISize(nsRenderingContext *aRenderingContext) #endif } } - data.ForceBreak(aRenderingContext); + data.ForceBreak(); mMinWidth = data.prevLines; return mMinWidth; @@ -802,10 +802,10 @@ nsBlockFrame::GetPrefISize(nsRenderingContext *aRenderingContext) AutoNoisyIndenter lineindent(gNoisyIntrinsic); #endif if (line->IsBlock()) { - data.ForceBreak(aRenderingContext); + data.ForceBreak(); data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, line->mFirstChild, nsLayoutUtils::PREF_ISIZE); - data.ForceBreak(aRenderingContext); + data.ForceBreak(); } else { if (!curFrame->GetPrevContinuation() && line == curFrame->begin_lines()) { @@ -835,7 +835,7 @@ nsBlockFrame::GetPrefISize(nsRenderingContext *aRenderingContext) #endif } } - data.ForceBreak(aRenderingContext); + data.ForceBreak(); mPrefWidth = data.prevLines; return mPrefWidth; @@ -873,7 +873,7 @@ nsBlockFrame::GetPrefWidthTightBounds(nsRenderingContext* aRenderingContext, { nscoord childX, childXMost; if (line->IsBlock()) { - data.ForceBreak(aRenderingContext); + data.ForceBreak(); rv = line->mFirstChild->GetPrefWidthTightBounds(aRenderingContext, &childX, &childXMost); NS_ENSURE_SUCCESS(rv, rv); @@ -907,7 +907,7 @@ nsBlockFrame::GetPrefWidthTightBounds(nsRenderingContext* aRenderingContext, } } } - data.ForceBreak(aRenderingContext); + data.ForceBreak(); return NS_OK; } diff --git a/layout/generic/nsCanvasFrame.cpp b/layout/generic/nsCanvasFrame.cpp index 4588a9c83dae..344a305a2a56 100644 --- a/layout/generic/nsCanvasFrame.cpp +++ b/layout/generic/nsCanvasFrame.cpp @@ -463,7 +463,7 @@ public: nsRenderingContext* aCtx) override { nsCanvasFrame* frame = static_cast(mFrame); - frame->PaintFocus(*aCtx, ToReferenceFrame()); + frame->PaintFocus(aCtx->GetDrawTarget(), ToReferenceFrame()); } NS_DISPLAY_DECL_NAME("CanvasFocus", TYPE_CANVAS_FOCUS) @@ -570,7 +570,7 @@ nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, } void -nsCanvasFrame::PaintFocus(nsRenderingContext& aRenderingContext, nsPoint aPt) +nsCanvasFrame::PaintFocus(DrawTarget* aDrawTarget, nsPoint aPt) { nsRect focusRect(aPt, GetSize()); @@ -591,7 +591,7 @@ nsCanvasFrame::PaintFocus(nsRenderingContext& aRenderingContext, nsPoint aPt) return; } - nsCSSRendering::PaintFocus(PresContext(), aRenderingContext, + nsCSSRendering::PaintFocus(PresContext(), aDrawTarget, focusRect, color->mColor); } diff --git a/layout/generic/nsCanvasFrame.h b/layout/generic/nsCanvasFrame.h index 5890ea060623..4381755a56cc 100644 --- a/layout/generic/nsCanvasFrame.h +++ b/layout/generic/nsCanvasFrame.h @@ -126,7 +126,7 @@ public: const nsRect& aDirtyRect, const nsDisplayListSet& aLists) override; - void PaintFocus(nsRenderingContext& aRenderingContext, nsPoint aPt); + void PaintFocus(mozilla::gfx::DrawTarget* aRenderingContext, nsPoint aPt); // nsIScrollPositionListener virtual void ScrollPositionWillChange(nscoord aX, nscoord aY) override; diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index d9f1457e3e4c..747ef8fc9721 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -4067,17 +4067,19 @@ nsFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext, bool canBreak = !CanContinueTextRun() && !parent->StyleContext()->ShouldSuppressLineBreak() && parent->StyleText()->WhiteSpaceCanWrap(parent); - - if (canBreak) - aData->OptionallyBreak(aRenderingContext); + + if (canBreak) { + aData->OptionallyBreak(); + } aData->trailingWhitespace = 0; aData->skipWhitespace = false; aData->trailingTextFrame = nullptr; aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, this, nsLayoutUtils::MIN_ISIZE); aData->atStartOfLine = false; - if (canBreak) - aData->OptionallyBreak(aRenderingContext); + if (canBreak) { + aData->OptionallyBreak(); + } } /* virtual */ void @@ -4092,7 +4094,7 @@ nsFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext, } void -nsIFrame::InlineMinISizeData::ForceBreak(nsRenderingContext *aRenderingContext) +nsIFrame::InlineMinISizeData::ForceBreak() { currentLine -= trailingWhitespace; prevLines = std::max(prevLines, currentLine); @@ -4109,8 +4111,7 @@ nsIFrame::InlineMinISizeData::ForceBreak(nsRenderingContext *aRenderingContext) } void -nsIFrame::InlineMinISizeData::OptionallyBreak(nsRenderingContext *aRenderingContext, - nscoord aHyphenWidth) +nsIFrame::InlineMinISizeData::OptionallyBreak(nscoord aHyphenWidth) { trailingTextFrame = nullptr; @@ -4122,11 +4123,11 @@ nsIFrame::InlineMinISizeData::OptionallyBreak(nsRenderingContext *aRenderingCont if (currentLine + aHyphenWidth < 0 || atStartOfLine) return; currentLine += aHyphenWidth; - ForceBreak(aRenderingContext); + ForceBreak(); } void -nsIFrame::InlinePrefISizeData::ForceBreak(nsRenderingContext *aRenderingContext) +nsIFrame::InlinePrefISizeData::ForceBreak() { if (floats.Length() != 0) { // preferred widths accumulated for floats that have already diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index ab6115dd5462..8ec315374f35 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1632,12 +1632,11 @@ public: // current line total is negative. When it is, we need to ignore // optional breaks to prevent min-width from ending up bigger than // pref-width. - void ForceBreak(nsRenderingContext *aRenderingContext); + void ForceBreak(); // If the break here is actually taken, aHyphenWidth must be added to the // width of the current line. - void OptionallyBreak(nsRenderingContext *aRenderingContext, - nscoord aHyphenWidth = 0); + void OptionallyBreak(nscoord aHyphenWidth = 0); // The last text frame processed so far in the current line, when // the last characters in that text frame are relevant for line @@ -1651,7 +1650,7 @@ public: }; struct InlinePrefISizeData : public InlineIntrinsicISizeData { - void ForceBreak(nsRenderingContext *aRenderingContext); + void ForceBreak(); }; /** diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 3ac9d167084a..a9a42b5da3d1 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -2457,9 +2457,9 @@ nsImageFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext, parent->StyleText()->WhiteSpaceCanWrap(parent) && !IsInAutoWidthTableCellForQuirk(this); - if (canBreak) - aData->OptionallyBreak(aRenderingContext); - + if (canBreak) { + aData->OptionallyBreak(); + } aData->trailingWhitespace = 0; aData->skipWhitespace = false; aData->trailingTextFrame = nullptr; @@ -2467,7 +2467,7 @@ nsImageFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext, this, nsLayoutUtils::MIN_ISIZE); aData->atStartOfLine = false; - if (canBreak) - aData->OptionallyBreak(aRenderingContext); - + if (canBreak) { + aData->OptionallyBreak(); + } } diff --git a/layout/generic/nsRubyBaseContainerFrame.cpp b/layout/generic/nsRubyBaseContainerFrame.cpp index 1187eaae6820..9eba5669ec7d 100644 --- a/layout/generic/nsRubyBaseContainerFrame.cpp +++ b/layout/generic/nsRubyBaseContainerFrame.cpp @@ -210,7 +210,7 @@ nsRubyBaseContainerFrame::AddInlineMinISize( gfxBreakPriority breakPriority = LineBreakBefore(baseFrame, aRenderingContext, nullptr, nullptr); if (breakPriority != gfxBreakPriority::eNoBreak) { - aData->OptionallyBreak(aRenderingContext); + aData->OptionallyBreak(); } } } diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 7a770cdd512a..043ed9ed22af 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -7926,14 +7926,12 @@ nsTextFrame::AddInlineMinISizeForFlow(nsRenderingContext *aRenderingContext, (i == textRun->GetLength() && (textRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TRAILING_BREAK))) { if (preformattedNewline) { - aData->ForceBreak(aRenderingContext); + aData->ForceBreak(); } else if (i < flowEndInTextRun && hyphBreakBefore && - hyphBreakBefore[i - start]) - { - aData->OptionallyBreak(aRenderingContext, - NSToCoordRound(provider.GetHyphenWidth())); + hyphBreakBefore[i - start]) { + aData->OptionallyBreak(NSToCoordRound(provider.GetHyphenWidth())); } else { - aData->OptionallyBreak(aRenderingContext); + aData->OptionallyBreak(); } wordStart = i; } @@ -8076,7 +8074,7 @@ nsTextFrame::AddInlinePrefISizeForFlow(nsRenderingContext *aRenderingContext, aData->currentLine = nscoord(afterTab + spacing.mAfter); lineStart = i + 1; } else if (preformattedNewline) { - aData->ForceBreak(aRenderingContext); + aData->ForceBreak(); lineStart = i; } } diff --git a/layout/printing/nsPrintEngine.cpp b/layout/printing/nsPrintEngine.cpp index 1cccf7c0dea4..d2c3c307f0c9 100644 --- a/layout/printing/nsPrintEngine.cpp +++ b/layout/printing/nsPrintEngine.cpp @@ -1519,7 +1519,7 @@ nsPrintEngine::FirePrintingErrorEvent(nsresult aPrintError) { nsCOMPtr cv = do_QueryInterface(mDocViewerPrint); nsCOMPtr doc = cv->GetDocument(); - nsCOMPtr event = + RefPtr event = NS_NewDOMCustomEvent(doc, nullptr, nullptr); MOZ_ASSERT(event); diff --git a/layout/reftests/box-shadow/boxshadow-inset-neg-spread2-ref.html b/layout/reftests/box-shadow/boxshadow-inset-neg-spread2-ref.html new file mode 100644 index 000000000000..b6b1a3dfc2af --- /dev/null +++ b/layout/reftests/box-shadow/boxshadow-inset-neg-spread2-ref.html @@ -0,0 +1,16 @@ + + + + + + +
+ + diff --git a/layout/reftests/box-shadow/boxshadow-inset-neg-spread2.html b/layout/reftests/box-shadow/boxshadow-inset-neg-spread2.html new file mode 100644 index 000000000000..b846f25aba45 --- /dev/null +++ b/layout/reftests/box-shadow/boxshadow-inset-neg-spread2.html @@ -0,0 +1,20 @@ + + + + + + +
+ + diff --git a/layout/reftests/box-shadow/boxshadow-large-offset-ref.html b/layout/reftests/box-shadow/boxshadow-large-offset-ref.html new file mode 100644 index 000000000000..2160b814791c --- /dev/null +++ b/layout/reftests/box-shadow/boxshadow-large-offset-ref.html @@ -0,0 +1,50 @@ + + + + + +
+
+
+
+
+ + diff --git a/layout/reftests/box-shadow/boxshadow-large-offset.html b/layout/reftests/box-shadow/boxshadow-large-offset.html new file mode 100644 index 000000000000..5df87c368b70 --- /dev/null +++ b/layout/reftests/box-shadow/boxshadow-large-offset.html @@ -0,0 +1,49 @@ + + + + + +
+
+
+
+ + diff --git a/layout/reftests/box-shadow/reftest.list b/layout/reftests/box-shadow/reftest.list index a5a57d1c32aa..b5351132f3df 100644 --- a/layout/reftests/box-shadow/reftest.list +++ b/layout/reftests/box-shadow/reftest.list @@ -25,6 +25,7 @@ random-if(d2d) == boxshadow-threecorners.html boxshadow-threecorners-ref.html fuzzy-if(OSX==1010,1,24) == boxshadow-large-border-radius.html boxshadow-large-border-radius-ref.html # Bug 1209649 == boxshadow-border-radius-int.html boxshadow-border-radius-int-ref.html == boxshadow-inset-neg-spread.html about:blank +== boxshadow-inset-neg-spread2.html boxshadow-inset-neg-spread2-ref.html fuzzy(26,3610) == boxshadow-rotated.html boxshadow-rotated-ref.html # Bug 1211264 == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref.html @@ -37,3 +38,4 @@ fuzzy-if(winWidget,5,30) == fieldset-inset.html fieldset-inset-ref.html # minor == 1178575.html 1178575-ref.html == 1178575-2.html 1178575-2-ref.html fuzzy(159,2) fails-if(!d2d) == 1212823-1.html 1212823-1-ref.html +== boxshadow-large-offset.html boxshadow-large-offset-ref.html diff --git a/mfbt/Vector.h b/mfbt/Vector.h index d18bfa70bd5c..c801ef11c353 100644 --- a/mfbt/Vector.h +++ b/mfbt/Vector.h @@ -31,8 +31,8 @@ namespace mozilla { -template -class VectorBase; +template +class Vector; namespace detail { @@ -52,7 +52,7 @@ static bool CapacityHasExcessSpace(size_t aCapacity) * This template class provides a default implementation for vector operations * when the element type is not known to be a POD, as judged by IsPod. */ -template +template struct VectorImpl { /* @@ -138,7 +138,7 @@ struct VectorImpl * not overflow. */ static inline bool - growTo(VectorBase& aV, size_t aNewCap) + growTo(Vector& aV, size_t aNewCap) { MOZ_ASSERT(!aV.usingInlineStorage()); MOZ_ASSERT(!CapacityHasExcessSpace(aNewCap)); @@ -165,8 +165,8 @@ struct VectorImpl * vector operations when the element type is known to be a POD, as judged by * IsPod. */ -template -struct VectorImpl +template +struct VectorImpl { static inline void new_(T* aDst) { @@ -229,7 +229,7 @@ struct VectorImpl } static inline bool - growTo(VectorBase& aV, size_t aNewCap) + growTo(Vector& aV, size_t aNewCap) { MOZ_ASSERT(!aV.usingInlineStorage()); MOZ_ASSERT(!CapacityHasExcessSpace(aNewCap)); @@ -251,20 +251,33 @@ struct VectorTesting; } // namespace detail /* - * A CRTP base class for vector-like classes. Unless you really really want - * your own vector class -- and you almost certainly don't -- you should use - * mozilla::Vector instead! + * STL-like container providing a short-lived, dynamic buffer. Vector calls the + * constructors/destructors of all elements stored in its internal buffer, so + * non-PODs may be safely used. Additionally, Vector will store the first N + * elements in-place before resorting to dynamic allocation. * - * See mozilla::Vector for interface requirements. + * T requirements: + * - default and copy constructible, assignable, destructible + * - operations do not throw + * MinInlineCapacity requirements: + * - any value, however, MinInlineCapacity is clamped to min/max values + * AllocPolicy: + * - see "Allocation policies" in AllocPolicy.h (defaults to + * mozilla::MallocAllocPolicy) + * + * Vector is not reentrant: T member functions called during Vector member + * functions must not call back into the same object! */ -template -class VectorBase : private AllocPolicy +template +class Vector final : private AllocPolicy { /* utilities */ static const bool kElemIsPod = IsPod::value; - typedef detail::VectorImpl Impl; - friend struct detail::VectorImpl; + typedef detail::VectorImpl Impl; + friend struct detail::VectorImpl; friend struct detail::VectorTesting; @@ -298,11 +311,11 @@ class VectorBase : private AllocPolicy }; static const size_t kInlineCapacity = - tl::Min::value>::value; + tl::Min::value>::value; /* Calculate inline buffer size; avoid 0-sized array. */ static const size_t kInlineBytes = - tl::Max<1, kInlineCapacity * ElemSize::value>::value; + tl::Max<1, kInlineCapacity * ElemSize::value>::value; /* member data */ @@ -338,7 +351,7 @@ class VectorBase : private AllocPolicy bool usingInlineStorage() const { - return mBegin == const_cast(this)->inlineStorage(); + return mBegin == const_cast(this)->inlineStorage(); } T* inlineStorage() @@ -379,20 +392,20 @@ class VectorBase : private AllocPolicy /* Append operations guaranteed to succeed due to pre-reserved space. */ template void internalAppend(U&& aU); - template - void internalAppendAll(const VectorBase& aU); + template + void internalAppendAll(const Vector& aU); void internalAppendN(const T& aT, size_t aN); template void internalAppend(const U* aBegin, size_t aLength); public: - static const size_t sMaxInlineStorage = N; + static const size_t sMaxInlineStorage = MinInlineCapacity; typedef T ElementType; - explicit VectorBase(AllocPolicy = AllocPolicy()); - explicit VectorBase(ThisVector&&); /* Move constructor. */ - ThisVector& operator=(ThisVector&&); /* Move assignment. */ - ~VectorBase(); + explicit Vector(AllocPolicy = AllocPolicy()); + Vector(Vector&&); /* Move constructor. */ + Vector& operator=(Vector&&); /* Move assignment. */ + ~Vector(); /* accessors */ @@ -400,7 +413,7 @@ public: AllocPolicy& allocPolicy() { return *this; } - enum { InlineLength = N }; + enum { InlineLength = MinInlineCapacity }; size_t length() const { return mLength; } @@ -462,7 +475,7 @@ public: class Range { - friend class VectorBase; + friend class Vector; T* mCur; T* mEnd; Range(T* aCur, T* aEnd) @@ -482,7 +495,7 @@ public: class ConstRange { - friend class VectorBase; + friend class Vector; const T* mCur; const T* mEnd; ConstRange(const T* aCur, const T* aEnd) @@ -576,8 +589,8 @@ public: return true; } - template - bool appendAll(const VectorBase& aU); + template + bool appendAll(const Vector& aU); bool appendN(const T& aT, size_t aN); template bool append(const U* aBegin, const U* aEnd); template bool append(const U* aBegin, size_t aLength); @@ -678,15 +691,11 @@ public: */ size_t sizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; - void swap(ThisVector& aOther); + void swap(Vector& aOther); private: - VectorBase(const VectorBase&) = delete; - void operator=(const VectorBase&) = delete; - - /* Move-construct/assign only from our derived class, ThisVector. */ - VectorBase(VectorBase&&) = delete; - void operator=(VectorBase&&) = delete; + Vector(const Vector&) = delete; + void operator=(const Vector&) = delete; }; /* This does the re-entrancy check plus several other sanity checks. */ @@ -699,9 +708,9 @@ private: /* Vector Implementation */ -template +template MOZ_ALWAYS_INLINE -VectorBase::VectorBase(AP aAP) +Vector::Vector(AP aAP) : AP(aAP) , mLength(0) , mCapacity(kInlineCapacity) @@ -714,9 +723,9 @@ VectorBase::VectorBase(AP aAP) } /* Move constructor. */ -template +template MOZ_ALWAYS_INLINE -VectorBase::VectorBase(TV&& aRhs) +Vector::Vector(Vector&& aRhs) : AllocPolicy(Move(aRhs)) #ifdef DEBUG , mEntered(false) @@ -752,20 +761,19 @@ VectorBase::VectorBase(TV&& aRhs) } /* Move assignment. */ -template -MOZ_ALWAYS_INLINE TV& -VectorBase::operator=(TV&& aRhs) +template +MOZ_ALWAYS_INLINE Vector& +Vector::operator=(Vector&& aRhs) { MOZ_ASSERT(this != &aRhs, "self-move assignment is prohibited"); - TV* tv = static_cast(this); - tv->~TV(); - new(tv) TV(Move(aRhs)); - return *tv; + this->~Vector(); + new(this) Vector(Move(aRhs)); + return *this; } -template +template MOZ_ALWAYS_INLINE -VectorBase::~VectorBase() +Vector::~Vector() { MOZ_REENTRANCY_GUARD_ET_AL; Impl::destroy(beginNoCheck(), endNoCheck()); @@ -779,9 +787,9 @@ VectorBase::~VectorBase() * move all elements in the inline buffer to this new buffer, * and fail on OOM. */ -template +template inline bool -VectorBase::convertToHeapStorage(size_t aNewCap) +Vector::convertToHeapStorage(size_t aNewCap) { MOZ_ASSERT(usingInlineStorage()); @@ -803,9 +811,9 @@ VectorBase::convertToHeapStorage(size_t aNewCap) return true; } -template +template MOZ_NEVER_INLINE bool -VectorBase::growStorageBy(size_t aIncr) +Vector::growStorageBy(size_t aIncr) { MOZ_ASSERT(mLength + aIncr > mCapacity); @@ -885,9 +893,9 @@ grow: return Impl::growTo(*this, newCap); } -template +template inline bool -VectorBase::initCapacity(size_t aRequest) +Vector::initCapacity(size_t aRequest) { MOZ_ASSERT(empty()); MOZ_ASSERT(usingInlineStorage()); @@ -906,9 +914,9 @@ VectorBase::initCapacity(size_t aRequest) return true; } -template +template inline bool -VectorBase::reserve(size_t aRequest) +Vector::reserve(size_t aRequest) { MOZ_REENTRANCY_GUARD_ET_AL; if (aRequest > mCapacity) { @@ -930,9 +938,9 @@ VectorBase::reserve(size_t aRequest) return true; } -template +template inline void -VectorBase::shrinkBy(size_t aIncr) +Vector::shrinkBy(size_t aIncr) { MOZ_REENTRANCY_GUARD_ET_AL; MOZ_ASSERT(aIncr <= mLength); @@ -940,9 +948,9 @@ VectorBase::shrinkBy(size_t aIncr) mLength -= aIncr; } -template +template MOZ_ALWAYS_INLINE bool -VectorBase::growBy(size_t aIncr) +Vector::growBy(size_t aIncr) { MOZ_REENTRANCY_GUARD_ET_AL; if (aIncr > mCapacity - mLength) { @@ -966,9 +974,9 @@ VectorBase::growBy(size_t aIncr) return true; } -template +template MOZ_ALWAYS_INLINE bool -VectorBase::growByUninitialized(size_t aIncr) +Vector::growByUninitialized(size_t aIncr) { MOZ_REENTRANCY_GUARD_ET_AL; if (aIncr > mCapacity - mLength) { @@ -984,9 +992,9 @@ VectorBase::growByUninitialized(size_t aIncr) return true; } -template +template MOZ_ALWAYS_INLINE void -VectorBase::infallibleGrowByUninitialized(size_t aIncr) +Vector::infallibleGrowByUninitialized(size_t aIncr) { MOZ_ASSERT(mLength + aIncr <= mCapacity); mLength += aIncr; @@ -997,9 +1005,9 @@ VectorBase::infallibleGrowByUninitialized(size_t aIncr) #endif } -template +template inline bool -VectorBase::resize(size_t aNewLength) +Vector::resize(size_t aNewLength) { size_t curLength = mLength; if (aNewLength > curLength) { @@ -1009,9 +1017,9 @@ VectorBase::resize(size_t aNewLength) return true; } -template +template MOZ_ALWAYS_INLINE bool -VectorBase::resizeUninitialized(size_t aNewLength) +Vector::resizeUninitialized(size_t aNewLength) { size_t curLength = mLength; if (aNewLength > curLength) { @@ -1021,18 +1029,18 @@ VectorBase::resizeUninitialized(size_t aNewLength) return true; } -template +template inline void -VectorBase::clear() +Vector::clear() { MOZ_REENTRANCY_GUARD_ET_AL; Impl::destroy(beginNoCheck(), endNoCheck()); mLength = 0; } -template +template inline void -VectorBase::clearAndFree() +Vector::clearAndFree() { clear(); @@ -1047,26 +1055,25 @@ VectorBase::clearAndFree() #endif } -template +template inline bool -VectorBase::canAppendWithoutRealloc(size_t aNeeded) const +Vector::canAppendWithoutRealloc(size_t aNeeded) const { return mLength + aNeeded <= mCapacity; } -template -template +template +template MOZ_ALWAYS_INLINE void -VectorBase::internalAppendAll( - const VectorBase& aOther) +Vector::internalAppendAll(const Vector& aOther) { internalAppend(aOther.begin(), aOther.length()); } -template +template template MOZ_ALWAYS_INLINE void -VectorBase::internalAppend(U&& aU) +Vector::internalAppend(U&& aU) { MOZ_ASSERT(mLength + 1 <= mReserved); MOZ_ASSERT(mReserved <= mCapacity); @@ -1074,9 +1081,9 @@ VectorBase::internalAppend(U&& aU) ++mLength; } -template +template MOZ_ALWAYS_INLINE bool -VectorBase::appendN(const T& aT, size_t aNeeded) +Vector::appendN(const T& aT, size_t aNeeded) { MOZ_REENTRANCY_GUARD_ET_AL; if (mLength + aNeeded > mCapacity) { @@ -1096,9 +1103,9 @@ VectorBase::appendN(const T& aT, size_t aNeeded) return true; } -template +template MOZ_ALWAYS_INLINE void -VectorBase::internalAppendN(const T& aT, size_t aNeeded) +Vector::internalAppendN(const T& aT, size_t aNeeded) { MOZ_ASSERT(mLength + aNeeded <= mReserved); MOZ_ASSERT(mReserved <= mCapacity); @@ -1106,10 +1113,10 @@ VectorBase::internalAppendN(const T& aT, size_t aNeeded) mLength += aNeeded; } -template +template template inline T* -VectorBase::insert(T* aP, U&& aVal) +Vector::insert(T* aP, U&& aVal) { MOZ_ASSERT(begin() <= aP); MOZ_ASSERT(aP <= end()); @@ -1133,9 +1140,9 @@ VectorBase::insert(T* aP, U&& aVal) return begin() + pos; } -template +template inline void -VectorBase::erase(T* aIt) +Vector::erase(T* aIt) { MOZ_ASSERT(begin() <= aIt); MOZ_ASSERT(aIt < end()); @@ -1146,9 +1153,9 @@ VectorBase::erase(T* aIt) popBack(); } -template +template inline void -VectorBase::erase(T* aBegin, T* aEnd) +Vector::erase(T* aBegin, T* aEnd) { MOZ_ASSERT(begin() <= aBegin); MOZ_ASSERT(aBegin <= aEnd); @@ -1159,10 +1166,10 @@ VectorBase::erase(T* aBegin, T* aEnd) shrinkBy(aEnd - aBegin); } -template +template template MOZ_ALWAYS_INLINE bool -VectorBase::append(const U* aInsBegin, const U* aInsEnd) +Vector::append(const U* aInsBegin, const U* aInsEnd) { MOZ_REENTRANCY_GUARD_ET_AL; size_t aNeeded = PointerRangeSize(aInsBegin, aInsEnd); @@ -1183,10 +1190,10 @@ VectorBase::append(const U* aInsBegin, const U* aInsEnd) return true; } -template +template template MOZ_ALWAYS_INLINE void -VectorBase::internalAppend(const U* aInsBegin, size_t aInsLength) +Vector::internalAppend(const U* aInsBegin, size_t aInsLength) { MOZ_ASSERT(mLength + aInsLength <= mReserved); MOZ_ASSERT(mReserved <= mCapacity); @@ -1194,10 +1201,10 @@ VectorBase::internalAppend(const U* aInsBegin, size_t aInsLength) mLength += aInsLength; } -template +template template MOZ_ALWAYS_INLINE bool -VectorBase::append(U&& aU) +Vector::append(U&& aU) { MOZ_REENTRANCY_GUARD_ET_AL; if (mLength == mCapacity) { @@ -1217,25 +1224,25 @@ VectorBase::append(U&& aU) return true; } -template -template +template +template MOZ_ALWAYS_INLINE bool -VectorBase::appendAll(const VectorBase& aOther) +Vector::appendAll(const Vector& aOther) { return append(aOther.begin(), aOther.length()); } -template +template template MOZ_ALWAYS_INLINE bool -VectorBase::append(const U* aInsBegin, size_t aInsLength) +Vector::append(const U* aInsBegin, size_t aInsLength) { return append(aInsBegin, aInsBegin + aInsLength); } -template +template MOZ_ALWAYS_INLINE void -VectorBase::popBack() +Vector::popBack() { MOZ_REENTRANCY_GUARD_ET_AL; MOZ_ASSERT(!empty()); @@ -1243,18 +1250,18 @@ VectorBase::popBack() endNoCheck()->~T(); } -template +template MOZ_ALWAYS_INLINE T -VectorBase::popCopy() +Vector::popCopy() { T ret = back(); popBack(); return ret; } -template +template inline T* -VectorBase::extractRawBuffer() +Vector::extractRawBuffer() { T* ret; if (usingInlineStorage()) { @@ -1278,9 +1285,9 @@ VectorBase::extractRawBuffer() return ret; } -template +template inline void -VectorBase::replaceRawBuffer(T* aP, size_t aLength) +Vector::replaceRawBuffer(T* aP, size_t aLength) { MOZ_REENTRANCY_GUARD_ET_AL; @@ -1313,23 +1320,23 @@ VectorBase::replaceRawBuffer(T* aP, size_t aLength) #endif } -template +template inline size_t -VectorBase::sizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const +Vector::sizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { return usingInlineStorage() ? 0 : aMallocSizeOf(beginNoCheck()); } -template +template inline size_t -VectorBase::sizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const +Vector::sizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this) + sizeOfExcludingThis(aMallocSizeOf); } -template +template inline void -VectorBase::swap(TV& aOther) +Vector::swap(Vector& aOther) { static_assert(N == 0, "still need to implement this for N != 0"); @@ -1354,44 +1361,6 @@ VectorBase::swap(TV& aOther) #endif } -/* - * STL-like container providing a short-lived, dynamic buffer. Vector calls the - * constructors/destructors of all elements stored in its internal buffer, so - * non-PODs may be safely used. Additionally, Vector will store the first N - * elements in-place before resorting to dynamic allocation. - * - * T requirements: - * - default and copy constructible, assignable, destructible - * - operations do not throw - * N requirements: - * - any value, however, N is clamped to min/max values - * AllocPolicy: - * - see "Allocation policies" in AllocPolicy.h (defaults to - * mozilla::MallocAllocPolicy) - * - * Vector is not reentrant: T member functions called during Vector member - * functions must not call back into the same object! - */ -template -class Vector - : public VectorBase > -{ - typedef VectorBase Base; - -public: - explicit Vector(AllocPolicy alloc = AllocPolicy()) : Base(alloc) {} - Vector(Vector&& vec) : Base(Move(vec)) {} - Vector& operator=(Vector&& aOther) - { - return Base::operator=(Move(aOther)); - } -}; - } // namespace mozilla #ifdef _MSC_VER diff --git a/modules/libjar/test/unit/test_bug370103.js b/modules/libjar/test/unit/test_bug370103.js index 5358c3e039e0..210cf7a1531f 100644 --- a/modules/libjar/test/unit/test_bug370103.js +++ b/modules/libjar/test/unit/test_bug370103.js @@ -1,7 +1,7 @@ var Cc = Components.classes; var Ci = Components.interfaces; var Cu = Components.utils; -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); // Regression test for bug 370103 - crash when passing a null listener to // nsIChannel.asyncOpen @@ -14,18 +14,11 @@ function run_test() { url = "jar:" + url + "!/test_bug370103"; // Try opening channel with null listener - var channel = ioService.newChannel2(url, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}); var exception = false; try { - channel.asyncOpen(null, null); + channel.asyncOpen2(null); } catch(e) { exception = true; diff --git a/modules/libjar/test/unit/test_bug407303.js b/modules/libjar/test/unit/test_bug407303.js index d137a3a65eb7..313d88c6414e 100644 --- a/modules/libjar/test/unit/test_bug407303.js +++ b/modules/libjar/test/unit/test_bug407303.js @@ -5,6 +5,7 @@ var Cc = Components.classes; var Ci = Components.interfaces; var Cu = Components.utils; Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); // XXX: NS_ERROR_UNKNOWN_HOST is not in Components.results const NS_ERROR_UNKNOWN_HOST = 0x804B001E; @@ -32,18 +33,10 @@ var listener = { function run_test() { Services.prefs.setBoolPref("network.jar.block-remote-files", false); - - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - - var channel = ios.newChannel2("jar:http://test.invalid/test.jar!/index.html", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - channel.asyncOpen(listener, null); + var channel = NetUtil.newChannel({ + uri: "jar:http://test.invalid/test.jar!/index.html", + loadUsingSystemPrincipal: true} + ); + channel.asyncOpen2(listener); do_test_pending(); } diff --git a/modules/libjar/test/unit/test_jarchannel.js b/modules/libjar/test/unit/test_jarchannel.js index 49421b14c2b0..eca6151d802f 100644 --- a/modules/libjar/test/unit/test_jarchannel.js +++ b/modules/libjar/test/unit/test_jarchannel.js @@ -15,6 +15,7 @@ const {classes: Cc, } = Components; Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); const ios = Cc["@mozilla.org/network/io-service;1"]. getService(Ci.nsIIOService); @@ -75,23 +76,16 @@ Listener.prototype = { */ function testAsync() { var uri = jarBase + "/inner40.zip"; - var chan = ios.newChannel2(uri, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var chan = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}); do_check_true(chan.contentLength < 0); - chan.asyncOpen(new Listener(function(l) { + chan.asyncOpen2(new Listener(function(l) { do_check_true(chan.contentLength > 0); do_check_true(l.gotStartRequest); do_check_true(l.gotStopRequest); do_check_eq(l.available, chan.contentLength); run_next_test(); - }), null); + })); } add_test(testAsync); @@ -104,15 +98,8 @@ add_test(testAsync); */ function testZipEntry() { var uri = jarBase + "/inner40.zip"; - var chan = ios.newChannel2(uri, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) - .QueryInterface(Ci.nsIJARChannel); + var chan = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIJARChannel); var entry = chan.zipEntry; do_check_true(entry.CRC32 == 0x8b635486); do_check_true(entry.realSize == 184); @@ -132,15 +119,8 @@ if (!inChild) { */ add_test(function testSync() { var uri = jarBase + "/inner40.zip"; - var chan = ios.newChannel2(uri, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - var stream = chan.open(); + var chan = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}); + var stream = chan.open2(); do_check_true(chan.contentLength > 0); do_check_eq(stream.available(), chan.contentLength); stream.close(); @@ -155,15 +135,8 @@ if (!inChild) { */ add_test(function testSyncNested() { var uri = "jar:" + jarBase + "/inner40.zip!/foo"; - var chan = ios.newChannel2(uri, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - var stream = chan.open(); + var chan = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}); + var stream = chan.open2(); do_check_true(chan.contentLength > 0); do_check_eq(stream.available(), chan.contentLength); stream.close(); @@ -177,22 +150,15 @@ if (!inChild) { */ add_test(function testAsyncNested(next) { var uri = "jar:" + jarBase + "/inner40.zip!/foo"; - var chan = ios.newChannel2(uri, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - chan.asyncOpen(new Listener(function(l) { + var chan = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}); + chan.asyncOpen2(new Listener(function(l) { do_check_true(chan.contentLength > 0); do_check_true(l.gotStartRequest); do_check_true(l.gotStopRequest); do_check_eq(l.available, chan.contentLength); run_next_test(); - }), null); + })); }); /** @@ -203,17 +169,9 @@ if (!inChild) { var copy = tmpDir.clone(); copy.append(fileBase); file.copyTo(copy.parent, copy.leafName); - var uri = "jar:" + ios.newFileURI(copy).spec + "!/inner40.zip"; - var chan = ios.newChannel2(uri, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - var stream = chan.open(); + var chan = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}); + var stream = chan.open2(); do_check_true(chan.contentLength > 0); stream.close(); @@ -240,15 +198,9 @@ if (!inChild) { file.copyTo(copy.parent, copy.leafName); var uri = "jar:" + ios.newFileURI(copy).spec + "!/inner40.zip"; - var chan = ios.newChannel2(uri, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - chan.asyncOpen(new Listener(function (l) { + var chan = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}); + + chan.asyncOpen2(new Listener(function (l) { do_check_true(chan.contentLength > 0); // Drop any jar caches @@ -262,7 +214,7 @@ if (!inChild) { } run_next_test(); - }), null); + })); }); } // if !inChild @@ -281,32 +233,15 @@ if (inChild) { var num = 10; var chan = []; for (var i = 0; i < num; i++) { - chan[i] = ios.newChannel2(uri, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) - .QueryInterface(Ci.nsIJARChannel); - chan[i].asyncOpen(new Listener(function(l) { - }), null); + chan[i] = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIJARChannel); + chan[i].asyncOpen2(new Listener(function(l) {})); } // Open the last channel - var chan_last = ios.newChannel2(uri, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) - .QueryInterface(Ci.nsIJARChannel); - chan_last.asyncOpen(new Listener(function(l) { - run_next_test(); - }), null); + var chan_last = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIJARChannel); + chan_last.asyncOpen2(new Listener(function(l) { run_next_test(); })); }); } // if inChild diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp index bfdf64a1ba4b..d0ed2eef48dc 100644 --- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp @@ -42,6 +42,8 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal, , mEnforceSecurity(false) , mInitialSecurityCheckDone(false) , mIsThirdPartyContext(true) + , mForcePreflight(false) + , mIsPreflight(false) { MOZ_ASSERT(mLoadingPrincipal); MOZ_ASSERT(mTriggeringPrincipal); @@ -111,6 +113,9 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) , mRedirectChainIncludingInternalRedirects( rhs.mRedirectChainIncludingInternalRedirects) , mRedirectChain(rhs.mRedirectChain) + , mCorsUnsafeHeaders(rhs.mCorsUnsafeHeaders) + , mForcePreflight(rhs.mForcePreflight) + , mIsPreflight(rhs.mIsPreflight) { } @@ -118,6 +123,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal, nsSecurityFlags aSecurityFlags, nsContentPolicyType aContentPolicyType, + LoadTainting aTainting, bool aUpgradeInsecureRequests, bool aUpgradeInsecurePreloads, uint64_t aInnerWindowID, @@ -128,12 +134,15 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal, bool aIsThirdPartyContext, const NeckoOriginAttributes& aOriginAttributes, nsTArray>& aRedirectChainIncludingInternalRedirects, - nsTArray>& aRedirectChain) + nsTArray>& aRedirectChain, + const nsTArray& aCorsUnsafeHeaders, + bool aForcePreflight, + bool aIsPreflight) : mLoadingPrincipal(aLoadingPrincipal) , mTriggeringPrincipal(aTriggeringPrincipal) , mSecurityFlags(aSecurityFlags) , mInternalContentPolicyType(aContentPolicyType) - , mTainting(LoadTainting::Basic) + , mTainting(aTainting) , mUpgradeInsecureRequests(aUpgradeInsecureRequests) , mUpgradeInsecurePreloads(aUpgradeInsecurePreloads) , mInnerWindowID(aInnerWindowID) @@ -143,6 +152,9 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal, , mInitialSecurityCheckDone(aInitialSecurityCheckDone) , mIsThirdPartyContext(aIsThirdPartyContext) , mOriginAttributes(aOriginAttributes) + , mCorsUnsafeHeaders(aCorsUnsafeHeaders) + , mForcePreflight(aForcePreflight) + , mIsPreflight(aIsPreflight) { MOZ_ASSERT(mLoadingPrincipal); MOZ_ASSERT(mTriggeringPrincipal); @@ -257,14 +269,6 @@ LoadInfo::GetSecurityFlags(nsSecurityFlags* aResult) return NS_OK; } -void -LoadInfo::SetWithCredentialsSecFlag() -{ - MOZ_ASSERT(!mEnforceSecurity, - "Request should not have been opened yet"); - mSecurityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS; -} - NS_IMETHODIMP LoadInfo::GetSecurityMode(uint32_t* aFlags) { @@ -284,14 +288,36 @@ LoadInfo::GetIsInThirdPartyContext(bool* aIsInThirdPartyContext) return NS_OK; } +static const uint32_t sCookiePolicyMask = + nsILoadInfo::SEC_COOKIES_DEFAULT | + nsILoadInfo::SEC_COOKIES_INCLUDE | + nsILoadInfo::SEC_COOKIES_SAME_ORIGIN | + nsILoadInfo::SEC_COOKIES_OMIT; + NS_IMETHODIMP -LoadInfo::GetRequireCorsWithCredentials(bool* aResult) +LoadInfo::GetCookiePolicy(uint32_t *aResult) { - *aResult = - (mSecurityFlags & nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS); + uint32_t policy = mSecurityFlags & sCookiePolicyMask; + if (policy == nsILoadInfo::SEC_COOKIES_DEFAULT) { + policy = (mSecurityFlags & SEC_REQUIRE_CORS_DATA_INHERITS) ? + nsILoadInfo::SEC_COOKIES_SAME_ORIGIN : nsILoadInfo::SEC_COOKIES_INCLUDE; + } + + *aResult = policy; return NS_OK; } +void +LoadInfo::SetIncludeCookiesSecFlag() +{ + MOZ_ASSERT(!mEnforceSecurity, + "Request should not have been opened yet"); + MOZ_ASSERT((mSecurityFlags & sCookiePolicyMask) == + nsILoadInfo::SEC_COOKIES_DEFAULT); + mSecurityFlags = (mSecurityFlags & ~sCookiePolicyMask) | + nsILoadInfo::SEC_COOKIES_INCLUDE; +} + NS_IMETHODIMP LoadInfo::GetForceInheritPrincipal(bool* aInheritPrincipal) { @@ -497,6 +523,44 @@ LoadInfo::RedirectChain() return mRedirectChain; } +void +LoadInfo::SetCorsPreflightInfo(const nsTArray& aHeaders, + bool aForcePreflight) +{ + MOZ_ASSERT(GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS); + MOZ_ASSERT(!mInitialSecurityCheckDone); + mCorsUnsafeHeaders = aHeaders; + mForcePreflight = aForcePreflight; +} + +const nsTArray& +LoadInfo::CorsUnsafeHeaders() +{ + return mCorsUnsafeHeaders; +} + +NS_IMETHODIMP +LoadInfo::GetForcePreflight(bool* aForcePreflight) +{ + *aForcePreflight = mForcePreflight; + return NS_OK; +} + +void +LoadInfo::SetIsPreflight() +{ + MOZ_ASSERT(GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS); + MOZ_ASSERT(!mInitialSecurityCheckDone); + mIsPreflight = true; +} + +NS_IMETHODIMP +LoadInfo::GetIsPreflight(bool* aIsPreflight) +{ + *aIsPreflight = mIsPreflight; + return NS_OK; +} + NS_IMETHODIMP LoadInfo::GetTainting(uint32_t* aTaintingOut) { diff --git a/netwerk/base/LoadInfo.h b/netwerk/base/LoadInfo.h index 35a9a57a0301..edece211cddd 100644 --- a/netwerk/base/LoadInfo.h +++ b/netwerk/base/LoadInfo.h @@ -61,6 +61,8 @@ public: // when a separate request is made with the same security properties. already_AddRefed CloneForNewRequest() const; + void SetIsPreflight(); + private: // private constructor that is only allowed to be called from within // HttpChannelParent and FTPChannelParent declared as friends undeneath. @@ -70,6 +72,7 @@ private: nsIPrincipal* aTriggeringPrincipal, nsSecurityFlags aSecurityFlags, nsContentPolicyType aContentPolicyType, + LoadTainting aTainting, bool aUpgradeInsecureRequests, bool aUpgradeInsecurePreloads, uint64_t aInnerWindowID, @@ -80,7 +83,10 @@ private: bool aIsThirdPartyRequest, const NeckoOriginAttributes& aOriginAttributes, nsTArray>& aRedirectChainIncludingInternalRedirects, - nsTArray>& aRedirectChain); + nsTArray>& aRedirectChain, + const nsTArray& aUnsafeHeaders, + bool aForcePreflight, + bool aIsPreflight); LoadInfo(const LoadInfo& rhs); friend nsresult @@ -95,7 +101,7 @@ private: // This function is the *only* function which can change the securityflags // of a loadinfo. It only exists because of the XHR code. Don't call it // from anywhere else! - void SetWithCredentialsSecFlag(); + void SetIncludeCookiesSecFlag(); friend class ::nsXMLHttpRequest; // if you add a member, please also update the copy constructor @@ -116,6 +122,9 @@ private: NeckoOriginAttributes mOriginAttributes; nsTArray> mRedirectChainIncludingInternalRedirects; nsTArray> mRedirectChain; + nsTArray mCorsUnsafeHeaders; + bool mForcePreflight; + bool mIsPreflight; }; } // namespace mozilla diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl index dff7c03c2966..1ca7ef7e3028 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl @@ -15,18 +15,21 @@ interface nsIPrincipal; #include "nsTArray.h" #include "mozilla/BasePrincipal.h" #include "mozilla/LoadTainting.h" + +class nsCString; %} [ref] native const_nsIPrincipalArray(const nsTArray>); native NeckoOriginAttributes(mozilla::NeckoOriginAttributes); [ref] native const_OriginAttributesRef(const mozilla::NeckoOriginAttributes); +[ref] native StringArrayRef(const nsTArray); typedef unsigned long nsSecurityFlags; /** * An nsILoadOwner represents per-load information about who started the load. */ -[scriptable, builtinclass, uuid(5a2dce9f-accd-45ae-98cc-319dec0ae4f0)] +[scriptable, builtinclass, uuid(41e311d0-5894-4aaa-80b5-5b7099dfc404)] interface nsILoadInfo : nsISupports { /** @@ -86,11 +89,21 @@ interface nsILoadInfo : nsISupports const unsigned long SEC_REQUIRE_CORS_DATA_INHERITS = (1<<4); /** - * Use this flag in addition to SEC_REQUIRE_CORS_DATA_INHERITS - * to make cross-origin CORS loads happen with credentials - * (such as cookies and client side certs). + * Choose cookie policy. The default policy is equivalent to "INCLUDE" for + * SEC_REQUIRE_SAME_ORIGIN_* and SEC_ALLOW_CROSS_ORIGIN_* modes, and + * equivalent to "SAME_ORIGIN" for SEC_REQUIRE_CORS_DATA_INHERITS mode. + * + * This means that if you want to perform a CORS load with credentials, pass + * SEC_COOKIES_INCLUDE. + * + * Note that these flags are still subject to the user's cookie policies. + * For example, if the user is blocking 3rd party cookies, those cookies + * will be blocked no matter which of these flags are set. */ - const unsigned long SEC_REQUIRE_CORS_WITH_CREDENTIALS = (1<<5); + const unsigned long SEC_COOKIES_DEFAULT = (0 << 5); + const unsigned long SEC_COOKIES_INCLUDE = (1 << 5); + const unsigned long SEC_COOKIES_SAME_ORIGIN = (2 << 5); + const unsigned long SEC_COOKIES_OMIT = (3 << 5); /** * Force inheriting of the Principal. The resulting resource will use the @@ -107,7 +120,7 @@ interface nsILoadInfo : nsISupports * * This flag can not be used together with SEC_SANDBOXED. */ - const unsigned long SEC_FORCE_INHERIT_PRINCIPAL = (1<<6); + const unsigned long SEC_FORCE_INHERIT_PRINCIPAL = (1<<7); /** * Sandbox the load. The resulting resource will use a freshly created @@ -119,17 +132,17 @@ interface nsILoadInfo : nsISupports * * This flag can not be used together with SEC_FORCE_INHERIT_PRINCIPAL. */ - const unsigned long SEC_SANDBOXED = (1<<7); + const unsigned long SEC_SANDBOXED = (1<<8); /** * Inherit the Principal for about:blank. */ - const unsigned long SEC_ABOUT_BLANK_INHERITS = (1<<8); + const unsigned long SEC_ABOUT_BLANK_INHERITS = (1<<9); /** * Allow access to chrome: packages that are content accessible. */ - const unsigned long SEC_ALLOW_CHROME = (1<<9); + const unsigned long SEC_ALLOW_CHROME = (1<<10); /** * Don't follow redirects. Instead the redirect response is returned @@ -142,7 +155,7 @@ interface nsILoadInfo : nsISupports * the response body might not be available. * This can happen if the redirect was cached. */ - const unsigned long SEC_DONT_FOLLOW_REDIRECTS = (1<<10); + const unsigned long SEC_DONT_FOLLOW_REDIRECTS = (1<<11); /** * The loadingPrincipal is the principal that is responsible for the load. @@ -241,10 +254,12 @@ interface nsILoadInfo : nsISupports [infallible] readonly attribute boolean isInThirdPartyContext; /** - * Determines whether credentials are sent with CORS requests. - * Using this flag requires SEC_REQUIRE_CORS_DATA_INHERITS also to be set. + * See the SEC_COOKIES_* flags above. This attribute will never return + * SEC_COOKIES_DEFAULT, but will instead return what the policy resolves to. + * I.e. SEC_COOKIES_SAME_ORIGIN for CORS mode, and SEC_COOKIES_INCLUDE + * otherwise. */ - [infallible] readonly attribute boolean requireCorsWithCredentials; + [infallible] readonly attribute unsigned long cookiePolicy; /** * If forceInheritPrincipal is true, the data coming from the channel should @@ -439,6 +454,36 @@ interface nsILoadInfo : nsISupports [noscript, notxpcom, nostdcall, binaryname(RedirectChain)] const_nsIPrincipalArray binaryRedirectChain(); + /** + * Sets the list of unsafe headers according to CORS spec, as well as + * potentially forces a preflight. + * Note that you do not need to set the Content-Type header. That will be + * automatically detected as needed. + * + * Only call this function when using the SEC_REQUIRE_CORS_DATA_INHERITS mode. + */ + [noscript, notxpcom, nostdcall] + void setCorsPreflightInfo(in StringArrayRef unsafeHeaders, + in boolean forcePreflight); + + /** + * A C++-friendly getter for the list of cors-unsafe headers. + * Please note that this array has the same lifetime as the + * loadInfo object - use with caution! + */ + [noscript, notxpcom, nostdcall, binaryname(CorsUnsafeHeaders)] + StringArrayRef corsUnsafeHeaders(); + + /** + * Returns value set through setCorsPreflightInfo. + */ + [infallible] readonly attribute boolean forcePreflight; + + /** + * A C++ friendly getter for the forcePreflight flag. + */ + [infallible] readonly attribute boolean isPreflight; + /** * Constants reflecting the channel tainting. These are mainly defined here * for script. Internal C++ code should use the enum defined in LoadTainting.h. diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp index cf87b01a8bbb..ad86c9524dca 100644 --- a/netwerk/base/nsIOService.cpp +++ b/netwerk/base/nsIOService.cpp @@ -50,6 +50,7 @@ #include "CaptivePortalService.h" #include "ClosingService.h" #include "ReferrerPolicy.h" +#include "nsContentSecurityManager.h" #ifdef MOZ_WIDGET_GONK #include "nsINetworkManager.h" @@ -415,8 +416,11 @@ nsIOService::AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan, // are in a captive portal, so we trigger a recheck. RecheckCaptivePortalIfLocalRedirect(newChan); + // This is silly. I wish there was a simpler way to get at the global + // reference of the contentSecurityManager. But it lives in the XPCOM + // service registry. nsCOMPtr sink = - do_GetService(NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID); + do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID); if (sink) { nsresult rv = helper->DelegateOnChannelRedirect(sink, oldChan, newChan, flags); diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp index 42e7795358f5..0389e94cc987 100644 --- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -1252,6 +1252,55 @@ NS_GetAppInfo(nsIChannel *aChannel, return true; } +bool +NS_HasBeenCrossOrigin(nsIChannel* aChannel, bool aReport) +{ + nsCOMPtr loadInfo = aChannel->GetLoadInfo(); + MOZ_RELEASE_ASSERT(loadInfo, "Origin tracking only works for channels created with a loadinfo"); + + // Always treat tainted channels as cross-origin. + if (loadInfo->GetTainting() != LoadTainting::Basic) { + return true; + } + + nsCOMPtr loadingPrincipal = loadInfo->LoadingPrincipal(); + uint32_t mode = loadInfo->GetSecurityMode(); + bool dataInherits = + mode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS || + mode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS || + mode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; + + bool aboutBlankInherits = dataInherits && loadInfo->GetAboutBlankInherits(); + + for (nsIPrincipal* principal : loadInfo->RedirectChain()) { + nsCOMPtr uri; + principal->GetURI(getter_AddRefs(uri)); + if (!uri) { + return true; + } + + if (aboutBlankInherits && NS_IsAboutBlank(uri)) { + continue; + } + + if (NS_FAILED(loadingPrincipal->CheckMayLoad(uri, aReport, dataInherits))) { + return true; + } + } + + nsCOMPtr uri; + NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); + if (!uri) { + return true; + } + + if (aboutBlankInherits && NS_IsAboutBlank(uri)) { + return false; + } + + return NS_FAILED(loadingPrincipal->CheckMayLoad(uri, aReport, dataInherits)); +} + nsresult NS_GetAppInfoFromClearDataNotification(nsISupports *aSubject, uint32_t *aAppID, diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h index 232534c0a505..c001c2e1e7e9 100644 --- a/netwerk/base/nsNetUtil.h +++ b/netwerk/base/nsNetUtil.h @@ -691,6 +691,12 @@ bool NS_UsePrivateBrowsing(nsIChannel *channel); bool NS_GetOriginAttributes(nsIChannel *aChannel, mozilla::NeckoOriginAttributes &aAttributes); +/** + * Returns true if the channel has visited any cross-origin URLs on any + * URLs that it was redirected through. + */ +bool NS_HasBeenCrossOrigin(nsIChannel* aChannel, bool aReport = false); + // Constants duplicated from nsIScriptSecurityManager so we avoid having necko // know about script security manager. #define NECKO_NO_APP_ID 0 diff --git a/netwerk/build/nsNetCID.h b/netwerk/build/nsNetCID.h index 31d03726c0cf..f2f96cd107b8 100644 --- a/netwerk/build/nsNetCID.h +++ b/netwerk/build/nsNetCID.h @@ -1004,15 +1004,6 @@ * Contracts that can be implemented by necko users. */ -/** - * This contract ID will be gotten as a service and gets the opportunity to look - * at and veto all redirects that are processed by necko. - * - * Must implement nsIChannelEventSink - */ -#define NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID \ - "@mozilla.org/netwerk/global-channel-event-sink;1" - /** * This contract ID will be gotten as a service implementing nsINetworkLinkService * and monitored by IOService for automatic online/offline management. diff --git a/netwerk/ipc/NeckoChannelParams.ipdlh b/netwerk/ipc/NeckoChannelParams.ipdlh index 9bb82bfae306..85785ef3c452 100644 --- a/netwerk/ipc/NeckoChannelParams.ipdlh +++ b/netwerk/ipc/NeckoChannelParams.ipdlh @@ -31,6 +31,7 @@ struct LoadInfoArgs PrincipalInfo triggeringPrincipalInfo; uint32_t securityFlags; uint32_t contentPolicyType; + uint32_t tainting; bool upgradeInsecureRequests; bool upgradeInsecurePreloads; uint64_t innerWindowID; @@ -42,6 +43,9 @@ struct LoadInfoArgs NeckoOriginAttributes originAttributes; PrincipalInfo[] redirectChainIncludingInternalRedirects; PrincipalInfo[] redirectChain; + nsCString[] corsUnsafeHeaders; + bool forcePreflight; + bool isPreflight; }; /** @@ -65,8 +69,6 @@ union OptionalHttpResponseHead struct CorsPreflightArgs { - bool withCredentials; - PrincipalInfo preflightPrincipal; nsCString[] unsafeHeaders; }; diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 637fcb2437ab..7d710a2f58b9 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -99,7 +99,6 @@ HttpBaseChannel::HttpBaseChannel() , mDecodedBodySize(0) , mEncodedBodySize(0) , mRequireCORSPreflight(false) - , mWithCredentials(false) , mReportCollector(new ConsoleReportCollector()) , mForceMainDocumentChannel(false) { @@ -2610,11 +2609,7 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI, // Preserve the CORS preflight information. nsCOMPtr httpInternal = do_QueryInterface(newChannel); if (mRequireCORSPreflight && httpInternal) { - rv = httpInternal->SetCorsPreflightParameters(mUnsafeHeaders, mWithCredentials, - mPreflightPrincipal); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + httpInternal->SetCorsPreflightParameters(mUnsafeHeaders); } if (preserveMethod) { @@ -3107,18 +3102,13 @@ HttpBaseChannel::EnsureSchedulingContextID() return true; } -NS_IMETHODIMP -HttpBaseChannel::SetCorsPreflightParameters(const nsTArray& aUnsafeHeaders, - bool aWithCredentials, - nsIPrincipal* aPrincipal) +void +HttpBaseChannel::SetCorsPreflightParameters(const nsTArray& aUnsafeHeaders) { - ENSURE_CALLED_BEFORE_CONNECT(); + MOZ_RELEASE_ASSERT(!mRequestObserversCalled); mRequireCORSPreflight = true; mUnsafeHeaders = aUnsafeHeaders; - mWithCredentials = aWithCredentials; - mPreflightPrincipal = aPrincipal; - return NS_OK; } } // namespace net diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index 39db21e70773..9c1029e51e8d 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -213,9 +213,7 @@ public: NS_IMETHOD SetRedirectMode(uint32_t aRedirectMode) override; NS_IMETHOD GetTopWindowURI(nsIURI **aTopWindowURI) override; NS_IMETHOD GetProxyURI(nsIURI **proxyURI) override; - NS_IMETHOD SetCorsPreflightParameters(const nsTArray& unsafeHeaders, - bool aWithCredentials, - nsIPrincipal* aPrincipal) override; + virtual void SetCorsPreflightParameters(const nsTArray& unsafeHeaders) override; inline void CleanRedirectCacheChainIfNecessary() { @@ -492,9 +490,7 @@ protected: bool EnsureSchedulingContextID(); bool mRequireCORSPreflight; - bool mWithCredentials; nsTArray mUnsafeHeaders; - nsCOMPtr mPreflightPrincipal; nsCOMPtr mReportCollector; diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 63c96d32f689..51ec48458b82 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -1537,12 +1537,14 @@ HttpChannelChild::OnRedirectVerifyCallback(nsresult result) RequestHeaderTuples emptyHeaders; RequestHeaderTuples* headerTuples = &emptyHeaders; nsLoadFlags loadFlags = 0; + OptionalCorsPreflightArgs corsPreflightArgs = mozilla::void_t(); nsCOMPtr newHttpChannelChild = do_QueryInterface(mRedirectChannelChild); if (newHttpChannelChild && NS_SUCCEEDED(result)) { newHttpChannelChild->AddCookiesToRequest(); newHttpChannelChild->GetClientSetRequestHeaders(&headerTuples); + newHttpChannelChild->GetClientSetCorsPreflightParameters(corsPreflightArgs); } /* If the redirect was canceled, bypass OMR and send an empty API @@ -1577,7 +1579,8 @@ HttpChannelChild::OnRedirectVerifyCallback(nsresult result) } if (mIPCOpen) - SendRedirect2Verify(result, *headerTuples, loadFlags, redirectURI); + SendRedirect2Verify(result, *headerTuples, loadFlags, redirectURI, + corsPreflightArgs); return NS_OK; } @@ -1846,18 +1849,7 @@ HttpChannelChild::ContinueAsyncOpen() } OptionalCorsPreflightArgs optionalCorsPreflightArgs; - if (mRequireCORSPreflight) { - CorsPreflightArgs args; - args.withCredentials() = mWithCredentials; - args.unsafeHeaders() = mUnsafeHeaders; - nsresult rv = PrincipalToPrincipalInfo(mPreflightPrincipal, &args.preflightPrincipal()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - optionalCorsPreflightArgs = args; - } else { - optionalCorsPreflightArgs = mozilla::void_t(); - } + GetClientSetCorsPreflightParameters(optionalCorsPreflightArgs); // NB: This call forces us to cache mTopWindowURI if we haven't already. nsCOMPtr uri; @@ -2345,6 +2337,18 @@ NS_IMETHODIMP HttpChannelChild::GetClientSetRequestHeaders(RequestHeaderTuples * return NS_OK; } +void +HttpChannelChild::GetClientSetCorsPreflightParameters(OptionalCorsPreflightArgs& aArgs) +{ + if (mRequireCORSPreflight) { + CorsPreflightArgs args; + args.unsafeHeaders() = mUnsafeHeaders; + aArgs = args; + } else { + aArgs = mozilla::void_t(); + } +} + NS_IMETHODIMP HttpChannelChild::RemoveCorsPreflightCacheEntry(nsIURI* aURI, nsIPrincipal* aPrincipal) diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index 36f109620e98..396a79a1fa03 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -476,14 +476,7 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI, if (aCorsPreflightArgs.type() == OptionalCorsPreflightArgs::TCorsPreflightArgs) { const CorsPreflightArgs& args = aCorsPreflightArgs.get_CorsPreflightArgs(); - nsCOMPtr preflightPrincipal = - PrincipalInfoToPrincipal(args.preflightPrincipal()); - rv = mChannel->SetCorsPreflightParameters(args.unsafeHeaders(), - args.withCredentials(), - preflightPrincipal); - if (NS_FAILED(rv)) { - return SendFailedAsyncOpen(rv); - } + mChannel->SetCorsPreflightParameters(args.unsafeHeaders()); } nsCOMPtr stream = DeserializeInputStream(uploadStream, fds); @@ -730,7 +723,8 @@ bool HttpChannelParent::RecvRedirect2Verify(const nsresult& result, const RequestHeaderTuples& changedHeaders, const uint32_t& loadFlags, - const OptionalURIParams& aAPIRedirectURI) + const OptionalURIParams& aAPIRedirectURI, + const OptionalCorsPreflightArgs& aCorsPreflightArgs) { LOG(("HttpChannelParent::RecvRedirect2Verify [this=%p result=%x]\n", this, result)); @@ -759,6 +753,14 @@ HttpChannelParent::RecvRedirect2Verify(const nsresult& result, if (loadFlags & nsIChannel::LOAD_REPLACE) { newHttpChannel->SetLoadFlags(loadFlags); } + + if (aCorsPreflightArgs.type() == OptionalCorsPreflightArgs::TCorsPreflightArgs) { + nsCOMPtr newInternalChannel = + do_QueryInterface(newHttpChannel); + MOZ_RELEASE_ASSERT(newInternalChannel); + const CorsPreflightArgs& args = aCorsPreflightArgs.get_CorsPreflightArgs(); + newInternalChannel->SetCorsPreflightParameters(args.unsafeHeaders()); + } } } diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h index 051dc3156c54..8885953b0227 100644 --- a/netwerk/protocol/http/HttpChannelParent.h +++ b/netwerk/protocol/http/HttpChannelParent.h @@ -139,7 +139,8 @@ protected: virtual bool RecvRedirect2Verify(const nsresult& result, const RequestHeaderTuples& changedHeaders, const uint32_t& loadFlags, - const OptionalURIParams& apiRedirectUri) override; + const OptionalURIParams& apiRedirectUri, + const OptionalCorsPreflightArgs& aCorsPreflightArgs) override; virtual bool RecvUpdateAssociatedContentSecurity(const int32_t& broken, const int32_t& no) override; virtual bool RecvDocumentChannelCleanup() override; diff --git a/netwerk/protocol/http/PHttpChannel.ipdl b/netwerk/protocol/http/PHttpChannel.ipdl index 203e65cedda5..0002376bd41b 100644 --- a/netwerk/protocol/http/PHttpChannel.ipdl +++ b/netwerk/protocol/http/PHttpChannel.ipdl @@ -9,15 +9,13 @@ include protocol PNecko; include InputStreamParams; include URIParams; include PBackgroundSharedTypes; +include NeckoChannelParams; include protocol PBlob; //FIXME: bug #792908 include "mozilla/net/NeckoMessageUtils.h"; -using RequestHeaderTuples from "mozilla/net/PHttpChannelParams.h"; using class nsHttpHeaderArray from "nsHttpHeaderArray.h"; -using class nsHttpResponseHead from "nsHttpResponseHead.h"; -using struct nsHttpAtom from "nsHttp.h"; using mozilla::net::NetAddr from "mozilla/net/DNS.h"; using struct mozilla::net::ResourceTimingStruct from "mozilla/net/TimingStruct.h"; @@ -47,7 +45,8 @@ parent: // Reports approval/veto of redirect by child process redirect observers Redirect2Verify(nsresult result, RequestHeaderTuples changedHeaders, - uint32_t loadFlags, OptionalURIParams apiRedirectTo); + uint32_t loadFlags, OptionalURIParams apiRedirectTo, + OptionalCorsPreflightArgs corsPreflightArgs); // For document loads we keep this protocol open after child's // OnStopRequest, and send this msg (instead of __delete__) to allow diff --git a/netwerk/protocol/http/nsCORSListenerProxy.cpp b/netwerk/protocol/http/nsCORSListenerProxy.cpp index 4ddfa23fded3..cdecf0093124 100644 --- a/netwerk/protocol/http/nsCORSListenerProxy.cpp +++ b/netwerk/protocol/http/nsCORSListenerProxy.cpp @@ -821,6 +821,8 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI)); NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr loadInfo = aChannel->GetLoadInfo(); + // exempt data URIs from the same origin check. if (aAllowDataURI == DataURIHandling::Allow && originalURI == uri) { bool dataScheme = false; @@ -829,8 +831,6 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, if (dataScheme) { return NS_OK; } - nsCOMPtr loadInfo; - aChannel->GetLoadInfo(getter_AddRefs(loadInfo)); if (loadInfo && loadInfo->GetAboutBlankInherits() && NS_IsAboutBlank(uri)) { return NS_OK; @@ -881,6 +881,11 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, return NS_OK; } + // Check if we need to do a preflight, and if so set one up. This must be + // called once we know that the request is going, or has gone, cross-origin. + rv = CheckPreflightNeeded(aChannel); + NS_ENSURE_SUCCESS(rv, rv); + // It's a cross site load mHasBeenCrossSite = true; @@ -899,8 +904,11 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), origin, false); NS_ENSURE_SUCCESS(rv, rv); - // Make cookie-less if needed - if (!mWithCredentials) { + // Make cookie-less if needed. We don't need to do anything here if the + // channel was opened with AsyncOpen2, since then AsyncOpen2 will take + // care of the cookie policy for us. + if (!mWithCredentials && + (!loadInfo || !loadInfo->GetEnforceSecurity())) { nsLoadFlags flags; rv = http->GetLoadFlags(&flags); NS_ENSURE_SUCCESS(rv, rv); @@ -913,6 +921,71 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, return NS_OK; } +nsresult +nsCORSListenerProxy::CheckPreflightNeeded(nsIChannel* aChannel) +{ + // If this caller isn't using AsyncOpen2, or if this *is* a preflight channel, + // then we shouldn't initiate preflight for this channel. + nsCOMPtr loadInfo = aChannel->GetLoadInfo(); + if (!loadInfo || + loadInfo->GetSecurityMode() != + nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS || + loadInfo->GetIsPreflight()) { + return NS_OK; + } + + bool doPreflight = loadInfo->GetForcePreflight(); + + nsCOMPtr http = do_QueryInterface(aChannel); + NS_ENSURE_TRUE(http, NS_ERROR_DOM_BAD_URI); + nsAutoCString method; + http->GetRequestMethod(method); + if (!method.LowerCaseEqualsLiteral("get") && + !method.LowerCaseEqualsLiteral("post") && + !method.LowerCaseEqualsLiteral("head")) { + doPreflight = true; + } + + // Avoid copying the array here + const nsTArray& loadInfoHeaders = loadInfo->CorsUnsafeHeaders(); + if (!loadInfoHeaders.IsEmpty()) { + doPreflight = true; + } + + // Add Content-Type header if needed + nsTArray headers; + nsAutoCString contentTypeHeader; + nsresult rv = http->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), + contentTypeHeader); + // GetRequestHeader return an error if the header is not set. Don't add + // "content-type" to the list if that's the case. + if (NS_SUCCEEDED(rv) && + !nsContentUtils::IsAllowedNonCorsContentType(contentTypeHeader) && + !loadInfoHeaders.Contains(NS_LITERAL_CSTRING("content-type"), + nsCaseInsensitiveCStringArrayComparator())) { + headers.AppendElements(loadInfoHeaders); + headers.AppendElement(NS_LITERAL_CSTRING("content-type")); + doPreflight = true; + } + + if (!doPreflight) { + return NS_OK; + } + + // A preflight is needed. But if we've already been cross-site, then + // we already did a preflight when that happened, and so we're not allowed + // to do another preflight again. + NS_ENSURE_FALSE(mHasBeenCrossSite, NS_ERROR_DOM_BAD_URI); + + nsCOMPtr internal = do_QueryInterface(http); + NS_ENSURE_TRUE(internal, NS_ERROR_DOM_BAD_URI); + + internal->SetCorsPreflightParameters( + headers.IsEmpty() ? loadInfoHeaders : headers); + + return NS_OK; +} + ////////////////////////////////////////////////////////////////////////// // Preflight proxy @@ -1236,9 +1309,7 @@ nsCORSListenerProxy::RemoveFromCorsPreflightCache(nsIURI* aURI, nsresult nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel, - nsIPrincipal* aPrincipal, nsICorsPreflightCallback* aCallback, - bool aWithCredentials, nsTArray& aUnsafeHeaders, nsIChannel** aPreflightChannel) { @@ -1264,18 +1335,17 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel, return NS_ERROR_FAILURE; } - nsCOMPtr loadInfo = static_cast - (originalLoadInfo.get())->CloneForNewRequest(); - - nsSecurityFlags securityMode = loadInfo->GetSecurityMode(); - - MOZ_ASSERT(securityMode == 0 || - securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, + MOZ_ASSERT(originalLoadInfo->GetSecurityMode() == + nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, "how did we end up here?"); + nsCOMPtr principal = originalLoadInfo->LoadingPrincipal(); + bool withCredentials = originalLoadInfo->GetCookiePolicy() == + nsILoadInfo::SEC_COOKIES_INCLUDE; + nsPreflightCache::CacheEntry* entry = sPreflightCache ? - sPreflightCache->GetEntry(uri, aPrincipal, aWithCredentials, false) : + sPreflightCache->GetEntry(uri, principal, withCredentials, false) : nullptr; if (entry && entry->CheckRequest(method, aUnsafeHeaders)) { @@ -1286,6 +1356,10 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel, // Either it wasn't cached or the cached result has expired. Build a // channel for the OPTIONS request. + nsCOMPtr loadInfo = static_cast + (originalLoadInfo.get())->CloneForNewRequest(); + static_cast(loadInfo.get())->SetIsPreflight(); + nsCOMPtr loadGroup; rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup)); NS_ENSURE_SUCCESS(rv, rv); @@ -1347,24 +1421,14 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel, // Set up listener which will start the original channel RefPtr preflightListener = - new nsCORSPreflightListener(aPrincipal, aCallback, aWithCredentials, + new nsCORSPreflightListener(principal, aCallback, withCredentials, method, preflightHeaders); rv = preflightChannel->SetNotificationCallbacks(preflightListener); NS_ENSURE_SUCCESS(rv, rv); // Start preflight - if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) { - rv = preflightChannel->AsyncOpen2(preflightListener); - } - else { - RefPtr corsListener = - new nsCORSListenerProxy(preflightListener, aPrincipal, - aWithCredentials); - rv = corsListener->Init(preflightChannel, DataURIHandling::Disallow); - NS_ENSURE_SUCCESS(rv, rv); - rv = preflightChannel->AsyncOpen(corsListener, nullptr); - } + rv = preflightChannel->AsyncOpen2(preflightListener); NS_ENSURE_SUCCESS(rv, rv); // Return newly created preflight channel diff --git a/netwerk/protocol/http/nsCORSListenerProxy.h b/netwerk/protocol/http/nsCORSListenerProxy.h index d58b9b113cca..32ce8d252712 100644 --- a/netwerk/protocol/http/nsCORSListenerProxy.h +++ b/netwerk/protocol/http/nsCORSListenerProxy.h @@ -72,9 +72,7 @@ private: static void RemoveFromCorsPreflightCache(nsIURI* aURI, nsIPrincipal* aRequestingPrincipal); static nsresult StartCORSPreflight(nsIChannel* aRequestChannel, - nsIPrincipal* aPrincipal, nsICorsPreflightCallback* aCallback, - bool aWithCredentials, nsTArray& aACUnsafeHeaders, nsIChannel** aPreflightChannel); @@ -82,6 +80,7 @@ private: nsresult UpdateChannel(nsIChannel* aChannel, DataURIHandling aAllowDataURI); nsresult CheckRequestApproved(nsIRequest* aRequest); + nsresult CheckPreflightNeeded(nsIChannel* aChannel); nsCOMPtr mOuterListener; // The principal that originally kicked off the request diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index b0ffabf15246..54d9a3d88a25 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -451,9 +451,7 @@ nsHttpChannel::ContinueConnect() mInterceptCache != INTERCEPTED) { MOZ_ASSERT(!mPreflightChannel); nsresult rv = - nsCORSListenerProxy::StartCORSPreflight(this, - mPreflightPrincipal, this, - mWithCredentials, + nsCORSListenerProxy::StartCORSPreflight(this, this, mUnsafeHeaders, getter_AddRefs(mPreflightChannel)); return rv; diff --git a/netwerk/protocol/http/nsIHttpChannelChild.idl b/netwerk/protocol/http/nsIHttpChannelChild.idl index 8d9e016163dd..9d545f09b1e3 100644 --- a/netwerk/protocol/http/nsIHttpChannelChild.idl +++ b/netwerk/protocol/http/nsIHttpChannelChild.idl @@ -6,11 +6,12 @@ #include "nsISupports.idl" [ptr] native RequestHeaderTuples(mozilla::net::RequestHeaderTuples); +[ref] native OptionalCorsPreflightArgsRef(mozilla::OptionalCorsPreflightArgs); interface nsIPrincipal; interface nsIURI; -[uuid(81acb360-edd2-428e-935f-300a32efb649)] +[uuid(893e29fb-2e84-454e-afc7-41fadbe93fd9)] interface nsIHttpChannelChild : nsISupports { void addCookiesToRequest(); @@ -22,6 +23,10 @@ interface nsIHttpChannelChild : nsISupports // Headers that the channel client has set via SetRequestHeader. readonly attribute RequestHeaderTuples clientSetRequestHeaders; + // Headers that the channel client has set via SetRequestHeader. + [notxpcom, nostdcall] + void GetClientSetCorsPreflightParameters(in OptionalCorsPreflightArgsRef args); + // This method is called by nsCORSListenerProxy if we need to remove // an entry from the CORS preflight cache in the parent process. void removeCorsPreflightCacheEntry(in nsIURI aURI, in nsIPrincipal aRequestingPrincipal); diff --git a/netwerk/protocol/http/nsIHttpChannelInternal.idl b/netwerk/protocol/http/nsIHttpChannelInternal.idl index cb4f5bc6c9a1..48b06cf6ca97 100644 --- a/netwerk/protocol/http/nsIHttpChannelInternal.idl +++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl @@ -39,7 +39,7 @@ interface nsIHttpUpgradeListener : nsISupports * using any feature exposed by this interface, be aware that this interface * will change and you will be broken. You have been warned. */ -[scriptable, uuid(23b3f883-ce09-4350-8f67-1d9858d619e1)] +[scriptable, uuid(332d5f9c-991c-45e3-922f-99e6fe0deb60)] interface nsIHttpChannelInternal : nsISupports { /** @@ -261,8 +261,6 @@ interface nsIHttpChannelInternal : nsISupports * Make cross-origin CORS loads happen with a CORS preflight, and specify * the CORS preflight parameters. */ - [noscript] - void setCorsPreflightParameters(in StringArrayRef unsafeHeaders, - in boolean withCredentials, - in nsIPrincipal preflightPrincipal); + [noscript, notxpcom, nostdcall] + void setCorsPreflightParameters(in StringArrayRef unsafeHeaders); }; diff --git a/netwerk/protocol/res/ExtensionProtocolHandler.cpp b/netwerk/protocol/res/ExtensionProtocolHandler.cpp index b1e2648dc788..83214e3acfc3 100644 --- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp +++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp @@ -18,6 +18,7 @@ #include "nsIStreamConverterService.h" #include "nsIPipe.h" #include "nsNetUtil.h" +#include "LoadInfo.h" namespace mozilla { @@ -121,6 +122,10 @@ ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI, aURI, getter_AddRefs(converter)); NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr loadInfo = + static_cast(aLoadInfo)->CloneForNewRequest(); + (*result)->SetLoadInfo(loadInfo); + rv = (*result)->AsyncOpen2(converter); } else { // Stylesheet loads for extension content scripts require a sync channel, diff --git a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp index bb6c3624b590..5ae50719ce2d 100644 --- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp +++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp @@ -966,3 +966,10 @@ nsViewSourceChannel::SetIsMainDocumentChannel(bool aValue) return !mHttpChannel ? NS_ERROR_NULL_POINTER : mHttpChannel->SetIsMainDocumentChannel(aValue); } + +// Have to manually forward SetCorsPreflightParameters since it's [notxpcom] +void +nsViewSourceChannel::SetCorsPreflightParameters(const nsTArray& aUnsafeHeaders) +{ + mHttpChannelInternal->SetCorsPreflightParameters(aUnsafeHeaders); +} diff --git a/netwerk/srtp/src/srtp/srtp.c b/netwerk/srtp/src/srtp/srtp.c index cdb05b8cfe4f..4d83707f981f 100644 --- a/netwerk/srtp/src/srtp/srtp.c +++ b/netwerk/srtp/src/srtp/srtp.c @@ -807,6 +807,8 @@ srtp_stream_init(srtp_stream_ctx_t *srtp, srtp_hdr_xtnd_t *xtn_hdr = (srtp_hdr_xtnd_t *)enc_start; enc_start += (ntohs(xtn_hdr->length) + 1); } + if (!((uint8_t*)enc_start <= (uint8_t*)hdr + *pkt_octet_len)) + return err_status_parse_err; enc_octet_len = (unsigned int)(*pkt_octet_len - ((enc_start - (uint32_t *)hdr) << 2)); } else { @@ -1076,6 +1078,8 @@ srtp_unprotect(srtp_ctx_t *ctx, void *srtp_hdr, int *pkt_octet_len) { srtp_hdr_xtnd_t *xtn_hdr = (srtp_hdr_xtnd_t *)enc_start; enc_start += (ntohs(xtn_hdr->length) + 1); } + if (!((uint8_t*)enc_start < (uint8_t*)hdr + (*pkt_octet_len - tag_len))) + return err_status_parse_err; enc_octet_len = (uint32_t)(*pkt_octet_len - tag_len - ((enc_start - (uint32_t *)hdr) << 2)); } else { diff --git a/parser/html/nsHtml5Highlighter.cpp b/parser/html/nsHtml5Highlighter.cpp index 7ae96ffed050..aa147b344f92 100644 --- a/parser/html/nsHtml5Highlighter.cpp +++ b/parser/html/nsHtml5Highlighter.cpp @@ -57,7 +57,7 @@ nsHtml5Highlighter::nsHtml5Highlighter(nsAHtml5TreeOpSink* aOpSink) , mCurrentRun(nullptr) , mAmpersand(nullptr) , mSlash(nullptr) - , mHandles(new nsIContent*[NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH]) + , mHandles(MakeUnique(NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH)) , mHandlesUsed(0) , mSeenBase(false) { @@ -641,8 +641,8 @@ nsIContent** nsHtml5Highlighter::AllocateContentHandle() { if (mHandlesUsed == NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH) { - mOldHandles.AppendElement(mHandles.forget()); - mHandles = new nsIContent*[NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH]; + mOldHandles.AppendElement(Move(mHandles)); + mHandles = MakeUnique(NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH); mHandlesUsed = 0; } #ifdef DEBUG diff --git a/parser/html/nsHtml5Highlighter.h b/parser/html/nsHtml5Highlighter.h index 086cc76826e0..e9474869e68f 100644 --- a/parser/html/nsHtml5Highlighter.h +++ b/parser/html/nsHtml5Highlighter.h @@ -342,7 +342,7 @@ class nsHtml5Highlighter /** * Memory for element handles. */ - nsAutoArrayPtr mHandles; + mozilla::UniquePtr mHandles; /** * Number of handles used in mHandles @@ -352,7 +352,7 @@ class nsHtml5Highlighter /** * A holder for old contents of mHandles */ - nsTArray > mOldHandles; + nsTArray> mOldHandles; /** * The element stack. diff --git a/parser/html/nsHtml5StreamParser.cpp b/parser/html/nsHtml5StreamParser.cpp index d8ef1382ad0c..705f518f7d2f 100644 --- a/parser/html/nsHtml5StreamParser.cpp +++ b/parser/html/nsHtml5StreamParser.cpp @@ -17,6 +17,7 @@ #include "nsHtml5RefPtr.h" #include "nsIScriptError.h" #include "mozilla/Preferences.h" +#include "mozilla/UniquePtrExtensions.h" #include "nsHtml5Highlighter.h" #include "expat_config.h" #include "expat.h" @@ -300,7 +301,7 @@ nsHtml5StreamParser::SetupDecodingAndWriteSniffingBufferAndCurrentSegment(const mUnicodeDecoder = EncodingUtils::DecoderForEncoding(mCharset); if (mSniffingBuffer) { uint32_t writeCount; - rv = WriteStreamBytes(mSniffingBuffer, mSniffingLength, &writeCount); + rv = WriteStreamBytes(mSniffingBuffer.get(), mSniffingLength, &writeCount); NS_ENSURE_SUCCESS(rv, rv); mSniffingBuffer = nullptr; } @@ -783,13 +784,13 @@ nsHtml5StreamParser::SniffStreamBytes(const uint8_t* aFromSegment, } if (!mSniffingBuffer) { - mSniffingBuffer = new (mozilla::fallible) - uint8_t[NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE]; + mSniffingBuffer = + MakeUniqueFallible(NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE); if (!mSniffingBuffer) { return NS_ERROR_OUT_OF_MEMORY; } } - memcpy(mSniffingBuffer + mSniffingLength, aFromSegment, aCount); + memcpy(&mSniffingBuffer[mSniffingLength], aFromSegment, aCount); mSniffingLength += aCount; *aWriteCount = aCount; return NS_OK; @@ -1126,20 +1127,20 @@ class nsHtml5DataAvailable : public nsRunnable { private: nsHtml5RefPtr mStreamParser; - nsAutoArrayPtr mData; + UniquePtr mData; uint32_t mLength; public: nsHtml5DataAvailable(nsHtml5StreamParser* aStreamParser, - uint8_t* aData, + UniquePtr aData, uint32_t aLength) : mStreamParser(aStreamParser) - , mData(aData) + , mData(Move(aData)) , mLength(aLength) {} NS_IMETHODIMP Run() { mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex); - mStreamParser->DoDataAvailable(mData, mLength); + mStreamParser->DoDataAvailable(mData.get(), mLength); return NS_OK; } }; @@ -1160,7 +1161,7 @@ nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest, uint32_t totalRead; // Main thread to parser thread dispatch requires copying to buffer first. if (NS_IsMainThread()) { - nsAutoArrayPtr data(new (mozilla::fallible) uint8_t[aLength]); + auto data = MakeUniqueFallible(aLength); if (!data) { return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY); } @@ -1170,7 +1171,7 @@ nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest, NS_ASSERTION(totalRead <= aLength, "Read more bytes than were available?"); nsCOMPtr dataAvailable = new nsHtml5DataAvailable(this, - data.forget(), + Move(data), totalRead); if (NS_FAILED(mThread->Dispatch(dataAvailable, nsIThread::DISPATCH_NORMAL))) { NS_WARNING("Dispatching DataAvailable event failed."); diff --git a/parser/html/nsHtml5StreamParser.h b/parser/html/nsHtml5StreamParser.h index 2550b9b99561..ad62327087e3 100644 --- a/parser/html/nsHtml5StreamParser.h +++ b/parser/html/nsHtml5StreamParser.h @@ -15,6 +15,7 @@ #include "nsHtml5OwningUTF16Buffer.h" #include "nsIInputStream.h" #include "mozilla/Mutex.h" +#include "mozilla/UniquePtr.h" #include "nsHtml5AtomTable.h" #include "nsHtml5Speculation.h" #include "nsITimer.h" @@ -398,7 +399,7 @@ class nsHtml5StreamParser : public nsICharsetDetectionObserver { /** * The buffer for sniffing the character encoding */ - nsAutoArrayPtr mSniffingBuffer; + mozilla::UniquePtr mSniffingBuffer; /** * The number of meaningful bytes in mSniffingBuffer diff --git a/parser/html/nsHtml5TreeBuilderCppSupplement.h b/parser/html/nsHtml5TreeBuilderCppSupplement.h index 70eb9abfc8aa..3e59df6627b7 100644 --- a/parser/html/nsHtml5TreeBuilderCppSupplement.h +++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h @@ -9,6 +9,7 @@ #include "nsNodeUtils.h" #include "nsIFrame.h" #include "mozilla/Likely.h" +#include "mozilla/UniquePtr.h" nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsHtml5OplessBuilder* aBuilder) : scriptingEnabled(false) @@ -997,8 +998,8 @@ nsHtml5TreeBuilder::AllocateContentHandle() return nullptr; } if (mHandlesUsed == NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH) { - mOldHandles.AppendElement(mHandles.forget()); - mHandles = new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH]; + mOldHandles.AppendElement(Move(mHandles)); + mHandles = MakeUnique(NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH); mHandlesUsed = 0; } #ifdef DEBUG diff --git a/parser/html/nsHtml5TreeBuilderHSupplement.h b/parser/html/nsHtml5TreeBuilderHSupplement.h index b4549c6f7bd3..0e614434d23a 100644 --- a/parser/html/nsHtml5TreeBuilderHSupplement.h +++ b/parser/html/nsHtml5TreeBuilderHSupplement.h @@ -14,9 +14,9 @@ nsTArray mOpQueue; nsTArray mSpeculativeLoadQueue; nsAHtml5TreeOpSink* mOpSink; - nsAutoArrayPtr mHandles; + mozilla::UniquePtr mHandles; int32_t mHandlesUsed; - nsTArray > mOldHandles; + nsTArray> mOldHandles; nsHtml5TreeOpStage* mSpeculativeLoadStage; nsresult mBroken; bool mCurrentHtmlScriptIsAsyncOrDefer; diff --git a/python/mozbuild/mozbuild/action/process_install_manifest.py b/python/mozbuild/mozbuild/action/process_install_manifest.py index 30893a9fbbed..60b97857662d 100644 --- a/python/mozbuild/mozbuild/action/process_install_manifest.py +++ b/python/mozbuild/mozbuild/action/process_install_manifest.py @@ -5,8 +5,16 @@ from __future__ import absolute_import, print_function, unicode_literals import argparse +import os import sys -from mozpack.copier import FileCopier +from mozpack.copier import ( + FileCopier, + FileRegistry, +) +from mozpack.files import ( + BaseFile, + FileFinder, +) from mozpack.manifests import InstallManifest @@ -14,22 +22,51 @@ COMPLETE = 'From {dest}: Kept {existing} existing; Added/updated {updated}; ' \ 'Removed {rm_files} files and {rm_dirs} directories.' -def process_manifest(destdir, paths, +def process_manifest(destdir, paths, track=None, remove_unaccounted=True, remove_all_directory_symlinks=True, remove_empty_directories=True, defines={}): + + if track: + if os.path.exists(track): + # We use the same format as install manifests for the tracking + # data. + manifest = InstallManifest(path=track) + remove_unaccounted = FileRegistry() + dummy_file = BaseFile() + + finder = FileFinder(destdir, find_executables=False, + find_dotfiles=True) + for dest in manifest._dests: + if '*' in dest: + for p, f in finder.find(dest): + remove_unaccounted.add(p, dummy_file) + else: + remove_unaccounted.add(dest, dummy_file) + else: + # If tracking is enabled and there is no file, we don't want to + # be removing anything. + remove_unaccounted=False + remove_empty_directories=False + remove_all_directory_symlinks=False + manifest = InstallManifest() for path in paths: manifest |= InstallManifest(path=path) copier = FileCopier() manifest.populate_registry(copier, defines_override=defines) - return copier.copy(destdir, + result = copier.copy(destdir, remove_unaccounted=remove_unaccounted, remove_all_directory_symlinks=remove_all_directory_symlinks, remove_empty_directories=remove_empty_directories) + if track: + manifest.write(path=track) + + return result + class DefinesAction(argparse.Action): def __call__(self, parser, namespace, values, option_string): @@ -59,6 +96,8 @@ def main(argv): help='Do not remove all directory symlinks from destination.') parser.add_argument('--no-remove-empty-directories', action='store_true', help='Do not remove empty directories from destination.') + parser.add_argument('--track', metavar="PATH", + help='Use installed files tracking information from the given path.') parser.add_argument('-D', action=DefinesAction, dest='defines', metavar="VAR[=VAL]", help='Define a variable to override what is specified in the manifest') @@ -66,7 +105,7 @@ def main(argv): args = parser.parse_args(argv) result = process_manifest(args.destdir, args.manifests, - remove_unaccounted=not args.no_remove, + track=args.track, remove_unaccounted=not args.no_remove, remove_all_directory_symlinks=not args.no_remove_all_directory_symlinks, remove_empty_directories=not args.no_remove_empty_directories, defines=args.defines) diff --git a/python/mozbuild/mozbuild/backend/fastermake.py b/python/mozbuild/mozbuild/backend/fastermake.py index ec09bdba9917..78c80f4b90bd 100644 --- a/python/mozbuild/mozbuild/backend/fastermake.py +++ b/python/mozbuild/mozbuild/backend/fastermake.py @@ -138,7 +138,8 @@ class FasterMakeBackend(CommonBackend): for jarinfo in pp.out: install_target = obj.install_target if jarinfo.base: - install_target = mozpath.join(install_target, jarinfo.base) + install_target = mozpath.normpath( + mozpath.join(install_target, jarinfo.base)) for e in jarinfo.entries: if e.is_locale: if jarinfo.relativesrcdir: diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index f07b9f1db1f8..ff4a1653f054 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -401,7 +401,7 @@ class RecursiveMakeBackend(CommonBackend): 'dist_private', 'dist_sdk', 'dist_xpi-stage', - 'tests', + '_tests', 'xpidl', ]} @@ -509,9 +509,6 @@ class RecursiveMakeBackend(CommonBackend): elif isinstance(obj, Defines): self._process_defines(obj, backend_file) - elif isinstance(obj, Exports): - self._process_exports(obj, obj.exports, backend_file) - elif isinstance(obj, GeneratedFile): dep_file = "%s.pp" % obj.output backend_file.write('GENERATED_FILES += %s\n' % obj.output) @@ -587,7 +584,7 @@ class RecursiveMakeBackend(CommonBackend): self._process_linked_libraries(obj, backend_file) elif isinstance(obj, FinalTargetFiles): - self._process_final_target_files(obj, obj.files) + self._process_final_target_files(obj, obj.files, backend_file) elif isinstance(obj, FinalTargetPreprocessedFiles): self._process_final_target_pp_files(obj, obj.files, backend_file) @@ -852,7 +849,7 @@ class RecursiveMakeBackend(CommonBackend): # Catch duplicate inserts. try: - self._install_manifests['tests'].add_optional_exists(manifest_stem) + self._install_manifests['_tests'].add_optional_exists(manifest_stem) except ValueError: pass @@ -956,27 +953,15 @@ class RecursiveMakeBackend(CommonBackend): backend_file.write(' %s' % define) backend_file.write('\n') - def _process_exports(self, obj, exports, backend_file): - # This may not be needed, but is present for backwards compatibility - # with the old make rules, just in case. - if not obj.dist_install: - return - - for source, dest in self._walk_hierarchy(obj, exports): - self._install_manifests['dist_include'].add_symlink(source, dest) - - if not os.path.exists(source): - raise Exception('File listed in EXPORTS does not exist: %s' % source) - def _process_test_harness_files(self, obj, backend_file): for path, files in obj.srcdir_files.iteritems(): for source in files: dest = '%s/%s' % (path, mozpath.basename(source)) - self._install_manifests['tests'].add_symlink(source, dest) + self._install_manifests['_tests'].add_symlink(source, dest) for path, patterns in obj.srcdir_pattern_files.iteritems(): for p in patterns: - self._install_manifests['tests'].add_pattern_symlink(p[0], p[1], path) + self._install_manifests['_tests'].add_pattern_symlink(p[0], p[1], path) for path, files in obj.objdir_files.iteritems(): prefix = 'TEST_HARNESS_%s' % path.replace('/', '_') @@ -1134,14 +1119,14 @@ INSTALL_TARGETS += %(prefix)s # the manifest is listed as a duplicate. for source, (dest, is_test) in obj.installs.items(): try: - self._install_manifests['tests'].add_symlink(source, dest) + self._install_manifests['_tests'].add_symlink(source, dest) except ValueError: if not obj.dupe_manifest and is_test: raise for base, pattern, dest in obj.pattern_installs: try: - self._install_manifests['tests'].add_pattern_symlink(base, + self._install_manifests['_tests'].add_pattern_symlink(base, pattern, dest) except ValueError: if not obj.dupe_manifest: @@ -1149,7 +1134,7 @@ INSTALL_TARGETS += %(prefix)s for dest in obj.external_installs: try: - self._install_manifests['tests'].add_optional_exists(dest) + self._install_manifests['_tests'].add_optional_exists(dest) except ValueError: if not obj.dupe_manifest: raise @@ -1298,24 +1283,40 @@ INSTALL_TARGETS += %(prefix)s # Process library-based defines self._process_defines(obj.defines, backend_file) - def _process_final_target_files(self, obj, files): + def _process_final_target_files(self, obj, files, backend_file): target = obj.install_target - if target.startswith('dist/bin'): - install_manifest = self._install_manifests['dist_bin'] - reltarget = mozpath.relpath(target, 'dist/bin') - elif target.startswith('dist/xpi-stage'): - install_manifest = self._install_manifests['dist_xpi-stage'] - reltarget = mozpath.relpath(target, 'dist/xpi-stage') - elif target.startswith('_tests'): - install_manifest = self._install_manifests['tests'] - reltarget = mozpath.relpath(target, '_tests') + for path in ( + 'dist/bin', + 'dist/xpi-stage', + '_tests', + 'dist/include', + ): + manifest = path.replace('/', '_') + if target.startswith(path): + install_manifest = self._install_manifests[manifest] + reltarget = mozpath.relpath(target, path) + break else: raise Exception("Cannot install to " + target) for path, files in files.walk(): + target_var = (mozpath.join(target, path) + if path else target).replace('/', '_') + have_objdir_files = False for f in files: - dest = mozpath.join(reltarget, path, mozpath.basename(f)) - install_manifest.add_symlink(f.full_path, dest) + if not isinstance(f, ObjDirPath): + dest = mozpath.join(reltarget, path, mozpath.basename(f)) + install_manifest.add_symlink(f.full_path, dest) + else: + backend_file.write('%s_FILES += %s\n' % ( + target_var, self._pretty_path(f, backend_file))) + have_objdir_files = True + if have_objdir_files: + backend_file.write('%s_DEST := $(DEPTH)/%s\n' + % (target_var, + mozpath.join(target, path))) + backend_file.write('%s_TARGET := export\n' % target_var) + backend_file.write('INSTALL_TARGETS += %s\n' % target_var) def _process_final_target_pp_files(self, obj, files, backend_file): # We'd like to install these via manifests as preprocessed files. diff --git a/python/mozbuild/mozbuild/frontend/context.py b/python/mozbuild/mozbuild/frontend/context.py index d09e31750497..d1f19ec45635 100644 --- a/python/mozbuild/mozbuild/frontend/context.py +++ b/python/mozbuild/mozbuild/frontend/context.py @@ -1268,7 +1268,7 @@ VARIABLES = { into account the values of ``AC_DEFINE`` instead of ``AC_SUBST``. """, None), - 'EXPORTS': (HierarchicalStringList, list, + 'EXPORTS': (ContextDerivedTypedHierarchicalStringList(Path), list, """List of files to be exported, and in which subdirectories. ``EXPORTS`` is generally used to list the include files to be exported to @@ -1281,6 +1281,10 @@ VARIABLES = { EXPORTS += ['foo.h'] EXPORTS.mozilla.dom += ['bar.h'] + + Entries in ``EXPORTS`` are paths, so objdir paths may be used, but + any files listed from the objdir must also be listed in + ``GENERATED_FILES``. """, None), 'PROGRAM' : (unicode, unicode, diff --git a/python/mozbuild/mozbuild/frontend/data.py b/python/mozbuild/mozbuild/frontend/data.py index 5ba15c607b8f..5e82505699f2 100644 --- a/python/mozbuild/mozbuild/frontend/data.py +++ b/python/mozbuild/mozbuild/frontend/data.py @@ -194,21 +194,6 @@ class Defines(BaseDefines): class HostDefines(BaseDefines): pass -class Exports(ContextDerived): - """Context derived container object for EXPORTS, which is a - HierarchicalStringList. - - We need an object derived from ContextDerived for use in the backend, so - this object fills that role. It just has a reference to the underlying - HierarchicalStringList, which is created when parsing EXPORTS. - """ - __slots__ = ('exports', 'dist_install') - - def __init__(self, context, exports, dist_install=True): - ContextDerived.__init__(self, context) - self.exports = exports - self.dist_install = dist_install - class TestHarnessFiles(ContextDerived): """Sandbox container object for TEST_HARNESS_FILES, which is a HierarchicalStringList. @@ -832,6 +817,19 @@ class TestingFiles(FinalTargetFiles): return '_tests' +class Exports(FinalTargetFiles): + """Context derived container object for EXPORTS, which is a + HierarchicalStringList. + + We need an object derived from ContextDerived for use in the backend, so + this object fills that role. It just has a reference to the underlying + HierarchicalStringList, which is created when parsing EXPORTS. + """ + @property + def install_target(self): + return 'dist/include' + + class GeneratedFile(ContextDerived): """Represents a generated file.""" diff --git a/python/mozbuild/mozbuild/frontend/emitter.py b/python/mozbuild/mozbuild/frontend/emitter.py index d45f4a5ba8a8..80287d94fecd 100644 --- a/python/mozbuild/mozbuild/frontend/emitter.py +++ b/python/mozbuild/mozbuild/frontend/emitter.py @@ -608,12 +608,9 @@ class TreeMetadataEmitter(LoggingMixin): for obj in self._process_sources(context, passthru): yield obj - exports = context.get('EXPORTS') - if exports: - yield Exports(context, exports, - dist_install=dist_install is not False) - + generated_files = set() for obj in self._process_generated_files(context): + generated_files.add(obj.output) yield obj for obj in self._process_test_harness_files(context): @@ -653,6 +650,7 @@ class TreeMetadataEmitter(LoggingMixin): components = [] for var, cls in ( + ('EXPORTS', Exports), ('FINAL_TARGET_FILES', FinalTargetFiles), ('FINAL_TARGET_PP_FILES', FinalTargetPreprocessedFiles), ('TESTING_FILES', TestingFiles), @@ -674,11 +672,23 @@ class TreeMetadataEmitter(LoggingMixin): if mozpath.split(base)[0] == 'res': has_resources = True for f in files: - path = f.full_path - if not os.path.exists(path): + if (var == 'FINAL_TARGET_PP_FILES' and + not isinstance(f, SourcePath)): raise SandboxValidationError( - 'File listed in %s does not exist: %s' - % (var, path), context) + ('Only source directory paths allowed in ' + + 'FINAL_TARGET_PP_FILES: %s') + % (f,), context) + if not isinstance(f, ObjDirPath): + path = f.full_path + if not os.path.exists(path): + raise SandboxValidationError( + 'File listed in %s does not exist: %s' + % (var, path), context) + else: + if mozpath.basename(f.full_path) not in generated_files: + raise SandboxValidationError( + ('Objdir file listed in %s not in ' + + 'GENERATED_FILES: %s') % (var, path), context) # Addons (when XPI_NAME is defined) and Applications (when # DIST_SUBDIR is defined) use a different preferences directory diff --git a/python/mozbuild/mozbuild/test/backend/data/exports-generated/dom1.h b/python/mozbuild/mozbuild/test/backend/data/exports-generated/dom1.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/backend/data/exports-generated/foo.h b/python/mozbuild/mozbuild/test/backend/data/exports-generated/foo.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/backend/data/exports-generated/gfx.h b/python/mozbuild/mozbuild/test/backend/data/exports-generated/gfx.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/backend/data/exports-generated/moz.build b/python/mozbuild/mozbuild/test/backend/data/exports-generated/moz.build new file mode 100644 index 000000000000..b604ef1a0013 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/exports-generated/moz.build @@ -0,0 +1,12 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +EXPORTS += ['!bar.h', 'foo.h'] +EXPORTS.mozilla += ['!mozilla2.h', 'mozilla1.h'] +EXPORTS.mozilla.dom += ['!dom2.h', '!dom3.h', 'dom1.h'] +EXPORTS.gfx += ['gfx.h'] + +GENERATED_FILES += ['bar.h'] +GENERATED_FILES += ['mozilla2.h'] +GENERATED_FILES += ['dom2.h'] +GENERATED_FILES += ['dom3.h'] diff --git a/python/mozbuild/mozbuild/test/backend/data/exports-generated/mozilla1.h b/python/mozbuild/mozbuild/test/backend/data/exports-generated/mozilla1.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py index a728d5ba12b9..9a878e1c01ba 100644 --- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py +++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py @@ -393,6 +393,49 @@ class TestRecursiveMakeBackend(BackendTester): self.maxDiff = None self.assertEqual(lines, expected) + def test_exports_generated(self): + """Ensure EXPORTS that are listed in GENERATED_FILES + are handled properly.""" + env = self._consume('exports-generated', RecursiveMakeBackend) + + # EXPORTS files should appear in the dist_include install manifest. + m = InstallManifest(path=mozpath.join(env.topobjdir, + '_build_manifests', 'install', 'dist_include')) + self.assertEqual(len(m), 4) + self.assertIn('foo.h', m) + self.assertIn('mozilla/mozilla1.h', m) + self.assertIn('mozilla/dom/dom1.h', m) + self.assertIn('gfx/gfx.h', m) + # EXPORTS files that are also GENERATED_FILES should be handled as + # INSTALL_TARGETS. + backend_path = mozpath.join(env.topobjdir, 'backend.mk') + lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] + expected = [ + 'GENERATED_FILES += bar.h', + 'EXTRA_MDDEPEND_FILES += bar.h.pp', + 'GENERATED_FILES += mozilla2.h', + 'EXTRA_MDDEPEND_FILES += mozilla2.h.pp', + 'GENERATED_FILES += dom2.h', + 'EXTRA_MDDEPEND_FILES += dom2.h.pp', + 'GENERATED_FILES += dom3.h', + 'EXTRA_MDDEPEND_FILES += dom3.h.pp', + 'dist_include_FILES += bar.h', + 'dist_include_DEST := $(DEPTH)/dist/include/', + 'dist_include_TARGET := export', + 'INSTALL_TARGETS += dist_include', + 'dist_include_mozilla_FILES += mozilla2.h', + 'dist_include_mozilla_DEST := $(DEPTH)/dist/include/mozilla', + 'dist_include_mozilla_TARGET := export', + 'INSTALL_TARGETS += dist_include_mozilla', + 'dist_include_mozilla_dom_FILES += dom2.h', + 'dist_include_mozilla_dom_FILES += dom3.h', + 'dist_include_mozilla_dom_DEST := $(DEPTH)/dist/include/mozilla/dom', + 'dist_include_mozilla_dom_TARGET := export', + 'INSTALL_TARGETS += dist_include_mozilla_dom', + ] + self.maxDiff = None + self.assertEqual(lines, expected) + def test_resources(self): """Ensure RESOURCE_FILES is handled properly.""" env = self._consume('resources', RecursiveMakeBackend) @@ -454,7 +497,7 @@ class TestRecursiveMakeBackend(BackendTester): """Pattern matches in test manifests' support-files should be recorded.""" env = self._consume('test-manifests-written', RecursiveMakeBackend) m = InstallManifest(path=mozpath.join(env.topobjdir, - '_build_manifests', 'install', 'tests')) + '_build_manifests', 'install', '_tests')) # This is not the most robust test in the world, but it gets the job # done. @@ -700,7 +743,7 @@ class TestRecursiveMakeBackend(BackendTester): env = self._consume('test-manifests-duplicate-support-files', RecursiveMakeBackend) - p = os.path.join(env.topobjdir, '_build_manifests', 'install', 'tests') + p = os.path.join(env.topobjdir, '_build_manifests', 'install', '_tests') m = InstallManifest(p) self.assertIn('testing/mochitest/tests/support-file.txt', m) @@ -755,7 +798,7 @@ class TestRecursiveMakeBackend(BackendTester): man_dir = mozpath.join(env.topobjdir, '_build_manifests', 'install') self.assertTrue(os.path.isdir(man_dir)) - full = mozpath.join(man_dir, 'tests') + full = mozpath.join(man_dir, '_tests') self.assertTrue(os.path.exists(full)) m = InstallManifest(path=full) diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-generated/foo.h b/python/mozbuild/mozbuild/test/frontend/data/exports-generated/foo.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-generated/moz.build b/python/mozbuild/mozbuild/test/frontend/data/exports-generated/moz.build new file mode 100644 index 000000000000..259d96fcd61f --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports-generated/moz.build @@ -0,0 +1,8 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +EXPORTS += ['foo.h'] +EXPORTS.mozilla += ['mozilla1.h'] +EXPORTS.mozilla += ['!mozilla2.h'] + +GENERATED_FILES += ['mozilla2.h'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-generated/mozilla1.h b/python/mozbuild/mozbuild/test/frontend/data/exports-generated/mozilla1.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-missing-generated/foo.h b/python/mozbuild/mozbuild/test/frontend/data/exports-missing-generated/foo.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-missing-generated/moz.build b/python/mozbuild/mozbuild/test/frontend/data/exports-missing-generated/moz.build new file mode 100644 index 000000000000..e0dfce2645c2 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports-missing-generated/moz.build @@ -0,0 +1,5 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +EXPORTS += ['foo.h'] +EXPORTS += ['!bar.h'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-missing/foo.h b/python/mozbuild/mozbuild/test/frontend/data/exports-missing/foo.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-missing/moz.build b/python/mozbuild/mozbuild/test/frontend/data/exports-missing/moz.build new file mode 100644 index 000000000000..e1f93aab5e3f --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/exports-missing/moz.build @@ -0,0 +1,6 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +EXPORTS += ['foo.h'] +EXPORTS.mozilla += ['mozilla1.h'] +EXPORTS.mozilla += ['mozilla2.h'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports-missing/mozilla1.h b/python/mozbuild/mozbuild/test/frontend/data/exports-missing/mozilla1.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/bar.h b/python/mozbuild/mozbuild/test/frontend/data/exports/bar.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/baz.h b/python/mozbuild/mozbuild/test/frontend/data/exports/baz.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/dom1.h b/python/mozbuild/mozbuild/test/frontend/data/exports/dom1.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/dom2.h b/python/mozbuild/mozbuild/test/frontend/data/exports/dom2.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/dom3.h b/python/mozbuild/mozbuild/test/frontend/data/exports/dom3.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/foo.h b/python/mozbuild/mozbuild/test/frontend/data/exports/foo.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/gfx.h b/python/mozbuild/mozbuild/test/frontend/data/exports/gfx.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/mem.h b/python/mozbuild/mozbuild/test/frontend/data/exports/mem.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/mem2.h b/python/mozbuild/mozbuild/test/frontend/data/exports/mem2.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/mozilla1.h b/python/mozbuild/mozbuild/test/frontend/data/exports/mozilla1.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/mozilla2.h b/python/mozbuild/mozbuild/test/frontend/data/exports/mozilla2.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/pprio.h b/python/mozbuild/mozbuild/test/frontend/data/exports/pprio.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/exports/pprthred.h b/python/mozbuild/mozbuild/test/frontend/data/exports/pprthred.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/frontend/data/final-target-pp-files-non-srcdir/moz.build b/python/mozbuild/mozbuild/test/frontend/data/final-target-pp-files-non-srcdir/moz.build new file mode 100644 index 000000000000..becbd8930b50 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/final-target-pp-files-non-srcdir/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +FINAL_TARGET_PP_FILES += [ + '!foo.js', +] diff --git a/python/mozbuild/mozbuild/test/frontend/test_emitter.py b/python/mozbuild/mozbuild/test/frontend/test_emitter.py index 16d4ce194b3f..508713b25d14 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py +++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py @@ -9,6 +9,9 @@ import unittest from mozunit import main +from mozbuild.frontend.context import ( + ObjDirPath, +) from mozbuild.frontend.data import ( AndroidResDirs, BrandingFiles, @@ -254,10 +257,42 @@ class TestEmitterBasic(unittest.TestCase): ('vpx', ['mem.h', 'mem2.h']), ] for (expect_path, expect_headers), (actual_path, actual_headers) in \ - zip(expected, [(path, list(seq)) for path, seq in objs[0].exports.walk()]): + zip(expected, [(path, list(seq)) for path, seq in objs[0].files.walk()]): self.assertEqual(expect_path, actual_path) self.assertEqual(expect_headers, actual_headers) + def test_exports_missing(self): + ''' + Missing files in EXPORTS is an error. + ''' + reader = self.reader('exports-missing') + with self.assertRaisesRegexp(SandboxValidationError, + 'File listed in EXPORTS does not exist:'): + objs = self.read_topsrcdir(reader) + + def test_exports_missing_generated(self): + ''' + An objdir file in EXPORTS that is not in GENERATED_FILES is an error. + ''' + reader = self.reader('exports-missing-generated') + with self.assertRaisesRegexp(SandboxValidationError, + 'Objdir file listed in EXPORTS not in GENERATED_FILES:'): + objs = self.read_topsrcdir(reader) + + def test_exports_generated(self): + reader = self.reader('exports-generated') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 2) + self.assertIsInstance(objs[0], GeneratedFile) + self.assertIsInstance(objs[1], Exports) + exports = [(path, list(seq)) for path, seq in objs[1].files.walk()] + self.assertEqual(exports, + [('', ['foo.h']), + ('mozilla', ['mozilla1.h', '!mozilla2.h'])]) + path, files = exports[1] + self.assertIsInstance(files[1], ObjDirPath) + def test_test_harness_files(self): reader = self.reader('test-harness-files') objs = self.read_topsrcdir(reader) @@ -834,6 +869,13 @@ class TestEmitterBasic(unittest.TestCase): reader = self.reader('dist-files-missing') self.read_topsrcdir(reader) + def test_final_target_pp_files_non_srcdir(self): + '''Test that non-srcdir paths in FINAL_TARGET_PP_FILES throws errors.''' + reader = self.reader('final-target-pp-files-non-srcdir') + with self.assertRaisesRegexp(SandboxValidationError, + 'Only source directory paths allowed in FINAL_TARGET_PP_FILES:'): + objs = self.read_topsrcdir(reader) + def test_android_res_dirs(self): """Test that ANDROID_RES_DIRS works properly.""" reader = self.reader('android-res-dirs') diff --git a/python/mozbuild/mozpack/copier.py b/python/mozbuild/mozpack/copier.py index 73439c08f812..5ad9d31c99ae 100644 --- a/python/mozbuild/mozpack/copier.py +++ b/python/mozbuild/mozpack/copier.py @@ -331,41 +331,50 @@ class FileCopier(FileRegistry): os.umask(umask) os.chmod(d, 0777 & ~umask) - # While we have remove_unaccounted, it doesn't apply to empty - # directories because it wouldn't make sense: an empty directory - # is empty, so removing it should have no effect. - existing_dirs = set() - existing_files = set() - for root, dirs, files in os.walk(destination): - # We need to perform the same symlink detection as above. os.walk() - # doesn't follow symlinks into directories by default, so we need - # to check dirs (we can't wait for root). - if have_symlinks: - filtered = [] - for d in dirs: - full = os.path.join(root, d) - st = os.lstat(full) - if stat.S_ISLNK(st.st_mode): - # This directory symlink is not a required - # directory: any such symlink would have been - # removed and a directory created above. - if remove_all_directory_symlinks: - os.remove(full) - result.removed_files.add(os.path.normpath(full)) + if isinstance(remove_unaccounted, FileRegistry): + existing_files = set(os.path.normpath(os.path.join(destination, p)) + for p in remove_unaccounted.paths()) + existing_dirs = set(os.path.normpath(os.path.join(destination, p)) + for p in remove_unaccounted + .required_directories()) + existing_dirs |= {os.path.normpath(destination)} + else: + # While we have remove_unaccounted, it doesn't apply to empty + # directories because it wouldn't make sense: an empty directory + # is empty, so removing it should have no effect. + existing_dirs = set() + existing_files = set() + for root, dirs, files in os.walk(destination): + # We need to perform the same symlink detection as above. + # os.walk() doesn't follow symlinks into directories by + # default, so we need to check dirs (we can't wait for root). + if have_symlinks: + filtered = [] + for d in dirs: + full = os.path.join(root, d) + st = os.lstat(full) + if stat.S_ISLNK(st.st_mode): + # This directory symlink is not a required + # directory: any such symlink would have been + # removed and a directory created above. + if remove_all_directory_symlinks: + os.remove(full) + result.removed_files.add( + os.path.normpath(full)) + else: + existing_files.add(os.path.normpath(full)) else: - existing_files.add(os.path.normpath(full)) - else: - filtered.append(d) + filtered.append(d) - dirs[:] = filtered + dirs[:] = filtered - existing_dirs.add(os.path.normpath(root)) + existing_dirs.add(os.path.normpath(root)) - for d in dirs: - existing_dirs.add(os.path.normpath(os.path.join(root, d))) + for d in dirs: + existing_dirs.add(os.path.normpath(os.path.join(root, d))) - for f in files: - existing_files.add(os.path.normpath(os.path.join(root, f))) + for f in files: + existing_files.add(os.path.normpath(os.path.join(root, f))) # Now we reconcile the state of the world against what we want. @@ -420,10 +429,25 @@ class FileCopier(FileRegistry): # Remove empty directories that aren't required. for d in sorted(remove_dirs, key=len, reverse=True): - # Permissions may not allow deletion. So ensure write access is - # in place before attempting delete. - os.chmod(d, 0700) - os.rmdir(d) + try: + try: + os.rmdir(d) + except OSError as e: + if e.errno in (errno.EPERM, errno.EACCES): + # Permissions may not allow deletion. So ensure write + # access is in place before attempting to rmdir again. + os.chmod(d, 0700) + os.rmdir(d) + else: + raise + except OSError as e: + # If remove_unaccounted is a # FileRegistry, then we have a + # list of directories that may not be empty, so ignore rmdir + # ENOTEMPTY errors for them. + if (isinstance(remove_unaccounted, FileRegistry) and + e.errno == errno.ENOTEMPTY): + continue + raise result.removed_directories.add(d) return result diff --git a/python/mozbuild/mozpack/manifests.py b/python/mozbuild/mozpack/manifests.py index c7b522e2eb59..f08e1f24a189 100644 --- a/python/mozbuild/mozpack/manifests.py +++ b/python/mozbuild/mozpack/manifests.py @@ -85,6 +85,8 @@ class InstallManifest(object): FIELD_SEPARATOR = '\x1f' + # Negative values are reserved for non-actionable items, that is, metadata + # that doesn't describe files in the destination. SYMLINK = 1 COPY = 2 REQUIRED_EXISTS = 3 @@ -163,8 +165,11 @@ class InstallManifest(object): silence_missing_directive_warnings=bool(int(warnings))) continue - raise UnreadableInstallManifest('Unknown record type: %d' % - record_type) + # Don't fail for non-actionable items, allowing + # forward-compatibility with those we will add in the future. + if record_type >= 0: + raise UnreadableInstallManifest('Unknown record type: %d' % + record_type) def __len__(self): return len(self._dests) diff --git a/python/mozbuild/mozpack/test/test_copier.py b/python/mozbuild/mozpack/test/test_copier.py index 53caf09d1832..7c3b80f2cdc7 100644 --- a/python/mozbuild/mozpack/test/test_copier.py +++ b/python/mozbuild/mozpack/test/test_copier.py @@ -421,6 +421,50 @@ class TestFileCopier(TestWithTmpDir): # existing when it does not. self.assertIn(self.tmppath('dest/foo/bar'), result.existing_files) + def test_remove_unaccounted_file_registry(self): + """Test FileCopier.copy(remove_unaccounted=FileRegistry())""" + + dest = self.tmppath('dest') + + copier = FileCopier() + copier.add('foo/bar/baz', GeneratedFile('foobarbaz')) + copier.add('foo/bar/qux', GeneratedFile('foobarqux')) + copier.add('foo/hoge/fuga', GeneratedFile('foohogefuga')) + copier.add('foo/toto/tata', GeneratedFile('footototata')) + + os.makedirs(os.path.join(dest, 'bar')) + with open(os.path.join(dest, 'bar', 'bar'), 'w') as fh: + fh.write('barbar'); + os.makedirs(os.path.join(dest, 'foo', 'toto')) + with open(os.path.join(dest, 'foo', 'toto', 'toto'), 'w') as fh: + fh.write('foototototo'); + + result = copier.copy(dest, remove_unaccounted=False) + + self.assertEqual(self.all_files(dest), + set(copier.paths()) | { 'foo/toto/toto', 'bar/bar'}) + self.assertEqual(self.all_dirs(dest), + {'foo/bar', 'foo/hoge', 'foo/toto', 'bar'}) + + copier2 = FileCopier() + copier2.add('foo/hoge/fuga', GeneratedFile('foohogefuga')) + + # We expect only files copied from the first copier to be removed, + # not the extra file that was there beforehand. + result = copier2.copy(dest, remove_unaccounted=copier) + + self.assertEqual(self.all_files(dest), + set(copier2.paths()) | { 'foo/toto/toto', 'bar/bar'}) + self.assertEqual(self.all_dirs(dest), + {'foo/hoge', 'foo/toto', 'bar'}) + self.assertEqual(result.updated_files, + {self.tmppath('dest/foo/hoge/fuga')}) + self.assertEqual(result.existing_files, set()) + self.assertEqual(result.removed_files, {self.tmppath(p) for p in + ('dest/foo/bar/baz', 'dest/foo/bar/qux', 'dest/foo/toto/tata')}) + self.assertEqual(result.removed_directories, + {self.tmppath('dest/foo/bar')}) + class TestFilePurger(TestWithTmpDir): def test_file_purger(self): diff --git a/security/manager/ssl/nsNSSIOLayer.cpp b/security/manager/ssl/nsNSSIOLayer.cpp index 6be1093d19c1..52064c5c35aa 100644 --- a/security/manager/ssl/nsNSSIOLayer.cpp +++ b/security/manager/ssl/nsNSSIOLayer.cpp @@ -2044,7 +2044,7 @@ nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket, RefPtr info( reinterpret_cast(socket->higher->secret)); - CERTCertificate* serverCert = SSL_PeerCertificate(socket); + ScopedCERTCertificate serverCert(SSL_PeerCertificate(socket)); if (!serverCert) { NS_NOTREACHED("Missing server certificate should have been detected during " "server cert authentication."); diff --git a/testing/config/marionette_requirements.txt b/testing/config/marionette_requirements.txt index aa1db8640281..296c5d61b882 100644 --- a/testing/config/marionette_requirements.txt +++ b/testing/config/marionette_requirements.txt @@ -1,4 +1,5 @@ -r mozbase_requirements.txt +../tools/wptserve ../marionette/transport ../marionette/driver ../marionette/marionette/runner/mixins/browsermob-proxy-py diff --git a/testing/marionette/client/marionette/runner/httpd.py b/testing/marionette/client/marionette/runner/httpd.py index 1c787c9e22cb..e987a8607524 100644 --- a/testing/marionette/client/marionette/runner/httpd.py +++ b/testing/marionette/client/marionette/runner/httpd.py @@ -4,7 +4,7 @@ import os -from mozhttpd import MozHttpd +from wptserve import server, handlers, routes as default_routes class FixtureServer(object): @@ -20,8 +20,14 @@ class FixtureServer(object): def start(self, block=False): if self.alive: return - self._server = MozHttpd(host=self.host, port=self.port, docroot=self.root, urlhandlers=[ - {"method": "POST", "path": "/file_upload", "function": upload_handler}]) + routes = [("POST", "/file_upload", upload_handler)] + routes.extend(default_routes.routes) + self._server = server.WebTestHttpd( + port=self.port, + doc_root=self.root, + routes=routes, + host=self.host, + ) self._server.start(block=block) self.port = self._server.httpd.server_port self.base_url = self.get_url() @@ -42,18 +48,19 @@ class FixtureServer(object): return self._server.get_url(path) @property - def urlhandlers(self): - return self._server.urlhandlers + def routes(self): + return self._server.router.routes -def upload_handler(query, postdata=None): - return (200, {}, query.headers.getheader("Content-Type")) +@handlers.handler +def upload_handler(request, response): + return 200, [], [request.headers.get("Content-Type")] or [] if __name__ == "__main__": here = os.path.abspath(os.path.dirname(__file__)) - root = os.path.join(os.path.dirname(here), "www") - httpd = FixtureServer(root, port=2829) + doc_root = os.path.join(os.path.dirname(here), "www") + httpd = FixtureServer(doc_root, port=2829) print "Started fixture server on http://%s:%d/" % (httpd.host, httpd.port) try: httpd.start(True) diff --git a/testing/marionette/client/requirements.txt b/testing/marionette/client/requirements.txt index 6ca53f2002cd..67dce7b022c0 100644 --- a/testing/marionette/client/requirements.txt +++ b/testing/marionette/client/requirements.txt @@ -1,7 +1,7 @@ marionette-driver >= 1.1.1 browsermob-proxy >= 0.6.0 manifestparser >= 1.1 -mozhttpd >= 0.7 +wptserve >= 1.3.0 mozinfo >= 0.8 mozprocess >= 0.9 mozrunner >= 6.9 diff --git a/testing/mozharness/configs/merge_day/aurora_to_beta.py b/testing/mozharness/configs/merge_day/aurora_to_beta.py index af9923bd0a10..a60ef4d91d7f 100644 --- a/testing/mozharness/configs/merge_day/aurora_to_beta.py +++ b/testing/mozharness/configs/merge_day/aurora_to_beta.py @@ -65,7 +65,7 @@ config = { "migration_behavior": "aurora_to_beta", "virtualenv_modules": [ - "requests==2.2.1", + "requests==2.8.1", ], "post_merge_builders": [ diff --git a/testing/mozharness/configs/merge_day/beta_to_release.py b/testing/mozharness/configs/merge_day/beta_to_release.py index 312a8375f58c..f4e45ad3caa3 100644 --- a/testing/mozharness/configs/merge_day/beta_to_release.py +++ b/testing/mozharness/configs/merge_day/beta_to_release.py @@ -41,7 +41,7 @@ config = { "pull_all_branches": True, "virtualenv_modules": [ - "requests==2.2.1", + "requests==2.8.1", ], "post_merge_builders": [ diff --git a/testing/mozharness/configs/merge_day/central_to_aurora.py b/testing/mozharness/configs/merge_day/central_to_aurora.py index de370e1dcae3..ea5a8102ec0b 100644 --- a/testing/mozharness/configs/merge_day/central_to_aurora.py +++ b/testing/mozharness/configs/merge_day/central_to_aurora.py @@ -92,7 +92,7 @@ config = { "balrog_credentials_file": "oauth.txt", "virtualenv_modules": [ - "requests==2.2.1", + "requests==2.8.1", ], "post_merge_builders": [ diff --git a/testing/mozharness/configs/vcs_sync/beagle.py b/testing/mozharness/configs/vcs_sync/beagle.py index 58538ef400dd..b1eeb7ac0523 100644 --- a/testing/mozharness/configs/vcs_sync/beagle.py +++ b/testing/mozharness/configs/vcs_sync/beagle.py @@ -758,7 +758,7 @@ config = { "mozfile==0.9", "mozinfo==0.5", "mozprocess==0.11", - "requests==2.2.1", + "requests==2.8.1", ], "find_links": [ "http://pypi.pvt.build.mozilla.org/pub", diff --git a/testing/mozharness/configs/vcs_sync/build-repos.py b/testing/mozharness/configs/vcs_sync/build-repos.py index fd7d25ef99fd..14fee3955e76 100644 --- a/testing/mozharness/configs/vcs_sync/build-repos.py +++ b/testing/mozharness/configs/vcs_sync/build-repos.py @@ -76,7 +76,7 @@ config = { "mozfile==0.9", "mozinfo==0.5", "mozprocess==0.11", - "requests==2.2.1", + "requests==2.8.1", ], "find_links": [ "http://pypi.pub.build.mozilla.org/pub" diff --git a/testing/mozharness/configs/vcs_sync/l10n.py b/testing/mozharness/configs/vcs_sync/l10n.py index afd71cf2d5c9..938189d18909 100644 --- a/testing/mozharness/configs/vcs_sync/l10n.py +++ b/testing/mozharness/configs/vcs_sync/l10n.py @@ -274,7 +274,7 @@ config = { "mozfile==0.9", "mozinfo==0.5", "mozprocess==0.11", - "requests==2.2.1", + "requests==2.8.1", ], "find_links": [ "http://pypi.pub.build.mozilla.org/pub", diff --git a/testing/mozharness/scripts/b2g_build.py b/testing/mozharness/scripts/b2g_build.py index 56edd0bc37f1..e4758d6305da 100755 --- a/testing/mozharness/scripts/b2g_build.py +++ b/testing/mozharness/scripts/b2g_build.py @@ -147,7 +147,7 @@ class B2GBuild(LocalesMixin, PurgeMixin, 'balrog_credentials_file': 'oauth.txt', 'build_resources_path': '%(abs_obj_dir)s/.mozbuild/build_resources.json', 'virtualenv_modules': [ - 'requests==2.2.1', + 'requests==2.8.1', ], 'virtualenv_path': 'venv', } diff --git a/testing/mozharness/scripts/desktop_l10n.py b/testing/mozharness/scripts/desktop_l10n.py index 47673dde38ab..0d6bfb417286 100755 --- a/testing/mozharness/scripts/desktop_l10n.py +++ b/testing/mozharness/scripts/desktop_l10n.py @@ -182,7 +182,7 @@ class DesktopSingleLocale(LocalesMixin, ReleaseMixin, MockMixin, BuildbotMixin, "hashType": "sha512", "taskcluster_credentials_file": "oauth.txt", 'virtualenv_modules': [ - 'requests==2.2.1', + 'requests==2.8.1', 'PyHawk-with-a-single-extra-commit==0.1.5', 'taskcluster==0.0.15', ], diff --git a/testing/mozharness/scripts/fx_desktop_build.py b/testing/mozharness/scripts/fx_desktop_build.py index 2716ac0a3d28..19f4ddc36547 100755 --- a/testing/mozharness/scripts/fx_desktop_build.py +++ b/testing/mozharness/scripts/fx_desktop_build.py @@ -90,7 +90,7 @@ class FxDesktopBuild(BuildScript, object): 'stage_username': 'ffxbld', 'stage_ssh_key': 'ffxbld_rsa', 'virtualenv_modules': [ - 'requests==2.2.1', + 'requests==2.8.1', 'PyHawk-with-a-single-extra-commit==0.1.5', 'taskcluster==0.0.15', ], diff --git a/testing/mozharness/scripts/mobile_l10n.py b/testing/mozharness/scripts/mobile_l10n.py index b0b8381ac49e..f98cac4f0226 100755 --- a/testing/mozharness/scripts/mobile_l10n.py +++ b/testing/mozharness/scripts/mobile_l10n.py @@ -125,7 +125,7 @@ class MobileSingleLocale(MockMixin, LocalesMixin, ReleaseMixin, 'config': { 'taskcluster_credentials_file': 'oauth.txt', 'virtualenv_modules': [ - 'requests==2.2.1', + 'requests==2.8.1', 'PyHawk-with-a-single-extra-commit==0.1.5', 'taskcluster==0.0.15', ], diff --git a/testing/mozharness/scripts/web_platform_tests.py b/testing/mozharness/scripts/web_platform_tests.py index 097129161abb..6c86bf299c82 100755 --- a/testing/mozharness/scripts/web_platform_tests.py +++ b/testing/mozharness/scripts/web_platform_tests.py @@ -169,6 +169,7 @@ class WebPlatformTest(TestingMixin, MercurialScript, BlobUploadMixin): "config/*", "mozbase/*", "marionette/*", + "tools/wptserve/*", "web-platform/*"], suite_categories=["web-platform"]) diff --git a/testing/talos/talos/talos_process.py b/testing/talos/talos/talos_process.py index 1c4a300bd735..d70d837900af 100644 --- a/testing/talos/talos/talos_process.py +++ b/testing/talos/talos/talos_process.py @@ -40,12 +40,18 @@ class Reader(object): def __init__(self, event): self.output = [] self.got_end_timestamp = False + self.got_timeout = False + self.timeout_message = '' self.event = event def __call__(self, line): if line.find('__endTimestamp') != -1: self.got_end_timestamp = True self.event.set() + elif line == 'TART: TIMEOUT': + self.got_timeout = True + self.timeout_message = 'TART' + self.event.set() if not (line.startswith('JavaScript error:') or line.startswith('JavaScript warning:')): @@ -108,6 +114,8 @@ def run_browser(command, minidump_dir, timeout=None, on_started=None, "Browser shutdown timed out after {0} seconds, terminating" " process.".format(wait_for_quit_timeout) ) + elif reader.got_timeout: + raise TalosError('TIMEOUT: %s' % reader.timeout_message) finally: # this also handle KeyboardInterrupt # ensure early the process is really terminated diff --git a/testing/taskcluster/tasks/branches/base_job_flags.yml b/testing/taskcluster/tasks/branches/base_job_flags.yml index f8ee90d4ffe2..2cf4f9691e0d 100644 --- a/testing/taskcluster/tasks/branches/base_job_flags.yml +++ b/testing/taskcluster/tasks/branches/base_job_flags.yml @@ -26,26 +26,44 @@ flags: # with "foobar-7". Note that a few aliases allowed chunks to be specified # without a leading `-`, for example 'mochitest-dt1'. That's no longer # supported. - mochitest: /mochitest.*/ - mochitests: /mochitest.*/ - jittest: /jittest.*/ - jittests: /jittest.*/ - mochitest-debug: /mochitest-debug-.*/ - mochitest-o: /.*mochitest-o.*/ - mochitest-a11y: /.*mochitest-o.*/ - xpcshell: /xpcshell.*/ - robocop: /robocop.*/ - mochitest-dt: /mochitest-devtools-chrome.*/ - mochitest-gl: /mochitest-webgl.*/ - mochitest-bc: /mochitest-browser-chrome.*/ - mochitest-browser: /mochitest-browser-chrome.*/ - reftest: /(plain-)?reftest.*/ - reftests: /(plain-)?reftest.*/ - web-platform-test: /web-platform-tests.*/ - web-platform-tests: /web-platform-tests.*/ + cppunit: /cppunit.*/ + crashtest: /crashtest.*/ + crashtest-e10s: /crashtest-e10s.*/ e10s: /.*e10s.*/ gaia-js-integration: /.*gaia-js-integration.*/ gaia-ui-test: /.*gaia-ui-test.*/ + jittest: /jittest.*/ + jittests: /jittest.*/ + jsreftest: /jsreftest.*/ + jsreftest-e10s: /jsreftest-e10s.*/ + luciddream: /luciddream.*/ + marionette: /marionette.*/ + mochitest: /mochitest.*/ + mochitests: /mochitest.*/ + mochitest-e10s: /mochitest-e10s.*/ + mochitests-e10s: /mochitest-e10s.*/ + mochitest-debug: /mochitest-debug-.*/ + mochitest-a11y: /.*mochitest-o.*/ + mochitest-bc: /mochitest-browser-chrome.*/ + mochitest-browser-chrome: /mochitest-browser-chrome.*/ + mochitest-bc-e10s: /mochitest-browser-chrome-e10s.*/ + mochitest-browser-chrome-e10s: /mochitest-browser-chrome-e10s.*/ + mochitest-dt: /mochitest-devtools-chrome.*/ + mochitest-dt-e10s: /mochitest-devtools-chrome-e10s.*/ + mochitest-gl: /mochitest-webgl.*/ + mochitest-o: /.*mochitest-o.*/ + mochitest-other: /mochitest-other.*/ + mochitest-push: /mochitest-push.*/ + reftest: /(plain-)?reftest.*/ + reftest-no-accel: /(plain-)?reftest-no-accel.*/ + reftests: /(plain-)?reftest.*/ + reftests-e10s: /(plain-)?reftest-e10s.*/ + robocop: /robocop.*/ + web-platform-test: /web-platform-tests.*/ + web-platform-tests: /web-platform-tests.*/ + web-platform-tests-e10s: /web-platform-tests-e10s.*/ + web-platform-tests-reftests: /web-platform-tests-reftests.*/ + xpcshell: /xpcshell.*/ builds: - emulator @@ -90,6 +108,7 @@ flags: tests: - cppunit - crashtest + - crashtest-e10s - crashtest-ipc - gaia-build - gaia-build-unit @@ -104,17 +123,31 @@ flags: - jetpack - jittests - jsreftest + - jsreftest-e10s + - luciddream - marionette - marionette-webapi - mochitest + - mochitest-browser-chrome + - mochitest-browser-chrome-e10s + - mochitest-devtools-chrome + - mochitest-devtools-chrome-e10s + - mochitest-e10s + - mochitest-jetpack - mochitest-media - mochitest-oop + - mochitest-other + - mochitest-push + - mochitest-webgl #- mochitest-webgl -- disabled per bug 1227637 / bug 1220658 - mozmill - reftest + - reftest-e10s - reftest-ipc - reftest-no-accel - reftest-sanity-oop - web-platform-tests + - web-platform-tests-e10s + - web-platform-tests-reftests - xpcshell diff --git a/testing/taskcluster/tasks/tests/fx_desktop_generic.yml b/testing/taskcluster/tasks/tests/fx_desktop_generic.yml new file mode 100644 index 000000000000..cbd7d43ffce4 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_desktop_generic.yml @@ -0,0 +1,8 @@ +--- +$inherits: + from: 'tasks/tests/fx_test_base.yml' +task: + payload: + command: + - bash + - /home/worker/bin/test.sh diff --git a/testing/taskcluster/tasks/tests/fx_unittest_base.yml b/testing/taskcluster/tasks/tests/fx_desktop_unittest.yml similarity index 58% rename from testing/taskcluster/tasks/tests/fx_unittest_base.yml rename to testing/taskcluster/tasks/tests/fx_desktop_unittest.yml index 07eb27436778..6189d3b7932f 100644 --- a/testing/taskcluster/tasks/tests/fx_unittest_base.yml +++ b/testing/taskcluster/tasks/tests/fx_desktop_unittest.yml @@ -1,9 +1,8 @@ --- $inherits: - from: 'tasks/test.yml' + from: 'tasks/tests/fx_test_base.yml' task: payload: - image: '{{#docker_image}}desktop-test{{/docker_image}}' command: - bash - /home/worker/bin/test.sh @@ -15,22 +14,8 @@ task: - --total-chunk={{total_chunks}} - --this-chunk={{chunk}} env: - NEED_WINDOW_MANAGER: true - NEED_PULSEAUDIO: true - GECKO_HEAD_REPOSITORY: '{{{head_repository}}}' - GECKO_HEAD_REV: '{{{head_rev}}}' MOZHARNESS_SCRIPT: 'mozharness/scripts/desktop_unittest.py' # TODO move linux_unittest.py to a platform specific config MOZHARNESS_CONFIG: > mozharness/configs/unittests/linux_unittest.py mozharness/configs/remove_executables.py - artifacts: - 'public/build': - type: directory - path: '/home/worker/artifacts/' - expires: '{{#from_now}}1 year{{/from_now}}' - - extra: - treeherderEnv: - - production - - staging diff --git a/testing/taskcluster/tasks/tests/fx_linux64_cppunit.yml b/testing/taskcluster/tasks/tests/fx_linux64_cppunit.yml new file mode 100644 index 000000000000..b88911fcd52e --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_cppunit.yml @@ -0,0 +1,26 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_generic.yml' +task: + payload: + command: + - --no-read-buildbot-config + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} + - --download-symbols=ondemand + - --cppunittest-suite=cppunittest + env: + MOZHARNESS_SCRIPT: 'mozharness/scripts/desktop_unittest.py' + MOZHARNESS_CONFIG: > + mozharness/configs/unittests/linux_unittest.py + mozharness/configs/remove_executables.py + metadata: + name: '[TC] Linux64 cppunit' + description: CPP unit tests + extra: + suite: + name: cppunittest + flavor: cppunittest + treeherder: + groupName: Desktop cppunit + symbol: Cpp diff --git a/testing/taskcluster/tasks/tests/fx_linux64_crashtest.yml b/testing/taskcluster/tasks/tests/fx_linux64_crashtest.yml new file mode 100644 index 000000000000..50e4efeba25d --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_crashtest.yml @@ -0,0 +1,15 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + metadata: + name: '[TC] Linux64 crashtest' + description: Crashtest run + extra: + suite: + name: reftest + flavor: crashtest + treeherder: + groupName: Desktop crashtest + groupSymbol: tc-R + symbol: C diff --git a/testing/taskcluster/tasks/tests/fx_linux64_crashtest_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_crashtest_e10s.yml new file mode 100644 index 000000000000..de24ea14b0d2 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_crashtest_e10s.yml @@ -0,0 +1,18 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + payload: + command: + - --e10s + metadata: + name: '[TC] Linux64 crashtest e10s' + description: Crashtest e10s run + extra: + suite: + name: reftest + flavor: crashtest + treeherder: + groupName: Desktop crashtest e10s + groupSymbol: tc-R-e10s + symbol: C diff --git a/testing/taskcluster/tasks/tests/fx_linux64_jittests.yml b/testing/taskcluster/tasks/tests/fx_linux64_jittests.yml new file mode 100644 index 000000000000..47fa4da0ef72 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_jittests.yml @@ -0,0 +1,16 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + metadata: + name: '[TC] Linux64 jittests {{chunk}}' + description: Jittests run {{chunk}} + extra: + chunks: + total: 2 + suite: + name: jittest + flavor: jittests-chunked + treeherder: + groupName: Desktop jittests + symbol: Jit{{chunk}} diff --git a/testing/taskcluster/tasks/tests/fx_linux64_jsreftest.yml b/testing/taskcluster/tasks/tests/fx_linux64_jsreftest.yml new file mode 100644 index 000000000000..37e5a6250a6c --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_jsreftest.yml @@ -0,0 +1,15 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + metadata: + name: '[TC] Linux64 jsreftest' + description: Jsreftest run + extra: + suite: + name: reftest + flavor: jsreftest + treeherder: + groupName: Desktop jsreftest + groupSymbol: tc-R + symbol: J diff --git a/testing/taskcluster/tasks/tests/fx_linux64_jsreftest_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_jsreftest_e10s.yml new file mode 100644 index 000000000000..89ab529052ab --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_jsreftest_e10s.yml @@ -0,0 +1,18 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + payload: + command: + - --e10s + metadata: + name: '[TC] Linux64 jsreftest e10s' + description: Jsreftest e10s run + extra: + suite: + name: reftest + flavor: jsreftest + treeherder: + groupName: Desktop jsreftest + groupSymbol: tc-R-e10s + symbol: J diff --git a/testing/taskcluster/tasks/tests/fx_linux64_luciddream.yml b/testing/taskcluster/tasks/tests/fx_linux64_luciddream.yml new file mode 100644 index 000000000000..353415ed1e6a --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_luciddream.yml @@ -0,0 +1,19 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_generic.yml' +task: + payload: + command: + - --no-read-buildbot-config + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} + env: + MOZHARNESS_SCRIPT: 'mozharness/scripts/luciddream_unittest.py' + MOZHARNESS_CONFIG: > + mozharness/configs/luciddream/linux_config.py + metadata: + name: '[TC] Linux64 luciddream' + description: Luciddream run + extra: + treeherder: + symbol: Ld diff --git a/testing/taskcluster/tasks/tests/fx_linux64_marionette.yml b/testing/taskcluster/tasks/tests/fx_linux64_marionette.yml new file mode 100644 index 000000000000..8e54927513d0 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_marionette.yml @@ -0,0 +1,25 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_generic.yml' +task: + payload: + command: + - --no-read-buildbot-config + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} + - --download-symbols=ondemand + env: + MOZHARNESS_SCRIPT: 'mozharness/scripts/marionette.py' + MOZHARNESS_CONFIG: > + mozharness/configs/marionette/prod_config.py + mozharness/configs/remove_executables.py + metadata: + name: '[TC] Linux64 marionette' + description: Marionette unittest run + extra: + suite: + name: marionette + flavor: marionette + treeherder: + groupName: Desktop marionette + symbol: Mn diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc.yml index 194ada935bdc..5a74d1851cf9 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc.yml @@ -1,6 +1,6 @@ --- $inherits: - from: 'tasks/tests/fx_unittest_base.yml' + from: 'tasks/tests/fx_desktop_unittest.yml' task: scopes: - 'docker-worker:capability:device:loopbackVideo' diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc_e10s.yml new file mode 100644 index 000000000000..012cc1a1e6f6 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc_e10s.yml @@ -0,0 +1,29 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + payload: + command: + - --e10s + scopes: + - 'docker-worker:capability:device:loopbackVideo' + - 'docker-worker:capability:device:loopbackAudio' + metadata: + name: '[TC] Linux64 mochitest-browser-chrome e10s M(bc{{chunk}})' + description: Mochitest browser-chrome e10s run {{chunk}} + payload: + capabilities: + devices: + loopbackVideo: true + loopbackAudio: true + extra: + chunks: + total: 7 + suite: + name: mochitest + flavor: browser-chrome-chunked + treeherder: + groupName: Desktop mochitests + groupSymbol: tc-M-e10s + symbol: bc{{chunk}} + diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml index 616c18a88823..76c047fb76e8 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml @@ -1,6 +1,6 @@ --- $inherits: - from: 'tasks/tests/fx_unittest_base.yml' + from: 'tasks/tests/fx_desktop_unittest.yml' task: scopes: - 'docker-worker:capability:device:loopbackVideo' @@ -15,7 +15,7 @@ task: loopbackAudio: true extra: chunks: - total: 2 + total: 8 suite: name: mochitest flavor: mochitest-devtools-chrome diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt_e10s.yml new file mode 100644 index 000000000000..003742bf390f --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt_e10s.yml @@ -0,0 +1,28 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + payload: + command: + - --e10s + scopes: + - 'docker-worker:capability:device:loopbackVideo' + - 'docker-worker:capability:device:loopbackAudio' + metadata: + name: '[TC] Linux64 mochitest-devtools-chrome e10s M(dt{{chunk}})' + description: Mochitest devtools-chrome e10s run {{chunk}} + payload: + capabilities: + devices: + loopbackVideo: true + loopbackAudio: true + extra: + chunks: + total: 8 + suite: + name: mochitest + flavor: mochitest-devtools-chrome + treeherder: + groupName: Desktop mochitests + groupSymbol: tc-M-e10s + symbol: dt{{chunk}} diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_gl.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_gl.yml index a6a1fb1eb9d1..e3ba5175ec46 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_gl.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_gl.yml @@ -1,5 +1,5 @@ $inherits: - from: 'tasks/tests/fx_unittest_base.yml' + from: 'tasks/tests/fx_desktop_unittest.yml' task: scopes: - 'docker-worker:capability:device:loopbackVideo' diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_jetpack.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_jetpack.yml new file mode 100644 index 000000000000..c7f03f28e8b8 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_jetpack.yml @@ -0,0 +1,26 @@ +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + payload: + command: + - --mochitesttest-suite=jetpack-addon + env: + scopes: + - 'docker-worker:capability:device:loopbackVideo' + - 'docker-worker:capability:device:loopbackAudio' + metadata: + name: '[TC] Linux64 mochitest-jetpack' + description: Mochitest jetpack run + payload: + capabilities: + devices: + loopbackVideo: true + loopbackAudio: true + extra: + suite: + name: mochitest + flavor: jetpack-package + treeherder: + groupName: Desktop mochitests + groupSymbol: tc-M + symbol: J diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_other.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_other.yml new file mode 100644 index 000000000000..884f5a870491 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_other.yml @@ -0,0 +1,22 @@ +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + scopes: + - 'docker-worker:capability:device:loopbackVideo' + - 'docker-worker:capability:device:loopbackAudio' + metadata: + name: '[TC] Linux64 mochitest-other' + description: Mochitest other run + payload: + capabilities: + devices: + loopbackVideo: true + loopbackAudio: true + extra: + suite: + name: mochitest + flavor: mochitest-other + treeherder: + groupName: Desktop mochitests + groupSymbol: tc-M + symbol: oth diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain.yml index ff89911bf230..d117ecbe247f 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain.yml @@ -1,6 +1,6 @@ --- $inherits: - from: 'tasks/tests/fx_unittest_base.yml' + from: 'tasks/tests/fx_desktop_unittest.yml' task: scopes: - 'docker-worker:capability:device:loopbackVideo' diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain_e10s.yml new file mode 100644 index 000000000000..61bad29e4af8 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain_e10s.yml @@ -0,0 +1,28 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + payload: + command: + - --e10s + scopes: + - 'docker-worker:capability:device:loopbackVideo' + - 'docker-worker:capability:device:loopbackAudio' + metadata: + name: '[TC] Linux64 mochitest-plain e10s {{chunk}}' + description: Mochitest plain e10s run {{chunk}} + payload: + capabilities: + devices: + loopbackVideo: true + loopbackAudio: true + extra: + chunks: + total: 5 + suite: + name: mochitest + flavor: plain-chunked + treeherder: + groupName: Desktop mochitests + groupSymbol: tc-M-e10s + symbol: {{chunk}} diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_push.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_push.yml new file mode 100644 index 000000000000..2f6bef043171 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_push.yml @@ -0,0 +1,22 @@ +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + scopes: + - 'docker-worker:capability:device:loopbackVideo' + - 'docker-worker:capability:device:loopbackAudio' + metadata: + name: '[TC] Linux64 mochitest-push M(p)' + description: Mochitest push run + payload: + capabilities: + devices: + loopbackVideo: true + loopbackAudio: true + extra: + suite: + name: mochitest + flavor: mochitest-push + treeherder: + groupName: Desktop mochitests + groupSymbol: tc-M + symbol: p diff --git a/testing/taskcluster/tasks/tests/fx_linux64_reftest.yml b/testing/taskcluster/tasks/tests/fx_linux64_reftest.yml index 49e09a100a39..f80766cc1fa1 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_reftest.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_reftest.yml @@ -1,6 +1,6 @@ --- $inherits: - from: 'tasks/tests/fx_unittest_base.yml' + from: 'tasks/tests/fx_desktop_unittest.yml' task: metadata: name: '[TC] Linux64 reftest {{chunk}}' @@ -14,4 +14,4 @@ task: treeherder: groupName: Desktop reftest groupSymbol: tc-R - symbol: {{chunk}} + symbol: R{{chunk}} diff --git a/testing/taskcluster/tasks/tests/fx_linux64_reftest_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_reftest_e10s.yml new file mode 100644 index 000000000000..1eeddecf5e8e --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_reftest_e10s.yml @@ -0,0 +1,20 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + payload: + command: + - --e10s + metadata: + name: '[TC] Linux64 reftest e10s {{chunk}}' + description: Reftest e10s run {{chunk}} + extra: + chunks: + total: 2 + suite: + name: reftest + flavor: reftest + treeherder: + groupName: Desktop reftest + groupSymbol: tc-R-e10s + symbol: R-e10s{{chunk}} diff --git a/testing/taskcluster/tasks/tests/fx_linux64_reftest_not_accelerated.yml b/testing/taskcluster/tasks/tests/fx_linux64_reftest_not_accelerated.yml new file mode 100644 index 000000000000..2008c481bca9 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_reftest_not_accelerated.yml @@ -0,0 +1,17 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + metadata: + name: '[TC] Linux64 reftest {{chunk}}' + description: Reftest not accelerated run {{chunk}} + extra: + chunks: + total: 2 + suite: + name: reftest + flavor: reftest-no-accel + treeherder: + groupName: Desktop reftest not accelerated + groupSymbol: tc-R + symbol: Ru{{chunk}} diff --git a/testing/taskcluster/tasks/tests/fx_linux64_web_platform_tests.yml b/testing/taskcluster/tasks/tests/fx_linux64_web_platform_tests.yml new file mode 100644 index 000000000000..a1a5ec7c2f97 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_web_platform_tests.yml @@ -0,0 +1,31 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_generic.yml' +task: + payload: + command: + - --no-read-buildbot-config + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} + - --download-symbols=ondemand + - --test-type=testharness + - --total-chunk={{total_chunks}} + - --this-chunk={{chunk}} + env: + MOZHARNESS_SCRIPT: 'mozharness/scripts/web_platform_tests.py' + MOZHARNESS_CONFIG: > + mozharness/configs/web_platform_tests/prod_config.py + mozharness/configs/remove_executables.py + metadata: + name: '[TC] Linux64 web-platform-tests {{chunk}}' + description: Web platform tests run {{chunk}} + extra: + chunks: + total: 8 + suite: + name: web-platform-tests + flavor: web-platform-tests + treeherder: + groupName: Desktop web-platform-tests + groupSymbol: tc-W + symbol: {{chunk}} diff --git a/testing/taskcluster/tasks/tests/fx_linux64_web_platform_tests_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_web_platform_tests_e10s.yml new file mode 100644 index 000000000000..4c2264073fa5 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_web_platform_tests_e10s.yml @@ -0,0 +1,29 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_generic.yml' +task: + payload: + command: + - --no-read-buildbot-config + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} + - --download-symbols=ondemand + - --test-type=testharness + - --e10s + - --total-chunk={{total_chunks}} + - --this-chunk={{chunk}} + env: + MOZHARNESS_SCRIPT: 'mozharness/scripts/web_platform_tests.py' + MOZHARNESS_CONFIG: > + mozharness/configs/web_platform_tests/prod_config.py + mozharness/configs/remove_executables.py + metadata: + name: '[TC] Linux64 web-platform-tests-e10s {{chunk}}' + description: Web platform e10s tests run {{chunk}} + extra: + chunks: + total: 8 + treeherder: + groupName: Desktop web-platform-tests + groupSymbol: tc-W-e10s + symbol: {{chunk}} diff --git a/testing/taskcluster/tasks/tests/fx_linux64_web_platform_tests_reftests.yml b/testing/taskcluster/tasks/tests/fx_linux64_web_platform_tests_reftests.yml new file mode 100644 index 000000000000..bb0ef196cfb2 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_web_platform_tests_reftests.yml @@ -0,0 +1,27 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_generic.yml' +task: + payload: + command: + - --no-read-buildbot-config + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} + - --download-symbols=ondemand + - --test-type=reftest + env: + MOZHARNESS_SCRIPT: 'mozharness/scripts/web_platform_tests.py' + MOZHARNESS_CONFIG: > + mozharness/configs/web_platform_tests/prod_config.py + mozharness/configs/remove_executables.py + metadata: + name: '[TC] Linux64 web-platform-tests-reftests' + description: Web platform reftest tests run + extra: + suite: + name: web-platform-tests-reftests + flavor: web-platform-tests-reftests + treeherder: + groupName: Desktop web-platform-tests + groupSymbol: tc-W + symbol: Wr diff --git a/testing/taskcluster/tasks/tests/fx_linux64_web_platform_tests_reftests_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_web_platform_tests_reftests_e10s.yml new file mode 100644 index 000000000000..8c643813528b --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_web_platform_tests_reftests_e10s.yml @@ -0,0 +1,28 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_generic.yml' +task: + payload: + command: + - --no-read-buildbot-config + - --installer-url={{build_url}} + - --test-packages-url={{test_packages_url}} + - --download-symbols=ondemand + - --test-type=reftest + - --e10s + env: + MOZHARNESS_SCRIPT: 'mozharness/scripts/web_platform_tests.py' + MOZHARNESS_CONFIG: > + mozharness/configs/web_platform_tests/prod_config.py + mozharness/configs/remove_executables.py + metadata: + name: '[TC] Linux64 web-platform-tests-reftests-e10s' + description: Web platform reftest e10s test run + extra: + suite: + name: web-platform-tests-reftests + flavor: web-platform-tests-reftests + treeherder: + groupName: Desktop web-platform-tests + groupSymbol: tc-W-e10s + symbol: Wr diff --git a/testing/taskcluster/tasks/tests/fx_linux64_xpcshell.yml b/testing/taskcluster/tasks/tests/fx_linux64_xpcshell.yml index 7c4c85136f41..38653b049ca7 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_xpcshell.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_xpcshell.yml @@ -1,13 +1,13 @@ --- $inherits: - from: 'tasks/tests/fx_unittest_base.yml' + from: 'tasks/tests/fx_desktop_unittest.yml' task: metadata: name: '[TC] Linux64 xpcshell {{chunk}}' description: XPCShell run {{chunk}} extra: chunks: - total: 2 + total: 4 suite: name: xpcshell flavor: xpcshell diff --git a/testing/taskcluster/tasks/tests/fx_test_base.yml b/testing/taskcluster/tasks/tests/fx_test_base.yml new file mode 100644 index 000000000000..2c77ffaede1a --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_test_base.yml @@ -0,0 +1,23 @@ +--- +$inherits: + from: 'tasks/test.yml' +task: + payload: + image: '{{#docker_image}}desktop-test{{/docker_image}}' + env: + NEED_WINDOW_MANAGER: true + NEED_PULSEAUDIO: true + GECKO_HEAD_REPOSITORY: '{{{head_repository}}}' + GECKO_HEAD_REV: '{{{head_rev}}}' + artifacts: + 'public/build': + type: directory + path: '/home/worker/artifacts/' + expires: '{{#from_now}}1 year{{/from_now}}' + + extra: + treeherder: + tier: 2 + treeherderEnv: + - production + - staging diff --git a/testing/web-platform/meta/XMLHttpRequest/send-redirect-to-cors.htm.ini b/testing/web-platform/meta/XMLHttpRequest/send-redirect-to-cors.htm.ini deleted file mode 100644 index 8a2f5593ca51..000000000000 --- a/testing/web-platform/meta/XMLHttpRequest/send-redirect-to-cors.htm.ini +++ /dev/null @@ -1,14 +0,0 @@ -[send-redirect-to-cors.htm] - type: testharness - [XMLHttpRequest: send() - Redirect to CORS-enabled resource (301)] - expected: FAIL - - [XMLHttpRequest: send() - Redirect to CORS-enabled resource (302)] - expected: FAIL - - [XMLHttpRequest: send() - Redirect to CORS-enabled resource (303)] - expected: FAIL - - [XMLHttpRequest: send() - Redirect to CORS-enabled resource (307)] - expected: FAIL - diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-navigation-fetch-event.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-navigation-fetch-event.https.html.ini new file mode 100644 index 000000000000..9b5cd518b859 --- /dev/null +++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-navigation-fetch-event.https.html.ini @@ -0,0 +1,3 @@ +[update-after-navigation-fetch-event.https.html] + type: testharness + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1226443 diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-oneday.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-oneday.https.html.ini index 2402f8e5105f..7eec4c67ce0e 100644 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-oneday.https.html.ini +++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-oneday.https.html.ini @@ -1,3 +1,4 @@ [update-after-oneday.https.html] type: testharness prefs: [dom.serviceWorkers.testUpdateOverOneDay: true] + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1226443 diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/appcache-ordering-main.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/appcache-ordering-main.https.html index d6191b0c01d3..4778f0c48ae2 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/appcache-ordering-main.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/appcache-ordering-main.https.html @@ -13,6 +13,8 @@ var SERVICE_WORKER_SCRIPT = "resources/empty-worker.js"; var resolve_install_appcache = undefined; var reject_install_appcache = undefined; +var frames = []; + // Called by the INSTALL_APPCACHE_URL child frame. function notify_appcache_installed(success) { if (success) @@ -24,6 +26,7 @@ function notify_appcache_installed(success) { function install_appcache() { return new Promise(function(resolve, reject) { var frame = document.createElement('iframe'); + frames.push(frame); frame.src = INSTALL_APPCACHE_URL; document.body.appendChild(frame); resolve_install_appcache = function() { @@ -47,6 +50,7 @@ function notify_is_appcached(is) { function is_appcached() { return new Promise(function(resolve) { var frame = document.createElement('iframe'); + frames.push(frame); frame.src = IS_APPCACHED_URL; document.body.appendChild(frame); resolve_is_appcached = function(is) { @@ -77,6 +81,7 @@ async_test(function(t) { }) .then(function(result) { assert_false(result, 'but serviceworkers should take priority'); + frames.forEach(function(f) { f.remove(); }); service_worker_unregister_and_done(t, SERVICE_WORKER_SCOPE); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html index 66301fe94047..f7581931eeca 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html @@ -6,18 +6,24 @@ diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-load.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-load.https.html index 89595d4d640a..e09a90152ba2 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-load.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/controller-on-load.https.html @@ -22,6 +22,7 @@ t.step(function() { assert_true(controller instanceof w.ServiceWorker, 'controller should be a ServiceWorker object'); assert_equals(controller.scriptURL, normalizeURL(url)); + frame.remove(); service_worker_unregister_and_done(t, scope); })) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html index 37833787ac9f..bdb56c213af6 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html @@ -19,6 +19,7 @@ async_test(function(t) { var channel = new MessageChannel(); channel.port1.onmessage = t.step_func(function(e) { assert_equals(e.data.results, 'finish'); + frame.remove(); service_worker_unregister_and_done(t, SCOPE); }); frame.contentWindow.postMessage({}, diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html index b53a4a41bc1c..556d04413fa4 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html @@ -52,14 +52,12 @@ function redirect_fetch_test(t, test) { var p = new Promise(function(resolve, reject) { var channel = new MessageChannel(); channel.port1.onmessage = function(e) { + frame.remove(); if (e.data.result === 'reject') { - frame.remove(); reject(e.data.detail); } else if (e.data.result === 'success') { - frame.remove(); resolve(e.data.result); } else { - frame.remove(); resolve(e.data.detail); } }; diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html index cd13df313dac..acc238fcb65a 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event.https.html @@ -192,7 +192,7 @@ async_test(function(t) { assert_equals(frame.contentDocument.body.textContent, 'POST:application/x-www-form-urlencoded:' + 'testName1=testValue1&testName2=testValue2'); - document.body.removeChild(frame); + frame.remove(); return service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html index 156880faa1f2..b468f8c358b2 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html @@ -73,12 +73,13 @@ function getLoadedWindowAsObject(win) { async_test(function(t) { var scope = 'resources/fetch-frame-resource/frame-basic'; + var frame; service_worker_unregister_and_register(t, worker, scope) .then(function(reg) { return wait_for_state(t, reg.installing, 'activated'); }) .then(function() { - var frame = document.createElement('iframe'); + frame = document.createElement('iframe'); frame.src = scope + '?url=' + encodeURIComponent(host_info['HTTPS_ORIGIN'] + path); @@ -90,6 +91,7 @@ async_test(function(t) { result.jsonpResult, 'success', 'Basic type response could be loaded in the iframe.'); + frame.remove(); return service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); @@ -97,12 +99,13 @@ async_test(function(t) { async_test(function(t) { var scope = 'resources/fetch-frame-resource/frame-cors'; + var frame; service_worker_unregister_and_register(t, worker, scope) .then(function(reg) { return wait_for_state(t, reg.installing, 'activated'); }) .then(function() { - var frame = document.createElement('iframe'); + frame = document.createElement('iframe'); frame.src = scope + '?mode=cors&url=' + encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + path + @@ -115,6 +118,7 @@ async_test(function(t) { result.jsonpResult, 'success', 'CORS type response could be loaded in the iframe.'); + frame.remove(); return service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); @@ -122,12 +126,13 @@ async_test(function(t) { async_test(function(t) { var scope = 'resources/fetch-frame-resource/frame-opaque'; + var frame; service_worker_unregister_and_register(t, worker, scope) .then(function(reg) { return wait_for_state(t, reg.installing, 'activated'); }) .then(function() { - var frame = document.createElement('iframe'); + frame = document.createElement('iframe'); frame.src = scope + '?mode=no-cors&url=' + encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + path); @@ -139,6 +144,7 @@ async_test(function(t) { result, null, 'Opaque type response could not be loaded in the iframe.'); + frame.remove(); return service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-header-visibility.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-header-visibility.https.html index afdc7a864647..36bf16f32798 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-header-visibility.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-header-visibility.https.html @@ -9,6 +9,7 @@ var worker = 'resources/fetch-rewrite-worker.js'; var path = base_path() + 'resources/fetch-access-control.py'; var host_info = get_host_info(); + var frame; async_test(function(t) { var scope = 'resources/fetch-header-visibility-iframe.html'; @@ -17,7 +18,7 @@ return wait_for_state(t, reg.installing, 'activated'); }) .then(function() { - var frame = document.createElement('iframe'); + frame = document.createElement('iframe'); frame.src = scope; document.body.appendChild(frame); @@ -42,6 +43,7 @@ }); }) .then(function(result) { + frame.remove(); return service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-waits-for-activate.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-waits-for-activate.https.html index d2b0eb9ed8c8..304680c97e63 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-waits-for-activate.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-waits-for-activate.https.html @@ -54,6 +54,7 @@ async_test(function(t) { expected_url, 'frame should now be loaded and controlled'); assert_equals(registration.active.state, 'activated', 'active worker should be in activated state'); + frame.remove(); return service_worker_unregister_and_done(t, scope); }).catch(unreached_rejection(t)); }, 'Fetch events should wait for the activate event to complete.'); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage-msgport-to-client.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage-msgport-to-client.https.html index cba4479488a8..38b4f56e784d 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage-msgport-to-client.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/postmessage-msgport-to-client.https.html @@ -4,6 +4,7 @@ + + + + + + + + + + + diff --git a/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html b/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html index 53d7c33f7e57..72cb045f4216 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html @@ -172,11 +172,11 @@ function backgroundScript() } } - browser.webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: ""}, ["blocking"]); - browser.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: ""}, ["blocking"]); - browser.webRequest.onSendHeaders.addListener(onRecord.bind(null, "sendHeaders"), {urls: ""}); - browser.webRequest.onResponseStarted.addListener(onRecord.bind(null, "responseStarted"), {urls: ""}); - browser.webRequest.onCompleted.addListener(onRecord.bind(null, "completed"), {urls: ""}); + browser.webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: [""]}, ["blocking"]); + browser.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: [""]}, ["blocking"]); + browser.webRequest.onSendHeaders.addListener(onRecord.bind(null, "sendHeaders"), {urls: [""]}); + browser.webRequest.onResponseStarted.addListener(onRecord.bind(null, "responseStarted"), {urls: [""]}); + browser.webRequest.onCompleted.addListener(onRecord.bind(null, "completed"), {urls: [""]}); function onTestMessage() { @@ -209,6 +209,11 @@ function* test_once() is(key, value.toUpperCase()); } + // Check a few Firefox-specific types. + is(resourceTypes.XBL, "xbl", "XBL resource type supported"); + is(resourceTypes.FONT, "font", "Font resource type supported"); + is(resourceTypes.WEBSOCKET, "websocket", "Websocket resource type supported"); + yield new Promise(resolve => { setTimeout(resolve, 0); }); let win = window.open(); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js b/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js new file mode 100644 index 000000000000..864bba01134c --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js @@ -0,0 +1,320 @@ +"use strict"; + +Components.utils.import("resource://gre/modules/Schemas.jsm"); +Components.utils.import("resource://gre/modules/BrowserUtils.jsm"); + +let json = [ + {namespace: "testing", + + properties: { + PROP1: {value: 20}, + prop2: {type: "string"}, + }, + + types: [ + { + id: "type1", + type: "string", + "enum": ["value1", "value2", "value3"], + }, + + { + id: "type2", + type: "object", + properties: { + prop1: {type: "integer"}, + prop2: {type: "array", items: {"$ref": "type1"}}, + }, + } + ], + + functions: [ + { + name: "foo", + type: "function", + parameters: [ + {name: "arg1", type: "integer", optional: true}, + {name: "arg2", type: "boolean", optional: true}, + ], + }, + + { + name: "bar", + type: "function", + parameters: [ + {name: "arg1", type: "integer", optional: true}, + {name: "arg2", type: "boolean"}, + ], + }, + + { + name: "baz", + type: "function", + parameters: [ + {name: "arg1", type: "object", properties: { + prop1: {type: "string"}, + prop2: {type: "integer", optional: true}, + prop3: {type: "integer", unsupported: true}, + }}, + ], + }, + + { + name: "qux", + type: "function", + parameters: [ + {name: "arg1", "$ref": "type1"}, + ], + }, + + { + name: "quack", + type: "function", + parameters: [ + {name: "arg1", "$ref": "type2"}, + ], + }, + + { + name: "quora", + type: "function", + parameters: [ + {name: "arg1", type: "function"}, + ], + }, + + { + name: "quileute", + type: "function", + parameters: [ + {name: "arg1", type: "integer", optional: true}, + {name: "arg2", type: "integer"}, + ], + }, + + { + name: "queets", + type: "function", + unsupported: true, + parameters: [], + }, + + { + name: "quintuplets", + type: "function", + parameters: [ + {name: "obj", type: "object", properties: [], additionalProperties: {type: "integer"}}, + ], + }, + + { + name: "quasar", + type: "function", + parameters: [ + {name: "abc", type: "object", properties: { + func: {type: "function", parameters: [ + {name: "x", type: "integer"} + ]} + }} + ], + }, + + { + name: "quosimodo", + type: "function", + parameters: [ + {name: "xyz", type: "object"}, + ], + }, + ], + + events: [ + { + name: "onFoo", + type: "function", + }, + + { + name: "onBar", + type: "function", + extraParameters: [{ + name: "filter", + type: "integer", + }], + } + ], + } +]; + +let tallied = null; + +function tally(kind, ns, name, args) { + tallied = [kind, ns, name, args]; +} + +function verify(...args) { + do_check_eq(JSON.stringify(tallied), JSON.stringify(args)); + tallied = null; +} + +let wrapper = { + callFunction(ns, name, args) { + tally("call", ns, name, args); + }, + + addListener(ns, name, listener, args) { + tally("addListener", ns, name, [listener, args]); + }, + removeListener(ns, name, listener) { + tally("removeListener", ns, name, [listener]); + }, + hasListener(ns, name, listener) { + tally("hasListener", ns, name, [listener]); + }, +}; + +add_task(function* () { + let url = "data:," + JSON.stringify(json); + let uri = BrowserUtils.makeURI(url); + yield Schemas.load(uri); + + let root = {}; + Schemas.inject(root, wrapper); + + do_check_eq(root.testing.PROP1, 20, "simple value property"); + do_check_eq(root.testing.type1.VALUE1, "value1", "enum type"); + do_check_eq(root.testing.type1.VALUE2, "value2", "enum type"); + + root.testing.foo(11, true); + verify("call", "testing", "foo", [11, true]); + + root.testing.foo(true); + verify("call", "testing", "foo", [null, true]); + + root.testing.foo(null, true); + verify("call", "testing", "foo", [null, true]); + + root.testing.foo(undefined, true); + verify("call", "testing", "foo", [null, true]); + + root.testing.foo(11); + verify("call", "testing", "foo", [11, null]); + + Assert.throws(() => root.testing.bar(11), + /Incorrect argument types/, + "should throw without required arg"); + + Assert.throws(() => root.testing.bar(11, true, 10), + /Incorrect argument types/, + "should throw with too many arguments"); + + root.testing.bar(true); + verify("call", "testing", "bar", [null, true]); + + root.testing.baz({ prop1: "hello", prop2: 22 }); + verify("call", "testing", "baz", [{ prop1: "hello", prop2: 22 }]); + + root.testing.baz({ prop1: "hello" }); + verify("call", "testing", "baz", [{ prop1: "hello", prop2: null }]); + + root.testing.baz({ prop1: "hello", prop2: null }); + verify("call", "testing", "baz", [{ prop1: "hello", prop2: null }]); + + Assert.throws(() => root.testing.baz({ prop2: 12 }), + /Property "prop1" is required/, + "should throw without required property"); + + Assert.throws(() => root.testing.baz({ prop1: "hi", prop3: 12 }), + /Property "prop3" is unsupported by Firefox/, + "should throw with unsupported property"); + + Assert.throws(() => root.testing.baz({ prop1: "hi", prop4: 12 }), + /Unexpected property "prop4"/, + "should throw with unexpected property"); + + Assert.throws(() => root.testing.baz({ prop1: 12 }), + /Expected string instead of 12/, + "should throw with wrong type"); + + root.testing.qux("value2"); + verify("call", "testing", "qux", ["value2"]); + + Assert.throws(() => root.testing.qux("value4"), + /Invalid enumeration value "value4"/, + "should throw for invalid enum value"); + + root.testing.quack({prop1: 12, prop2: ["value1", "value3"]}); + verify("call", "testing", "quack", [{prop1: 12, prop2: ["value1", "value3"]}]); + + Assert.throws(() => root.testing.quack({prop1: 12, prop2: ["value1", "value3", "value4"]}), + /Invalid enumeration value "value4"/, + "should throw for invalid array type"); + + function f() {} + root.testing.quora(f); + do_check_eq(JSON.stringify(tallied.slice(0, -1)), JSON.stringify(["call", "testing", "quora"])); + do_check_eq(tallied[3][0], f); + tallied = null; + + let g = () => 0; + root.testing.quora(g); + do_check_eq(JSON.stringify(tallied.slice(0, -1)), JSON.stringify(["call", "testing", "quora"])); + do_check_eq(tallied[3][0], g); + tallied = null; + + root.testing.quileute(10); + verify("call", "testing", "quileute", [null, 10]); + + Assert.throws(() => root.testing.queets(), + /queets is not a function/, + "should throw for unsupported functions"); + + root.testing.quintuplets({a: 10, b: 20, c: 30}); + verify("call", "testing", "quintuplets", [{a: 10, b: 20, c: 30}]); + + Assert.throws(() => root.testing.quintuplets({a: 10, b: 20, c: 30, d: "hi"}), + /Expected integer instead of "hi"/, + "should throw for wrong additionalProperties type"); + + root.testing.quasar({func: f}); + do_check_eq(JSON.stringify(tallied.slice(0, -1)), JSON.stringify(["call", "testing", "quasar"])); + do_check_eq(tallied[3][0].func, f); + tallied = null; + + root.testing.quosimodo({a: 10, b: 20, c: 30}); + verify("call", "testing", "quosimodo", [{a: 10, b: 20, c: 30}]); + + Assert.throws(() => root.testing.quosimodo(10), + /Incorrect argument types/, + "should throw for wrong type"); + + root.testing.onFoo.addListener(f); + do_check_eq(JSON.stringify(tallied.slice(0, -1)), JSON.stringify(["addListener", "testing", "onFoo"])); + do_check_eq(tallied[3][0], f); + do_check_eq(JSON.stringify(tallied[3][1]), JSON.stringify([])); + tallied = null; + + root.testing.onFoo.removeListener(f); + do_check_eq(JSON.stringify(tallied.slice(0, -1)), JSON.stringify(["removeListener", "testing", "onFoo"])); + do_check_eq(tallied[3][0], f); + tallied = null; + + root.testing.onFoo.hasListener(f); + do_check_eq(JSON.stringify(tallied.slice(0, -1)), JSON.stringify(["hasListener", "testing", "onFoo"])); + do_check_eq(tallied[3][0], f); + tallied = null; + + Assert.throws(() => root.testing.onFoo.addListener(10), + /Invalid listener/, + "addListener with non-function should throw"); + + root.testing.onBar.addListener(f, 10); + do_check_eq(JSON.stringify(tallied.slice(0, -1)), JSON.stringify(["addListener", "testing", "onBar"])); + do_check_eq(tallied[3][0], f); + do_check_eq(JSON.stringify(tallied[3][1]), JSON.stringify([10])); + tallied = null; + + Assert.throws(() => root.testing.onBar.addListener(f, "hi"), + /Incorrect argument types/, + "addListener with wrong extra parameter should throw"); +}); diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell.ini b/toolkit/components/extensions/test/xpcshell/xpcshell.ini index 451bd1b3a18c..78290c1d380d 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini @@ -4,3 +4,4 @@ tail = skip-if = toolkit == 'android' || toolkit == 'gonk' [test_locale_converter.js] +[test_ext_schemas.js] diff --git a/toolkit/components/telemetry/ThreadHangStats.h b/toolkit/components/telemetry/ThreadHangStats.h index 2513c347b4ad..6ab077fa62ce 100644 --- a/toolkit/components/telemetry/ThreadHangStats.h +++ b/toolkit/components/telemetry/ThreadHangStats.h @@ -47,10 +47,14 @@ public: /* HangStack stores an array of const char pointers, with optional internal storage for strings. */ -class HangStack : public mozilla::Vector +class HangStack { +public: + static const size_t sMaxInlineStorage = 8; + private: - typedef mozilla::Vector Base; + typedef mozilla::Vector Impl; + Impl mImpl; // Stack entries can either be a static const char* // or a pointer to within this buffer. @@ -60,7 +64,7 @@ public: HangStack() { } HangStack(HangStack&& aOther) - : Base(mozilla::Move(aOther)) + : mImpl(mozilla::Move(aOther.mImpl)) , mBuffer(mozilla::Move(aOther.mBuffer)) { } @@ -78,8 +82,34 @@ public: return !operator==(aOther); } + const char*& operator[](size_t aIndex) { + return mImpl[aIndex]; + } + + const char* const& operator[](size_t aIndex) const { + return mImpl[aIndex]; + } + + size_t capacity() const { return mImpl.capacity(); } + size_t length() const { return mImpl.length(); } + bool empty() const { return mImpl.empty(); } + bool canAppendWithoutRealloc(size_t aNeeded) const { + return mImpl.canAppendWithoutRealloc(aNeeded); + } + void infallibleAppend(const char* aEntry) { mImpl.infallibleAppend(aEntry); } + bool reserve(size_t aRequest) { return mImpl.reserve(aRequest); } + const char** begin() { return mImpl.begin(); } + const char* const* begin() const { return mImpl.begin(); } + const char** end() { return mImpl.end(); } + const char* const* end() const { return mImpl.end(); } + const char*& back() { return mImpl.back(); } + void erase(const char** aEntry) { mImpl.erase(aEntry); } + void erase(const char** aBegin, const char** aEnd) { + mImpl.erase(aBegin, aEnd); + } + void clear() { - Base::clear(); + mImpl.clear(); mBuffer.clear(); } diff --git a/toolkit/xre/glxtest.cpp b/toolkit/xre/glxtest.cpp index 7812d83bb3c1..49059649f6b5 100644 --- a/toolkit/xre/glxtest.cpp +++ b/toolkit/xre/glxtest.cpp @@ -27,6 +27,10 @@ #include #include "stdint.h" +#if MOZ_WIDGET_GTK == 2 +#include +#endif + #ifdef __SUNPRO_CC #include #endif @@ -70,6 +74,11 @@ extern pid_t glxtest_pid; // the write end of the pipe, which we're going to write to static int write_end_of_the_pipe = -1; +#if MOZ_WIDGET_GTK == 2 +static int gtk_write_end_of_the_pipe = -1; +int gtk_read_end_of_the_pipe = -1; +#endif + // C++ standard collides with C standard in that it doesn't allow casting void* to function pointer types. // So the work-around is to convert first to size_t. // http://www.trilithium.com/johan/2004/12/problem-with-dlsym/ @@ -120,6 +129,36 @@ void glxtest() dup2(fd, i); close(fd); +#if MOZ_WIDGET_GTK == 2 + // On Gtk+2 builds, try to get the Gtk+3 version if it's installed, and + // use that in nsSystemInfo for secondaryLibrary. Better safe than sorry, + // we want to load the Gtk+3 library in a subprocess, and since we already + // have such a subprocess for the GLX test, we piggy back on it. + void *gtk3 = dlopen("libgtk-3.so.0", RTLD_LOCAL | RTLD_LAZY); + if (gtk3) { + auto gtk_get_major_version = reinterpret_cast( + dlsym(gtk3, "gtk_get_major_version")); + auto gtk_get_minor_version = reinterpret_cast( + dlsym(gtk3, "gtk_get_minor_version")); + auto gtk_get_micro_version = reinterpret_cast( + dlsym(gtk3, "gtk_get_micro_version")); + + if (gtk_get_major_version && gtk_get_minor_version && + gtk_get_micro_version) { + // 64 bytes is going to be well enough for "GTK " followed by 3 integers + // separated with dots. + char gtkver[64]; + int len = snprintf(gtkver, sizeof(gtkver), "GTK %u.%u.%u", + gtk_get_major_version(), gtk_get_minor_version(), + gtk_get_micro_version()); + if (len > 0 && size_t(len) < sizeof(gtkver)) { + mozilla::Unused << write(gtk_write_end_of_the_pipe, gtkver, len); + } + } + } +#endif + + if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER")) fatal_error("The MOZ_AVOID_OPENGL_ALTOGETHER environment variable is defined"); @@ -264,11 +303,22 @@ bool fire_glxtest_process() perror("pipe"); return false; } +#if MOZ_WIDGET_GTK == 2 + int gtkpfd[2]; + if (pipe(gtkpfd) == -1) { + perror("pipe"); + return false; + } +#endif pid_t pid = fork(); if (pid < 0) { perror("fork"); close(pfd[0]); close(pfd[1]); +#if MOZ_WIDGET_GTK == 2 + close(gtkpfd[0]); + close(gtkpfd[1]); +#endif return false; } // The child exits early to avoid running the full shutdown sequence and avoid conflicting with threads @@ -276,13 +326,24 @@ bool fire_glxtest_process() if (pid == 0) { close(pfd[0]); write_end_of_the_pipe = pfd[1]; +#if MOZ_WIDGET_GTK == 2 + close(gtkpfd[0]); + gtk_write_end_of_the_pipe = gtkpfd[1]; +#endif glxtest(); close(pfd[1]); +#if MOZ_WIDGET_GTK == 2 + close(gtkpfd[1]); +#endif _exit(0); } close(pfd[1]); mozilla::widget::glxtest_pipe = pfd[0]; mozilla::widget::glxtest_pid = pid; +#if MOZ_WIDGET_GTK == 2 + close(gtkpfd[1]); + gtk_read_end_of_the_pipe = gtkpfd[0]; +#endif return false; } diff --git a/uriloader/prefetch/nsPrefetchService.cpp b/uriloader/prefetch/nsPrefetchService.cpp index f068ce55cfd3..c1b56c8e6912 100644 --- a/uriloader/prefetch/nsPrefetchService.cpp +++ b/uriloader/prefetch/nsPrefetchService.cpp @@ -106,7 +106,7 @@ nsPrefetchNode::OpenChannel() } else { securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; if (corsMode == CORS_USE_CREDENTIALS) { - securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS; + securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; } } nsresult rv = NS_NewChannelInternal(getter_AddRefs(mChannel), diff --git a/view/nsView.cpp b/view/nsView.cpp index 4c47125d31b1..2dd2449bf68d 100644 --- a/view/nsView.cpp +++ b/view/nsView.cpp @@ -1062,7 +1062,7 @@ nsView::WillPaintWindow(nsIWidget* aWidget) } bool -nsView::PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) +nsView::PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion aRegion) { NS_ASSERTION(this == nsView::GetViewFor(aWidget), "wrong view for widget?"); diff --git a/view/nsView.h b/view/nsView.h index bc6fd1698f4e..a69909c3aab0 100644 --- a/view/nsView.h +++ b/view/nsView.h @@ -59,6 +59,7 @@ public: friend class nsViewManager; typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect; + typedef mozilla::LayoutDeviceIntRegion LayoutDeviceIntRegion; NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW @@ -382,7 +383,8 @@ public: virtual bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) override; virtual bool RequestWindowClose(nsIWidget* aWidget) override; virtual void WillPaintWindow(nsIWidget* aWidget) override; - virtual bool PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) override; + virtual bool PaintWindow(nsIWidget* aWidget, + LayoutDeviceIntRegion aRegion) override; virtual void DidPaintWindow() override; virtual void DidCompositeWindow(const mozilla::TimeStamp& aCompositeStart, const mozilla::TimeStamp& aCompositeEnd) override; diff --git a/view/nsViewManager.cpp b/view/nsViewManager.cpp index 5fe87bacc5de..b345a74f6461 100644 --- a/view/nsViewManager.cpp +++ b/view/nsViewManager.cpp @@ -281,7 +281,7 @@ nsView* nsViewManager::GetDisplayRootFor(nsView* aView) aContext may be null, in which case layers should be used for rendering. */ -void nsViewManager::Refresh(nsView *aView, const nsIntRegion& aRegion) +void nsViewManager::Refresh(nsView* aView, const LayoutDeviceIntRegion& aRegion) { NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager"); @@ -291,6 +291,7 @@ void nsViewManager::Refresh(nsView *aView, const nsIntRegion& aRegion) // damageRegion is the damaged area, in twips, relative to the view origin nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel()); + // move region from widget coordinates into view coordinates damageRegion.MoveBy(-aView->ViewToWidgetOffset()); @@ -714,7 +715,8 @@ void nsViewManager::WillPaintWindow(nsIWidget* aWidget) } } -bool nsViewManager::PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) +bool nsViewManager::PaintWindow(nsIWidget* aWidget, + LayoutDeviceIntRegion aRegion) { if (!aWidget || !mContext) return false; diff --git a/view/nsViewManager.h b/view/nsViewManager.h index 1a26958a5cbc..21759a1d9b94 100644 --- a/view/nsViewManager.h +++ b/view/nsViewManager.h @@ -28,6 +28,7 @@ public: friend class nsView; typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect; + typedef mozilla::LayoutDeviceIntRegion LayoutDeviceIntRegion; NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW @@ -346,7 +347,7 @@ private: void InvalidateViews(nsView *aView); // aView is the view for aWidget and aRegion is relative to aWidget. - void Refresh(nsView *aView, const nsIntRegion& aRegion); + void Refresh(nsView* aView, const LayoutDeviceIntRegion& aRegion); // Utilities @@ -380,7 +381,7 @@ private: bool IsPaintingAllowed() { return RootViewManager()->mRefreshDisableCount == 0; } void WillPaintWindow(nsIWidget* aWidget); - bool PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion); + bool PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion aRegion); void DidPaintWindow(); // Call this when you need to let the viewmanager know that it now has diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index 3ef7e5b7dd75..8135dfd49478 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -444,7 +444,7 @@ PuppetWidget::SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPo nsresult PuppetWidget::SynthesizeNativeTouchPoint(uint32_t aPointerId, TouchPointerState aPointerState, - nsIntPoint aPointerScreenPoint, + ScreenIntPoint aPointerScreenPoint, double aPointerPressure, uint32_t aPointerOrientation, nsIObserver* aObserver) @@ -460,7 +460,7 @@ PuppetWidget::SynthesizeNativeTouchPoint(uint32_t aPointerId, } nsresult -PuppetWidget::SynthesizeNativeTouchTap(nsIntPoint aPointerScreenPoint, +PuppetWidget::SynthesizeNativeTouchTap(ScreenIntPoint aPointerScreenPoint, bool aLongTap, nsIObserver* aObserver) { @@ -999,7 +999,7 @@ PuppetWidget::Paint() ctx->Clip(); AutoLayerManagerSetup setupLayerManager(this, ctx, BufferMode::BUFFER_NONE); - GetCurrentWidgetListener()->PaintWindow(this, region.ToUnknownRegion()); + GetCurrentWidgetListener()->PaintWindow(this, region); if (mTabChild) { mTabChild->NotifyPainted(); } diff --git a/widget/PuppetWidget.h b/widget/PuppetWidget.h index c6d0f50d870d..45f691074e14 100644 --- a/widget/PuppetWidget.h +++ b/widget/PuppetWidget.h @@ -242,11 +242,11 @@ public: nsIObserver* aObserver) override; virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId, TouchPointerState aPointerState, - nsIntPoint aPointerScreenPoint, + ScreenIntPoint aPointerScreenPoint, double aPointerPressure, uint32_t aPointerOrientation, nsIObserver* aObserver) override; - virtual nsresult SynthesizeNativeTouchTap(nsIntPoint aPointerScreenPoint, + virtual nsresult SynthesizeNativeTouchTap(ScreenIntPoint aPointerScreenPoint, bool aLongTap, nsIObserver* aObserver) override; virtual nsresult ClearNativeTouchSequence(nsIObserver* aObserver) override; diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index 844005dd28e4..395c23e2164a 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -476,7 +476,7 @@ public: virtual bool DispatchWindowEvent(mozilla::WidgetGUIEvent& event); void WillPaintWindow(); - bool PaintWindow(nsIntRegion aRegion); + bool PaintWindow(LayoutDeviceIntRegion aRegion); #ifdef ACCESSIBILITY already_AddRefed GetDocumentAccessible(); diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 96e0270a489a..fef65107af29 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -1488,7 +1488,7 @@ void nsChildView::WillPaintWindow() } } -bool nsChildView::PaintWindow(nsIntRegion aRegion) +bool nsChildView::PaintWindow(LayoutDeviceIntRegion aRegion) { nsCOMPtr widget = GetWidgetForListenerEvents(); @@ -3756,10 +3756,10 @@ NSEvent* gLastDragMouseDownEvent = nil; if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) { nsBaseWidget::AutoLayerManagerSetup setupLayerManager(mGeckoChild, targetContext, BufferMode::BUFFER_NONE); - painted = mGeckoChild->PaintWindow(region.ToUnknownRegion()); + painted = mGeckoChild->PaintWindow(region); } else if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) { // We only need this so that we actually get DidPaintWindow fired - painted = mGeckoChild->PaintWindow(region.ToUnknownRegion()); + painted = mGeckoChild->PaintWindow(region); } targetContext = nullptr; @@ -3830,7 +3830,7 @@ NSEvent* gLastDragMouseDownEvent = nil; mGeckoChild->GetBounds(geckoBounds); LayoutDeviceIntRegion region(geckoBounds); - mGeckoChild->PaintWindow(region.ToUnknownRegion()); + mGeckoChild->PaintWindow(region); // Force OpenGL to refresh the very first time we draw. This works around a // Mac OS X bug that stops windows updating on OS X when we use OpenGL. diff --git a/widget/gonk/nsWindow.cpp b/widget/gonk/nsWindow.cpp index 7de9bef7a1ac..d2bd45e7b153 100644 --- a/widget/gonk/nsWindow.cpp +++ b/widget/gonk/nsWindow.cpp @@ -258,7 +258,7 @@ private: nsresult nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId, TouchPointerState aPointerState, - nsIntPoint aPointerScreenPoint, + ScreenIntPoint aPointerScreenPoint, double aPointerPressure, uint32_t aPointerOrientation, nsIObserver* aObserver) @@ -286,7 +286,7 @@ nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId, if (index >= 0) { // found an existing touch point, update it SingleTouchData& point = mSynthesizedTouchInput->mTouches[index]; - point.mScreenPoint = ScreenIntPoint::FromUnknownPoint(aPointerScreenPoint); + point.mScreenPoint = aPointerScreenPoint; point.mRotationAngle = (float)aPointerOrientation; point.mForce = (float)aPointerPressure; inputToDispatch.mType = MultiTouchInput::MULTITOUCH_MOVE; @@ -294,7 +294,7 @@ nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId, // new touch point, add it mSynthesizedTouchInput->mTouches.AppendElement(SingleTouchData( (int32_t)aPointerId, - ScreenIntPoint::FromUnknownPoint(aPointerScreenPoint), + aPointerScreenPoint, ScreenSize(0, 0), (float)aPointerOrientation, (float)aPointerPressure)); @@ -312,7 +312,7 @@ nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId, : MultiTouchInput::MULTITOUCH_CANCEL); inputToDispatch.mTouches.AppendElement(SingleTouchData( (int32_t)aPointerId, - ScreenIntPoint::FromUnknownPoint(aPointerScreenPoint), + aPointerScreenPoint, ScreenSize(0, 0), (float)aPointerOrientation, (float)aPointerPressure)); diff --git a/widget/gonk/nsWindow.h b/widget/gonk/nsWindow.h index 30a5b438dcee..57c81a7c0b92 100644 --- a/widget/gonk/nsWindow.h +++ b/widget/gonk/nsWindow.h @@ -88,7 +88,7 @@ public: nsEventStatus& aStatus); virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId, TouchPointerState aPointerState, - nsIntPoint aPointerScreenPoint, + ScreenIntPoint aPointerScreenPoint, double aPointerPressure, uint32_t aPointerOrientation, nsIObserver* aObserver) override; diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 00974125f59a..ef82a47629c3 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -2213,7 +2213,7 @@ nsWindow::OnExposeEvent(cairo_t *cr) // If this widget uses OMTC... if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) { - listener->PaintWindow(this, region.ToUnknownRegion()); + listener->PaintWindow(this, region); listener->DidPaintWindow(); return TRUE; } @@ -2277,7 +2277,7 @@ nsWindow::OnExposeEvent(cairo_t *cr) { if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) { AutoLayerManagerSetup setupLayerManager(this, ctx, layerBuffering); - painted = listener->PaintWindow(this, region.ToUnknownRegion()); + painted = listener->PaintWindow(this, region); } } diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index cb071b2012e4..d6977157003f 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -1840,7 +1840,7 @@ nsBaseWidget::GetWidgetScreen() } nsresult -nsIWidget::SynthesizeNativeTouchTap(nsIntPoint aPointerScreenPoint, bool aLongTap, +nsIWidget::SynthesizeNativeTouchTap(ScreenIntPoint aPointerScreenPoint, bool aLongTap, nsIObserver* aObserver) { AutoObserverNotifier notifier(aObserver, "touchtap"); @@ -1916,7 +1916,7 @@ nsIWidget::OnLongTapTimerCallback(nsITimer* aTimer, void* aClosure) return; } - AutoObserverNotifier notiifer(self->mLongTapTouchPoint->mObserver, "touchtap"); + AutoObserverNotifier notifier(self->mLongTapTouchPoint->mObserver, "touchtap"); // finished, remove the touch point self->mLongTapTimer->Cancel(); diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h index 69dee5107ad0..08563ab8f91e 100644 --- a/widget/nsBaseWidget.h +++ b/widget/nsBaseWidget.h @@ -414,7 +414,7 @@ protected: virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId, TouchPointerState aPointerState, - nsIntPoint aPointerScreenPoint, + ScreenIntPoint aPointerScreenPoint, double aPointerPressure, uint32_t aPointerOrientation, nsIObserver* aObserver) override diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 32d6a0127c72..ae565f24cc57 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -337,6 +337,7 @@ class nsIWidget : public nsISupports { typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect; typedef mozilla::LayoutDeviceIntRegion LayoutDeviceIntRegion; typedef mozilla::LayoutDeviceIntSize LayoutDeviceIntSize; + typedef mozilla::ScreenIntPoint ScreenIntPoint; // Used in UpdateThemeGeometries. struct ThemeGeometry { @@ -1643,7 +1644,7 @@ class nsIWidget : public nsISupports { */ virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId, TouchPointerState aPointerState, - nsIntPoint aPointerScreenPoint, + ScreenIntPoint aPointerScreenPoint, double aPointerPressure, uint32_t aPointerOrientation, nsIObserver* aObserver) = 0; @@ -1656,7 +1657,7 @@ class nsIWidget : public nsISupports { * @param aObserver The observer that will get notified once the events * have been dispatched. */ - virtual nsresult SynthesizeNativeTouchTap(nsIntPoint aPointerScreenPoint, + virtual nsresult SynthesizeNativeTouchTap(ScreenIntPoint aPointerScreenPoint, bool aLongTap, nsIObserver* aObserver); @@ -1692,7 +1693,7 @@ private: class LongTapInfo { public: - LongTapInfo(int32_t aPointerId, nsIntPoint& aPoint, + LongTapInfo(int32_t aPointerId, ScreenIntPoint& aPoint, mozilla::TimeDuration aDuration, nsIObserver* aObserver) : mPointerId(aPointerId), @@ -1704,7 +1705,7 @@ private: } int32_t mPointerId; - nsIntPoint mPosition; + ScreenIntPoint mPosition; mozilla::TimeDuration mDuration; nsCOMPtr mObserver; mozilla::TimeStamp mStamp; diff --git a/widget/nsIWidgetListener.cpp b/widget/nsIWidgetListener.cpp index 050ad8de1319..97a05bb33ab2 100644 --- a/widget/nsIWidgetListener.cpp +++ b/widget/nsIWidgetListener.cpp @@ -96,7 +96,7 @@ nsIWidgetListener::WillPaintWindow(nsIWidget* aWidget) bool nsIWidgetListener::PaintWindow(nsIWidget* aWidget, - nsIntRegion aRegion) + LayoutDeviceIntRegion aRegion) { return false; } diff --git a/widget/nsIWidgetListener.h b/widget/nsIWidgetListener.h index 76b87073f065..550c5e5c1702 100644 --- a/widget/nsIWidgetListener.h +++ b/widget/nsIWidgetListener.h @@ -11,6 +11,7 @@ #include "mozilla/TimeStamp.h" #include "nsRegionFwd.h" +#include "Units.h" class nsView; class nsIPresShell; @@ -128,7 +129,8 @@ public: * This is called at a time when it is not OK to change the geometry of * this widget or of other widgets. */ - virtual bool PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion); + virtual bool PaintWindow(nsIWidget* aWidget, + mozilla::LayoutDeviceIntRegion aRegion); /** * Indicates that a paint occurred. diff --git a/widget/qt/nsWindow.cpp b/widget/qt/nsWindow.cpp index a490500f4615..f2224e6d7a5a 100644 --- a/widget/qt/nsWindow.cpp +++ b/widget/qt/nsWindow.cpp @@ -875,7 +875,8 @@ nsWindow::OnPaint() switch (GetLayerManager()->GetBackendType()) { case mozilla::layers::LayersBackend::LAYERS_CLIENT: { - nsIntRegion region(nsIntRect(0, 0, mWidget->width(), mWidget->height())); + LayoutDeviceIntRegion region( + LayoutDeviceIntRect(0, 0, mWidget->width(), mWidget->height())); listener->PaintWindow(this, region); break; } diff --git a/widget/uikit/nsWindow.h b/widget/uikit/nsWindow.h index 85b624ae819a..7f716026fb31 100644 --- a/widget/uikit/nsWindow.h +++ b/widget/uikit/nsWindow.h @@ -91,7 +91,7 @@ public: } void WillPaintWindow(); - bool PaintWindow(nsIntRegion aRegion); + bool PaintWindow(LayoutDeviceIntRegion aRegion); bool HasModalDescendents() { return false; } diff --git a/widget/uikit/nsWindow.mm b/widget/uikit/nsWindow.mm index e6ca0f9bbe7b..00433291ad41 100644 --- a/widget/uikit/nsWindow.mm +++ b/widget/uikit/nsWindow.mm @@ -282,7 +282,7 @@ private: mGeckoChild->GetBounds(geckoBounds); LayoutDeviceIntRegion region(geckoBounds); - mGeckoChild->PaintWindow(region.ToUnknownRegion()); + mGeckoChild->PaintWindow(region); } // Called asynchronously after setNeedsDisplay in order to avoid entering the @@ -344,10 +344,11 @@ private: CGContextSaveGState(aContext); - nsIntRegion region = nsIntRect(NSToIntRound(aRect.origin.x * scale), - NSToIntRound(aRect.origin.y * scale), - NSToIntRound(aRect.size.width * scale), - NSToIntRound(aRect.size.height * scale)); + LayoutDeviceIntRegion region = + LayoutDeviceIntRect(NSToIntRound(aRect.origin.x * scale), + NSToIntRound(aRect.origin.y * scale), + NSToIntRound(aRect.size.width * scale), + NSToIntRound(aRect.size.height * scale)); // Create Cairo objects. RefPtr targetSurface; @@ -376,10 +377,10 @@ private: } // Set up the clip region. - nsIntRegionRectIterator iter(region); + LayoutDeviceIntRegion::RectIterator iter(region); targetContext->NewPath(); for (;;) { - const nsIntRect* r = iter.Next(); + const LayoutDeviceIntRect* r = iter.Next(); if (!r) break; targetContext->Rectangle(gfxRect(r->x, r->y, r->width, r->height)); @@ -730,7 +731,7 @@ void nsWindow::WillPaintWindow() } } -bool nsWindow::PaintWindow(nsIntRegion aRegion) +bool nsWindow::PaintWindow(LayoutDeviceIntRegion aRegion) { if (!mWidgetListener) return false; diff --git a/widget/windows/nsWindowBase.cpp b/widget/windows/nsWindowBase.cpp index 0ad509d15027..55cbec2a6b74 100644 --- a/widget/windows/nsWindowBase.cpp +++ b/widget/windows/nsWindowBase.cpp @@ -70,7 +70,7 @@ nsWindowBase::InitTouchInjection() } bool -nsWindowBase::InjectTouchPoint(uint32_t aId, nsIntPoint& aPointerScreenPoint, +nsWindowBase::InjectTouchPoint(uint32_t aId, ScreenIntPoint& aPointerScreenPoint, POINTER_FLAGS aFlags, uint32_t aPressure, uint32_t aOrientation) { @@ -108,7 +108,7 @@ nsWindowBase::InjectTouchPoint(uint32_t aId, nsIntPoint& aPointerScreenPoint, nsresult nsWindowBase::SynthesizeNativeTouchPoint(uint32_t aPointerId, nsIWidget::TouchPointerState aPointerState, - nsIntPoint aPointerScreenPoint, + ScreenIntPoint aPointerScreenPoint, double aPointerPressure, uint32_t aPointerOrientation, nsIObserver* aObserver) diff --git a/widget/windows/nsWindowBase.h b/widget/windows/nsWindowBase.h index 4c7732d0375b..ec5f4fc8d609 100644 --- a/widget/windows/nsWindowBase.h +++ b/widget/windows/nsWindowBase.h @@ -90,7 +90,7 @@ public: */ virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId, TouchPointerState aPointerState, - nsIntPoint aPointerScreenPoint, + ScreenIntPoint aPointerScreenPoint, double aPointerPressure, uint32_t aPointerOrientation, nsIObserver* aObserver) override; @@ -105,21 +105,21 @@ public: protected: static bool InitTouchInjection(); - bool InjectTouchPoint(uint32_t aId, nsIntPoint& aPointerScreenPoint, + bool InjectTouchPoint(uint32_t aId, ScreenIntPoint& aPointerScreenPoint, POINTER_FLAGS aFlags, uint32_t aPressure = 1024, uint32_t aOrientation = 90); class PointerInfo { public: - PointerInfo(int32_t aPointerId, nsIntPoint& aPoint) : + PointerInfo(int32_t aPointerId, ScreenIntPoint& aPoint) : mPointerId(aPointerId), mPosition(aPoint) { } int32_t mPointerId; - nsIntPoint mPosition; + ScreenIntPoint mPosition; }; nsClassHashtable mActivePointers; diff --git a/widget/windows/nsWindowGfx.cpp b/widget/windows/nsWindowGfx.cpp index b0d086b17521..ecaab98ae840 100644 --- a/widget/windows/nsWindowGfx.cpp +++ b/widget/windows/nsWindowGfx.cpp @@ -406,8 +406,9 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) { AutoLayerManagerSetup - setupLayerManager(this, thebesContext, doubleBuffering); - result = listener->PaintWindow(this, region); + setupLayerManager(this, thebesContext, doubleBuffering); + result = listener->PaintWindow( + this, LayoutDeviceIntRegion::FromUnknownRegion(region)); } #ifdef MOZ_XUL @@ -514,7 +515,8 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) } break; case LayersBackend::LAYERS_CLIENT: - result = listener->PaintWindow(this, region); + result = listener->PaintWindow( + this, LayoutDeviceIntRegion::FromUnknownRegion(region)); break; default: NS_ERROR("Unknown layers backend used!"); diff --git a/xpcom/base/nsSystemInfo.cpp b/xpcom/base/nsSystemInfo.cpp index 6eb441cfe81f..36a2fd3ed18f 100644 --- a/xpcom/base/nsSystemInfo.cpp +++ b/xpcom/base/nsSystemInfo.cpp @@ -680,12 +680,31 @@ nsSystemInfo::Init() #if defined(MOZ_WIDGET_GTK) // This must be done here because NSPR can only separate OS's when compiled, not libraries. - char* gtkver = PR_smprintf("GTK %u.%u.%u", gtk_major_version, - gtk_minor_version, gtk_micro_version); - if (gtkver) { + // 64 bytes is going to be well enough for "GTK " followed by 3 integers + // separated with dots. + char gtkver[64]; + ssize_t gtkver_len = 0; + +#if MOZ_WIDGET_GTK == 2 + extern int gtk_read_end_of_the_pipe; + + if (gtk_read_end_of_the_pipe != -1) { + do { + gtkver_len = read(gtk_read_end_of_the_pipe, >kver, sizeof(gtkver)); + } while (gtkver_len < 0 && errno == EINTR); + close(gtk_read_end_of_the_pipe); + } +#endif + + if (gtkver_len <= 0) { + gtkver_len = snprintf(gtkver, sizeof(gtkver), "GTK %u.%u.%u", + gtk_major_version, gtk_minor_version, + gtk_micro_version); + } + + if (gtkver_len > 0) { rv = SetPropertyAsACString(NS_LITERAL_STRING("secondaryLibrary"), - nsDependentCString(gtkver)); - PR_smprintf_free(gtkver); + nsDependentCString(gtkver, gtkver_len)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } diff --git a/xpcom/io/nsMultiplexInputStream.cpp b/xpcom/io/nsMultiplexInputStream.cpp index c72ff0de9b69..ed96295e8df5 100644 --- a/xpcom/io/nsMultiplexInputStream.cpp +++ b/xpcom/io/nsMultiplexInputStream.cpp @@ -141,25 +141,10 @@ nsMultiplexInputStream::GetCount(uint32_t* aCount) return NS_OK; } -#ifdef DEBUG -static bool -SeekableStreamAtBeginning(nsIInputStream* aStream) -{ - int64_t streamPos; - nsCOMPtr stream = do_QueryInterface(aStream); - if (stream && NS_SUCCEEDED(stream->Tell(&streamPos)) && streamPos != 0) { - return false; - } - return true; -} -#endif - NS_IMETHODIMP nsMultiplexInputStream::AppendStream(nsIInputStream* aStream) { MutexAutoLock lock(mLock); - NS_ASSERTION(SeekableStreamAtBeginning(aStream), - "Appended stream not at beginning."); return mStreams.AppendElement(aStream) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } @@ -167,8 +152,6 @@ NS_IMETHODIMP nsMultiplexInputStream::InsertStream(nsIInputStream* aStream, uint32_t aIndex) { MutexAutoLock lock(mLock); - NS_ASSERTION(SeekableStreamAtBeginning(aStream), - "Inserted stream not at beginning."); mStreams.InsertElementAt(aIndex, aStream); if (mCurrentStream > aIndex || (mCurrentStream == aIndex && mStartedReadingCurrent)) { @@ -231,15 +214,14 @@ nsMultiplexInputStream::Available(uint64_t* aResult) return mStatus; } - nsresult rv; uint64_t avail = 0; uint32_t len = mStreams.Length(); for (uint32_t i = mCurrentStream; i < len; i++) { uint64_t streamAvail; - rv = AvailableMaybeSeek(mStreams[i], &streamAvail); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; + mStatus = AvailableMaybeSeek(mStreams[i], &streamAvail); + if (NS_WARN_IF(NS_FAILED(mStatus))) { + return mStatus; } avail += streamAvail; }