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