merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-12-07 14:15:43 +01:00
commit 5f74e29a2e
466 changed files with 11051 additions and 3039 deletions

View File

@ -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

View File

@ -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

View File

@ -24,6 +24,10 @@ SOURCES += [
'!xpcAccEvents.cpp',
]
EXPORTS += [
'!xpcAccEvents.h',
]
LOCAL_INCLUDES += [
'/accessible/base',
'/accessible/generic',

View File

@ -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);

View File

@ -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,

View File

@ -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);
}

View File

@ -6,4 +6,6 @@
JAR_MANIFESTS += ['jar.mn']
DIRS += ['schemas']
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']

View File

@ -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.

View File

@ -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

View File

@ -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']

File diff suppressed because it is too large Load Diff

View File

@ -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 <code>browser.windows</code> 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 <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> 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 <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> 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 <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> 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 <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> 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 &mdash; 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 <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> 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 <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> 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 <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects for that window. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> 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 <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> 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 <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> 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 <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> 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 <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
}
],
"parameters": [
{"type": "integer", "name": "windowId", "minimum": -1, "description": "ID of the newly focused window."}
]
}
]
}
]

View File

@ -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",
});

View File

@ -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");

View File

@ -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);
},

View File

@ -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',

View File

@ -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

View File

@ -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<nsIPrincipal> oldPrincipal;
GetChannelResultPrincipal(oldChannel, getter_AddRefs(oldPrincipal));
nsCOMPtr<nsIURI> newURI;
newChannel->GetURI(getter_AddRefs(newURI));
nsCOMPtr<nsIURI> 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 //
/////////////////////////////////////

View File

@ -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*

View File

@ -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) \

View File

@ -32,7 +32,8 @@ public:
nsTArray<RefPtr<BlobImpl>>& GetBlobImpls() { Flush(); return mBlobImpls; }
already_AddRefed<Blob> GetBlobInternal(nsISupports* aParent,
const nsACString& aContentType);
const nsACString& aContentType,
ErrorResult& aRv);
protected:
bool ExpandBufferSize(uint64_t aSize)

View File

@ -1233,9 +1233,11 @@ Element::GetAttributeNode(const nsAString& aName)
already_AddRefed<Attr>
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<Attr>

View File

@ -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<nsIChannel> channel;

View File

@ -257,7 +257,7 @@ Blob::ToFile()
}
already_AddRefed<File>
Blob::ToFile(const nsAString& aName) const
Blob::ToFile(const nsAString& aName, ErrorResult& aRv) const
{
nsAutoTArray<RefPtr<BlobImpl>, 1> blobImpls;
blobImpls.AppendElement(mImpl);
@ -266,7 +266,10 @@ Blob::ToFile(const nsAString& aName) const
mImpl->GetType(contentType);
RefPtr<MultipartBlobImpl> impl =
new MultipartBlobImpl(blobImpls, aName, contentType);
MultipartBlobImpl::Create(blobImpls, aName, contentType, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
RefPtr<File> file = new File(mParent, impl);
return file.forget();
@ -347,7 +350,11 @@ Blob::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
{
RefPtr<MultipartBlobImpl> impl = new MultipartBlobImpl();
impl->InitializeBlob();
impl->InitializeBlob(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
MOZ_ASSERT(!impl->IsFile());
RefPtr<Blob> 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<Blob>
BlobSet::GetBlobInternal(nsISupports* aParent, const nsACString& aContentType)
BlobSet::GetBlobInternal(nsISupports* aParent,
const nsACString& aContentType,
ErrorResult& aRv)
{
RefPtr<Blob> blob = Blob::Create(aParent,
new MultipartBlobImpl(GetBlobImpls(),
NS_ConvertASCIItoUTF16(aContentType)));
RefPtr<BlobImpl> blobImpl =
MultipartBlobImpl::Create(GetBlobImpls(),
NS_ConvertASCIItoUTF16(aContentType),
aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
RefPtr<Blob> blob = Blob::Create(aParent, blobImpl);
return blob.forget();
}

View File

@ -118,7 +118,8 @@ public:
// This method creates a new File object with the given name and the same
// BlobImpl.
already_AddRefed<File> ToFile(const nsAString& aName) const;
already_AddRefed<File> ToFile(const nsAString& aName,
ErrorResult& aRv) const;
already_AddRefed<Blob>
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) {

View File

@ -25,6 +25,37 @@ using namespace mozilla::dom;
NS_IMPL_ISUPPORTS_INHERITED0(MultipartBlobImpl, BlobImpl)
/* static */ already_AddRefed<MultipartBlobImpl>
MultipartBlobImpl::Create(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
const nsAString& aName,
const nsAString& aContentType,
ErrorResult& aRv)
{
RefPtr<MultipartBlobImpl> blobImpl =
new MultipartBlobImpl(aBlobImpls, aName, aContentType);
blobImpl->SetLengthAndModifiedDate(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return blobImpl.forget();
}
/* static */ already_AddRefed<MultipartBlobImpl>
MultipartBlobImpl::Create(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
const nsAString& aContentType,
ErrorResult& aRv)
{
RefPtr<MultipartBlobImpl> 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<BlobImpl> impl =
new MultipartBlobImpl(blobImpls, aContentType);
RefPtr<BlobImpl> 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<File*>(blob.get())->Impl());
mBlobImpls = blobSet.GetBlobImpls();
SetLengthAndModifiedDate();
SetLengthAndModifiedDate(aRv);
NS_WARN_IF(aRv.Failed());
}
void

View File

@ -25,25 +25,17 @@ public:
NS_DECL_ISUPPORTS_INHERITED
// Create as a file
MultipartBlobImpl(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
static already_AddRefed<MultipartBlobImpl>
Create(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
const nsAString& aName,
const nsAString& aContentType)
: BlobImplBase(aName, aContentType, UINT64_MAX),
mBlobImpls(aBlobImpls),
mIsFromNsIFile(false)
{
SetLengthAndModifiedDate();
}
const nsAString& aContentType,
ErrorResult& aRv);
// Create as a blob
MultipartBlobImpl(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
const nsAString& aContentType)
: BlobImplBase(aContentType, UINT64_MAX),
mBlobImpls(aBlobImpls),
mIsFromNsIFile(false)
{
SetLengthAndModifiedDate();
}
static already_AddRefed<MultipartBlobImpl>
Create(const nsTArray<RefPtr<BlobImpl>>& 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<RefPtr<BlobImpl>>& aBlobImpls,
const nsAString& aName,
const nsAString& aContentType)
: BlobImplBase(aName, aContentType, UINT64_MAX),
mBlobImpls(aBlobImpls),
mIsFromNsIFile(false)
{
}
MultipartBlobImpl(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
const nsAString& aContentType)
: BlobImplBase(aContentType, UINT64_MAX),
mBlobImpls(aBlobImpls),
mIsFromNsIFile(false)
{
}
virtual ~MultipartBlobImpl() {}
void SetLengthAndModifiedDate();
void SetLengthAndModifiedDate(ErrorResult& aRv);
nsTArray<RefPtr<BlobImpl>> mBlobImpls;
bool mIsFromNsIFile;

View File

@ -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<BeaconStreamListener> beaconListener = new BeaconStreamListener();
// Start a preflight if cross-origin and content type is not whitelisted
nsCOMPtr<nsIScriptSecurityManager> 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<nsIInterfaceRequestor> soc = nsContentUtils::SameOriginChecker();
channel->SetNotificationCallbacks(soc);
nsCOMPtr<nsIHttpChannelInternal> internalChannel =
do_QueryInterface(channel);
if (!internalChannel) {
aRv.Throw(NS_ERROR_FAILURE);
return false;
}
nsTArray<nsCString> 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);

View File

@ -555,7 +555,8 @@ namespace {
// Recursive!
already_AddRefed<BlobImpl>
EnsureBlobForBackgroundManager(BlobImpl* aBlobImpl,
PBackgroundChild* aManager = nullptr)
PBackgroundChild* aManager,
ErrorResult& aRv)
{
MOZ_ASSERT(aBlobImpl);
RefPtr<BlobImpl> blobImpl = aBlobImpl;
@ -604,7 +605,11 @@ EnsureBlobForBackgroundManager(BlobImpl* aBlobImpl,
RefPtr<BlobImpl>& 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> 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<File> needs to go out of scope before toObjectOrNull() is
@ -668,7 +684,14 @@ WriteBlob(JSStructuredCloneWriter* aWriter,
MOZ_ASSERT(aBlob);
MOZ_ASSERT(aHolder);
RefPtr<BlobImpl> blobImpl = EnsureBlobForBackgroundManager(aBlob->Impl());
ErrorResult rv;
RefPtr<BlobImpl> 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> 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 = File::Create(aHolder->ParentDuringRead(), blobImpl);
@ -753,14 +782,22 @@ WriteFileList(JSStructuredCloneWriter* aWriter,
return false;
}
ErrorResult rv;
nsTArray<RefPtr<BlobImpl>> blobImpls;
for (uint32_t i = 0; i < aFileList->Length(); ++i) {
RefPtr<BlobImpl> blobImpl =
EnsureBlobForBackgroundManager(aFileList->Item(i)->Impl());
MOZ_ASSERT(blobImpl);
aHolder->BlobImpls().AppendElement(blobImpl);
EnsureBlobForBackgroundManager(aFileList->Item(i)->Impl(), nullptr, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return false;
}
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;
}
}
}

View File

@ -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;

View File

@ -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<Attr>
nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr,
bool aWithNS,
ErrorResult& aError)
nsDOMAttributeMap::SetNamedItemNS(Attr& aAttr, ErrorResult& aError)
{
NS_ENSURE_TRUE(mContent, nullptr);
@ -293,11 +291,6 @@ nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr,
// Get nodeinfo and preexisting attribute (if it exists)
RefPtr<NodeInfo> 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);
@ -312,7 +305,6 @@ nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr,
break;
}
}
}
RefPtr<Attr> attr;

View File

@ -141,11 +141,6 @@ public:
Attr* NamedGetter(const nsAString& aAttrName, bool& aFound);
bool NameIsEnumerable(const nsAString& aName);
already_AddRefed<Attr>
SetNamedItem(Attr& aAttr, ErrorResult& aError)
{
return SetNamedItemInternal(aAttr, false, aError);
}
already_AddRefed<Attr>
RemoveNamedItem(mozilla::dom::NodeInfo* aNodeInfo, ErrorResult& aError);
already_AddRefed<Attr>
RemoveNamedItem(const nsAString& aName, ErrorResult& aError);
@ -158,10 +153,7 @@ public:
GetNamedItemNS(const nsAString& aNamespaceURI,
const nsAString& aLocalName);
already_AddRefed<Attr>
SetNamedItemNS(Attr& aNode, ErrorResult& aError)
{
return SetNamedItemInternal(aNode, true, aError);
}
SetNamedItemNS(Attr& aNode, ErrorResult& aError);
already_AddRefed<Attr>
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<Attr>
SetNamedItemInternal(Attr& aNode, bool aWithNS, ErrorResult& aError);
already_AddRefed<mozilla::dom::NodeInfo>
GetAttrNodeInfo(const nsAString& aNamespaceURI,
const nsAString& aLocalName);

View File

@ -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<nsIAsyncInputStream> stream;
mAsyncStream.swap(stream);
@ -283,25 +282,34 @@ nsDOMFileReader::DoOnLoadEnd(nsresult aStatus,
RefPtr<Blob> 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;

View File

@ -1075,9 +1075,10 @@ nsDOMWindowUtils::SendNativeTouchPoint(uint32_t aPointerId,
}
NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
<uint32_t, nsIWidget::TouchPointerState, nsIntPoint, double, uint32_t, nsIObserver*>
<uint32_t, nsIWidget::TouchPointerState, ScreenIntPoint, double, uint32_t, nsIObserver*>
(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
<nsIntPoint, bool, nsIObserver*>
<ScreenIntPoint, bool, nsIObserver*>
(widget, &nsIWidget::SynthesizeNativeTouchTap,
nsIntPoint(aScreenX, aScreenY), aLongTap, aObserver));
ScreenIntPoint(aScreenX, aScreenY), aLongTap, aObserver));
return NS_OK;
}

View File

@ -25,7 +25,8 @@ namespace {
// Implements steps 3 and 4 of the "create an entry" algorithm of FormData.
already_AddRefed<File>
CreateNewFileInstance(Blob& aBlob, const Optional<nsAString>& aFilename)
CreateNewFileInstance(Blob& aBlob, const Optional<nsAString>& 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<nsAString>& aFilename)
filename = NS_LITERAL_STRING("blob");
}
return aBlob.ToFile(filename);
RefPtr<File> 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<nsAString>& aFilename)
const Optional<nsAString>& aFilename,
ErrorResult& aRv)
{
RefPtr<File> file = CreateNewFileInstance(aBlob, aFilename);
RefPtr<File> 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<nsAString>& aFilename)
const Optional<nsAString>& aFilename,
ErrorResult& aRv)
{
FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
if (tuple) {
RefPtr<File> file = CreateNewFileInstance(aBlob, aFilename);
RefPtr<File> 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> blob = static_cast<Blob*>(domBlob.get());
if (domBlob) {
Optional<nsAString> 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;
}

View File

@ -93,16 +93,20 @@ public:
Constructor(const mozilla::dom::GlobalObject& aGlobal,
const mozilla::dom::Optional<mozilla::dom::NonNull<mozilla::dom::HTMLFormElement> >& 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<nsAString>& aFilename);
const mozilla::dom::Optional<nsAString>& aFilename,
mozilla::ErrorResult& aRv);
void Delete(const nsAString& aName);
void Get(const nsAString& aName, mozilla::dom::Nullable<OwningFileOrUSVString>& aOutValue);
void GetAll(const nsAString& aName, nsTArray<OwningFileOrUSVString>& aValues);
bool Has(const nsAString& aName);
void Set(const nsAString& aName, Blob& aBlob,
const mozilla::dom::Optional<nsAString>& aFilename);
void Set(const nsAString& aName, const nsAString& aValue);
const mozilla::dom::Optional<nsAString>& 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;

View File

@ -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;

View File

@ -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<nsILoadInfo> 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<nsIHttpChannel> 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<nsILoadInfo> 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<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
MOZ_ASSERT(loadInfo);
bool isCrossSite = loadInfo->GetTainting() != LoadTainting::Basic;
if (isCrossSite) {
nsCOMPtr<nsIHTMLDocument> 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<RequestBody>& 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<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
static_cast<LoadInfo*>(loadInfo.get())->SetWithCredentialsSecFlag();
static_cast<LoadInfo*>(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<RequestBody>& 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<RequestBody>& 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<nsILoadInfo> 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<nsXMLHttpRequest> 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,28 +3316,15 @@ 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<AsyncVerifyRedirectCallbackForwarder> fwd =
new AsyncVerifyRedirectCallbackForwarder(this);
nsCOMPtr<nsIAsyncVerifyRedirectCallback> fwd =
EnsureXPCOMifier();
rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
nsresult rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
aNewChannel,
aFlags, fwd);
if (NS_FAILED(rv)) {
@ -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<nsIHttpChannel> 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)

View File

@ -41,7 +41,6 @@
#undef Status
#endif
class AsyncVerifyRedirectCallbackForwarder;
class nsFormData;
class nsIJARChannel;
class nsILoadGroup;
@ -426,7 +425,8 @@ private:
return Send(Nullable<RequestBody>(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<nsAString>& 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->)

View File

@ -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);
}

View File

@ -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));
});

View File

@ -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]

View File

@ -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();
}

View File

@ -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");

View File

@ -0,0 +1,77 @@
<!DOCTYPE HTML>
<html>
<head>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1198095
-->
<title>Test for Bug 1198095</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1198095">Mozilla Bug 1198095</a>
<pre id="test">
<script class="testbody" type="text/javascript;version=1.7">
var fileData1 = '1234567890';
var fileData2 = '43210';
var r, firstBlob;
var openerURL = SimpleTest.getTestFileURL("file_bug1198095.js");
var opener = SpecialPowers.loadChromeScript(openerURL);
opener.addMessageListener("file.opened", onFileOpened);
opener.addMessageListener("file.modified", onFileModified);
opener.sendAsyncMessage("file.open", fileData1);
function onLoadEnd1(e) {
e.target.removeEventListener('loadend', onLoadEnd1);
is(e.target, r, "Target and r are ok");
ok(e.target.readyState, FileReader.DONE, "The file has been read.");
ok(e.target.result instanceof ArrayBuffer, "The result is an ArrayBuffer");
var view = new Uint8Array(e.target.result);
is(view.length, fileData1.length, "File data length matches");
for (var i = 0; i < fileData1.length; ++i) {
is(String.fromCharCode(view[i]), fileData1[i], "Byte matches");
}
opener.sendAsyncMessage("file.modify", fileData2);
}
function onLoadEnd2(e) {
e.target.removeEventListener('loadend', onLoadEnd2);
ok(false, "This method should not be called - loadEnd2!");
}
function onError1(e) {
ok(false, "This method should not be called - error1!");
}
function onError2(e) {
e.target.removeEventListener('error', onError2);
SimpleTest.finish();
}
function onFileOpened(blob) {
firstBlob = blob;
r = new FileReader();
r.addEventListener("loadend", onLoadEnd1, false);
r.addEventListener("error", onError1, false);
r.readAsArrayBuffer(firstBlob);
}
function onFileModified(blob) {
r.addEventListener("loadend", onLoadEnd2, false);
r.removeEventListener('error', onError1);
r.addEventListener("error", onError2, false);
r.readAsArrayBuffer(firstBlob);
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body> </html>

View File

@ -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() {

View File

@ -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";

View File

@ -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 {

View File

@ -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)

View File

@ -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*>(event),
EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr, event,
presContext, aStatus);
return NS_SUCCEEDED(rv);
}

View File

@ -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<nsIURI> 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<nsIChannel> 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<nsIInterfaceRequestor> 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<nsCString, 5> unsafeHeaders;
mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
nsCOMPtr<nsIHttpChannelInternal> 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<nsILoadInfo> 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<InternalResponse>
FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse,
nsIURI* aFinalURI,
@ -559,6 +472,23 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
nsCOMPtr<nsIJARChannel> 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<nsIURI> 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<nsIURI> 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<nsIChannelEventSink*>(this);
NS_ADDREF_THIS();
return NS_OK;
}
if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
*aResult = static_cast<nsIStreamListener*>(this);
NS_ADDREF_THIS();

View File

@ -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<nsIOutputStream> mPipeOutputStream;
RefPtr<FetchDriverObserver> mObserver;
nsCOMPtr<nsIDocument> mDocument;
bool mHasBeenCrossSite;
DebugOnly<bool> mResponseAvailableCalled;
DebugOnly<bool> 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<InternalResponse>

View File

@ -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<nsAString> dummy;
mFormData->Append(name, *file, dummy);
ErrorResult rv;
mFormData->Append(name, *file, dummy, rv);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
}
return true;

View File

@ -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);

View File

@ -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));

View File

@ -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;

View File

@ -904,13 +904,17 @@ CreateBlobImpl(const nsTArray<BlobData>& aBlobDatas,
MOZ_ASSERT(!isMutable);
}
ErrorResult rv;
RefPtr<BlobImpl> 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,10 +1165,10 @@ RemoteInputStream::ReallyBlockAndWaitForStream()
#ifdef DEBUG
if (waited && mWeakSeekableStream) {
int64_t position;
MOZ_ASSERT(NS_SUCCEEDED(mWeakSeekableStream->Tell(&position)),
"Failed to determine initial stream position!");
if (NS_SUCCEEDED(mWeakSeekableStream->Tell(&position))) {
MOZ_ASSERT(!position, "Stream not starting at 0!");
}
}
#endif
}

View File

@ -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);

View File

@ -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)
{

View File

@ -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;

51
dom/media/ADTSDecoder.cpp Normal file
View File

@ -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<MediaDecoderReader> reader =
new MediaFormatReader(this, new ADTSDemuxer(GetResource()));
return new MediaDecoderStateMachine(this, reader);
}
/* static */ bool
ADTSDecoder::IsEnabled()
{
PDMFactory::Init();
RefPtr<PDMFactory> 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

30
dom/media/ADTSDecoder.h Normal file
View File

@ -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_

877
dom/media/ADTSDemuxer.cpp Normal file
View File

@ -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 <inttypes.h>
#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::InitPromise>
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<MediaTrackDemuxer>
ADTSDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
{
if (!mTrackDemuxer) {
return nullptr;
}
return RefPtr<ADTSTrackDemuxer>(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<MediaRawData> 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<AudioInfo>();
}
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<MediaRawData>
ADTSTrackDemuxer::DemuxSample()
{
return GetNextFrame(FindNextFrame());
}
media::TimeUnit
ADTSTrackDemuxer::SeekPosition() const
{
return Duration(mFrameIndex);
}
#endif
UniquePtr<TrackInfo>
ADTSTrackDemuxer::GetInfo() const
{
return mInfo->Clone();
}
RefPtr<ADTSTrackDemuxer::SeekPromise>
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::SamplesPromise>
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<SamplesHolder> frames = new SamplesHolder();
while (aNumSamples--) {
RefPtr<MediaRawData> 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::SkipAccessPointPromise>
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<MediaResource> 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<MediaRawData> 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<MediaRawData>
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<MediaRawData> frame = new MediaRawData();
frame->mOffset = offset;
nsAutoPtr<MediaRawDataWriter> 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<int64_t>(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<int64_t>(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<int64_t>(aSize, streamLen - aOffset);
}
uint32_t read = 0;
ADTSLOGV("ADTSTrackDemuxer::Read -> ReadAt(%d)", aSize);
const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer),
static_cast<uint32_t>(aSize), &read);
NS_ENSURE_SUCCESS(rv, 0);
return static_cast<int32_t>(read);
}
double
ADTSTrackDemuxer::AverageFrameLength() const
{
if (mNumParsedFrames) {
return static_cast<double>(mTotalFrameLen) / mNumParsedFrames;
}
return 0.0;
}
} // namespace mozilla

146
dom/media/ADTSDemuxer.h Normal file
View File

@ -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<InitPromise> Init() override;
bool HasTrackType(TrackInfo::TrackType aType) const override;
uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
bool IsSeekable() const override;
bool ShouldComputeStartTime() const override { return false; }
private:
bool InitInternal();
RefPtr<MediaResource> mSource;
RefPtr<ADTSTrackDemuxer> 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<MediaRawData> DemuxSample();
media::TimeUnit SeekPosition() const;
#endif
// MediaTrackDemuxer interface.
UniquePtr<TrackInfo> GetInfo() const override;
RefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
void Reset() override;
RefPtr<SkipAccessPointPromise> 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<MediaRawData> 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<AudioInfo> mInfo;
};
} // mozilla
#endif // !ADTS_DEMUXER_H_

View File

@ -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

View File

@ -4,6 +4,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <MediaStreamGraphImpl.h>
#include "mozilla/dom/AudioContext.h"
#include "CubebUtils.h"
#ifdef XP_MACOSX

View File

@ -1248,6 +1248,7 @@ MediaFormatReader::Flush(TrackType aTrack)
auto& decoder = GetDecoderData(aTrack);
if (!decoder.mDecoder) {
decoder.ResetState();
return;
}

View File

@ -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<PledgeSourceSet> 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<MediaEngine> 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<MediaEngine> fakeBackend, realBackend;
if (fakeCams || fakeMics) {
fakeBackend = new MediaEngineDefault(aFakeTracks);
}
if ((!fakeCams && hasVideo) || (!fakeMics && hasAudio)) {
RefPtr<MediaManager> manager = MediaManager_GetInstance();
backend = manager->GetBackend(aWindowId);
realBackend = manager->GetBackend(aWindowId);
}
ScopedDeletePtr<SourceSet> result(new SourceSet);
if (hasVideo) {
nsTArray<RefPtr<VideoDevice>> videos;
GetSources(backend, aVideoType, &MediaEngine::EnumerateVideoDevices, videos,
videoLoopDev);
GetSources(fakeCams? fakeBackend : realBackend, aVideoType,
&MediaEngine::EnumerateVideoDevices, videos, videoLoopDev);
for (auto& source : videos) {
result->AppendElement(source);
}
}
if (hasAudio) {
nsTArray<RefPtr<AudioDevice>> audios;
GetSources(backend, aAudioType,
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<SourceSet> result(handoff); // grab result
@ -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

View File

@ -295,6 +295,7 @@ MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
, mManager(aManager)
, mType(aType)
, mMonitor("MediaSourceTrackDemuxer")
, mLastSeek(Some(TimeUnit()))
{
}
@ -327,6 +328,7 @@ MediaSourceTrackDemuxer::Reset()
RefPtr<MediaSourceTrackDemuxer> self = this;
nsCOMPtr<nsIRunnable> 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::SamplesPromise>
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<MediaRawData> sample =
mManager->GetSample(mType,

View File

@ -129,6 +129,7 @@ private:
// Monitor protecting members below accessed from multiple threads.
Monitor mMonitor;
media::TimeUnit mNextRandomAccessPoint;
Maybe<media::TimeUnit> mLastSeek;
};
} // namespace mozilla

View File

@ -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',

View File

@ -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<AudioDataValue[]> samples(
new (fallible) AudioDataValue[frames.value() * mChannelCount]);
auto samples =
MakeUniqueFallible<AudioDataValue[]>(frames.value() * mChannelCount);
if (!samples) {
return nullptr;
}

View File

@ -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<TextureClient> 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> graphicBuffer = texData->GetGraphicBuffer();
if (!graphicBuffer.get()) {
return nullptr;
}
RefPtr<TextureClient> textureClient =
TextureClient::CreateWithData(texData, TextureFlags::DEALLOCATE_CLIENT, aAllocator);
return textureClient.forget();
}
bool IsCompatible(TextureClient* aTextureClient) override
{
if (!aTextureClient) {
return false;
}
sp<GraphicBuffer> graphicBuffer =
static_cast<GrallocTextureData*>(aTextureClient->GetInternalData())->GetGraphicBuffer();
if (!graphicBuffer.get() ||
static_cast<uint32_t>(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<GraphicBuffer>& aSource, sp<GraphicBuffer>& aDestination)
{
@ -281,7 +364,7 @@ CopyGraphicBuffer(sp<GraphicBuffer>& aSource, sp<GraphicBuffer>& 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<uint8_t*>(srcPtr);
srcData.mYSkip = 0;
@ -297,28 +380,7 @@ CopyGraphicBuffer(sp<GraphicBuffer>& aSource, sp<GraphicBuffer>& aDestination)
srcData.mCrSkip = 0;
srcData.mCbChannel = srcData.mCrChannel + (srcData.mCbCrStride * srcData.mCbCrSize.height);
srcData.mCbSkip = 0;
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<uint8_t*>(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;
break;
default:
NS_ERROR("Unsupported input gralloc image type. Should never be here.");
}
// Build PlanarYCbCrData for destination buffer.
PlanarYCbCrData destData;
destData.mYChannel = static_cast<uint8_t*>(destPtr);
@ -337,6 +399,18 @@ CopyGraphicBuffer(sp<GraphicBuffer>& aSource, sp<GraphicBuffer>& aDestination)
destData.mCbSkip = 0;
CopyYUV(srcData, destData);
break;
}
case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
CopyVenus(static_cast<uint8_t*>(srcPtr),
static_cast<uint8_t*>(destPtr),
aSource->getWidth(),
aSource->getHeight());
break;
default:
NS_ERROR("Unsupported input gralloc image type. Should never be here.");
}
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<GraphicBuffer> destBuffer =
static_cast<GrallocTextureData*>(textureClient->GetInternalData())->GetGraphicBuffer();

View File

@ -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");

View File

@ -117,19 +117,20 @@ protected:
template<class DeviceType>
static bool
AreUnfitSettings(const dom::MediaTrackConstraints &aConstraints,
SomeSettingsFit(const dom::MediaTrackConstraints &aConstraints,
nsTArray<RefPtr<DeviceType>>& aSources)
{
nsTArray<const dom::MediaTrackConstraintSet*> aggregateConstraints;
aggregateConstraints.AppendElement(&aConstraints);
MOZ_ASSERT(aSources.Length());
for (auto& source : aSources) {
if (source->GetBestFitnessDistance(aggregateConstraints) != UINT32_MAX) {
return false;
}
}
return true;
}
}
return false;
}
public:
// Apply constrains to a supplied list of sources (removes items from the list)
@ -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";
}
}

View File

@ -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) {

View File

@ -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,21 +94,12 @@ 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);
}
static nsresult
DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo,
nsCOMPtr<nsIStreamListener>& aInAndOutListener)
@ -122,7 +109,8 @@ DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo,
RefPtr<nsCORSListenerProxy> 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<nsIURI> 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<nsIURI> 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<nsILoadInfo> 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<nsIPrincipal> oldPrincipal;
nsContentUtils::GetSecurityManager()->
GetChannelResultPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
nsCOMPtr<nsIURI> newURI;
aNewChannel->GetURI(getter_AddRefs(newURI));
nsCOMPtr<nsIURI> 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<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
MOZ_ASSERT(loadInfo);
nsCOMPtr<nsIURI> 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 =====

View File

@ -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<nsIStreamListener>& aInAndOutListener);
private:
static nsresult CheckChannel(nsIChannel* aChannel);
virtual ~nsContentSecurityManager() {}
};

View File

@ -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);
}

View File

@ -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,
},
],
},

View File

@ -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",

View File

@ -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('<script>var worker = new Worker("'+resource+'?res=worker&redir=other&id=worker-redir");</script>');
return;
}
// script that XHR's to a resource that redirects to another site
if (query["testid"] == "xhr-src") {
response.write('<script src="'+resource+'?res=xhr"></script>');

View File

@ -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"];

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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",
},
],
},

View File

@ -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<FormDataEntryValue> 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<USVString, FormDataEntryValue>;
};

View File

@ -5,7 +5,7 @@
interface NamedNodeMap {
getter Attr? getNamedItem(DOMString name);
[Throws]
[Throws, BinaryName="setNamedItemNS"]
Attr? setNamedItem(Attr arg);
[Throws]
Attr removeNamedItem(DOMString name);

View File

@ -1079,7 +1079,7 @@ public:
// Step 8 "Queue a task..." for updatefound.
nsCOMPtr<nsIRunnable> upr =
NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(
NS_NewRunnableMethodWithArg<RefPtr<ServiceWorkerRegistrationInfo>>(
swm,
&ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
mRegistration);
@ -1767,9 +1767,8 @@ ServiceWorkerRegistrationInfo::Activate()
// "Queue a task to fire a simple event named controllerchange..."
nsCOMPtr<nsIRunnable> controllerChangeRunnable =
NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm,
&ServiceWorkerManager::FireControllerChange,
this);
NS_NewRunnableMethodWithArg<RefPtr<ServiceWorkerRegistrationInfo>>(
swm, &ServiceWorkerManager::FireControllerChange, this);
NS_DispatchToMainThread(controllerChangeRunnable);
nsCOMPtr<nsIRunnable> 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;
}

View File

@ -171,7 +171,7 @@ nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument,
NS_ENSURE_SUCCESS(rv, rv);
event->SetTrusted(true);
bool dummy;
rv = rootCont->DispatchEvent(static_cast<Event*>(event), &dummy);
rv = rootCont->DispatchEvent(event, &dummy);
NS_ENSURE_SUCCESS(rv, rv);
// Observe the document so we know when to switch to "normal" view

View File

@ -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<nsIURI> 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)

View File

@ -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
/* */

View File

@ -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
* <xsl:import> and document()

View File

@ -0,0 +1,4 @@
<?xml-stylesheet type="text/xsl" href="file_bug1222624.xsl"?>
<root>
<load>file_bug1222624_data2.xml</load>
</root>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="file_bug1222624_sub.xsl"/>
<xsl:template match="/root">
<result>
<xsl:call-template name="external"/>
<xsl:value-of select="document('file_bug1222624_data1.xml')"/>
<xsl:text>!</xsl:text>
<xsl:value-of select="document(load)"/>
</result>
</xsl:template>
</xsl:stylesheet>

View File

@ -0,0 +1 @@
<data>doc1</data>

View File

@ -0,0 +1 @@
<data>doc2</data>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:include href="file_bug1222624_sub_sub.xsl"/>
</xsl:stylesheet>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="external">
<external/>
</xsl:template>
</xsl:stylesheet>

Some files were not shown because too many files have changed in this diff Show More