Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-12-18 15:30:53 +01:00
commit a5bb018cf4
248 changed files with 6464 additions and 3604 deletions

View File

@ -15,8 +15,8 @@
"unpack": true
},
{
"size": 73029932,
"digest": "ef1818acf065838dcb72554e521f9fd7098f0a3690cb6a3106d7bf18f46c342bfdd5a2b7d86e92ee3ddb9e478380343e58ecf8fd242807b8881a2d53fbec5ab3",
"size": 193213220,
"digest": "58b8ebd8de923117831dcbba71172a53e26c25bd16c2b2bb363a1254f2cd4e87f95e2c5f41e2dce10e18e43a17e0a395637c9ddcbf1e27673582792f9095c62e",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

View File

@ -5197,8 +5197,10 @@ var TabletModeUpdater = {
};
var gTabletModePageCounter = {
enabled: false,
inc() {
if (!AppConstants.isPlatformAndVersionAtLeast("win", "10.0")) {
this.enabled = AppConstants.isPlatformAndVersionAtLeast("win", "10.0");
if (!this.enabled) {
this.inc = () => {};
return;
}
@ -5214,8 +5216,11 @@ var gTabletModePageCounter = {
},
finish() {
Services.telemetry.getKeyedHistogramById("FX_TABLETMODE_PAGE_LOAD").add("tablet", this._tabletCount);
Services.telemetry.getKeyedHistogramById("FX_TABLETMODE_PAGE_LOAD").add("desktop", this._desktopCount);
if (this.enabled) {
let histogram = Services.telemetry.getKeyedHistogramById("FX_TABLETMODE_PAGE_LOAD");
histogram.add("tablet", this._tabletCount);
histogram.add("desktop", this._desktopCount);
}
},
};

View File

@ -4691,6 +4691,9 @@
let hoveredTab = this._hoveredTab;
if (hoveredTab) {
hoveredTab._mouseleave();
}
hoveredTab = this.querySelector("tab:hover");
if (hoveredTab) {
hoveredTab._mouseenter();
}
]]></body>

View File

@ -14,17 +14,17 @@
"unpack": true
},
{
"size": 193213220,
"digest": "58b8ebd8de923117831dcbba71172a53e26c25bd16c2b2bb363a1254f2cd4e87f95e2c5f41e2dce10e18e43a17e0a395637c9ddcbf1e27673582792f9095c62e",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true
},
{
"size": 167175,
"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
"algorithm": "sha512",
"filename": "sccache.tar.bz2",
"unpack": true
},
{
"size": 73029932,
"digest": "ef1818acf065838dcb72554e521f9fd7098f0a3690cb6a3106d7bf18f46c342bfdd5a2b7d86e92ee3ddb9e478380343e58ecf8fd242807b8881a2d53fbec5ab3",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true
}
]

View File

@ -10,8 +10,8 @@
"unpack": true
},
{
"size": 128301120,
"digest": "d2d71103a6cec84b150b8f08bfef2682aa713c2d6eadce584f79836ef27ba4380ffb444165d999b79605f18ad165641a7a8cc0e04a201675ad5f655a59adbda9",
"size": 215952362,
"digest": "5e9825dbe83b2a157879076da70fc5c989a1638e30d3b14a9901b166db09013c356a9dc4eaf6c16209a1832d9cb1c67ca869e9b9003cab8355a7f03b3dc08775",
"algorithm": "sha512",
"filename": "rustc.tar.bz2",
"unpack": true

View File

@ -30,6 +30,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
"resource://gre/modules/TelemetryEnvironment.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog",
"resource://gre/modules/TelemetryLog.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryUtils",
"resource://gre/modules/TelemetryUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
"resource://services-common/utils.js");
XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
@ -160,10 +162,6 @@ function loadJSONAsync(file, options) {
});
}
function telemetryEnabled() {
return gPrefsTelemetry.get(PREF_TELEMETRY_ENABLED, false);
}
// Returns a promise that is resolved with the AddonInstall for that URL.
function addonInstallForURL(url, hash) {
let deferred = Promise.defer();
@ -389,7 +387,7 @@ Experiments.Experiments.prototype = {
this._shutdown = false;
configureLogging();
gExperimentsEnabled = gPrefs.get(PREF_ENABLED, false);
gExperimentsEnabled = gPrefs.get(PREF_ENABLED, false) && TelemetryUtils.isTelemetryEnabled;
this._log.trace("enabled=" + gExperimentsEnabled + ", " + this.enabled);
gPrefs.observe(PREF_LOGGING, configureLogging);
@ -580,7 +578,7 @@ Experiments.Experiments.prototype = {
_toggleExperimentsEnabled: Task.async(function* (enabled) {
this._log.trace("_toggleExperimentsEnabled(" + enabled + ")");
let wasEnabled = gExperimentsEnabled;
gExperimentsEnabled = enabled && telemetryEnabled();
gExperimentsEnabled = enabled && TelemetryUtils.isTelemetryEnabled;
if (wasEnabled == gExperimentsEnabled) {
return;

View File

@ -233,3 +233,7 @@ function replaceExperiments(experiment, list) {
},
});
}
// Experiments require Telemetry to be enabled, and that's not true for debug
// builds. Let's just enable it here instead of going through each test.
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);

View File

@ -0,0 +1,21 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource:///modules/experiments/Experiments.jsm");
add_test(function test_experiments_activation() {
do_get_profile();
loadAddonManager();
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, false);
let experiments = Experiments.instance();
Assert.ok(!experiments.enabled, "Experiments must be disabled if Telemetry is disabled.");
// TODO: Test that Experiments are turned back on when bug 1232648 lands.
run_next_test();
});

View File

@ -24,6 +24,7 @@ generated-files =
[test_disableExperiments.js]
[test_fetch.js]
[test_telemetry.js]
[test_telemetry_disabled.js]
[test_healthreport.js]
[test_previous_provider.js]
[test_upgrade.js]

View File

@ -72,7 +72,7 @@ SEARCHPLUGINS_NAMES = $(shell cat $(call MERGE_FILE,/searchplugins/list.txt)) dd
SEARCHPLUGINS_FILENAMES = $(subst :hidden,,$(SEARCHPLUGINS_NAMES))
SEARCHPLUGINS_PATH := .deps/generated_$(AB_CD)
SEARCHPLUGINS_TARGET := libs searchplugins
SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_FILENAMES)),$(or $(wildcard $(call EN_US_OR_L10N_FILE,searchplugins/$(plugin))),$(info Missing searchplugin: $(plugin))))
SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_FILENAMES)),$(or $(wildcard $(call EN_US_OR_L10N_FILE,searchplugins/$(plugin))),$(warning Missing searchplugin: $(plugin))))
# Some locale-specific search plugins may have preprocessor directives, but the
# default en-US ones do not.
SEARCHPLUGINS_FLAGS := --silence-missing-directive-warnings

View File

@ -123,7 +123,7 @@ groupbox.collapsable caption .caption-icon {
background-position: center;
-moz-margin-start: 2px;
-moz-margin-end: 2px;
background-image: url("chrome://global/skin/tree/twisty-open.png");
background-image: url("chrome://global/skin/tree/twisty.svg#open");
}
groupbox.collapsable[closed="true"] {
@ -133,7 +133,7 @@ groupbox.collapsable[closed="true"] {
}
groupbox.collapsable[closed="true"] caption .caption-icon {
background-image: url("chrome://global/skin/tree/twisty-clsd.png");
background-image: url("chrome://global/skin/tree/twisty.svg#clsd");
}
groupbox tree {

View File

@ -25,10 +25,10 @@ AC_DEFUN([MOZ_RUST_SUPPORT], [
fi
if test -n "$MOZ_RUST" && test -z "$_RUSTC_MAJOR_VERSION" -o \
"$_RUSTC_MAJOR_VERSION" -lt 1 -o \
\( "$_RUSTC_MAJOR_VERSION" -eq 1 -a "$_RUSTC_MINOR_VERSION" -lt 4 \); then
\( "$_RUSTC_MAJOR_VERSION" -eq 1 -a "$_RUSTC_MINOR_VERSION" -lt 5 \); then
AC_MSG_ERROR([Rust compiler ${RUSTC_VERSION} is too old.
To compile Rust language sources please install at least
version 1.4 of the 'rustc' toolchain and make sure it is
version 1.5 of the 'rustc' toolchain and make sure it is
first in your path.
You can verify this by typing 'rustc --version'.])
fi

View File

@ -945,7 +945,7 @@ ifdef MOZ_RUST
# in the target's LIBS.
$(RSOBJS):
$(REPORT_BUILD)
$(RUSTC) $(RUSTFLAGS) --crate-type staticlib -o $(call mk_libname,$<) $(_VPATH_SRCS)
$(RUSTC) $(RUSTFLAGS) --crate-type staticlib --emit dep-info=$(MDDEPDIR)/$(call mk_libname,$<).pp,link=$(call mk_libname,$<) $(_VPATH_SRCS)
endif
$(SOBJS):

View File

@ -6405,14 +6405,6 @@ AC_SUBST(TAR)
AC_CHECK_PROGS(WGET, wget, "")
AC_SUBST(WGET)
dnl ========================================================
dnl Signing
dnl ========================================================
if test -n "$MOZ_SIGN_CMD"; then
AC_DEFINE(MOZ_SIGNING)
fi
dnl ========================================================
dnl Maintenance Service
dnl ========================================================

View File

@ -2,27 +2,20 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test for dynamically registering and unregistering themes
const CHROME_URL = "chrome://mochitests/content/browser/devtools/client/framework/test/";
var toolbox;
function test()
{
gBrowser.selectedTab = gBrowser.addTab();
let target = TargetFactory.forTab(gBrowser.selectedTab);
add_task(function* themeRegistration() {
let tab = yield addTab("data:text/html,test");
let target = TargetFactory.forTab(tab);
toolbox = yield gDevTools.showToolbox(target);
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
gDevTools.showToolbox(target).then(testRegister);
}, true);
content.location = "data:text/html,test for dynamically registering and unregistering themes";
}
function testRegister(aToolbox)
{
toolbox = aToolbox
gDevTools.once("theme-registered", themeRegistered);
let themeId = yield new Promise(resolve => {
gDevTools.once("theme-registered", (e, themeId) => {
resolve(themeId);
});
gDevTools.registerTheme({
id: "test-theme",
@ -30,32 +23,24 @@ function testRegister(aToolbox)
stylesheets: [CHROME_URL + "doc_theme.css"],
classList: ["theme-test"],
});
}
});
function themeRegistered(event, themeId)
{
is(themeId, "test-theme", "theme-registered event handler sent theme id");
ok(gDevTools.getThemeDefinitionMap().has(themeId), "theme added to map");
});
add_task(function* themeInOptionsPanel() {
yield toolbox.selectTool("options");
// Test that new theme appears in the Options panel
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "options").then(() => {
let panel = toolbox.getCurrentPanel();
let doc = panel.panelWin.frameElement.contentDocument;
let panelWin = toolbox.getCurrentPanel().panelWin;
let doc = panelWin.frameElement.contentDocument;
let themeOption = doc.querySelector("#devtools-theme-box > radio[value=test-theme]");
ok(themeOption, "new theme exists in the Options panel");
// Apply the new theme.
applyTheme();
});
}
function applyTheme()
{
let panelWin = toolbox.getCurrentPanel().panelWin;
let doc = panelWin.frameElement.contentDocument;
let testThemeOption = doc.querySelector("#devtools-theme-box > radio[value=test-theme]");
let lightThemeOption = doc.querySelector("#devtools-theme-box > radio[value=light]");
@ -65,24 +50,26 @@ function applyTheme()
// Select test theme.
testThemeOption.click();
info("Waiting for theme to finish loading");
yield once(panelWin, "theme-switch-complete");
color = panelWin.getComputedStyle(testThemeOption).color;
is(color, "rgb(255, 0, 0)", "style applied");
// Select light theme
lightThemeOption.click();
info("Waiting for theme to finish loading");
yield once(panelWin, "theme-switch-complete");
color = panelWin.getComputedStyle(testThemeOption).color;
isnot(color, "rgb(255, 0, 0)", "style unapplied");
// Select test theme again.
testThemeOption.click();
});
// Then unregister the test theme.
testUnregister();
}
function testUnregister()
{
add_task(function* themeUnregistration() {
gDevTools.unregisterTheme("test-theme");
ok(!gDevTools.getThemeDefinitionMap().has("test-theme"), "theme removed from map");
@ -94,20 +81,9 @@ function testUnregister()
// The default light theme must be selected now.
is(themeBox.selectedItem, themeBox.querySelector("[value=light]"),
"theme light must be selected");
// Make sure the tab-attaching process is done before we destroy the toolbox.
let target = TargetFactory.forTab(gBrowser.selectedTab);
let actor = target.activeTab.actor;
target.client.attachTab(actor, (response) => {
cleanup();
});
}
function cleanup()
{
toolbox.destroy().then(function() {
add_task(function* cleanup() {
yield toolbox.destroy();
toolbox = null;
gBrowser.removeCurrentTab();
finish();
});
}

View File

@ -39,24 +39,24 @@ devtools.jar:
content/animationinspector/animation-controller.js (animationinspector/animation-controller.js)
content/animationinspector/animation-panel.js (animationinspector/animation-panel.js)
content/animationinspector/animation-inspector.xhtml (animationinspector/animation-inspector.xhtml)
content/sourceeditor/codemirror/comment/comment.js (sourceeditor/codemirror/addon/comment/comment.js)
content/sourceeditor/codemirror/edit/trailingspace.js (sourceeditor/codemirror/addon/edit/trailingspace.js)
content/sourceeditor/codemirror/edit/matchbrackets.js (sourceeditor/codemirror/addon/edit/matchbrackets.js)
content/sourceeditor/codemirror/edit/closebrackets.js (sourceeditor/codemirror/addon/edit/closebrackets.js)
content/sourceeditor/codemirror/dialog/dialog.js (sourceeditor/codemirror/addon/dialog/dialog.js)
content/sourceeditor/codemirror/dialog/dialog.css (sourceeditor/codemirror/addon/dialog/dialog.css)
content/sourceeditor/codemirror/fold/foldcode.js (sourceeditor/codemirror/addon/fold/foldcode.js)
content/sourceeditor/codemirror/fold/brace-fold.js (sourceeditor/codemirror/addon/fold/brace-fold.js)
content/sourceeditor/codemirror/fold/comment-fold.js (sourceeditor/codemirror/addon/fold/comment-fold.js)
content/sourceeditor/codemirror/fold/xml-fold.js (sourceeditor/codemirror/addon/fold/xml-fold.js)
content/sourceeditor/codemirror/fold/foldgutter.js (sourceeditor/codemirror/addon/fold/foldgutter.js)
content/sourceeditor/codemirror/hint/show-hint.js (sourceeditor/codemirror/addon/hint/show-hint.js)
content/sourceeditor/codemirror/search/search.js (sourceeditor/codemirror/addon/search/search.js)
content/sourceeditor/codemirror/search/searchcursor.js (sourceeditor/codemirror/addon/search/searchcursor.js)
content/sourceeditor/codemirror/selection/active-line.js (sourceeditor/codemirror/addon/selection/active-line.js)
content/sourceeditor/codemirror/tern/tern.js (sourceeditor/codemirror/addon/tern/tern.js)
content/sourceeditor/codemirror/codemirror.js (sourceeditor/codemirror/lib/codemirror.js)
content/sourceeditor/codemirror/codemirror.css (sourceeditor/codemirror/lib/codemirror.css)
content/sourceeditor/codemirror/addon/comment/comment.js (sourceeditor/codemirror/addon/comment/comment.js)
content/sourceeditor/codemirror/addon/edit/trailingspace.js (sourceeditor/codemirror/addon/edit/trailingspace.js)
content/sourceeditor/codemirror/addon/edit/matchbrackets.js (sourceeditor/codemirror/addon/edit/matchbrackets.js)
content/sourceeditor/codemirror/addon/edit/closebrackets.js (sourceeditor/codemirror/addon/edit/closebrackets.js)
content/sourceeditor/codemirror/addon/dialog/dialog.js (sourceeditor/codemirror/addon/dialog/dialog.js)
content/sourceeditor/codemirror/addon/dialog/dialog.css (sourceeditor/codemirror/addon/dialog/dialog.css)
content/sourceeditor/codemirror/addon/fold/foldcode.js (sourceeditor/codemirror/addon/fold/foldcode.js)
content/sourceeditor/codemirror/addon/fold/brace-fold.js (sourceeditor/codemirror/addon/fold/brace-fold.js)
content/sourceeditor/codemirror/addon/fold/comment-fold.js (sourceeditor/codemirror/addon/fold/comment-fold.js)
content/sourceeditor/codemirror/addon/fold/xml-fold.js (sourceeditor/codemirror/addon/fold/xml-fold.js)
content/sourceeditor/codemirror/addon/fold/foldgutter.js (sourceeditor/codemirror/addon/fold/foldgutter.js)
content/sourceeditor/codemirror/addon/hint/show-hint.js (sourceeditor/codemirror/addon/hint/show-hint.js)
content/sourceeditor/codemirror/addon/search/search.js (sourceeditor/codemirror/addon/search/search.js)
content/sourceeditor/codemirror/addon/search/searchcursor.js (sourceeditor/codemirror/addon/search/searchcursor.js)
content/sourceeditor/codemirror/addon/selection/active-line.js (sourceeditor/codemirror/addon/selection/active-line.js)
content/sourceeditor/codemirror/addon/tern/tern.js (sourceeditor/codemirror/addon/tern/tern.js)
content/sourceeditor/codemirror/lib/codemirror.js (sourceeditor/codemirror/lib/codemirror.js)
content/sourceeditor/codemirror/lib/codemirror.css (sourceeditor/codemirror/lib/codemirror.css)
content/sourceeditor/codemirror/mode/javascript.js (sourceeditor/codemirror/mode/javascript.js)
content/sourceeditor/codemirror/mode/xml.js (sourceeditor/codemirror/mode/xml.js)
content/sourceeditor/codemirror/mode/css.js (sourceeditor/codemirror/mode/css.js)

View File

@ -7,7 +7,8 @@ var toolbox;
add_task(function*() {
let target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = yield gDevTools.showToolbox(target);
let root = toolbox.frame.contentDocument.documentElement;
let doc = toolbox.frame.contentDocument;
let root = doc.documentElement;
let platform = root.getAttribute("platform");
let expectedPlatform = getPlatform();
@ -15,7 +16,24 @@ add_task(function*() {
let theme = Services.prefs.getCharPref("devtools.theme");
let className = "theme-" + theme;
ok(root.classList.contains(className), ":root has " + className + " class (current theme)");
ok(root.classList.contains(className),
":root has " + className + " class (current theme)");
// Convert the xpath result into an array of strings
// like `href="{URL}" type="text/css"`
let sheetsIterator = doc.evaluate("processing-instruction('xml-stylesheet')",
doc, null, XPathResult.ANY_TYPE, null);
let sheetsInDOM = [];
let sheet;
while (sheet = sheetsIterator.iterateNext()) {
sheetsInDOM.push(sheet.data);
}
let sheetsFromTheme = gDevTools.getThemeDefinition(theme).stylesheets;
info ("Checking for existence of " + sheetsInDOM.length + " sheets");
for (let sheet of sheetsFromTheme) {
ok(sheetsInDOM.some(s=>s.includes(sheet)), "There is a stylesheet for " + sheet);
}
yield toolbox.destroy();
});

View File

@ -3,8 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function() {
const DEVTOOLS_SKIN_URL = "chrome://devtools/skin/";
const SCROLLBARS_URL = "chrome://devtools/skin/floating-scrollbars-light.css";
let documentElement = document.documentElement;
let devtoolsStyleSheets = new WeakMap();
function forceStyle() {
let computedStyle = window.getComputedStyle(documentElement);
@ -19,6 +20,45 @@
documentElement.style.display = display; // Restore
}
/*
* Append a new processing instruction and return an object with
* - styleSheet: DOMNode
* - loadPromise: Promise that resolves once the sheets loads or errors
*/
function appendStyleSheet(url) {
let styleSheetAttr = `href="${url}" type="text/css"`;
let styleSheet = document.createProcessingInstruction(
"xml-stylesheet", styleSheetAttr);
let loadPromise = new Promise((resolve, reject) => {
function onload() {
styleSheet.removeEventListener("load", onload);
styleSheet.removeEventListener("error", onerror);
resolve();
}
function onerror() {
styleSheet.removeEventListener("load", onload);
styleSheet.removeEventListener("error", onerror);
reject("Failed to load theme file " + url);
}
styleSheet.addEventListener("load", onload);
styleSheet.addEventListener("error", onerror);
});
document.insertBefore(styleSheet, documentElement);
return {styleSheet, loadPromise};
}
/*
* Notify the window that a theme switch finished so tests can check the DOM
*/
function notifyWindow() {
window.dispatchEvent(new CustomEvent("theme-switch-complete", {}));
}
/*
* Apply all the sheets from `newTheme` and remove all of the sheets
* from `oldTheme`
*/
function switchTheme(newTheme, oldTheme) {
if (newTheme === oldTheme) {
return;
@ -28,8 +68,8 @@
// Unload all theme stylesheets related to the old theme.
if (oldThemeDef) {
for (let url of oldThemeDef.stylesheets) {
StylesheetUtils.removeSheet(window, url, "author");
for (let sheet of devtoolsStyleSheets.get(oldThemeDef) || []) {
sheet.remove();
}
}
@ -42,8 +82,17 @@
newThemeDef = gDevTools.getThemeDefinition("light");
}
// Store the sheets in a WeakMap for access later when the theme gets
// unapplied. It's hard to query for processing instructions so this
// is an easy way to access them later without storing a property on
// the window
devtoolsStyleSheets.set(newThemeDef, []);
let loadEvents = [];
for (let url of newThemeDef.stylesheets) {
StylesheetUtils.loadSheet(window, url, "author");
let {styleSheet,loadPromise} = appendStyleSheet(url);
devtoolsStyleSheets.get(newThemeDef).push(styleSheet);
loadEvents.push(loadPromise);
}
// Floating scroll-bars like in OSX
@ -53,21 +102,10 @@
// TODO: extensions might want to customize scrollbar styles too.
if (!hiddenDOMWindow.matchMedia("(-moz-overlay-scrollbars)").matches) {
let scrollbarsUrl = Services.io.newURI(
DEVTOOLS_SKIN_URL + "floating-scrollbars-light.css", null, null);
if (newTheme == "dark") {
StylesheetUtils.loadSheet(
window,
scrollbarsUrl,
"agent"
);
StylesheetUtils.loadSheet(window, SCROLLBARS_URL, "agent");
} else if (oldTheme == "dark") {
StylesheetUtils.removeSheet(
window,
scrollbarsUrl,
"agent"
);
StylesheetUtils.removeSheet(window, SCROLLBARS_URL, "agent");
}
forceStyle();
}
@ -92,6 +130,8 @@
// Final notification for further theme-switching related logic.
gDevTools.emit("theme-switched", window, newTheme, oldTheme);
Promise.all(loadEvents).then(notifyWindow, console.error.bind(console));
}
function handlePrefChange(event, data) {
@ -101,7 +141,6 @@
}
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://devtools/client/framework/gDevTools.jsm");
const {require} = Components.utils.import("resource://devtools/shared/Loader.jsm", {});

View File

@ -8,8 +8,8 @@ const cssAutoCompleter = require("devtools/client/sourceeditor/css-autocompleter
const { AutocompletePopup } = require("devtools/client/shared/autocomplete-popup");
const CM_TERN_SCRIPTS = [
"chrome://devtools/content/sourceeditor/codemirror/tern/tern.js",
"chrome://devtools/content/sourceeditor/codemirror/hint/show-hint.js"
"chrome://devtools/content/sourceeditor/codemirror/addon/tern/tern.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/hint/show-hint.js"
];
const autocompleteMap = new WeakMap();

View File

@ -47,35 +47,35 @@ const { OS } = Services.appinfo;
const CM_STYLES = [
"chrome://devtools/skin/common.css",
"chrome://devtools/content/sourceeditor/codemirror/codemirror.css",
"chrome://devtools/content/sourceeditor/codemirror/dialog/dialog.css",
"chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css",
"chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css",
"chrome://devtools/content/sourceeditor/codemirror/mozilla.css"
];
const CM_SCRIPTS = [
"chrome://devtools/content/shared/theme-switching.js",
"chrome://devtools/content/sourceeditor/codemirror/codemirror.js",
"chrome://devtools/content/sourceeditor/codemirror/dialog/dialog.js",
"chrome://devtools/content/sourceeditor/codemirror/search/searchcursor.js",
"chrome://devtools/content/sourceeditor/codemirror/search/search.js",
"chrome://devtools/content/sourceeditor/codemirror/edit/matchbrackets.js",
"chrome://devtools/content/sourceeditor/codemirror/edit/closebrackets.js",
"chrome://devtools/content/sourceeditor/codemirror/comment/comment.js",
"chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/search/searchcursor.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/search/search.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/edit/matchbrackets.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/edit/closebrackets.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/comment/comment.js",
"chrome://devtools/content/sourceeditor/codemirror/mode/javascript.js",
"chrome://devtools/content/sourceeditor/codemirror/mode/xml.js",
"chrome://devtools/content/sourceeditor/codemirror/mode/css.js",
"chrome://devtools/content/sourceeditor/codemirror/mode/htmlmixed.js",
"chrome://devtools/content/sourceeditor/codemirror/mode/clike.js",
"chrome://devtools/content/sourceeditor/codemirror/selection/active-line.js",
"chrome://devtools/content/sourceeditor/codemirror/edit/trailingspace.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/selection/active-line.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/edit/trailingspace.js",
"chrome://devtools/content/sourceeditor/codemirror/keymap/emacs.js",
"chrome://devtools/content/sourceeditor/codemirror/keymap/vim.js",
"chrome://devtools/content/sourceeditor/codemirror/keymap/sublime.js",
"chrome://devtools/content/sourceeditor/codemirror/fold/foldcode.js",
"chrome://devtools/content/sourceeditor/codemirror/fold/brace-fold.js",
"chrome://devtools/content/sourceeditor/codemirror/fold/comment-fold.js",
"chrome://devtools/content/sourceeditor/codemirror/fold/xml-fold.js",
"chrome://devtools/content/sourceeditor/codemirror/fold/foldgutter.js"
"chrome://devtools/content/sourceeditor/codemirror/addon/fold/foldcode.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/fold/brace-fold.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/fold/comment-fold.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/fold/xml-fold.js",
"chrome://devtools/content/sourceeditor/codemirror/addon/fold/foldgutter.js"
];
const CM_IFRAME =

View File

@ -3,15 +3,15 @@
<head>
<meta charset="utf-8">
<title>CodeMirror: Basic Tests</title>
<link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/codemirror.css">
<link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="cm_mode_test.css">
<!--<link rel="stylesheet" href="../doc/docs.css">-->
<script src="chrome://devtools/content/sourceeditor/codemirror/codemirror.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/search/searchcursor.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/dialog/dialog.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/edit/matchbrackets.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/comment/comment.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/addon/search/searchcursor.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/addon/edit/matchbrackets.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/addon/comment/comment.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/mode/javascript.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/keymap/vim.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/keymap/emacs.js"></script>

View File

@ -3,15 +3,15 @@
<head>
<meta charset="utf-8">
<title>CodeMirror: VIM/Emacs tests</title>
<link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/codemirror.css">
<link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="cm_mode_test.css">
<!--<link rel="stylesheet" href="../doc/docs.css">-->
<script src="chrome://devtools/content/sourceeditor/codemirror/codemirror.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/search/searchcursor.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/dialog/dialog.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/edit/matchbrackets.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/comment/comment.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/addon/search/searchcursor.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/addon/edit/matchbrackets.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/addon/comment/comment.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/mode/javascript.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/keymap/vim.js"></script>
<script src="chrome://devtools/content/sourceeditor/codemirror/keymap/sublime.js"></script>

View File

@ -290,7 +290,7 @@ body {
height: 100%;
box-sizing: border-box;
--timelime-border-color: var(--theme-body-color);
--timeline-border-color: var(--theme-body-color);
--timeline-background-color: var(--theme-splitter-color);
/* Iterations of the animation are displayed with a repeating linear-gradient
@ -299,25 +299,25 @@ body {
the border of this element */
background-image:
linear-gradient(to right,
var(--timelime-border-color) 0,
var(--timelime-border-color) 1px,
var(--timeline-border-color) 0,
var(--timeline-border-color) 1px,
transparent 1px,
transparent 2px);
background-repeat: repeat-x;
background-position: -1px 0;
border: 1px solid var(--timelime-border-color);
border: 1px solid var(--timeline-border-color);
/* The background color is set independently */
background-color: var(--timeline-background-color);
}
.animation-timeline .animation .cssanimation {
--timelime-border-color: var(--theme-highlight-lightorange);
--timeline-border-color: var(--theme-highlight-lightorange);
--timeline-background-color: var(--theme-contrast-background);
}
.animation-timeline .animation .csstransition {
--timelime-border-color: var(--theme-highlight-bluegrey);
--timeline-border-color: var(--theme-highlight-bluegrey);
--timeline-background-color: var(--theme-highlight-blue);
}
@ -369,14 +369,14 @@ body {
box-sizing: border-box;
height: calc(100% + 2px);
border: 1px solid var(--timelime-border-color);
border: 1px solid var(--timeline-border-color);
border-width: 1px 0 1px 1px;
background-image: repeating-linear-gradient(45deg,
transparent,
transparent 1px,
var(--theme-selection-color) 1px,
var(--theme-selection-color) 4px);
background-color: var(--timelime-border-color);
background-color: var(--timeline-border-color);
}
.animation-timeline .animation .delay.negative {

View File

@ -6,7 +6,7 @@
box-sizing: border-box;
}
.theme-sidebar body {
body.theme-sidebar {
/* The view will grow bigger as the window gets resized, until 400px */
max-width: 400px;
margin: 0px auto;

File diff suppressed because it is too large Load Diff

View File

@ -4,25 +4,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/devtools/DominatorTree.h"
#include "js/Debug.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/dom/DominatorTreeBinding.h"
namespace mozilla {
namespace devtools {
static MallocSizeOf
getCurrentThreadDebuggerMallocSizeOf()
{
auto ccrt = CycleCollectedJSRuntime::Get();
MOZ_ASSERT(ccrt);
auto rt = ccrt->Runtime();
MOZ_ASSERT(rt);
auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(rt);
MOZ_ASSERT(mallocSizeOf);
return mallocSizeOf;
}
dom::Nullable<uint64_t>
DominatorTree::GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv)
{
@ -31,7 +17,7 @@ DominatorTree::GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv)
if (node.isNothing())
return dom::Nullable<uint64_t>();
auto mallocSizeOf = getCurrentThreadDebuggerMallocSizeOf();
auto mallocSizeOf = GetCurrentThreadDebuggerMallocSizeOf();
JS::ubi::Node::Size size = 0;
if (!mDominatorTree.getRetainedSize(*node, mallocSizeOf, size)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
@ -83,7 +69,7 @@ DominatorTree::GetImmediatelyDominated(uint64_t aNodeId,
return;
// Get all immediately dominated nodes and their retained sizes.
MallocSizeOf mallocSizeOf = getCurrentThreadDebuggerMallocSizeOf();
MallocSizeOf mallocSizeOf = GetCurrentThreadDebuggerMallocSizeOf();
Maybe<JS::ubi::DominatorTree::DominatedSetRange> range = mDominatorTree.getDominatedSet(*node);
MOZ_ASSERT(range.isSome(), "The node should be known, since we got it from the heap snapshot.");
size_t length = range->length();

View File

@ -55,6 +55,18 @@ using ::google::protobuf::io::ZeroCopyInputStream;
using JS::ubi::AtomOrTwoByteChars;
MallocSizeOf
GetCurrentThreadDebuggerMallocSizeOf()
{
auto ccrt = CycleCollectedJSRuntime::Get();
MOZ_ASSERT(ccrt);
auto rt = ccrt->Runtime();
MOZ_ASSERT(rt);
auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(rt);
MOZ_ASSERT(mallocSizeOf);
return mallocSizeOf;
}
/*** Cycle Collection Boilerplate *****************************************************************/
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HeapSnapshot, mParent)
@ -482,7 +494,7 @@ HeapSnapshot::TakeCensus(JSContext* cx, JS::HandleObject options,
return;
}
JS::ubi::CensusHandler handler(census, rootCount);
JS::ubi::CensusHandler handler(census, rootCount, GetCurrentThreadDebuggerMallocSizeOf());
{
JS::AutoCheckCannotGC nogc;
@ -504,7 +516,7 @@ HeapSnapshot::TakeCensus(JSContext* cx, JS::HandleObject options,
}
}
if (NS_WARN_IF(!handler.report(rval))) {
if (NS_WARN_IF(!handler.report(cx, rval))) {
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}

View File

@ -221,6 +221,9 @@ WriteHeapGraph(JSContext* cx,
ignoreNodeCount, ignoreEdgeCount);
}
// Get the mozilla::MallocSizeOf for the current thread's JSRuntime.
MallocSizeOf GetCurrentThreadDebuggerMallocSizeOf();
} // namespace devtools
} // namespace mozilla

View File

@ -6,34 +6,34 @@
#include "FileReader.h"
#include "nsContentCID.h"
#include "nsContentUtils.h"
#include "nsDOMClassInfoID.h"
#include "nsError.h"
#include "nsIFile.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsIEventTarget.h"
#include "nsIGlobalObject.h"
#include "nsITimer.h"
#include "nsITransport.h"
#include "nsIStreamTransportService.h"
#include "nsXPCOM.h"
#include "nsIDOMEventListener.h"
#include "nsJSEnvironment.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Base64.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/EncodingUtils.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileReaderBinding.h"
#include "mozilla/dom/ProgressEvent.h"
#include "xpcpublic.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDOMJSUtils.h"
#include "nsError.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "xpcpublic.h"
#include "jsfriendapi.h"
#include "nsITransport.h"
#include "nsIStreamTransportService.h"
#include "WorkerPrivate.h"
#include "WorkerScope.h"
namespace mozilla {
namespace dom {
using namespace workers;
#define ABORT_STR "abort"
#define LOAD_STR "load"
#define LOADSTART_STR "loadstart"
@ -56,7 +56,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileReader,
DOMEventTargetHelper)
tmp->mResultArrayBuffer = nullptr;
tmp->Shutdown();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBlob)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
@ -76,6 +76,20 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(FileReader, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(FileReader, DOMEventTargetHelper)
class MOZ_RAII FileReaderDecreaseBusyCounter
{
RefPtr<FileReader> mFileReader;
public:
explicit FileReaderDecreaseBusyCounter(FileReader* aFileReader)
: mFileReader(aFileReader)
{}
~FileReaderDecreaseBusyCounter()
{
mFileReader->DecreaseBusyCounter();
}
};
void
FileReader::RootResultArrayBuffer()
{
@ -84,7 +98,8 @@ FileReader::RootResultArrayBuffer()
//FileReader constructors/initializers
FileReader::FileReader(nsPIDOMWindow* aWindow)
FileReader::FileReader(nsPIDOMWindow* aWindow,
WorkerPrivate* aWorkerPrivate)
: DOMEventTargetHelper(aWindow)
, mFileData(nullptr)
, mDataLen(0)
@ -95,14 +110,18 @@ FileReader::FileReader(nsPIDOMWindow* aWindow)
, mReadyState(EMPTY)
, mTotal(0)
, mTransferred(0)
, mTarget(do_GetCurrentThread())
, mBusyCount(0)
, mWorkerPrivate(aWorkerPrivate)
{
MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerPrivate && !aWindow);
MOZ_ASSERT_IF(NS_IsMainThread(), !mWorkerPrivate);
SetDOMStringToNull(mResult);
}
FileReader::~FileReader()
{
FreeFileData();
mResultArrayBuffer = nullptr;
Shutdown();
DropJSObjects(this);
}
@ -111,9 +130,17 @@ FileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
{
// The owner can be null when this object is used by chrome code.
nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aGlobal.GetAsSupports());
RefPtr<FileReader> fileReader = new FileReader(owner);
WorkerPrivate* workerPrivate = nullptr;
if (!owner && nsContentUtils::IsCallerChrome()) {
if (!NS_IsMainThread()) {
JSContext* cx = aGlobal.Context();
workerPrivate = GetWorkerPrivateFromContext(cx);
MOZ_ASSERT(workerPrivate);
}
RefPtr<FileReader> fileReader = new FileReader(owner, workerPrivate);
if (!owner && nsContentUtils::ThreadsafeIsCallerChrome()) {
// Instead of grabbing some random global from the context stack,
// let's use the default one (junk scope) for now.
// We should move away from this Init...
@ -215,7 +242,17 @@ FileReader::DoOnLoadEnd(nsresult aStatus,
switch (mDataFormat) {
case FILE_AS_ARRAYBUFFER: {
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(DOMEventTargetHelper::GetParentObject()))) {
nsCOMPtr<nsIGlobalObject> globalObject;
if (NS_IsMainThread()) {
globalObject = do_QueryInterface(GetParentObject());
} else {
MOZ_ASSERT(mWorkerPrivate);
MOZ_ASSERT(mBusyCount);
globalObject = mWorkerPrivate->GlobalScope();
}
if (!globalObject || !jsapi.Init(globalObject)) {
FreeFileData();
return NS_ERROR_FAILURE;
}
@ -256,9 +293,29 @@ FileReader::DoOnLoadEnd(nsresult aStatus,
}
nsresult
FileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
FileReader::DoAsyncWait()
{
MOZ_ASSERT(aStream);
nsresult rv = IncreaseBusyCounter();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mAsyncStream->AsyncWait(this,
/* aFlags*/ 0,
/* aRequestedCount */ 0,
mTarget);
if (NS_WARN_IF(NS_FAILED(rv))) {
DecreaseBusyCounter();
return rv;
}
return NS_OK;
}
nsresult
FileReader::DoReadData(uint64_t aCount)
{
MOZ_ASSERT(mAsyncStream);
if (mDataFormat == FILE_AS_BINARY) {
//Continuously update our binary string as data comes in
@ -271,7 +328,7 @@ FileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
uint32_t bytesRead = 0;
aStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
mAsyncStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
&bytesRead);
NS_ASSERTION(bytesRead == aCount, "failed to read data");
}
@ -287,7 +344,7 @@ FileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
}
uint32_t bytesRead = 0;
aStream->Read(mFileData + mDataLen, aCount, &bytesRead);
mAsyncStream->Read(mFileData + mDataLen, aCount, &bytesRead);
NS_ASSERTION(bytesRead == aCount, "failed to read data");
}
@ -361,10 +418,7 @@ FileReader::ReadFileContent(Blob& aBlob,
return;
}
aRv = mAsyncStream->AsyncWait(this,
/* aFlags*/ 0,
/* aRequestedCount */ 0,
NS_GetCurrentThread());
aRv = DoAsyncWait();
if (NS_WARN_IF(aRv.Failed())) {
return;
}
@ -467,6 +521,7 @@ FileReader::StartProgressEventTimer()
mProgressEventWasDelayed = false;
mTimerIsActive = true;
mProgressNotifier->Cancel();
mProgressNotifier->SetTarget(mTarget);
mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
nsITimer::TYPE_ONE_SHOT);
}
@ -550,18 +605,20 @@ FileReader::OnInputStreamReady(nsIAsyncInputStream* aStream)
return NS_OK;
}
// We use this class to decrease the busy counter at the end of this method.
// In theory we can do it immediatelly but, for debugging reasons, we want to
// be 100% sure we have a feature when OnLoadEnd() is called.
FileReaderDecreaseBusyCounter RAII(this);
uint64_t aCount;
nsresult rv = aStream->Available(&aCount);
if (NS_SUCCEEDED(rv) && aCount) {
rv = DoReadData(aStream, aCount);
rv = DoReadData(aCount);
}
if (NS_SUCCEEDED(rv)) {
rv = aStream->AsyncWait(this,
/* aFlags*/ 0,
/* aRequestedCount */ 0,
NS_GetCurrentThread());
rv = DoAsyncWait();
}
if (NS_FAILED(rv) || !aCount) {
@ -643,5 +700,56 @@ FileReader::Abort(ErrorResult& aRv)
DispatchProgressEvent(NS_LITERAL_STRING(LOADEND_STR));
}
nsresult
FileReader::IncreaseBusyCounter()
{
if (mWorkerPrivate && mBusyCount++ == 0 &&
!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
void
FileReader::DecreaseBusyCounter()
{
MOZ_ASSERT_IF(mWorkerPrivate, mBusyCount);
if (mWorkerPrivate && --mBusyCount == 0) {
mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
}
}
bool
FileReader::Notify(JSContext* aCx, Status aStatus)
{
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
if (aStatus > Running) {
Shutdown();
}
return true;
}
void
FileReader::Shutdown()
{
FreeFileData();
mResultArrayBuffer = nullptr;
if (mAsyncStream) {
mAsyncStream->Close();
mAsyncStream = nullptr;
}
if (mWorkerPrivate && mBusyCount != 0) {
mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
mWorkerPrivate = nullptr;
mBusyCount = 0;
}
}
} // dom namespace
} // mozilla namespace

View File

@ -9,37 +9,45 @@
#include "mozilla/Attributes.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/DOMError.h"
#include "nsCOMPtr.h"
#include "nsIAsyncInputStream.h"
#include "nsIStreamListener.h"
#include "nsISupportsUtils.h"
#include "nsIInterfaceRequestor.h"
#include "nsITimer.h"
#include "nsJSUtils.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsWeakReference.h"
#include "prtime.h"
#include "WorkerFeature.h"
#define NS_PROGRESS_EVENT_INTERVAL 50
class nsITimer;
class nsIEventTarget;
namespace mozilla {
namespace dom {
class Blob;
class DOMError;
namespace workers {
class WorkerPrivate;
}
extern const uint64_t kUnknownSize;
class FileReaderDecreaseBusyCounter;
class FileReader final : public DOMEventTargetHelper,
public nsIInterfaceRequestor,
public nsSupportsWeakReference,
public nsIInputStreamCallback,
public nsITimerCallback
public nsITimerCallback,
public workers::WorkerFeature
{
friend class FileReaderDecreaseBusyCounter;
public:
explicit FileReader(nsPIDOMWindow* aWindow);
FileReader(nsPIDOMWindow* aWindow,
workers::WorkerPrivate* aWorkerPrivate);
NS_DECL_ISUPPORTS_INHERITED
@ -47,9 +55,11 @@ public:
NS_DECL_NSIINPUTSTREAMCALLBACK
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(FileReader, DOMEventTargetHelper)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(FileReader,
DOMEventTargetHelper)
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
// WebIDL
static already_AddRefed<FileReader>
@ -96,6 +106,9 @@ public:
ReadFileContent(aBlob, EmptyString(), FILE_AS_BINARY, aRv);
}
// WorkerFeature
bool Notify(JSContext* aCx, workers::Status) override;
private:
virtual ~FileReader();
@ -131,7 +144,8 @@ private:
void DispatchError(nsresult rv, nsAString& finalEvent);
nsresult DispatchProgressEvent(const nsAString& aType);
nsresult DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount);
nsresult DoAsyncWait();
nsresult DoReadData(uint64_t aCount);
nsresult DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent,
nsAString& aTerminationEvent);
@ -143,6 +157,11 @@ private:
mDataLen = 0;
}
nsresult IncreaseBusyCounter();
void DecreaseBusyCounter();
void Shutdown();
char *mFileData;
RefPtr<Blob> mBlob;
nsCString mCharset;
@ -166,6 +185,13 @@ private:
uint64_t mTotal;
uint64_t mTransferred;
nsCOMPtr<nsIEventTarget> mTarget;
uint64_t mBusyCount;
// Kept alive with a WorkerFeature.
workers::WorkerPrivate* mWorkerPrivate;
};
} // dom namespace

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ImageEncoder.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
@ -13,7 +14,10 @@
#include "mozilla/SyncRunnable.h"
#include "mozilla/unused.h"
#include "gfxUtils.h"
#include "nsIThreadPool.h"
#include "nsNetUtil.h"
#include "nsXPCOMCIDInternal.h"
#include "WorkerPrivate.h"
using namespace mozilla::gfx;
@ -69,27 +73,28 @@ GetBRGADataSourceSurfaceSync(already_AddRefed<layers::Image> aImage)
return helper->GetDataSurfaceSafe();
}
class EncodingCompleteEvent : public nsRunnable
class EncodingCompleteEvent : public nsCancelableRunnable
{
virtual ~EncodingCompleteEvent() {}
public:
NS_DECL_ISUPPORTS_INHERITED
EncodingCompleteEvent(nsIThread* aEncoderThread,
EncodeCompleteCallback* aEncodeCompleteCallback)
explicit EncodingCompleteEvent(EncodeCompleteCallback* aEncodeCompleteCallback)
: mImgSize(0)
, mType()
, mImgData(nullptr)
, mEncoderThread(aEncoderThread)
, mEncodeCompleteCallback(aEncodeCompleteCallback)
, mFailed(false)
{}
{
if (!NS_IsMainThread() && workers::GetCurrentThreadWorkerPrivate()) {
mCreationThread = NS_GetCurrentThread();
} else {
NS_GetMainThread(getter_AddRefs(mCreationThread));
}
}
NS_IMETHOD Run() override
{
nsresult rv = NS_OK;
MOZ_ASSERT(NS_IsMainThread());
if (!mFailed) {
// The correct parentObject has to be set by the mEncodeCompleteCallback.
@ -102,7 +107,6 @@ public:
mEncodeCompleteCallback = nullptr;
mEncoderThread->Shutdown();
return rv;
}
@ -118,17 +122,20 @@ public:
mFailed = true;
}
nsIThread* GetCreationThread()
{
return mCreationThread;
}
private:
uint64_t mImgSize;
nsAutoString mType;
void* mImgData;
nsCOMPtr<nsIThread> mEncoderThread;
nsCOMPtr<nsIThread> mCreationThread;
RefPtr<EncodeCompleteCallback> mEncodeCompleteCallback;
bool mFailed;
};
NS_IMPL_ISUPPORTS_INHERITED0(EncodingCompleteEvent, nsRunnable);
class EncodingRunnable : public nsRunnable
{
virtual ~EncodingRunnable() {}
@ -207,7 +214,8 @@ public:
} else {
mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
}
rv = NS_DispatchToMainThread(mEncodingCompleteEvent);
rv = mEncodingCompleteEvent->GetCreationThread()->
Dispatch(mEncodingCompleteEvent, nsIThread::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
// Better to leak than to crash.
Unused << mEncodingCompleteEvent.forget();
@ -231,6 +239,8 @@ private:
NS_IMPL_ISUPPORTS_INHERITED0(EncodingRunnable, nsRunnable);
StaticRefPtr<nsIThreadPool> ImageEncoder::sThreadPool;
/* static */
nsresult
ImageEncoder::ExtractData(nsAString& aType,
@ -262,12 +272,13 @@ ImageEncoder::ExtractDataFromLayersImageAsync(nsAString& aType,
return NS_IMAGELIB_ERROR_NO_ENCODER;
}
nsCOMPtr<nsIThread> encoderThread;
nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv = EnsureThreadPool();
if (NS_FAILED(rv)) {
return rv;
}
RefPtr<EncodingCompleteEvent> completeEvent =
new EncodingCompleteEvent(encoderThread, aEncodeCallback);
new EncodingCompleteEvent(aEncodeCallback);
nsIntSize size(aImage->GetSize().width, aImage->GetSize().height);
nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
@ -279,7 +290,7 @@ ImageEncoder::ExtractDataFromLayersImageAsync(nsAString& aType,
imgIEncoder::INPUT_FORMAT_HOSTARGB,
size,
aUsingCustomOptions);
return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
return sThreadPool->Dispatch(event, NS_DISPATCH_NORMAL);
}
/* static */
@ -297,12 +308,13 @@ ImageEncoder::ExtractDataAsync(nsAString& aType,
return NS_IMAGELIB_ERROR_NO_ENCODER;
}
nsCOMPtr<nsIThread> encoderThread;
nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv = EnsureThreadPool();
if (NS_FAILED(rv)) {
return rv;
}
RefPtr<EncodingCompleteEvent> completeEvent =
new EncodingCompleteEvent(encoderThread, aEncodeCallback);
new EncodingCompleteEvent(aEncodeCallback);
nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
aOptions,
@ -313,7 +325,7 @@ ImageEncoder::ExtractDataAsync(nsAString& aType,
aFormat,
aSize,
aUsingCustomOptions);
return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
return sThreadPool->Dispatch(event, NS_DISPATCH_NORMAL);
}
/*static*/ nsresult
@ -479,5 +491,48 @@ ImageEncoder::GetImageEncoder(nsAString& aType)
return encoder.forget();
}
/* static */
nsresult
ImageEncoder::EnsureThreadPool()
{
if (!sThreadPool) {
nsCOMPtr<nsIThreadPool> threadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
sThreadPool = threadPool;
if (!NS_IsMainThread()) {
NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
ClearOnShutdown(&sThreadPool);
}));
} else {
ClearOnShutdown(&sThreadPool);
}
const uint32_t kThreadLimit = 2;
const uint32_t kIdleThreadLimit = 1;
const uint32_t kIdleThreadTimeoutMs = 30000;
nsresult rv = sThreadPool->SetName(NS_LITERAL_CSTRING("EncodingRunnable"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = sThreadPool->SetThreadLimit(kThreadLimit);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = sThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = sThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -16,6 +16,7 @@
#include "nsSize.h"
class nsICanvasRenderingContextInternal;
class nsIThreadPool;
namespace mozilla {
@ -107,6 +108,11 @@ private:
// undefined in this case.
static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
static nsresult EnsureThreadPool();
// Thread pool for dispatching EncodingRunnable.
static StaticRefPtr<nsIThreadPool> sThreadPool;
friend class EncodingRunnable;
};

View File

@ -28,6 +28,7 @@
#include "mozilla/dom/SubtleCryptoBinding.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/WebCryptoCommon.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
@ -306,7 +307,7 @@ StructuredCloneHolder::Read(nsISupports* aParent,
// If we are tranferring something, we cannot call 'Read()' more than once.
if (mSupportsTransferring) {
mBlobImplArray.Clear();
mClonedImages.Clear();
mClonedSurfaces.Clear();
Clear();
}
}
@ -976,7 +977,7 @@ StructuredCloneHolder::CustomReadHandler(JSContext* aCx,
nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
// aIndex is the index of the cloned image.
return ImageBitmap::ReadStructuredClone(aCx, aReader,
parent, GetImages(), aIndex);
parent, GetSurfaces(), aIndex);
}
return ReadFullySerializableObjects(aCx, aReader, aTag);
@ -1021,7 +1022,7 @@ StructuredCloneHolder::CustomWriteHandler(JSContext* aCx,
ImageBitmap* imageBitmap = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
return ImageBitmap::WriteStructuredClone(aWriter,
GetImages(),
GetSurfaces(),
imageBitmap);
}
}
@ -1072,7 +1073,8 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
MOZ_ASSERT(aContent);
OffscreenCanvasCloneData* data =
static_cast<OffscreenCanvasCloneData*>(aContent);
RefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(data);
nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
RefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(parent, data);
delete data;
JS::Rooted<JS::Value> value(aCx);
@ -1085,6 +1087,26 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
return true;
}
if (aTag == SCTAG_DOM_IMAGEBITMAP) {
MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
mSupportedContext == SameProcessDifferentThread);
MOZ_ASSERT(aContent);
ImageBitmapCloneData* data =
static_cast<ImageBitmapCloneData*>(aContent);
nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
RefPtr<ImageBitmap> bitmap = ImageBitmap::CreateFromCloneData(parent, data);
delete data;
JS::Rooted<JS::Value> value(aCx);
if (!GetOrCreateDOMReflector(aCx, bitmap, &value)) {
JS_ClearPendingException(aCx);
return false;
}
aReturnObject.set(&value.toObject());
return true;
}
return false;
}
@ -1133,6 +1155,21 @@ StructuredCloneHolder::CustomWriteTransferHandler(JSContext* aCx,
return true;
}
ImageBitmap* bitmap = nullptr;
rv = UNWRAP_OBJECT(ImageBitmap, aObj, bitmap);
if (NS_SUCCEEDED(rv)) {
MOZ_ASSERT(bitmap);
*aExtraData = 0;
*aTag = SCTAG_DOM_IMAGEBITMAP;
*aOwnership = JS::SCTAG_TMO_CUSTOM;
*aContent = bitmap->ToCloneData();
MOZ_ASSERT(*aContent);
bitmap->Close();
return true;
}
}
}
@ -1163,6 +1200,16 @@ StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag,
delete data;
return;
}
if (aTag == SCTAG_DOM_IMAGEBITMAP) {
MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
mSupportedContext == SameProcessDifferentThread);
MOZ_ASSERT(aContent);
ImageBitmapCloneData* data =
static_cast<ImageBitmapCloneData*>(aContent);
delete data;
return;
}
}
} // dom namespace

View File

@ -22,6 +22,10 @@ namespace layers {
class Image;
}
namespace gfx {
class DataSourceSurface;
}
namespace dom {
class StructuredCloneHolderBase
@ -181,7 +185,7 @@ public:
bool HasClonedDOMObjects() const
{
return !mBlobImplArray.IsEmpty() ||
!mClonedImages.IsEmpty();
!mClonedSurfaces.IsEmpty();
}
nsTArray<RefPtr<BlobImpl>>& BlobImpls()
@ -212,9 +216,9 @@ public:
return mPortIdentifiers;
}
nsTArray<RefPtr<layers::Image>>& GetImages()
nsTArray<RefPtr<gfx::DataSourceSurface>>& GetSurfaces()
{
return mClonedImages;
return mClonedSurfaces;
}
// Implementations of the virtual methods to allow cloning of objects which
@ -291,10 +295,10 @@ protected:
nsTArray<RefPtr<BlobImpl>> mBlobImplArray;
// This is used for sharing the backend of ImageBitmaps.
// The layers::Image object must be thread-safely reference-counted.
// The layers::Image object will not be written ever via any ImageBitmap
// The DataSourceSurface object must be thread-safely reference-counted.
// The DataSourceSurface object will not be written ever via any ImageBitmap
// instance, so no race condition will occur.
nsTArray<RefPtr<layers::Image>> mClonedImages;
nsTArray<RefPtr<gfx::DataSourceSurface>> mClonedSurfaces;
// This raw pointer is only set within ::Read() and is unset by the end.
nsISupports* MOZ_NON_OWNING_REF mParent;

View File

@ -6775,66 +6775,6 @@ nsContentUtils::HaveEqualPrincipals(nsIDocument* aDoc1, nsIDocument* aDoc2)
return principalsEqual;
}
static void
CheckForWindowedPlugins(nsISupports* aSupports, void* aResult)
{
nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
if (!content || !content->IsInDoc()) {
return;
}
nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(content));
if (!olc) {
return;
}
RefPtr<nsNPAPIPluginInstance> plugin;
olc->GetPluginInstance(getter_AddRefs(plugin));
if (!plugin) {
return;
}
bool isWindowless = false;
nsresult res = plugin->IsWindowless(&isWindowless);
if (NS_SUCCEEDED(res) && !isWindowless) {
*static_cast<bool*>(aResult) = true;
}
}
static bool
DocTreeContainsWindowedPlugins(nsIDocument* aDoc, void* aResult)
{
if (!nsContentUtils::IsChromeDoc(aDoc)) {
aDoc->EnumerateActivityObservers(CheckForWindowedPlugins, aResult);
}
if (*static_cast<bool*>(aResult)) {
// Return false to stop iteration, we found a windowed plugin.
return false;
}
aDoc->EnumerateSubDocuments(DocTreeContainsWindowedPlugins, aResult);
// Return false to stop iteration if we found a windowed plugin in
// the sub documents.
return !*static_cast<bool*>(aResult);
}
/* static */
bool
nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc)
{
#ifdef XP_MACOSX
// We control dispatch to all mac plugins.
return false;
#endif
bool result = false;
// Find the top of the document's branch, the child of the chrome document.
nsIDocument* doc = aDoc;
nsIDocument* parent = nullptr;
while (doc && (parent = doc->GetParentDocument()) && !IsChromeDoc(parent)) {
doc = parent;
}
DocTreeContainsWindowedPlugins(doc, &result);
return result;
}
/* static */
bool
nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIContent* aContent)
@ -6842,10 +6782,30 @@ nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIContent* aContent)
#ifdef XP_MACOSX
// We control dispatch to all mac plugins.
return false;
#else
if (!aContent || !aContent->IsInDoc()) {
return false;
}
nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(aContent);
if (!olc) {
return false;
}
RefPtr<nsNPAPIPluginInstance> plugin;
olc->GetPluginInstance(getter_AddRefs(plugin));
if (!plugin) {
return false;
}
bool isWindowless = false;
nsresult res = plugin->IsWindowless(&isWindowless);
if (NS_FAILED(res)) {
return false;
}
return !isWindowless;
#endif
bool result = false;
CheckForWindowedPlugins(aContent, &result);
return result;
}
/* static */

View File

@ -2059,14 +2059,6 @@ public:
return sPrivacyResistFingerprinting;
}
/**
* Returns true if the doc tree branch which contains aDoc contains any
* plugins which we don't control event dispatch for, i.e. do any plugins
* in the same tab as this document receive key events outside of our
* control? This always returns false on MacOSX.
*/
static bool HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc);
/**
* Return true if this doc is controlled by a ServiceWorker.
*/

View File

@ -7668,7 +7668,6 @@ nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
false, // aDoJSFixups
true, // aNavigate
nullptr, nullptr, // No args
GetPrincipal(), // aCalleePrincipal
nullptr, // aJSCallerContext
getter_AddRefs(window));
if (NS_SUCCEEDED(rv) && window) {
@ -7689,7 +7688,6 @@ nsGlobalWindow::OpenJS(const nsAString& aUrl, const nsAString& aName,
true, // aDoJSFixups
true, // aNavigate
nullptr, nullptr, // No args
GetPrincipal(), // aCalleePrincipal
nsContentUtils::GetCurrentJSContext(), // aJSCallerContext
_retval);
}
@ -7709,7 +7707,6 @@ nsGlobalWindow::OpenDialog(const nsAString& aUrl, const nsAString& aName,
false, // aDoJSFixups
true, // aNavigate
nullptr, aExtraArgument, // Arguments
GetPrincipal(), // aCalleePrincipal
nullptr, // aJSCallerContext
_retval);
}
@ -7729,7 +7726,6 @@ nsGlobalWindow::OpenNoNavigate(const nsAString& aUrl,
false, // aDoJSFixups
false, // aNavigate
nullptr, nullptr, // No args
GetPrincipal(), // aCalleePrincipal
nullptr, // aJSCallerContext
_retval);
@ -7759,7 +7755,6 @@ nsGlobalWindow::OpenDialogOuter(JSContext* aCx, const nsAString& aUrl,
false, // aDoJSFixups
true, // aNavigate
argvArray, nullptr, // Arguments
GetPrincipal(), // aCalleePrincipal
aCx, // aJSCallerContext
getter_AddRefs(dialog));
return dialog.forget();
@ -8839,7 +8834,6 @@ nsGlobalWindow::ShowModalDialogOuter(const nsAString& aUrl, nsIVariant* aArgumen
true, // aDoJSFixups
true, // aNavigate
nullptr, argHolder, // args
GetPrincipal(), // aCalleePrincipal
nullptr, // aJSCallerContext
getter_AddRefs(dlgWin));
nsContentUtils::SetMicroTaskLevel(oldMicroTaskLevel);
@ -11270,7 +11264,6 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
bool aDoJSFixups, bool aNavigate,
nsIArray *argv,
nsISupports *aExtraArgument,
nsIPrincipal *aCalleePrincipal,
JSContext *aJSCallerContext,
nsIDOMWindow **aReturn)
{

View File

@ -1392,7 +1392,6 @@ private:
bool aNavigate,
nsIArray *argv,
nsISupports *aExtraArgument,
nsIPrincipal *aCalleePrincipal,
JSContext *aJSCallerContext,
nsIDOMWindow **aReturn);

View File

@ -5219,19 +5219,17 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
IntRect dstWriteRect = srcReadRect;
dstWriteRect.MoveBy(-aX, -aY);
uint8_t* src;
uint32_t srcStride;
if (readback) {
srcStride = rawData.mStride;
src = rawData.mData + srcReadRect.y * srcStride + srcReadRect.x * 4;
}
JS::AutoCheckCannotGC nogc;
bool isShared;
uint8_t* data = JS_GetUint8ClampedArrayData(darray, &isShared, nogc);
MOZ_ASSERT(!isShared); // Should not happen, data was created above
if (!readback) {
uint8_t* src;
uint32_t srcStride;
if (readback) {
srcStride = rawData.mStride;
src = rawData.mData + srcReadRect.y * srcStride + srcReadRect.x * 4;
} else {
src = data;
srcStride = aWidth * 4;
}
@ -5579,9 +5577,9 @@ CanvasRenderingContext2D::GetBufferProvider(LayerManager* aManager)
return mBufferProvider;
}
already_AddRefed<CanvasLayer>
already_AddRefed<Layer>
CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
Layer *aOldLayer,
LayerManager *aManager)
{
if (mOpaque || mIsSkiaGL) {
@ -5624,8 +5622,10 @@ CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
data.mBufferProvider = provider;
}
if (userData && userData->IsForContext(this) && aOldLayer->IsDataValid(data)) {
RefPtr<CanvasLayer> ret = aOldLayer;
if (userData &&
userData->IsForContext(this) &&
static_cast<CanvasLayer*>(aOldLayer)->IsDataValid(data)) {
RefPtr<Layer> ret = aOldLayer;
return ret.forget();
}
}

View File

@ -459,8 +459,8 @@ public:
bool GetIsOpaque() override { return mOpaque; }
NS_IMETHOD Reset() override;
mozilla::layers::PersistentBufferProvider* GetBufferProvider(mozilla::layers::LayerManager* aManager);
already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer *aOldLayer,
LayerManager *aManager) override;
virtual bool ShouldForceInactiveLayer(LayerManager *aManager) override;
void MarkContextClean() override;

View File

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CanvasRenderingContextHelper.h"
#include "ImageBitmapRenderingContext.h"
#include "ImageEncoder.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/Telemetry.h"
@ -26,36 +27,6 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
JS::Handle<JS::Value> aParams,
ErrorResult& aRv)
{
nsAutoString type;
nsContentUtils::ASCIIToLower(aType, type);
nsAutoString params;
bool usingCustomParseOptions;
aRv = ParseParams(aCx, type, aParams, params, &usingCustomParseOptions);
if (aRv.Failed()) {
return;
}
if (mCurrentContext) {
// We disallow canvases of width or height zero, and set them to 1, so
// we will have a discrepancy with the sizes of the canvas and the context.
// That discrepancy is OK, the rest are not.
nsIntSize elementSize = GetWidthHeight();
if ((elementSize.width != mCurrentContext->GetWidth() &&
(elementSize.width != 0 || mCurrentContext->GetWidth() != 1)) ||
(elementSize.height != mCurrentContext->GetHeight() &&
(elementSize.height != 0 || mCurrentContext->GetHeight() != 1))) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
}
UniquePtr<uint8_t[]> imageBuffer;
int32_t format = 0;
if (mCurrentContext) {
imageBuffer = mCurrentContext->GetImageBuffer(&format);
}
// Encoder callback when encoding is complete.
class EncodeCallback : public EncodeCompleteCallback
{
@ -97,6 +68,49 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
RefPtr<EncodeCompleteCallback> callback =
new EncodeCallback(aGlobal, &aCallback);
ToBlob(aCx, aGlobal, callback, aType, aParams, aRv);
}
void
CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
nsIGlobalObject* aGlobal,
EncodeCompleteCallback* aCallback,
const nsAString& aType,
JS::Handle<JS::Value> aParams,
ErrorResult& aRv)
{
nsAutoString type;
nsContentUtils::ASCIIToLower(aType, type);
nsAutoString params;
bool usingCustomParseOptions;
aRv = ParseParams(aCx, type, aParams, params, &usingCustomParseOptions);
if (aRv.Failed()) {
return;
}
if (mCurrentContext) {
// We disallow canvases of width or height zero, and set them to 1, so
// we will have a discrepancy with the sizes of the canvas and the context.
// That discrepancy is OK, the rest are not.
nsIntSize elementSize = GetWidthHeight();
if ((elementSize.width != mCurrentContext->GetWidth() &&
(elementSize.width != 0 || mCurrentContext->GetWidth() != 1)) ||
(elementSize.height != mCurrentContext->GetHeight() &&
(elementSize.height != 0 || mCurrentContext->GetHeight() != 1))) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
}
UniquePtr<uint8_t[]> imageBuffer;
int32_t format = 0;
if (mCurrentContext) {
imageBuffer = mCurrentContext->GetImageBuffer(&format);
}
RefPtr<EncodeCompleteCallback> callback = aCallback;
aRv = ImageEncoder::ExtractDataAsync(type,
params,
usingCustomParseOptions,
@ -138,6 +152,11 @@ CanvasRenderingContextHelper::CreateContext(CanvasContextType aContextType)
return nullptr;
break;
case CanvasContextType::ImageBitmap:
ret = new ImageBitmapRenderingContext();
break;
}
MOZ_ASSERT(ret);

View File

@ -18,13 +18,15 @@ class ErrorResult;
namespace dom {
class EncodeCompleteCallback;
class FileCallback;
enum class CanvasContextType : uint8_t {
NoContext,
Canvas2D,
WebGL1,
WebGL2
WebGL2,
ImageBitmap
};
/**
@ -57,6 +59,10 @@ protected:
const nsAString& aType, JS::Handle<JS::Value> aParams,
ErrorResult& aRv);
void ToBlob(JSContext* aCx, nsIGlobalObject* aGlobal, EncodeCompleteCallback* aCallback,
const nsAString& aType, JS::Handle<JS::Value> aParams,
ErrorResult& aRv);
virtual already_AddRefed<nsICanvasRenderingContextInternal>
CreateContext(CanvasContextType aContextType);

View File

@ -61,6 +61,11 @@ GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_typ
}
}
if (str.EqualsLiteral("bitmaprenderer")) {
*out_type = dom::CanvasContextType::ImageBitmap;
return true;
}
return false;
}

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/ImageBitmap.h"
#include "mozilla/dom/ImageBitmapBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/StructuredCloneTags.h"
@ -145,15 +146,14 @@ CropAndCopyDataSourceSurface(DataSourceSurface* aSurface, const IntRect& aCropRe
}
/*
* Encapsulate the given _aSurface_ into a layers::CairoImage.
* Encapsulate the given _aSurface_ into a layers::SourceSurfaceImage.
*/
static already_AddRefed<layers::Image>
CreateImageFromSurface(SourceSurface* aSurface)
{
MOZ_ASSERT(aSurface);
RefPtr<layers::CairoImage> image =
new layers::CairoImage(aSurface->GetSize(), aSurface);
RefPtr<layers::SourceSurfaceImage> image =
new layers::SourceSurfaceImage(aSurface->GetSize(), aSurface);
return image.forget();
}
@ -250,13 +250,13 @@ CreateImageFromRawData(const gfx::IntSize& aSize,
/*
* This is a synchronous task.
* This class is used to create a layers::CairoImage from raw data in the main
* This class is used to create a layers::SourceSurfaceImage from raw data in the main
* thread. While creating an ImageBitmap from an ImageData, we need to create
* a SouceSurface from the ImageData's raw data and then set the SourceSurface
* into a layers::CairoImage. However, the layers::CairoImage asserts the
* into a layers::SourceSurfaceImage. However, the layers::SourceSurfaceImage asserts the
* setting operation in the main thread, so if we are going to create an
* ImageBitmap from an ImageData off the main thread, we post an event to the
* main thread to create a layers::CairoImage from an ImageData's raw data.
* main thread to create a layers::SourceSurfaceImage from an ImageData's raw data.
*/
class CreateImageFromRawDataInMainThreadSyncTask final :
public WorkerMainThreadRunnable
@ -408,6 +408,14 @@ ImageBitmap::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return ImageBitmapBinding::Wrap(aCx, this, aGivenProto);
}
void
ImageBitmap::Close()
{
mData = nullptr;
mSurface = nullptr;
mPictureRect.SetEmpty();
}
void
ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv)
{
@ -419,6 +427,10 @@ ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
{
MOZ_ASSERT(aTarget);
if (!mData) {
return nullptr;
}
if (!mSurface) {
mSurface = mData->GetAsSourceSurface();
}
@ -493,6 +505,68 @@ ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
return surface.forget();
}
already_AddRefed<layers::Image>
ImageBitmap::TransferAsImage()
{
RefPtr<layers::Image> image = mData;
Close();
return image.forget();
}
ImageBitmapCloneData*
ImageBitmap::ToCloneData()
{
ImageBitmapCloneData* result = new ImageBitmapCloneData();
result->mPictureRect = mPictureRect;
RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
result->mSurface = surface->GetDataSurface();
MOZ_ASSERT(result->mSurface);
return result;
}
/* static */ already_AddRefed<ImageBitmap>
ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
ImageBitmapCloneData* aData)
{
RefPtr<layers::Image> data =
CreateImageFromSurface(aData->mSurface);
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
ErrorResult rv;
ret->SetPictureRect(aData->mPictureRect, rv);
return ret.forget();
}
/* static */ already_AddRefed<ImageBitmap>
ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
OffscreenCanvas& aOffscreenCanvas,
ErrorResult& aRv)
{
// Check origin-clean.
if (aOffscreenCanvas.IsWriteOnly()) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
nsLayoutUtils::SurfaceFromElementResult res =
nsLayoutUtils::SurfaceFromOffscreenCanvas(&aOffscreenCanvas,
nsLayoutUtils::SFE_WANT_FIRST_FRAME);
RefPtr<SourceSurface> surface = res.GetSourceSurface();
if (NS_WARN_IF(!surface)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
RefPtr<layers::Image> data =
CreateImageFromSurface(surface);
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
return ret.forget();
}
/* static */ already_AddRefed<ImageBitmap>
ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl,
const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
@ -1154,7 +1228,7 @@ ImageBitmap::Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
ImageBitmap::ReadStructuredClone(JSContext* aCx,
JSStructuredCloneReader* aReader,
nsIGlobalObject* aParent,
const nsTArray<RefPtr<layers::Image>>& aClonedImages,
const nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
uint32_t aIndex)
{
MOZ_ASSERT(aCx);
@ -1177,8 +1251,8 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx,
int32_t picRectHeight = BitwiseCast<int32_t>(picRectHeight_);
// Create a new ImageBitmap.
MOZ_ASSERT(!aClonedImages.IsEmpty());
MOZ_ASSERT(aIndex < aClonedImages.Length());
MOZ_ASSERT(!aClonedSurfaces.IsEmpty());
MOZ_ASSERT(aIndex < aClonedSurfaces.Length());
// RefPtr<ImageBitmap> needs to go out of scope before toObjectOrNull() is
// called because the static analysis thinks dereferencing XPCOM objects
@ -1187,8 +1261,9 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx,
// while destructors are running.
JS::Rooted<JS::Value> value(aCx);
{
RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]);
RefPtr<ImageBitmap> imageBitmap =
new ImageBitmap(aParent, aClonedImages[aIndex]);
new ImageBitmap(aParent, img);
ErrorResult error;
imageBitmap->SetPictureRect(IntRect(picRectX, picRectY,
@ -1208,7 +1283,7 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx,
/*static*/ bool
ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
nsTArray<RefPtr<layers::Image>>& aClonedImages,
nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
ImageBitmap* aImageBitmap)
{
MOZ_ASSERT(aWriter);
@ -1219,8 +1294,8 @@ ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
const uint32_t picRectWidth = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width);
const uint32_t picRectHeight = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
// Indexing the cloned images and send the index to the receiver.
uint32_t index = aClonedImages.Length();
// Indexing the cloned surfaces and send the index to the receiver.
uint32_t index = aClonedSurfaces.Length();
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) ||
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
@ -1228,8 +1303,24 @@ ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
return false;
}
aClonedImages.AppendElement(aImageBitmap->mData);
RefPtr<SourceSurface> surface =
aImageBitmap->mData->GetAsSourceSurface();
RefPtr<DataSourceSurface> snapshot = surface->GetDataSurface();
RefPtr<DataSourceSurface> dstDataSurface;
{
// DataSourceSurfaceD2D1::GetStride() will call EnsureMapped implicitly and
// won't Unmap after exiting function. So instead calling GetStride()
// directly, using ScopedMap to get stride.
DataSourceSurface::ScopedMap map(snapshot, DataSourceSurface::READ);
dstDataSurface =
Factory::CreateDataSourceSurfaceWithStride(snapshot->GetSize(),
snapshot->GetFormat(),
map.GetStride(),
true);
}
MOZ_ASSERT(dstDataSurface);
Factory::CopyDataSourceSurface(snapshot, dstDataSurface);
aClonedSurfaces.AppendElement(dstDataSurface);
return true;
}

View File

@ -25,6 +25,7 @@ namespace mozilla {
class ErrorResult;
namespace gfx {
class DataSourceSurface;
class SourceSurface;
}
@ -33,6 +34,7 @@ class Image;
}
namespace dom {
class OffscreenCanvas;
namespace workers {
class WorkerStructuredCloneClosure;
@ -50,6 +52,12 @@ class CreateImageBitmapFromBlob;
class CreateImageBitmapFromBlobTask;
class CreateImageBitmapFromBlobWorkerTask;
struct ImageBitmapCloneData final
{
RefPtr<gfx::DataSourceSurface> mSurface;
gfx::IntRect mPictureRect;
};
/*
* ImageBitmap is an opaque handler to several kinds of image-like objects from
* HTMLImageElement, HTMLVideoElement, HTMLCanvasElement, ImageData to
@ -83,6 +91,8 @@ public:
return mPictureRect.Height();
}
void Close();
/*
* The PrepareForDrawTarget() might return null if the mPictureRect does not
* intersect with the size of mData.
@ -90,6 +100,24 @@ public:
already_AddRefed<gfx::SourceSurface>
PrepareForDrawTarget(gfx::DrawTarget* aTarget);
/*
* Transfer ownership of buffer to caller. So this function call
* Close() implicitly.
*/
already_AddRefed<layers::Image>
TransferAsImage();
ImageBitmapCloneData*
ToCloneData();
static already_AddRefed<ImageBitmap>
CreateFromCloneData(nsIGlobalObject* aGlobal, ImageBitmapCloneData* aData);
static already_AddRefed<ImageBitmap>
CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
OffscreenCanvas& aOffscreenCanvas,
ErrorResult& aRv);
static already_AddRefed<Promise>
Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
@ -98,12 +126,12 @@ public:
ReadStructuredClone(JSContext* aCx,
JSStructuredCloneReader* aReader,
nsIGlobalObject* aParent,
const nsTArray<RefPtr<layers::Image>>& aClonedImages,
const nsTArray<RefPtr<gfx::DataSourceSurface>>& aClonedSurfaces,
uint32_t aIndex);
static bool
WriteStructuredClone(JSStructuredCloneWriter* aWriter,
nsTArray<RefPtr<layers::Image>>& aClonedImages,
nsTArray<RefPtr<gfx::DataSourceSurface>>& aClonedSurfaces,
ImageBitmap* aImageBitmap);
friend CreateImageBitmapFromBlob;

View File

@ -0,0 +1,303 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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 "ImageBitmapRenderingContext.h"
#include "mozilla/dom/ImageBitmapRenderingContextBinding.h"
#include "ImageContainer.h"
#include "ImageLayers.h"
namespace mozilla {
namespace dom {
ImageBitmapRenderingContext::ImageBitmapRenderingContext()
: mWidth(0)
, mHeight(0)
{
}
ImageBitmapRenderingContext::~ImageBitmapRenderingContext()
{
RemovePostRefreshObserver();
}
JSObject*
ImageBitmapRenderingContext::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return ImageBitmapRenderingContextBinding::Wrap(aCx, this, aGivenProto);
}
already_AddRefed<layers::Image>
ImageBitmapRenderingContext::ClipToIntrinsicSize()
{
if (!mImage) {
return nullptr;
}
// If image is larger than canvas intrinsic size, clip it to the intrinsic size.
RefPtr<gfx::SourceSurface> surface;
RefPtr<layers::Image> result;
if (mWidth < mImage->GetSize().width ||
mHeight < mImage->GetSize().height) {
surface = MatchWithIntrinsicSize();
} else {
surface = mImage->GetAsSourceSurface();
}
result = new layers::SourceSurfaceImage(gfx::IntSize(mWidth, mHeight), surface);
return result.forget();
}
void
ImageBitmapRenderingContext::TransferImageBitmap(ImageBitmap& aImageBitmap)
{
Reset();
mImage = aImageBitmap.TransferAsImage();
if (!mImage) {
return;
}
Redraw(gfxRect(0, 0, mWidth, mHeight));
}
int32_t
ImageBitmapRenderingContext::GetWidth() const
{
return mWidth;
}
int32_t
ImageBitmapRenderingContext::GetHeight() const
{
return mHeight;
}
NS_IMETHODIMP
ImageBitmapRenderingContext::SetDimensions(int32_t aWidth, int32_t aHeight)
{
mWidth = aWidth;
mHeight = aHeight;
return NS_OK;
}
NS_IMETHODIMP
ImageBitmapRenderingContext::InitializeWithSurface(nsIDocShell* aDocShell,
gfxASurface* aSurface,
int32_t aWidth,
int32_t aHeight)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
already_AddRefed<DataSourceSurface>
ImageBitmapRenderingContext::MatchWithIntrinsicSize()
{
RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
RefPtr<DataSourceSurface> temp =
Factory::CreateDataSourceSurface(IntSize(mWidth, mHeight), surface->GetFormat());
if (!temp) {
return nullptr;
}
DataSourceSurface::ScopedMap map(temp, DataSourceSurface::READ_WRITE);
if (!map.IsMapped()) {
return nullptr;
}
RefPtr<DrawTarget> dt =
Factory::CreateDrawTargetForData(BackendType::CAIRO,
map.GetData(),
temp->GetSize(),
map.GetStride(),
temp->GetFormat());
if (!dt) {
return nullptr;
}
dt->ClearRect(Rect(0, 0, mWidth, mHeight));
dt->CopySurface(surface,
IntRect(0, 0, surface->GetSize().width,
surface->GetSize().height),
IntPoint(0, 0));
return temp.forget();
}
mozilla::UniquePtr<uint8_t[]>
ImageBitmapRenderingContext::GetImageBuffer(int32_t* aFormat)
{
*aFormat = 0;
if (!mImage) {
return nullptr;
}
RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
RefPtr<DataSourceSurface> data = surface->GetDataSurface();
if (!data) {
return nullptr;
}
if (data->GetSize() != IntSize(mWidth, mHeight)) {
data = MatchWithIntrinsicSize();
}
*aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
return SurfaceToPackedBGRA(data);
}
NS_IMETHODIMP
ImageBitmapRenderingContext::GetInputStream(const char* aMimeType,
const char16_t* aEncoderOptions,
nsIInputStream** aStream)
{
nsCString enccid("@mozilla.org/image/encoder;2?type=");
enccid += aMimeType;
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
if (!encoder) {
return NS_ERROR_FAILURE;
}
int32_t format = 0;
UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(&format);
if (!imageBuffer) {
return NS_ERROR_FAILURE;
}
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer.get(), format,
encoder, aEncoderOptions, aStream);
}
already_AddRefed<mozilla::gfx::SourceSurface>
ImageBitmapRenderingContext::GetSurfaceSnapshot(bool* aPremultAlpha)
{
if (!mImage) {
return nullptr;
}
if (aPremultAlpha) {
*aPremultAlpha = true;
}
RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
if (surface->GetSize() != IntSize(mWidth, mHeight)) {
return MatchWithIntrinsicSize();
}
return surface.forget();
}
NS_IMETHODIMP
ImageBitmapRenderingContext::SetIsOpaque(bool aIsOpaque)
{
return NS_OK;
}
bool
ImageBitmapRenderingContext::GetIsOpaque()
{
return false;
}
NS_IMETHODIMP
ImageBitmapRenderingContext::Reset()
{
if (mCanvasElement) {
mCanvasElement->InvalidateCanvas();
}
mImage = nullptr;
return NS_OK;
}
already_AddRefed<Layer>
ImageBitmapRenderingContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer,
LayerManager* aManager)
{
if (!mImage) {
// No DidTransactionCallback will be received, so mark the context clean
// now so future invalidations will be dispatched.
MarkContextClean();
return nullptr;
}
RefPtr<ImageLayer> imageLayer;
if (aOldLayer) {
imageLayer = static_cast<ImageLayer*>(aOldLayer);
} else {
imageLayer = aManager->CreateImageLayer();
}
RefPtr<ImageContainer> imageContainer = imageLayer->GetContainer();
if (!imageContainer) {
imageContainer = aManager->CreateImageContainer();
imageLayer->SetContainer(imageContainer);
}
nsAutoTArray<ImageContainer::NonOwningImage, 1> imageList;
RefPtr<layers::Image> image = ClipToIntrinsicSize();
imageList.AppendElement(ImageContainer::NonOwningImage(image));
imageContainer->SetCurrentImages(imageList);
return imageLayer.forget();
}
void
ImageBitmapRenderingContext::MarkContextClean()
{
}
NS_IMETHODIMP
ImageBitmapRenderingContext::Redraw(const gfxRect& aDirty)
{
if (!mCanvasElement) {
return NS_OK;
}
mozilla::gfx::Rect rect = ToRect(aDirty);
mCanvasElement->InvalidateCanvasContent(&rect);
return NS_OK;
}
NS_IMETHODIMP
ImageBitmapRenderingContext::SetIsIPC(bool aIsIPC)
{
return NS_OK;
}
void
ImageBitmapRenderingContext::DidRefresh()
{
}
void
ImageBitmapRenderingContext::MarkContextCleanForFrameCapture()
{
}
bool
ImageBitmapRenderingContext::IsContextCleanForFrameCapture()
{
return true;
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmapRenderingContext)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmapRenderingContext)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ImageBitmapRenderingContext,
mCanvasElement,
mOffscreenCanvas)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmapRenderingContext)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
}
}

View File

@ -0,0 +1,97 @@
/* 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 ImageBitmapRenderingContext_h
#define ImageBitmapRenderingContext_h
#include "nsICanvasRenderingContextInternal.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace gfx {
class DataSourceSurface;
class SourceSurface;
}
namespace layers {
class Image;
class ImageContainer;
}
namespace dom {
/**
* The purpose of ImageBitmapRenderingContext is to provide a faster and efficient
* way to display ImageBitmap. Simply call TransferImageBitmap() then we'll transfer
* the surface of ImageBitmap to this context and then to use it to display.
*
* See more details in spec: https://wiki.whatwg.org/wiki/OffscreenCanvas
*/
class ImageBitmapRenderingContext final :
public nsICanvasRenderingContextInternal,
public nsWrapperCache
{
virtual ~ImageBitmapRenderingContext();
public:
ImageBitmapRenderingContext();
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// nsISupports interface + CC
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ImageBitmapRenderingContext)
void TransferImageBitmap(ImageBitmap& aImageBitmap);
// nsICanvasRenderingContextInternal
virtual int32_t GetWidth() const override;
virtual int32_t GetHeight() const override;
NS_IMETHOD SetDimensions(int32_t aWidth, int32_t aHeight) override;
NS_IMETHOD InitializeWithSurface(nsIDocShell* aDocShell,
gfxASurface* aSurface,
int32_t aWidth,
int32_t aHeight) override;
virtual mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
NS_IMETHOD GetInputStream(const char* aMimeType,
const char16_t* aEncoderOptions,
nsIInputStream** aStream) override;
virtual already_AddRefed<mozilla::gfx::SourceSurface>
GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) override;
NS_IMETHOD SetIsOpaque(bool aIsOpaque) override;
virtual bool GetIsOpaque() override;
NS_IMETHOD Reset() override;
virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer,
LayerManager* aManager) override;
virtual void MarkContextClean() override;
NS_IMETHOD Redraw(const gfxRect& aDirty) override;
NS_IMETHOD SetIsIPC(bool aIsIPC) override;
virtual void DidRefresh() override;
virtual void MarkContextCleanForFrameCapture() override;
virtual bool IsContextCleanForFrameCapture() override;
protected:
already_AddRefed<gfx::DataSourceSurface> MatchWithIntrinsicSize();
already_AddRefed<layers::Image> ClipToIntrinsicSize();
int32_t mWidth;
int32_t mHeight;
RefPtr<layers::Image> mImage;
};
}
}
#endif /* ImageBitmapRenderingContext_h */

View File

@ -8,6 +8,7 @@
#include "mozilla/dom/OffscreenCanvasBinding.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/layers/AsyncCanvasRenderer.h"
#include "mozilla/layers/CanvasClient.h"
#include "mozilla/layers/ImageBridgeChild.h"
@ -24,12 +25,13 @@ namespace dom {
OffscreenCanvasCloneData::OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
uint32_t aWidth, uint32_t aHeight,
layers::LayersBackend aCompositorBackend,
bool aNeutered)
bool aNeutered, bool aIsWriteOnly)
: mRenderer(aRenderer)
, mWidth(aWidth)
, mHeight(aHeight)
, mCompositorBackendType(aCompositorBackend)
, mNeutered(aNeutered)
, mIsWriteOnly(aIsWriteOnly)
{
}
@ -37,12 +39,15 @@ OffscreenCanvasCloneData::~OffscreenCanvasCloneData()
{
}
OffscreenCanvas::OffscreenCanvas(uint32_t aWidth,
OffscreenCanvas::OffscreenCanvas(nsIGlobalObject* aGlobal,
uint32_t aWidth,
uint32_t aHeight,
layers::LayersBackend aCompositorBackend,
layers::AsyncCanvasRenderer* aRenderer)
: mAttrDirty(false)
: DOMEventTargetHelper(aGlobal)
, mAttrDirty(false)
, mNeutered(false)
, mIsWriteOnly(false)
, mWidth(aWidth)
, mHeight(aHeight)
, mCompositorBackendType(aCompositorBackend)
@ -55,12 +60,6 @@ OffscreenCanvas::~OffscreenCanvas()
ClearResources();
}
OffscreenCanvas*
OffscreenCanvas::GetParentObject() const
{
return nullptr;
}
JSObject*
OffscreenCanvas::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
@ -68,6 +67,19 @@ OffscreenCanvas::WrapObject(JSContext* aCx,
return OffscreenCanvasBinding::Wrap(aCx, this, aGivenProto);
}
/* static */ already_AddRefed<OffscreenCanvas>
OffscreenCanvas::Constructor(const GlobalObject& aGlobal,
uint32_t aWidth,
uint32_t aHeight,
ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
RefPtr<OffscreenCanvas> offscreenCanvas =
new OffscreenCanvas(global, aWidth, aHeight,
layers::LayersBackend::LAYERS_NONE, nullptr);
return offscreenCanvas.forget();
}
void
OffscreenCanvas::ClearResources()
{
@ -79,7 +91,9 @@ OffscreenCanvas::ClearResources()
if (mCanvasRenderer) {
nsCOMPtr<nsIThread> activeThread = mCanvasRenderer->GetActiveThread();
MOZ_RELEASE_ASSERT(activeThread);
MOZ_RELEASE_ASSERT(activeThread == NS_GetCurrentThread());
bool current;
activeThread->IsOnCurrentThread(&current);
MOZ_RELEASE_ASSERT(current);
mCanvasRenderer->SetCanvasClient(nullptr);
mCanvasRenderer->mContext = nullptr;
mCanvasRenderer->mGLContext = nullptr;
@ -107,7 +121,8 @@ OffscreenCanvas::GetContext(JSContext* aCx,
}
if (!(contextType == CanvasContextType::WebGL1 ||
contextType == CanvasContextType::WebGL2))
contextType == CanvasContextType::WebGL2 ||
contextType == CanvasContextType::ImageBitmap))
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return nullptr;
@ -124,6 +139,8 @@ OffscreenCanvas::GetContext(JSContext* aCx,
}
if (mCanvasRenderer) {
if (contextType == CanvasContextType::WebGL1 ||
contextType == CanvasContextType::WebGL2) {
WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
gl::GLContext* gl = webGL->GL();
mCanvasRenderer->mContext = mCurrentContext;
@ -148,6 +165,7 @@ OffscreenCanvas::GetContext(JSContext* aCx,
screen->Morph(Move(factory));
}
}
}
return result;
}
@ -165,6 +183,12 @@ OffscreenCanvas::CreateContext(CanvasContextType aContextType)
void
OffscreenCanvas::CommitFrameToCompositor()
{
if (!mCanvasRenderer) {
// This offscreen canvas doesn't associate to any HTML canvas element.
// So, just bail out.
return;
}
// The attributes has changed, we have to notify main
// thread to change canvas size.
if (mAttrDirty) {
@ -191,15 +215,121 @@ OffscreenCanvasCloneData*
OffscreenCanvas::ToCloneData()
{
return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth, mHeight,
mCompositorBackendType, mNeutered);
mCompositorBackendType, mNeutered, mIsWriteOnly);
}
already_AddRefed<ImageBitmap>
OffscreenCanvas::TransferToImageBitmap()
{
ErrorResult rv;
RefPtr<ImageBitmap> result = ImageBitmap::CreateFromOffscreenCanvas(GetGlobalObject(), *this, rv);
// Clear the content.
if ((mCurrentContextType == CanvasContextType::WebGL1 ||
mCurrentContextType == CanvasContextType::WebGL2))
{
WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
webGL->ClearScreen();
}
return result.forget();
}
already_AddRefed<Promise>
OffscreenCanvas::ToBlob(JSContext* aCx,
const nsAString& aType,
JS::Handle<JS::Value> aParams,
ErrorResult& aRv)
{
// do a trust check if this is a write-only canvas
if (mIsWriteOnly) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
nsCOMPtr<nsIGlobalObject> global = GetGlobalObject();
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
// Encoder callback when encoding is complete.
class EncodeCallback : public EncodeCompleteCallback
{
public:
EncodeCallback(nsIGlobalObject* aGlobal, Promise* aPromise)
: mGlobal(aGlobal)
, mPromise(aPromise) {}
// This is called on main thread.
nsresult ReceiveBlob(already_AddRefed<Blob> aBlob)
{
RefPtr<Blob> blob = aBlob;
ErrorResult rv;
uint64_t size = blob->GetSize(rv);
if (rv.Failed()) {
rv.SuppressException();
} else {
AutoJSAPI jsapi;
if (jsapi.Init(mGlobal)) {
JS_updateMallocCounter(jsapi.cx(), size);
}
}
if (mPromise) {
RefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
mPromise->MaybeResolve(newBlob);
}
mGlobal = nullptr;
mPromise = nullptr;
return rv.StealNSResult();
}
nsCOMPtr<nsIGlobalObject> mGlobal;
RefPtr<Promise> mPromise;
};
RefPtr<EncodeCompleteCallback> callback =
new EncodeCallback(global, promise);
CanvasRenderingContextHelper::ToBlob(aCx, global,
callback, aType, aParams, aRv);
return promise.forget();
}
already_AddRefed<gfx::SourceSurface>
OffscreenCanvas::GetSurfaceSnapshot(bool* aPremultAlpha)
{
if (!mCurrentContext) {
return nullptr;
}
return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha);
}
nsCOMPtr<nsIGlobalObject>
OffscreenCanvas::GetGlobalObject()
{
if (NS_IsMainThread()) {
return GetParentObject();
}
dom::workers::WorkerPrivate* workerPrivate =
dom::workers::GetCurrentThreadWorkerPrivate();
return workerPrivate->GlobalScope();
}
/* static */ already_AddRefed<OffscreenCanvas>
OffscreenCanvas::CreateFromCloneData(OffscreenCanvasCloneData* aData)
OffscreenCanvas::CreateFromCloneData(nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData)
{
MOZ_ASSERT(aData);
RefPtr<OffscreenCanvas> wc =
new OffscreenCanvas(aData->mWidth, aData->mHeight,
new OffscreenCanvas(aGlobal, aData->mWidth, aData->mHeight,
aData->mCompositorBackendType, aData->mRenderer);
if (aData->mNeutered) {
wc->SetNeutered();

View File

@ -25,6 +25,8 @@ class CanvasClient;
} // namespace layers
namespace dom {
class Blob;
class ImageBitmap;
// This is helper class for transferring OffscreenCanvas to worker thread.
// Because OffscreenCanvas is not thread-safe. So we cannot pass Offscreen-
@ -35,7 +37,7 @@ struct OffscreenCanvasCloneData final
OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
uint32_t aWidth, uint32_t aHeight,
layers::LayersBackend aCompositorBackend,
bool aNeutered);
bool aNeutered, bool aIsWriteOnly);
~OffscreenCanvasCloneData();
RefPtr<layers::AsyncCanvasRenderer> mRenderer;
@ -43,6 +45,7 @@ struct OffscreenCanvasCloneData final
uint32_t mHeight;
layers::LayersBackend mCompositorBackendType;
bool mNeutered;
bool mIsWriteOnly;
};
class OffscreenCanvas final : public DOMEventTargetHelper
@ -52,16 +55,23 @@ public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
OffscreenCanvas(uint32_t aWidth,
OffscreenCanvas(nsIGlobalObject* aGlobal,
uint32_t aWidth,
uint32_t aHeight,
layers::LayersBackend aCompositorBackend,
layers::AsyncCanvasRenderer* aRenderer);
OffscreenCanvas* GetParentObject() const;
nsCOMPtr<nsIGlobalObject> GetParentObject() const { return GetOwnerGlobal(); }
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
static already_AddRefed<OffscreenCanvas>
Constructor(const GlobalObject& aGlobal,
uint32_t aWidth,
uint32_t aHeight,
ErrorResult& aRv);
void ClearResources();
uint32_t Width() const
@ -100,13 +110,24 @@ public:
}
}
already_AddRefed<ImageBitmap>
TransferToImageBitmap();
already_AddRefed<Promise>
ToBlob(JSContext* aCx,
const nsAString& aType,
JS::Handle<JS::Value> aParams,
ErrorResult& aRv);
nsICanvasRenderingContextInternal* GetContext() const
{
return mCurrentContext;
}
already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr);
static already_AddRefed<OffscreenCanvas>
CreateFromCloneData(OffscreenCanvasCloneData* aData);
CreateFromCloneData(nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData);
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
@ -147,6 +168,16 @@ public:
return mNeutered;
}
void SetWriteOnly()
{
mIsWriteOnly = true;
}
bool IsWriteOnly() const
{
return mIsWriteOnly;
}
layers::LayersBackend GetCompositorBackendType() const
{
return mCompositorBackendType;
@ -155,6 +186,8 @@ public:
private:
~OffscreenCanvas();
nsCOMPtr<nsIGlobalObject> GetGlobalObject();
void CanvasAttrChanged()
{
mAttrDirty = true;
@ -164,6 +197,7 @@ private:
bool mAttrDirty;
bool mNeutered;
bool mIsWriteOnly;
uint32_t mWidth;
uint32_t mHeight;

View File

@ -5,10 +5,10 @@
#include "WebGL2Context.h"
#include "gfxPrefs.h"
#include "GLContext.h"
#include "mozilla/dom/WebGL2RenderingContextBinding.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "WebGLBuffer.h"
#include "WebGLFormats.h"
@ -37,7 +37,7 @@ WebGL2Context::CreateFormatUsage(gl::GLContext* gl) const
/*static*/ bool
WebGL2Context::IsSupported()
{
return Preferences::GetBool("webgl.enable-prototype-webgl2", false);
return gfxPrefs::WebGL2Enabled();
}
/*static*/ WebGL2Context*

View File

@ -1131,9 +1131,9 @@ private:
RefPtr<HTMLCanvasElement> mCanvas;
};
already_AddRefed<layers::CanvasLayer>
already_AddRefed<layers::Layer>
WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
CanvasLayer* oldLayer,
Layer* oldLayer,
LayerManager* manager)
{
if (IsContextLost())
@ -1141,7 +1141,7 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
if (!mResetLayer && oldLayer &&
oldLayer->HasUserData(&gWebGLLayerUserData)) {
RefPtr<layers::CanvasLayer> ret = oldLayer;
RefPtr<layers::Layer> ret = oldLayer;
return ret.forget();
}

View File

@ -331,8 +331,8 @@ public:
return ActiveBoundTextureForTarget(texTarget);
}
already_AddRefed<CanvasLayer>
GetCanvasLayer(nsDisplayListBuilder* builder, CanvasLayer* oldLayer,
already_AddRefed<Layer>
GetCanvasLayer(nsDisplayListBuilder* builder, Layer* oldLayer,
LayerManager* manager) override;
// Note that 'clean' here refers to its invalidation state, not the

View File

@ -542,9 +542,12 @@ FormatUsageAuthority::CreateForWebGL1(gl::GLContext* gl)
fnSet(EffectiveFormat::RGBA8 , true, true);
fnSet(EffectiveFormat::RGBA4 , true, true);
fnSet(EffectiveFormat::RGB5_A1, true, true);
fnSet(EffectiveFormat::RGB8 , false, true);
fnSet(EffectiveFormat::RGB565 , true, true);
// RGB8 is not guaranteed to be renderable, but we should allow it for web-compat.
// Min-capability mode should mark this as non-renderable.
fnSet(EffectiveFormat::RGB8, true, true);
fnSet(EffectiveFormat::Luminance8Alpha8, false, true);
fnSet(EffectiveFormat::Luminance8 , false, true);
fnSet(EffectiveFormat::Alpha8 , false, true);

View File

@ -7,7 +7,7 @@
TEST_DIRS += ['compiledtest']
# Number changes to this file to avoid bug 1081323 (clobber after changing a manifest):
# 1
# 2
MOCHITEST_MANIFESTS += [
'test/crossorigin/mochitest.ini',
@ -34,6 +34,7 @@ EXPORTS.mozilla.dom += [
'CanvasRenderingContextHelper.h',
'CanvasUtils.h',
'ImageBitmap.h',
'ImageBitmapRenderingContext.h',
'ImageBitmapSource.h',
'ImageData.h',
'OffscreenCanvas.h',
@ -50,6 +51,7 @@ UNIFIED_SOURCES += [
'DocumentRendererChild.cpp',
'DocumentRendererParent.cpp',
'ImageBitmap.cpp',
'ImageBitmapRenderingContext.cpp',
'ImageData.cpp',
'OffscreenCanvas.cpp',
]

View File

@ -26,6 +26,7 @@ class nsDisplayListBuilder;
namespace mozilla {
namespace layers {
class CanvasLayer;
class Layer;
class LayerManager;
} // namespace layers
namespace gfx {
@ -39,6 +40,7 @@ class nsICanvasRenderingContextInternal :
{
public:
typedef mozilla::layers::CanvasLayer CanvasLayer;
typedef mozilla::layers::Layer Layer;
typedef mozilla::layers::LayerManager LayerManager;
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASRENDERINGCONTEXTINTERNAL_IID)
@ -129,8 +131,8 @@ public:
// Return the CanvasLayer for this context, creating
// one for the given layer manager if not available.
virtual already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* builder,
CanvasLayer *oldLayer,
virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* builder,
Layer *oldLayer,
LayerManager *manager) = 0;
// Return true if the canvas should be forced to be "inactive" to ensure

View File

@ -218,6 +218,7 @@ skip-if = toolkit != 'cocoa'
[test_2d.path.rect.zero.6.html]
disabled = bug 407107
[test_2d.strokeRect.zero.5.html]
[test_bitmaprenderer.html]
[test_bug232227.html]
[test_bug613794.html]
[test_bug753758.html]
@ -246,11 +247,13 @@ support-files = captureStream_common.js
support-files = file_drawWindow_source.html file_drawWindow_common.js
skip-if = (buildapp == 'b2g' && toolkit != 'gonk')
[test_imagebitmap.html]
[test_imagebitmap_close.html]
[test_imagebitmap_cropping.html]
[test_imagebitmap_on_worker.html]
[test_imagebitmap_structuredclone.html]
[test_imagebitmap_structuredclone_iframe.html]
[test_imagebitmap_structuredclone_window.html]
[test_imagebitmap_transfer.html]
[test_ImageData_ctor.html]
[test_isPointInStroke.html]
[test_mozDashOffset.html]
@ -267,6 +270,10 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965
[test_createPattern_broken.html]
[test_setlinedash.html]
[test_filter.html]
[test_offscreencanvas_toblob.html]
tags = offscreencanvas
[test_offscreencanvas_toimagebitmap.html]
tags = offscreencanvas
[test_offscreencanvas_basic_webgl.html]
tags = offscreencanvas
[test_offscreencanvas_dynamic_fallback.html]

View File

@ -1,27 +1,49 @@
/* WebWorker for test_offscreencanvas_*.html */
(function(){
var port = null;
function ok(expect, msg) {
if (port) {
port.postMessage({type: "test", result: !!expect, name: msg});
} else {
postMessage({type: "test", result: !!expect, name: msg});
function isInWorker() {
try {
return !(self instanceof Window);
} catch (e) {
return true;
}
}
function postMessageGeneral(data) {
if (isInWorker()) {
if (port) {
port.postMessage(data);
} else {
postMessage(data);
}
} else {
postMessage(data, "*");
}
}
function ok(expect, msg) {
postMessageGeneral({type: "test", result: !!expect, name: msg});
}
function finish() {
if (port) {
port.postMessage({type: "finish"});
} else {
postMessage({type: "finish"});
}
postMessageGeneral({type: "finish"});
}
function drawCount(count) {
postMessageGeneral({type: "draw", count: count});
}
function sendBlob(blob) {
postMessageGeneral({type: "blob", blob: blob});
}
function sendImageBitmap(img) {
if (port) {
port.postMessage({type: "draw", count: count});
port.postMessage({type: "imagebitmap", bitmap: img});
} else {
postMessage({type: "draw", count: count});
postMessage({type: "imagebitmap", bitmap: img});
}
}
@ -140,7 +162,7 @@ function createDrawFunc(canvas) {
// Start drawing
checkGLError('after setup');
return function(prefix) {
return function(prefix, needCommitFrame) {
if (prefix) {
prefix = "[" + prefix + "] ";
} else {
@ -152,7 +174,9 @@ function createDrawFunc(canvas) {
preDraw(prefix);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
postDraw(prefix);
if (needCommitFrame) {
gl.commit();
}
checkGLError(prefix);
};
}
@ -161,6 +185,9 @@ function createDrawFunc(canvas) {
function entryFunction(testStr, subtests, offscreenCanvas) {
var test = testStr;
var canvas = offscreenCanvas;
if (test == "webgl_imagebitmap") {
canvas = new OffscreenCanvas(64, 64);
}
if (test != "subworker") {
ok(canvas, "Canvas successfully transfered to worker");
@ -190,7 +217,7 @@ function entryFunction(testStr, subtests, offscreenCanvas) {
finish();
return;
}
draw("loop " +count);
draw("loop " +count, true);
}, 0);
}
//------------------------------------------------------------------------
@ -205,11 +232,38 @@ function entryFunction(testStr, subtests, offscreenCanvas) {
var count = 0;
var iid = setInterval(function() {
++count;
draw("loop " + count);
draw("loop " + count, true);
drawCount(count);
}, 0);
}
//------------------------------------------------------------------------
// Test toBlob
//------------------------------------------------------------------------
else if (test == "webgl_toblob") {
draw = createDrawFunc(canvas);
if (!draw) {
return;
}
draw("", false);
canvas.toBlob().then(function(blob) {
sendBlob(blob);
});
}
//------------------------------------------------------------------------
// Test toImageBitmap
//------------------------------------------------------------------------
else if (test == "webgl_imagebitmap") {
draw = createDrawFunc(canvas);
if (!draw) {
return;
}
draw("", false);
var imgBitmap = canvas.transferToImageBitmap();
sendImageBitmap(imgBitmap);
}
//------------------------------------------------------------------------
// Canvas Size Change from Worker
//------------------------------------------------------------------------
else if (test == "webgl_changesize") {
@ -219,22 +273,22 @@ function entryFunction(testStr, subtests, offscreenCanvas) {
return;
}
draw("64x64");
draw("64x64", true);
setTimeout(function() {
canvas.width = 128;
canvas.height = 128;
draw("Increased to 128x128");
draw("Increased to 128x128", true);
setTimeout(function() {
canvas.width = 32;
canvas.width = 32;
draw("Decreased to 32x32");
draw("Decreased to 32x32", true);
setTimeout(function() {
canvas.width = 64;
canvas.height = 64;
draw("Increased to 64x64");
draw("Increased to 64x64", true);
ok(true, "Worker is done");
finish();
@ -297,3 +351,9 @@ onconnect = function(evt) {
port.start();
};
if (!isInWorker()) {
window.entryFunction = entryFunction;
}
})();

View File

@ -0,0 +1,163 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebGL in OffscreenCanvas</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<script type="text/js-worker">
function ok(expect, msg) {
postMessage({"type": "status", status: !!expect, msg: msg});
}
onmessage = function(event) {
var bitmap = event.data.bitmap;
ok(!!bitmap, "Get the ImageBitmap from the main script.");
var offscreenCanvas = new OffscreenCanvas(64, 64);
var ctx = offscreenCanvas.getContext('bitmaprenderer');
ok(!!ctx, "Get bitmaprenderer context on worker.");
ctx.transferImageBitmap(bitmap);
var resultBitmap = offscreenCanvas.transferToImageBitmap();
postMessage({"type": "bitmap", bitmap: resultBitmap}, [resultBitmap]);
}
</script>
<script>
SimpleTest.waitForExplicitFinish();
function createCanvas(width, height) {
var htmlCanvas = document.createElement('canvas');
htmlCanvas.width = width;
htmlCanvas.height = height;
document.body.appendChild(htmlCanvas);
return htmlCanvas;
}
function runTest(canvasWidth, canvasHeight, nextTest) {
var canvas1 = createCanvas(canvasWidth, canvasHeight);
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
var canvasRef = createCanvas(90, 90);
var ctx = canvasRef.getContext("2d");
// Clear with black transparent first
ctx.fillStyle = "rgba(0, 0, 0, 0)";
ctx.fillRect(0, 0, 90, 90);
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
createImageBitmap(canvas1).then(function(bmp) {
document.body.removeChild(canvas1);
var canvas2 = createCanvas(90, 90);
var ctx2 = canvas2.getContext("bitmaprenderer");
ctx2.transferImageBitmap(bmp);
ok(canvasRef.toDataURL() == canvas2.toDataURL(), "toDataURL should return same result.");
// Exam render result
canvasRef.style.display = "none";
canvas2.style.display = "block";
var snapshot = snapshotWindow(window);
canvasRef.style.display = "block";
canvas2.style.display = "none";
var snapshotRef = snapshotWindow(window);
var results = compareSnapshots(snapshot, snapshotRef, true);
ok(results[0], "Screenshots should be the same");
document.body.removeChild(canvasRef);
document.body.removeChild(canvas2);
nextTest();
});
}
function scaleTest() {
var canvas1 = createCanvas(64, 64);
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
var canvas2 = createCanvas(64, 64);
var ctx2 = canvas2.getContext("2d");
ctx2.fillStyle = "#00FF00";
ctx2.fillRect(0, 0, 64, 64);
var p1 = createImageBitmap(canvas1);
var p2 = createImageBitmap(canvas2);
Promise.all([p1, p2]).then(function(bitmaps) {
document.body.removeChild(canvas1);
document.body.removeChild(canvas2);
// Create a large canvas then shrink.
var canvas3 = createCanvas(128, 128);
var ctx3 = canvas3.getContext("bitmaprenderer");
ctx3.transferImageBitmap(bitmaps[0]);
var snapshotLargeRef = snapshotWindow(window);
canvas3.width = 32;
canvas3.height = 32;
var snapshotSmall = snapshotWindow(window);
document.body.removeChild(canvas3);
// Create a small canvas then grow.
var canvas4 = createCanvas(32, 32);
var ctx4 = canvas4.getContext("bitmaprenderer");
ctx4.transferImageBitmap(bitmaps[1]);
var snapshotSmallRef = snapshotWindow(window);
canvas4.width = 128;
canvas4.height = 128;
var snapshotLarge = snapshotWindow(window);
document.body.removeChild(canvas4);
var resultsLarge = compareSnapshots(snapshotLarge, snapshotLargeRef, true);
ok(resultsLarge[0], "Screenshots should be the same");
var resultsSmall = compareSnapshots(snapshotSmall, snapshotSmallRef, true);
ok(resultsSmall[0], "Screenshots should be the same");
runTestOnWorker();
});
}
function runTestOnWorker() {
var canvas1 = createCanvas(64, 64);
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"});
var worker = new Worker(window.URL.createObjectURL(blob));
createImageBitmap(canvas1).then(function(bmp) {
worker.postMessage({bitmap: bmp}, [bmp]);
worker.onmessage = function(event) {
if (event.data.type == "status") {
ok(event.data.status, event.data.msg);
} else if (event.data.type == "bitmap") {
var canvas2 = createCanvas(64, 64);
var ctx2 = canvas2.getContext('bitmaprenderer');
ctx2.transferImageBitmap(event.data.bitmap);
ok(canvas1.toDataURL() == canvas2.toDataURL(), 'toDataURL should be the same');
SimpleTest.finish();
}
}
});
}
SpecialPowers.pushPrefEnv({'set': [
['gfx.offscreencanvas.enabled', true],
]}, runTest.bind(this, 64, 64, runTest.bind(this, 128, 128, scaleTest)));
</script>
</body>
</html>

View File

@ -0,0 +1,93 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebGL in OffscreenCanvas</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<script type="text/js-worker">
function ok(expect, msg) {
postMessage({"type": "status", status: !!expect, msg: msg});
}
onmessage = function(event) {
var bitmap = event.data.bitmap;
ok(!!bitmap, "Get the ImageBitmap from the main script.");
bitmap.close();
ok(bitmap.width == 0 && bitmap.height == 0, "After close(), width and height should return 0");
postMessage({"type": "finish"});
}
</script>
<script>
SimpleTest.waitForExplicitFinish();
function createCanvas() {
var htmlCanvas = document.createElement('canvas');
htmlCanvas.width = 64;
htmlCanvas.height = 64;
document.body.appendChild(htmlCanvas);
return htmlCanvas;
}
function runTest() {
var canvas1 = createCanvas();
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
var canvasRef = createCanvas();
var ctx = canvasRef.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
createImageBitmap(canvas1).then(function(bmp) {
var canvas2 = createCanvas();
var ctx2 = canvas2.getContext("2d");
ctx2.drawImage(bmp, 0, 0);
ok(canvasRef.toDataURL() == canvas2.toDataURL(), "toDataURL should return same result.");
document.body.removeChild(canvas2);
bmp.close();
ok(bmp.width == 0 && bmp.height == 0, "After close(), width and height should return 0");
var canvas2 = createCanvas();
var ctx2 = canvas2.getContext("2d");
var beforeDrawImageDataURL = canvas2.toDataURL();
ctx2.drawImage(bmp, 0, 0);
var afterDrawImageDataURL = canvas2.toDataURL();
ok(beforeDrawImageDataURL == afterDrawImageDataURL,
"Drawing operations with a closed ImageBitmap should do nothing.");
runTestOnWorker();
});
}
function runTestOnWorker() {
var canvas1 = createCanvas();
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"});
var worker = new Worker(window.URL.createObjectURL(blob));
createImageBitmap(canvas1).then(function(bmp) {
worker.postMessage({bitmap: bmp}, [bmp]);
worker.onmessage = function(event) {
if (event.data.type == "status") {
ok(event.data.status, event.data.msg);
} else if (event.data.type == "finish") {
SimpleTest.finish();
}
}
});
}
runTest();
</script>
</body>
</html>

View File

@ -0,0 +1,111 @@
<!DOCTYPE HTML>
<title>Test ImageBitmap : Transfer</title>
<meta charset="utf-8">
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<body>
<script type="text/javascript">
var gImage1;
var gImage2;
var gImageBitmap1;
var gImageBitmap2;
function isPixel(ctx1, ctx2, x, y) {
var pixel1 = ctx1.getImageData(x, y, 1, 1);
var pixel2 = ctx2.getImageData(x, y, 1, 1);
ok(pixel1.data[0] == pixel2.data[0] &&
pixel1.data[1] == pixel2.data[1] &&
pixel1.data[2] == pixel2.data[2] &&
pixel1.data[3] == pixel2.data[3],
"Color(" + pixel1.data[0] + ", " + pixel1.data[1] + ", " + pixel1.data[2] + ", " + pixel1.data[3] + ") should qual to Color(" + pixel2.data[0] + ", " + pixel2.data[1] + ", " + pixel2.data[2] + ", " + pixel2.data[3] + ")");
}
function compareImageBitmapWithImageElement(imageBitmap, imageElement) {
var canvas1 = document.createElement('canvas');
var canvas2 = document.createElement('canvas');
canvas1.width = imageElement.naturalWidth;
canvas1.height = imageElement.naturalHeight;
canvas2.width = imageElement.naturalWidth;
canvas2.height = imageElement.naturalHeight;
var ctx1 = canvas1.getContext('2d');
var ctx2 = canvas2.getContext('2d');
ctx1.drawImage(imageElement, 0, 0);
ctx2.drawImage(imageBitmap, 0, 0);
document.body.appendChild(canvas1);
document.body.appendChild(canvas2);
for (var t = 0; t < 20; ++t) {
// check one random pixel
var randomX = Math.floor(Math.random() * imageElement.naturalWidth);
var randomY = Math.floor(Math.random() * imageElement.naturalHeight);
isPixel(ctx1, ctx2, randomX, randomY);
}
}
var worker = new Worker("imagebitmap_structuredclone.js");
worker.onmessage = function(event) {
if (event.data.type == "status") {
ok(event.data.status, event.data.msg);
} else if (event.data.type == "finish") {
SimpleTest.finish();
} else if (event.data.type == "bitmap1") {
compareImageBitmapWithImageElement(event.data.bitmap, gImage1);
} else if (event.data.type == "bitmap2") {
compareImageBitmapWithImageElement(event.data.bitmap, gImage2);
}
}
function prepareTwoImageBitmap() {
gImage1 = document.createElement('img');
gImage2 = document.createElement('img');
gImage1.src = "image_rgrg-256x256.png";
gImage2.src = "image_yellow.png";
var p1 = new Promise(function(resolve, reject) {
gImage1.onload = function() {
var promise = createImageBitmap(gImage1);
promise.then(function(bitmap) {
gImageBitmap1 = bitmap;
resolve(true);
});
}
});
var p2 = new Promise(function(resolve, reject) {
gImage2.onload = function() {
var promise = createImageBitmap(gImage2);
promise.then(function(bitmap) {
gImageBitmap2 = bitmap;
resolve(true);
});
}
});
return Promise.all([p1, p2]);
}
function runTests() {
ok(worker, "Worker created successfully.");
prepareTwoImageBitmap().then(function(){
worker.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2},
[gImageBitmap1, gImageBitmap2]);
ok(gImageBitmap1.width == 0 && gImageBitmap1.height == 0,
"After transfer, ImageBitmap become neutered");
ok(gImageBitmap2.width == 0 && gImageBitmap2.height == 0,
"After transfer, ImageBitmap become neutered");
});
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(runTests);
</script>
</body>

View File

@ -0,0 +1,91 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebGL in OffscreenCanvas</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="offscreencanvas.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<canvas id="c" width="64" height="64"></canvas>
<canvas id="c-mt" width="64" height="64"></canvas>
<canvas id="c-ref" width="64" height="64"></canvas>
<script>
SimpleTest.waitForExplicitFinish();
function testBlob(blob, callback) {
// testing toBlob
// Fill c-ref with green color.
var c = document.getElementById("c-ref");
var ctx = c.getContext("2d");
ctx.rect(0, 0, 64, 64);
ctx.fillStyle = "#00FF00";
ctx.fill();
var reader = new FileReader();
reader.onload = function(e) {
ok(c.toDataURL() == e.target.result, "toBlob should return a 64x64 green square");
callback();
};
reader.readAsDataURL(blob);
}
function runTestOnMainThread() {
var htmlCanvas = document.getElementById("c-mt");
ok(htmlCanvas, "Should have HTML canvas element");
window.onmessage = function(evt) {
var msg = evt.data || {};
if (msg.type == "test") {
ok(msg.result, msg.name);
}
if (msg.type == "blob") {
testBlob(msg.blob, SimpleTest.finish);
}
}
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
entryFunction('webgl_toblob', '', offscreenCanvas);
}
function runTest() {
var htmlCanvas = document.getElementById("c");
var worker = new Worker("offscreencanvas.js");
ok(htmlCanvas, "Should have HTML canvas element");
ok(worker, "Web worker successfully created");
worker.onmessage = function(evt) {
var msg = evt.data || {};
if (msg.type == "test") {
ok(msg.result, msg.name);
}
if (msg.type == "blob") {
testBlob(msg.blob, function() {
worker.terminate();
runTestOnMainThread();
});
}
}
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
worker.postMessage({test: 'webgl_toblob', canvas: offscreenCanvas}, [offscreenCanvas]);
}
SpecialPowers.pushPrefEnv({'set': [
['gfx.offscreencanvas.enabled', true],
['webgl.force-enabled', true],
]}, runTest);
</script>
</body>
</html>

View File

@ -0,0 +1,69 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebGL in OffscreenCanvas</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<canvas id="c" width="64" height="64"></canvas>
<canvas id="c2" width="64" height="64"></canvas>
<canvas id="c-ref" width="64" height="64"></canvas>
<script>
SimpleTest.waitForExplicitFinish();
function runTest() {
var worker = new Worker("offscreencanvas.js");
ok(worker, "Web worker successfully created");
worker.onmessage = function(evt) {
var msg = evt.data || {};
if (msg.type == "test") {
ok(msg.result, msg.name);
}
if (msg.type == "imagebitmap") {
// testing toBlob
// Fill c-ref with green color.
var c = document.getElementById("c-ref");
var ctx = c.getContext("2d");
ctx.rect(0, 0, 64, 64);
ctx.fillStyle = "#00FF00";
ctx.fill();
var htmlCanvas = document.getElementById("c");
var bitmapRenderer = htmlCanvas.getContext("bitmaprenderer");
bitmapRenderer.transferImageBitmap(msg.bitmap);
ok(c.toDataURL() == htmlCanvas.toDataURL(),
"imagebitmap should return a 64x64 green square");
// The ownership of msg.bitmap should be transferred to canvas "c" when
// we called transferImageBitmap. So we test if the ownership is actually
// transferred here.
var htmlCanvas = document.getElementById("c2");
var bitmapRenderer = htmlCanvas.getContext("bitmaprenderer");
bitmapRenderer.transferImageBitmap(msg.bitmap);
SimpleTest.doesThrow(
function() { c2.toDataURL(); },
"ImageBitmap has been transferred, toDataURL will throw.");
worker.terminate();
SimpleTest.finish();
}
}
worker.postMessage({test: 'webgl_imagebitmap'});
}
SpecialPowers.pushPrefEnv({'set': [
['gfx.offscreencanvas.enabled', true],
['webgl.force-enabled', true],
]}, runTest);
</script>
</body>
</html>

View File

@ -13,7 +13,6 @@ fail-if = (os == 'b2g')
support-files = captureStream_common.js
[webgl-mochitest/test_cubemap_must_be_square.html]
[webgl-mochitest/test_depth_tex_lazy_clear.html]
skip-if = 1
[webgl-mochitest/test_draw.html]
[webgl-mochitest/test_fb_param.html]
[webgl-mochitest/test_fb_param_crash.html]

View File

@ -374,7 +374,8 @@ HTMLCanvasElement::~HTMLCanvasElement()
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement, nsGenericHTMLElement,
mCurrentContext, mPrintCallback,
mPrintState, mOriginalCanvas)
mPrintState, mOriginalCanvas,
mOffscreenCanvas)
NS_IMPL_ADDREF_INHERITED(HTMLCanvasElement, Element)
NS_IMPL_RELEASE_INHERITED(HTMLCanvasElement, Element)
@ -779,10 +780,17 @@ HTMLCanvasElement::TransferControlToOffscreen(ErrorResult& aRv)
renderer->SetWidth(sz.width);
renderer->SetHeight(sz.height);
mOffscreenCanvas = new OffscreenCanvas(sz.width,
nsCOMPtr<nsIGlobalObject> global =
do_QueryInterface(OwnerDoc()->GetInnerWindow());
mOffscreenCanvas = new OffscreenCanvas(global,
sz.width,
sz.height,
GetCompositorBackendType(),
renderer);
if (mWriteOnly) {
mOffscreenCanvas->SetWriteOnly();
}
if (!mContextObserver) {
mContextObserver = new HTMLCanvasElementObserver(this);
}
@ -1038,9 +1046,9 @@ HTMLCanvasElement::GetOpaqueAttr()
return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
}
already_AddRefed<CanvasLayer>
already_AddRefed<Layer>
HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
Layer *aOldLayer,
LayerManager *aManager)
{
// The address of sOffscreenCanvasLayerUserDataDummy is used as the user
@ -1056,7 +1064,7 @@ HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
if (mOffscreenCanvas) {
if (!mResetLayer &&
aOldLayer && aOldLayer->HasUserData(&sOffscreenCanvasLayerUserDataDummy)) {
RefPtr<CanvasLayer> ret = aOldLayer;
RefPtr<Layer> ret = aOldLayer;
return ret.forget();
}
@ -1173,7 +1181,7 @@ void
HTMLCanvasElement::SetFrameCapture(already_AddRefed<SourceSurface> aSurface)
{
RefPtr<SourceSurface> surface = aSurface;
RefPtr<CairoImage> image = new CairoImage(surface->GetSize(), surface);
RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(surface->GetSize(), surface);
// Loop backwards to allow removing elements in the loop.
for (int i = mRequestedFrameListeners.Length() - 1; i >= 0; --i) {

View File

@ -31,6 +31,7 @@ namespace layers {
class AsyncCanvasRenderer;
class CanvasLayer;
class Image;
class Layer;
class LayerManager;
} // namespace layers
namespace gfx {
@ -120,6 +121,7 @@ class HTMLCanvasElement final : public nsGenericHTMLElement,
typedef layers::AsyncCanvasRenderer AsyncCanvasRenderer;
typedef layers::CanvasLayer CanvasLayer;
typedef layers::Layer Layer;
typedef layers::LayerManager LayerManager;
public:
@ -308,8 +310,8 @@ public:
* Helpers called by various users of Canvas
*/
already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer *aOldLayer,
LayerManager *aManager);
// Should return true if the canvas layer should always be marked inactive.
// We should return true here if we can't do accelerated compositing with

View File

@ -996,7 +996,7 @@ HTMLImageElement::PictureSourceSrcsetChanged(nsIContent *aSourceNode,
mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue);
}
if (!mInDocResponsiveContent) {
if (!mInDocResponsiveContent && IsInComposedDoc()) {
nsIDocument* doc = GetOurOwnerDoc();
if (doc) {
doc->AddResponsiveContent(this);

View File

@ -295,20 +295,25 @@ NS_IMPL_ISUPPORTS(UploadLastDir::ContentPrefCallback, nsIContentPrefCallback2)
NS_IMETHODIMP
UploadLastDir::ContentPrefCallback::HandleCompletion(uint16_t aReason)
{
nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
NS_ENSURE_STATE(localFile);
if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR ||
!mResult) {
// Default to "desktop" directory for each platform
nsCOMPtr<nsIFile> homeDir;
NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(homeDir));
localFile = do_QueryInterface(homeDir);
} else {
nsCOMPtr<nsIFile> localFile;
nsAutoString prefStr;
if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR || !mResult) {
prefStr = Preferences::GetString("dom.input.fallbackUploadDir");
if (prefStr.IsEmpty()) {
// If no custom directory was set through the pref, default to
// "desktop" directory for each platform.
NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(localFile));
}
}
if (!localFile) {
if (prefStr.IsEmpty() && mResult) {
nsCOMPtr<nsIVariant> pref;
mResult->GetValue(getter_AddRefs(pref));
pref->GetAsAString(prefStr);
}
localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
localFile->InitWithPath(prefStr);
}

View File

@ -600,3 +600,5 @@ skip-if = buildapp == 'b2g' # bug 1129014
[test_image_clone_load.html]
[test_bug1203668.html]
[test_bug1166138.html]
[test_filepicker_default_directory.html]
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'

View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1194893
-->
<head>
<title>Test for filepicker default directory</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.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=1194893">Mozilla Bug 1194893</a>
<div id="content">
<input type="file" id="f">
</div>
<pre id="text">
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
const { Cc: Cc, Ci: Ci } = SpecialPowers;
// Platform-independent directory names are #define'd in xpcom/io/nsDirectoryServiceDefs.h
var defaultUploadDirectory = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get("Desk", Ci.nsIFile);
// When we want to test an upload directory other than the default, we need to
// get a valid directory in a platform-independent way. Since NS_OS_DESKTOP_DIR
// may fallback to NS_OS_HOME_DIR, let's use NS_OS_TMP_DIR.
var customUploadDirectory = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get("TmpD", Ci.nsIFile);
// Useful for debugging
//info("defaultUploadDirectory" + defaultUploadDirectory.path);
//info("customUploadDirectory" + customUploadDirectory.path);
var MockFilePicker = SpecialPowers.MockFilePicker;
MockFilePicker.init(window);
// need to show the MockFilePicker so .displayDirectory gets set
var f = document.getElementById("f");
f.focus();
var testIndex = 0;
var tests = [
["", defaultUploadDirectory.path],
[customUploadDirectory.path, customUploadDirectory.path]
]
MockFilePicker.showCallback = function(filepicker) {
info(SpecialPowers.wrap(MockFilePicker).displayDirectory.path);
is(SpecialPowers.wrap(MockFilePicker).displayDirectory.path,
tests[testIndex][1]);
if (++testIndex == tests.length) {
MockFilePicker.cleanup();
SimpleTest.finish();
} else {
launchNextTest();
}
}
function launchNextTest() {
SpecialPowers.pushPrefEnv(
{ 'set': [
['dom.input.fallbackUploadDir', tests[testIndex][0]],
]},
function () {
f.click();
});
}
launchNextTest();
</script>
</pre>
</body>
</html>

View File

@ -44,7 +44,7 @@ StructuredCloneData::Copy(const StructuredCloneData& aData)
MOZ_ASSERT(BlobImpls().IsEmpty());
BlobImpls().AppendElements(aData.BlobImpls());
MOZ_ASSERT(GetImages().IsEmpty());
MOZ_ASSERT(GetSurfaces().IsEmpty());
return true;
}

View File

@ -790,6 +790,7 @@ MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */);
MOZ_ASSERT(!mIsDormant, "should be out of dormant by now");
MOZ_ASSERT(aTime >= 0.0, "Cannot seek to a negative value.");
int64_t timeUsecs = 0;

View File

@ -782,6 +782,17 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
self->WaitRequestRef(aRejection.mType).Complete();
}));
// We are out of data to decode and will enter buffering mode soon.
// We want to play the frames we have already decoded, so we stop pre-rolling
// and ensure that loadeddata is fired as required.
if (isAudio) {
StopPrerollingAudio();
} else {
StopPrerollingVideo();
}
if (mState == DECODER_STATE_BUFFERING || mState == DECODER_STATE_DECODING) {
MaybeFinishDecodeFirstFrame();
}
return;
}
@ -1481,7 +1492,7 @@ MediaDecoderStateMachine::Seek(SeekTarget aTarget)
return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__);
}
NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
MOZ_ASSERT(mState > DECODER_STATE_DECODING_METADATA,
"We should have got duration already");
if (mState < DECODER_STATE_DECODING ||

View File

@ -9,6 +9,7 @@
#include "mozilla/AbstractThread.h"
#include "mozilla/Atomics.h"
#include "mozilla/IndexSequence.h"
#include "mozilla/Mutex.h"
#include "mozilla/Tuple.h"
#include "mozilla/TypeTraits.h"

View File

@ -546,6 +546,9 @@ MediaFormatReader::OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure
NotifyError(aTrack);
break;
case DemuxerFailureReason::WAITING_FOR_DATA:
if (!decoder.mWaitingForData) {
decoder.mNeedDraining = true;
}
NotifyWaitingForData(aTrack);
break;
case DemuxerFailureReason::CANCELED:
@ -691,6 +694,9 @@ MediaFormatReader::NotifyWaitingForData(TrackType aTrack)
MOZ_ASSERT(OnTaskQueue());
auto& decoder = GetDecoderData(aTrack);
decoder.mWaitingForData = true;
if (decoder.mTimeThreshold) {
decoder.mTimeThreshold.ref().mWaiting = true;
}
ScheduleUpdate(aTrack);
}
@ -760,17 +766,31 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack)
if (!decoder.mReceivedNewData) {
return false;
}
decoder.mReceivedNewData = false;
decoder.mWaitingForData = false;
bool hasLastEnd;
media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
// Update our cached TimeRange.
decoder.mTimeRanges = decoder.mTrackDemuxer->GetBuffered();
if (decoder.mTimeRanges.Length() &&
(!hasLastEnd || decoder.mTimeRanges.GetEnd() > lastEnd)) {
if (decoder.mDrainComplete || decoder.mDraining) {
// We do not want to clear mWaitingForData or mDemuxEOS while
// a drain is in progress in order to properly complete the operation.
return false;
}
bool hasLastEnd;
media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
if (hasLastEnd) {
if (decoder.mLastTimeRangesEnd && decoder.mLastTimeRangesEnd.ref() > lastEnd) {
// New data was added after our previous end, we can clear the EOS flag.
decoder.mDemuxEOS = false;
}
decoder.mLastTimeRangesEnd = Some(lastEnd);
}
decoder.mReceivedNewData = false;
if (decoder.mTimeThreshold) {
decoder.mTimeThreshold.ref().mWaiting = false;
}
decoder.mWaitingForData = false;
if (decoder.mError) {
return false;
@ -808,6 +828,8 @@ MediaFormatReader::RequestDemuxSamples(TrackType aTrack)
if (decoder.mDemuxEOS) {
// Nothing left to demux.
// We do not want to attempt to demux while in waiting for data mode
// as it would retrigger an unecessary drain.
return;
}
@ -883,6 +905,7 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
info->GetID());
decoder.mInfo = info;
decoder.mLastStreamSourceID = info->GetID();
decoder.mNextStreamSourceID.reset();
// Flush will clear our array of queued samples. So make a copy now.
nsTArray<RefPtr<MediaRawData>> samples{decoder.mQueuedSamples};
Flush(aTrack);
@ -892,38 +915,11 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
decoder.mQueuedSamples.AppendElements(Move(samples));
NotifyDecodingRequested(aTrack);
} else {
MOZ_ASSERT(decoder.mTimeThreshold.isNothing());
SeekTarget seekTarget =
decoder.mTimeThreshold.refOr(SeekTarget(TimeUnit::FromMicroseconds(sample->mTime), false));
LOG("Stream change occurred on a non-keyframe. Seeking to:%lld",
sample->mTime);
decoder.mTimeThreshold = Some(TimeUnit::FromMicroseconds(sample->mTime));
RefPtr<MediaFormatReader> self = this;
decoder.ResetDemuxer();
decoder.mSeekRequest.Begin(decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref())
->Then(OwnerThread(), __func__,
[self, aTrack] (media::TimeUnit aTime) {
auto& decoder = self->GetDecoderData(aTrack);
decoder.mSeekRequest.Complete();
self->NotifyDecodingRequested(aTrack);
},
[self, aTrack] (DemuxerFailureReason aResult) {
auto& decoder = self->GetDecoderData(aTrack);
decoder.mSeekRequest.Complete();
switch (aResult) {
case DemuxerFailureReason::WAITING_FOR_DATA:
self->NotifyWaitingForData(aTrack);
break;
case DemuxerFailureReason::END_OF_STREAM:
self->NotifyEndOfStream(aTrack);
break;
case DemuxerFailureReason::CANCELED:
case DemuxerFailureReason::SHUTDOWN:
break;
default:
self->NotifyError(aTrack);
break;
}
decoder.mTimeThreshold.reset();
}));
seekTarget.mTime.ToMicroseconds());
InternalSeek(aTrack, seekTarget);
}
return;
}
@ -957,6 +953,42 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
decoder.mInputExhausted = false;
}
void
MediaFormatReader::InternalSeek(TrackType aTrack, const SeekTarget& aTarget)
{
MOZ_ASSERT(OnTaskQueue());
auto& decoder = GetDecoderData(aTrack);
decoder.mTimeThreshold = Some(aTarget);
RefPtr<MediaFormatReader> self = this;
decoder.ResetDemuxer();
decoder.mSeekRequest.Begin(decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref().mTime)
->Then(OwnerThread(), __func__,
[self, aTrack] (media::TimeUnit aTime) {
auto& decoder = self->GetDecoderData(aTrack);
decoder.mSeekRequest.Complete();
self->NotifyDecodingRequested(aTrack);
},
[self, aTrack] (DemuxerFailureReason aResult) {
auto& decoder = self->GetDecoderData(aTrack);
decoder.mSeekRequest.Complete();
switch (aResult) {
case DemuxerFailureReason::WAITING_FOR_DATA:
self->NotifyWaitingForData(aTrack);
break;
case DemuxerFailureReason::END_OF_STREAM:
self->NotifyEndOfStream(aTrack);
break;
case DemuxerFailureReason::CANCELED:
case DemuxerFailureReason::SHUTDOWN:
break;
default:
self->NotifyError(aTrack);
break;
}
decoder.mTimeThreshold.reset();
}));
}
void
MediaFormatReader::DrainDecoder(TrackType aTrack)
{
@ -992,7 +1024,6 @@ MediaFormatReader::Update(TrackType aTrack)
LOGV("Processing update for %s", TrackTypeToStr(aTrack));
bool needInput = false;
bool needOutput = false;
auto& decoder = GetDecoderData(aTrack);
decoder.mUpdateScheduled = false;
@ -1006,87 +1037,108 @@ MediaFormatReader::Update(TrackType aTrack)
return;
}
if (!decoder.HasPromise() && decoder.mWaitingForData) {
// Nothing more we can do at present.
LOGV("Still waiting for data.");
return;
}
// Record number of frames decoded and parsed. Automatically update the
// stats counters using the AutoNotifyDecoded stack-based class.
AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder);
if (aTrack == TrackInfo::kVideoTrack) {
uint64_t delta =
decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames;
a.mDecoded = static_cast<uint32_t>(delta);
mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal;
// Drop any frames found prior our internal seek target.
while (decoder.mTimeThreshold && decoder.mOutput.Length()) {
RefPtr<MediaData>& output = decoder.mOutput[0];
SeekTarget target = decoder.mTimeThreshold.ref();
media::TimeUnit time = media::TimeUnit::FromMicroseconds(output->mTime);
if (time >= target.mTime) {
// We have reached our internal seek target.
decoder.mTimeThreshold.reset();
}
if (time < target.mTime || target.mDropTarget) {
LOGV("Internal Seeking: Dropping %s frame time:%f wanted:%f (kf:%d)",
TrackTypeToStr(aTrack),
media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(),
target.mTime.ToSeconds(),
output->mKeyframe);
decoder.mOutput.RemoveElementAt(0);
}
}
if (decoder.HasPromise()) {
needOutput = true;
if (!decoder.mOutput.IsEmpty()) {
if (decoder.mOutput.Length()) {
// We have a decoded sample ready to be returned.
if (aTrack == TrackType::kVideoTrack) {
uint64_t delta =
decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames;
a.mDecoded = static_cast<uint32_t>(delta);
mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal;
nsCString error;
mVideo.mIsHardwareAccelerated =
mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated(error);
}
while (decoder.mOutput.Length()) {
RefPtr<MediaData> output = decoder.mOutput[0];
decoder.mOutput.RemoveElementAt(0);
decoder.mSizeOfQueue -= 1;
if (decoder.mTimeThreshold.isNothing() ||
media::TimeUnit::FromMicroseconds(output->mTime) >= decoder.mTimeThreshold.ref()) {
decoder.mLastSampleTime =
Some(media::TimeUnit::FromMicroseconds(output->mTime));
ReturnOutput(output, aTrack);
decoder.mTimeThreshold.reset();
break;
} else {
LOGV("Internal Seeking: Dropping frame time:%f wanted:%f (kf:%d)",
media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(),
decoder.mTimeThreshold.ref().ToSeconds(),
output->mKeyframe);
}
}
} else if (decoder.mError) {
LOG("Rejecting %s promise: DECODE_ERROR", TrackTypeToStr(aTrack));
decoder.RejectPromise(DECODE_ERROR, __func__);
return;
} else if (decoder.mDrainComplete) {
bool wasDraining = decoder.mDraining;
decoder.mDrainComplete = false;
decoder.mDraining = false;
if (decoder.mError) {
LOG("Decoding Error");
decoder.RejectPromise(DECODE_ERROR, __func__);
return;
} else if (decoder.mDemuxEOS) {
if (decoder.mDemuxEOS) {
LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack));
decoder.RejectPromise(END_OF_STREAM, __func__);
}
} else if (decoder.mError) {
decoder.RejectPromise(DECODE_ERROR, __func__);
return;
} else if (decoder.mWaitingForData) {
LOG("Waiting For Data");
if (wasDraining && decoder.mLastSampleTime &&
!decoder.mNextStreamSourceID) {
// We have completed draining the decoder following WaitingForData.
// Set up the internal seek machinery to be able to resume from the
// last sample decoded.
LOG("Seeking to last sample time: %lld",
decoder.mLastSampleTime.ref().ToMicroseconds());
InternalSeek(aTrack, SeekTarget(decoder.mLastSampleTime.ref(), true));
}
LOG("Rejecting %s promise: WAITING_FOR_DATA", TrackTypeToStr(aTrack));
decoder.RejectPromise(WAITING_FOR_DATA, __func__);
}
// Now that draining has completed, we check if we have received
// new data again as the result may now be different from the earlier
// run.
if (UpdateReceivedNewData(aTrack)) {
LOGV("Nothing more to do");
return;
}
}
}
if (decoder.mNeedDraining) {
DrainDecoder(aTrack);
return;
}
if (!NeedInput(decoder)) {
bool needInput = NeedInput(decoder);
LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d ahead:%d sid:%u",
TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted,
decoder.mNumSamplesInput, decoder.mNumSamplesOutput,
uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()),
decoder.mWaitingForData, !decoder.HasPromise(), decoder.mLastStreamSourceID);
if (decoder.mWaitingForData &&
(!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) {
// Nothing more we can do at present.
LOGV("Still waiting for data.");
return;
}
if (!needInput) {
LOGV("No need for additional input (pending:%u)",
uint32_t(decoder.mOutput.Length()));
return;
}
needInput = true;
LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u ahead:%d sid:%u",
TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted,
decoder.mNumSamplesInput, decoder.mNumSamplesOutput,
uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()),
!decoder.HasPromise(), decoder.mLastStreamSourceID);
// Demux samples if we don't have some.
RequestDemuxSamples(aTrack);

View File

@ -127,6 +127,22 @@ private:
// Decode any pending already demuxed samples.
bool DecodeDemuxedSamples(TrackType aTrack,
MediaRawData* aSample);
struct SeekTarget {
SeekTarget(const media::TimeUnit& aTime, bool aDropTarget)
: mTime(aTime)
, mDropTarget(aDropTarget)
, mWaiting(false)
{}
media::TimeUnit mTime;
bool mDropTarget;
bool mWaiting;
};
// Perform an internal seek to aTime. If aDropTarget is true then
// the first sample past the target will be dropped.
void InternalSeek(TrackType aTrack, const SeekTarget& aTarget);
// Drain the current decoder.
void DrainDecoder(TrackType aTrack);
void NotifyNewOutput(TrackType aTrack, MediaData* aSample);
@ -261,8 +277,11 @@ private:
bool mDraining;
bool mDrainComplete;
// If set, all decoded samples prior mTimeThreshold will be dropped.
// Used for internal seeking when a change of stream is detected.
Maybe<media::TimeUnit> mTimeThreshold;
// Used for internal seeking when a change of stream is detected or when
// encountering data discontinuity.
Maybe<SeekTarget> mTimeThreshold;
// Time of last sample returned.
Maybe<media::TimeUnit> mLastSampleTime;
// Decoded samples returned my mDecoder awaiting being returned to
// state machine upon request.
@ -300,6 +319,7 @@ private:
mDraining = false;
mDrainComplete = false;
mTimeThreshold.reset();
mLastSampleTime.reset();
mOutput.Clear();
mNumSamplesInput = 0;
mNumSamplesOutput = 0;
@ -316,6 +336,7 @@ private:
uint32_t mLastStreamSourceID;
Maybe<uint32_t> mNextStreamSourceID;
media::TimeIntervals mTimeRanges;
Maybe<media::TimeUnit> mLastTimeRangesEnd;
RefPtr<SharedTrackInfo> mInfo;
};

View File

@ -480,7 +480,10 @@ DecodedStream::CreateData(MozPromiseHolder<GenericPromise>&& aPromise)
self->mOutputStreamManager.Disconnect();
delete data;
});
AbstractThread::MainThread()->Dispatch(r.forget());
// We are in tail dispatching phase. Don't call
// AbstractThread::MainThread()->Dispatch() to avoid reentrant
// AutoTaskDispatcher.
NS_DispatchToMainThread(r.forget());
}
}
RefPtr<DecodedStream> mThis;

View File

@ -4,6 +4,7 @@
* 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 "MediaQueue.h"
#include "VideoSink.h"
namespace mozilla {

View File

@ -295,7 +295,7 @@ MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
, mManager(aManager)
, mType(aType)
, mMonitor("MediaSourceTrackDemuxer")
, mLastSeek(Some(TimeUnit()))
, mReset(true)
{
}
@ -328,7 +328,8 @@ MediaSourceTrackDemuxer::Reset()
RefPtr<MediaSourceTrackDemuxer> self = this;
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableFunction([self] () {
self->mLastSeek = Some(TimeUnit());
self->mNextSample.reset();
self->mReset = true;
self->mManager->Seek(self->mType, TimeUnit(), TimeUnit());
{
MonitorAutoLock mon(self->mMonitor);
@ -380,42 +381,50 @@ 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__);
}
TimeUnit seekTime =
mManager->Seek(mType, aTime, MediaSourceDemuxer::EOS_FUZZ);
bool error;
RefPtr<MediaRawData> sample =
mManager->GetSample(mType,
media::TimeUnit(),
error);
MOZ_ASSERT(!error && sample);
mNextSample = Some(sample);
mReset = false;
{
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 (mReset) {
// 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())) {
if (!buffered.Contains(TimeUnit::FromMicroseconds(0))) {
return SamplesPromise::CreateAndReject(
mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
DemuxerFailureReason::WAITING_FOR_DATA, __func__);
}
mLastSeek.reset();
mReset = false;
}
bool error;
RefPtr<MediaRawData> sample =
mManager->GetSample(mType,
MediaSourceDemuxer::EOS_FUZZ,
error);
bool error = false;
RefPtr<MediaRawData> sample;
if (mNextSample) {
sample = mNextSample.ref();
mNextSample.reset();
} else {
sample = mManager->GetSample(mType, MediaSourceDemuxer::EOS_FUZZ, error);
if (!sample) {
if (error) {
return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
@ -424,6 +433,7 @@ MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples)
mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
DemuxerFailureReason::WAITING_FOR_DATA, __func__);
}
}
RefPtr<SamplesHolder> samples = new SamplesHolder;
samples->mSamples.AppendElement(sample);
if (mNextRandomAccessPoint.ToMicroseconds() <= sample->mTime) {

View File

@ -129,7 +129,10 @@ private:
// Monitor protecting members below accessed from multiple threads.
Monitor mMonitor;
media::TimeUnit mNextRandomAccessPoint;
Maybe<media::TimeUnit> mLastSeek;
Maybe<RefPtr<MediaRawData>> mNextSample;
// Set to true following a reset. Ensure that the next sample demuxed
// is available at position 0.
bool mReset;
};
} // namespace mozilla

View File

@ -40,7 +40,9 @@ skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac"))
[test_BufferingWait.html]
skip-if = toolkit == 'android' #timeout android bug 1199531
[test_BufferingWait_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_DrainOnMissingData_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_EndOfStream.html]
skip-if = (true || toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187 and bug 1182946
[test_EndOfStream_mp4.html]
@ -51,7 +53,7 @@ skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac"))
[test_FrameSelection.html]
[test_HaveMetadataUnbufferedSeek.html]
[test_HaveMetadataUnbufferedSeek_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_LoadedDataFired_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_LoadedMetadataFired.html]
@ -98,11 +100,11 @@ skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac"))
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_TruncatedDuration.html]
[test_TruncatedDuration_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_WaitingOnMissingData.html]
skip-if = true # Disabled due to bug 1124493 and friends. WebM MSE is deprioritized.
[test_WaitingOnMissingData_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_WaitingToEndedTransition_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+

View File

@ -41,14 +41,15 @@ runWithMSE(function(ms, v) {
/* Note - Missing |46712, 67833 - 46712| segment here corresponding to (0.8, 1.2] */
/* Note - Missing |67833, 88966 - 67833| segment here corresponding to (1.2, 1.6] */
loadSegment.bind(null, sb, new Uint8Array(arrayBuffer, 88966))).then(function() {
var promise = waitUntilTime(0.27);
// 0.767 is the time of the last video sample +- 40ms.
var promise = waitUntilTime(.767-0.04);
info("Playing video. It should play for a bit, then fire 'waiting'");
v.play();
return promise;
}).then(function() {
window.firstStop = Date.now();
loadSegment(sb, new Uint8Array(arrayBuffer, 46712, 67833 - 46712));
return waitUntilTime(0.66);
return waitUntilTime(1.167-0.04);
}).then(function() {
var waitDuration = (Date.now() - window.firstStop) / 1000;
ok(waitDuration < 15, "Should not spend an inordinate amount of time buffering: " + waitDuration);

View File

@ -41,18 +41,16 @@ runWithMSE(function(ms, v) {
/* Note - Missing |bipbop4| segment here corresponding to (2.41, 3.20] */
.then(fetchAndLoad.bind(null, sb, 'bipbop/bipbop', ['5'], '.m4s'))
.then(function() {
// Some decoders (Windows in particular) may keep up to 25 frames queued
// before returning a sample. 0.7 is 1.62s - 25 * 0.03333
var promise = waitUntilTime(0.7);
// last audio sample has a start time of 1.578956s
var promise = waitUntilTime(1.57895);
info("Playing video. It should play for a bit, then fire 'waiting'");
v.play();
return promise;
}).then(function() {
window.firstStop = Date.now();
fetchAndLoad(sb, 'bipbop/bipbop', ['3'], '.m4s');
// Some decoders (Windows in particular) may keep up to 25 frames queued
// before returning a sample. 1.5 is 2.41s - 25 * 0.03333
return waitUntilTime(1.5);
// last audio sample has a start time of 2.368435
return waitUntilTime(2.36843);
}).then(function() {
var waitDuration = (Date.now() - window.firstStop) / 1000;
ok(waitDuration < 15, "Should not spend an inordinate amount of time buffering: " + waitDuration);

View File

@ -0,0 +1,60 @@
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=windows-1252">
<title>MSE: |waiting| event when source data is missing</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="mediasource.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test"><script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
runWithMSE(function(ms, el) {
el.controls = true;
once(ms, 'sourceopen').then(function() {
ok(true, "Receive a sourceopen event");
var videosb = ms.addSourceBuffer("video/mp4");
fetchAndLoad(videosb, 'bipbop/bipbop_video', ['init'], '.mp4')
.then(function() {
// Set appendWindowEnd to ensure we only have about 6 frames worth.
// We must feed at least 6 frames to pass the MDSM pre-roll.
videosb.appendWindowEnd = .4;
return fetchAndLoad(videosb, 'bipbop/bipbop_video', ['1'], '.m4s');
})
.then(function() {
info("Invoking play()");
var promises = [];
promises.push(once(el, 'playing'));
el.play();
return Promise.all(promises);
})
.then(function() {
info("got playing");
return once(el, 'waiting');
}).then(function() {
info("got waiting");
info("Loading more data");
// Waiting will be fired on the last frame +- 40ms.
isfuzzy(el.currentTime, videosb.buffered.end(0) - 1/30,
0.04, "Got a waiting event at " + el.currentTime);
videosb.appendWindowEnd = 1;
var p = once(el, 'ended');
var loads = fetchAndLoad(videosb, 'bipbop/bipbop_video', [1], '.m4s');
loads.then(() => ms.endOfStream());
return p;
}).then(function() {
// These fuzz factors are bigger than they should be. We should investigate
// and fix them in bug 1137574.
is(el.duration, 0.801666, "Video has correct duration: " + el.duration);
is(el.currentTime, el.duration, "Video has correct currentTime.");
SimpleTest.finish();
});
});
});
</script>
</pre>
</body>
</html>

View File

@ -24,6 +24,18 @@ SimpleTest.waitForExplicitFinish();
runWithMSE(function(ms, el) {
el.controls = true;
once(ms, 'sourceopen').then(function() {
// Log events for debugging.
var events = ["suspend", "play", "canplay", "canplaythrough", "loadstart", "loadedmetadata",
"loadeddata", "playing", "ended", "error", "stalled", "emptied", "abort",
"waiting", "pause", "durationchange", "seeking", "seeked"];
function logEvent(e) {
var v = e.target;
info("got " + e.type + " event");
}
events.forEach(function(e) {
el.addEventListener(e, logEvent, false);
});
ok(true, "Receive a sourceopen event");
var videosb = ms.addSourceBuffer("video/mp4");
el.addEventListener("error", function(e) {
@ -37,9 +49,6 @@ runWithMSE(function(ms, el) {
var promises = [];
promises.push(once(el, 'loadeddata'));
promises.push(once(el, 'canplay'));
// Load [0, 1.601666). We must ensure that we load over 25 frames as the
// windows H264 decoder will not produce a sample until then
// (bug 1191138).
promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(1, 3), '.m4s'));
return Promise.all(promises);
})

View File

@ -40,9 +40,9 @@ runWithMSE(function(ms, el) {
// currentTime is based on the current video frame, so if the audio ends just before
// the next video frame, currentTime can be up to 1 frame's worth earlier than
// min(audioEnd, videoEnd).
// Some decoders (Windows in particular) may keep up to 25 frames queued.
isfuzzy(el.currentTime, Math.min(audiosb.buffered.end(0), videosb.buffered.end(0)) - 1/60,
25 * 1/30, "Got a waiting event at " + el.currentTime);
// 0.0465 is the length of the last audio frame.
ok(el.currentTime >= (Math.min(audiosb.buffered.end(0), videosb.buffered.end(0)) - 0.0465),
"Got a waiting event at " + el.currentTime);
info("Loading more data");
var p = once(el, 'ended');
var loads = Promise.all([fetchAndLoad(audiosb, 'bipbop/bipbop_audio', [5], '.m4s'),

View File

@ -17,6 +17,10 @@ runWithMSE(function(ms, el) {
ok(true, "Receive a sourceopen event");
var audiosb = ms.addSourceBuffer("audio/mp4");
var videosb = ms.addSourceBuffer("video/mp4");
// ensure tracks end at approximately the same time to ensure ended event is
// always fired (bug 1233639).
audiosb.appendWindowEnd = 3.9;
videosb.appendWindowEnd = 3.9;
fetchAndLoad(audiosb, 'bipbop/bipbop_audio', ['init'], '.mp4')
.then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', ['init'], '.mp4'))
.then(fetchAndLoad.bind(null, audiosb, 'bipbop/bipbop_audio', range(1, 5), '.m4s'))
@ -34,19 +38,12 @@ runWithMSE(function(ms, el) {
var p = once(el, 'waiting');
el.play();
return p;
}).then(function() {
// currentTime is based on the current video frame, so if the audio ends just before
// the next video frame, currentTime can be up to 1 frame's worth earlier than
// min(audioEnd, videoEnd).
// Some decoders (Windows in particular) may keep up to 25 frames queued.
isfuzzy(el.currentTime, Math.min(audiosb.buffered.end(0), videosb.buffered.end(0)) - 1/60,
25 * 1/30, "Got a waiting event at " + el.currentTime);
}).then(function() {
var p = once(el, 'ended');
ms.endOfStream();
return p;
}).then(function() {
is(el.duration, 4.005, "Video has correct duration: " + el.duration);
is(el.duration, 3.854512, "Video has correct duration: " + el.duration);
is(el.currentTime, el.duration, "Video has correct currentTime.");
SimpleTest.finish();
});

View File

@ -226,23 +226,12 @@ OmxDataDecoder::DoAsyncShutdown()
mWatchManager.Unwatch(mOmxState, &OmxDataDecoder::OmxStateRunner);
mWatchManager.Unwatch(mPortSettingsChanged, &OmxDataDecoder::PortSettingsChanged);
// Do flush so all port can be returned to client.
// Flush to all ports, so all buffers can be returned from component.
RefPtr<OmxDataDecoder> self = this;
mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
->Then(mOmxTaskQueue, __func__,
[self] () -> RefPtr<OmxCommandPromise> {
LOG("DoAsyncShutdown: flush complete, collecting buffers...");
self->CollectBufferPromises(OMX_DirMax)
->Then(self->mOmxTaskQueue, __func__,
[self] () {
LOG("DoAsyncShutdown: releasing all buffers.");
self->ReleaseBuffers(OMX_DirInput);
self->ReleaseBuffers(OMX_DirOutput);
},
[self] () {
self->mOmxLayer->Shutdown();
});
LOG("DoAsyncShutdown: flush complete");
return self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr);
},
[self] () {
@ -251,8 +240,30 @@ OmxDataDecoder::DoAsyncShutdown()
->CompletionPromise()
->Then(mOmxTaskQueue, __func__,
[self] () -> RefPtr<OmxCommandPromise> {
LOG("DoAsyncShutdown: OMX_StateIdle");
return self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateLoaded, nullptr);
RefPtr<OmxCommandPromise> p =
self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateLoaded, nullptr);
LOG("DoAsyncShutdown: collecting buffers...");
self->CollectBufferPromises(OMX_DirMax)
->Then(self->mOmxTaskQueue, __func__,
[self] () {
// According to spec 3.1.1.2.2.1:
// OMX_StateLoaded needs to be sent before releasing buffers.
// And state transition from OMX_StateIdle to OMX_StateLoaded
// is completed when all of the buffers have been removed
// from the component.
// Here the buffer promises are not resolved due to displaying
// in layer, it needs to wait before the layer returns the
// buffers.
LOG("DoAsyncShutdown: all buffers collected, releasing buffers...");
self->ReleaseBuffers(OMX_DirInput);
self->ReleaseBuffers(OMX_DirOutput);
},
[self] () {
self->mOmxLayer->Shutdown();
});
return p;
},
[self] () {
self->mOmxLayer->Shutdown();

View File

@ -196,7 +196,7 @@ MediaEngineTabVideoSource::NotifyPull(MediaStreamGraph*,
MonitorAutoLock mon(mMonitor);
// Note: we're not giving up mImage here
RefPtr<layers::CairoImage> image = mImage;
RefPtr<layers::SourceSurfaceImage> image = mImage;
StreamTime delta = aDesiredTime - aSource->GetEndOfAppendedData(aID);
if (delta > 0) {
// nullptr images are allowed
@ -298,7 +298,7 @@ MediaEngineTabVideoSource::Draw() {
return;
}
RefPtr<layers::CairoImage> image = new layers::CairoImage(size, surface);
RefPtr<layers::SourceSurfaceImage> image = new layers::SourceSurfaceImage(size, surface);
MonitorAutoLock mon(mMonitor);
mImage = image;

View File

@ -88,7 +88,7 @@ private:
ScopedFreePtr<unsigned char> mData;
size_t mDataSize;
nsCOMPtr<nsIDOMWindow> mWindow;
RefPtr<layers::CairoImage> mImage;
RefPtr<layers::SourceSurfaceImage> mImage;
nsCOMPtr<nsITimer> mTimer;
Monitor mMonitor;
nsCOMPtr<nsITabSource> mTabSource;

View File

@ -957,7 +957,7 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect,
RefPtr<gfx::SourceSurface> sourceSurface =
gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr, surface);
RefPtr<CairoImage> image = new CairoImage(surface->GetSize(), sourceSurface);
RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(surface->GetSize(), sourceSurface);
nsAutoTArray<ImageContainer::NonOwningImage,1> imageList;
imageList.AppendElement(

View File

@ -139,62 +139,6 @@ CSPService::ShouldLoad(uint32_t aContentType,
return NS_OK;
}
// ----- THIS IS A TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
// ----- PLEASE REMOVE ONCE bug 925004 LANDS. -----
// Cache the app status for this origin.
uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
nsAutoCString sourceOrigin;
if (aRequestPrincipal && aRequestOrigin) {
aRequestOrigin->GetPrePath(sourceOrigin);
if (!mAppStatusCache.Get(sourceOrigin, &status)) {
aRequestPrincipal->GetAppStatus(&status);
mAppStatusCache.Put(sourceOrigin, status);
}
}
if (status == nsIPrincipal::APP_STATUS_CERTIFIED) {
// The CSP for certified apps is :
// "default-src * data: blob:; script-src 'self'; object-src 'none'; style-src 'self' app://theme.gaiamobile.org:*"
// That means we can optimize for this case by:
// - loading same origin scripts and stylesheets, and stylesheets from the
// theme url space.
// - never loading objects.
// - accepting everything else.
switch (aContentType) {
case nsIContentPolicy::TYPE_SCRIPT:
case nsIContentPolicy::TYPE_STYLESHEET:
{
// Whitelist the theme resources.
auto themeOrigin = Preferences::GetCString("b2g.theme.origin");
nsAutoCString contentOrigin;
aContentLocation->GetPrePath(contentOrigin);
if (!(sourceOrigin.Equals(contentOrigin) ||
(themeOrigin && themeOrigin.Equals(contentOrigin)))) {
*aDecision = nsIContentPolicy::REJECT_SERVER;
}
}
break;
case nsIContentPolicy::TYPE_OBJECT:
*aDecision = nsIContentPolicy::REJECT_SERVER;
break;
default:
*aDecision = nsIContentPolicy::ACCEPT;
}
// Only cache and return if we are successful. If not, we want the error
// to be reported, and thus fallback to the slow path.
if (*aDecision == nsIContentPolicy::ACCEPT) {
return NS_OK;
}
}
// ----- END OF TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
// query the principal of the document; if no document is passed, then
// fall back to using the requestPrincipal (e.g. service workers do not
// pass a document).

View File

@ -703,6 +703,8 @@ var interfaceNamesInGlobalScope =
"Image",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageBitmap",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageBitmapRenderingContext",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "ImageCapture", disabled: true},
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -11,7 +11,7 @@
*/
[Constructor,
Exposed=(Window,System)]
Exposed=(Window,Worker,System)]
interface FileReader : EventTarget {
// async read methods
[Throws]

View File

@ -23,6 +23,18 @@ interface ImageBitmap {
readonly attribute unsigned long height;
};
// It's crucial that there be a way to explicitly dispose of ImageBitmaps
// since they refer to potentially large graphics resources. Some uses
// of this API proposal will result in repeated allocations of ImageBitmaps,
// and garbage collection will not reliably reclaim them quickly enough.
// Here we reuse close(), which also exists on another Transferable type,
// MessagePort. Potentially, all Transferable types should inherit from a
// new interface type "Closeable".
partial interface ImageBitmap {
// Dispose of all graphical resources associated with this ImageBitmap.
void close();
};
[NoInterfaceObject, Exposed=(Window,Worker)]
interface ImageBitmapFactories {
[Throws]

View File

@ -0,0 +1,36 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* https://wiki.whatwg.org/wiki/OffscreenCanvas
*
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
* Opera Software ASA. You are granted a license to use, reproduce
* and create derivative works of this document.
*/
// The new ImageBitmapRenderingContext is a canvas rendering context
// which only provides the functionality to replace the canvas's
// contents with the given ImageBitmap. Its context id (the first argument
// to getContext) is "bitmaprenderer".
[Exposed=(Window,Worker)]
interface ImageBitmapRenderingContext {
// Displays the given ImageBitmap in the canvas associated with this
// rendering context. Ownership of the ImageBitmap is transferred to
// the canvas. The caller may not use its reference to the ImageBitmap
// after making this call. (This semantic is crucial to enable prompt
// reclamation of expensive graphics resources, rather than relying on
// garbage collection to do so.)
//
// The ImageBitmap conceptually replaces the canvas's bitmap, but
// it does not change the canvas's intrinsic width or height.
//
// The ImageBitmap, when displayed, is clipped to the rectangle
// defined by the canvas's instrinsic width and height. Pixels that
// would be covered by the canvas's bitmap which are not covered by
// the supplied ImageBitmap are rendered transparent black. Any CSS
// styles affecting the display of the canvas are applied as usual.
void transferImageBitmap(ImageBitmap bitmap);
};

View File

@ -5,14 +5,10 @@
*
* For more information on this interface, please see
* https://wiki.whatwg.org/wiki/OffscreenCanvas
*
* Current implementation focus on transfer canvas from main thread to worker.
* So there are some spec doesn't implement, such as [Constructor], toBlob() and
* transferToImageBitmap in OffscreenCanvas. Bug 1172796 will implement
* remaining spec.
*/
[Exposed=(Window,Worker),
[Constructor(unsigned long width, unsigned long height),
Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabled"]
interface OffscreenCanvas : EventTarget {
[Pure, SetterThrows]
@ -23,6 +19,11 @@ interface OffscreenCanvas : EventTarget {
[Throws]
nsISupports? getContext(DOMString contextId,
optional any contextOptions = null);
ImageBitmap transferToImageBitmap();
[Throws]
Promise<Blob> toBlob(optional DOMString type = "",
optional any encoderOptions);
};
// OffscreenCanvas implements Transferable;

View File

@ -265,6 +265,7 @@ WEBIDL_FILES = [
'IDBTransaction.webidl',
'IDBVersionChangeEvent.webidl',
'ImageBitmap.webidl',
'ImageBitmapRenderingContext.webidl',
'ImageCapture.webidl',
'ImageData.webidl',
'ImageDocument.webidl',

View File

@ -629,6 +629,7 @@ private:
// If one load info cancels or hits an error, it can race with the start
// callback coming from another load info.
if (mCanceledMainThread || !mCacheCreator) {
aRequest->Cancel(NS_ERROR_FAILURE);
return NS_ERROR_FAILURE;
}

View File

@ -0,0 +1,29 @@
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.importGlobalProperties(["File"]);
var fileNum = 1;
function createFileWithData(fileData) {
var willDelete = fileData === null;
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
var testFile = dirSvc.get("ProfD", Ci.nsIFile);
testFile.append("fileAPItestfile" + fileNum);
fileNum++;
var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
0666, 0);
if (willDelete) {
fileData = "some irrelevant test data\n";
}
outStream.write(fileData, fileData.length);
outStream.close();
var domFile = new File(testFile);
if (willDelete) {
testFile.remove(/* recursive: */ false);
}
return domFile;
}
addMessageListener("files.open", function (message) {
sendAsyncMessage("files.opened", message.map(createFileWithData));
});

View File

@ -119,6 +119,8 @@ support-files =
worker_referrer.js
websocket_https.html
websocket_https_worker.js
worker_fileReader.js
fileapi_chromeScript.js
[test_404.html]
[test_atob.html]
@ -236,3 +238,4 @@ skip-if = (os == "win") || (os == "mac") || toolkit == 'android' #bug 798220
[test_referrer.html]
[test_sharedWorker_ports.html]
[test_sharedWorker_lifetime.html]
[test_fileReader.html]

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