зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
2b1a321946
2
CLOBBER
2
CLOBBER
|
@ -22,4 +22,4 @@
|
|||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 1212347 - Disable geckoview_example by default and forget build artifacts.
|
||||
Bug 1182727 - Update the clang toolchain
|
||||
|
|
|
@ -255,9 +255,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "ReaderParent",
|
||||
"resource:///modules/ReaderParent.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent",
|
||||
"resource://gre/modules/LoginManagerParent.jsm");
|
||||
|
||||
var gInitialPages = [
|
||||
"about:blank",
|
||||
"about:newtab",
|
||||
|
@ -1195,10 +1192,6 @@ var gBrowserInit = {
|
|||
}
|
||||
}, false, true);
|
||||
|
||||
gBrowser.addEventListener("InsecureLoginFormsStateChange", function() {
|
||||
gIdentityHandler.refreshForInsecureLoginForms();
|
||||
});
|
||||
|
||||
let uriToLoad = this._getUriToLoad();
|
||||
if (uriToLoad && uriToLoad != "about:blank") {
|
||||
if (uriToLoad instanceof Ci.nsISupportsArray) {
|
||||
|
@ -7037,7 +7030,10 @@ var gIdentityHandler = {
|
|||
}
|
||||
|
||||
// Then, update the user interface with the available data.
|
||||
this.refreshIdentityBlock();
|
||||
|
||||
if (this._identityBox) {
|
||||
this.refreshIdentityBlock();
|
||||
}
|
||||
|
||||
// NOTE: We do NOT update the identity popup (the control center) when
|
||||
// we receive a new security state. If the user opened the popup and looks
|
||||
|
@ -7045,20 +7041,6 @@ var gIdentityHandler = {
|
|||
// contents.
|
||||
},
|
||||
|
||||
/**
|
||||
* This is called asynchronously when requested by the Logins module, after
|
||||
* the insecure login forms state for the page has been updated.
|
||||
*/
|
||||
refreshForInsecureLoginForms() {
|
||||
// Check this._uri because we don't want to refresh the user interface if
|
||||
// this is called before the first page load in the window for any reason.
|
||||
if (!this._uri) {
|
||||
Cu.reportError("Unexpected early call to refreshForInsecureLoginForms.");
|
||||
return;
|
||||
}
|
||||
this.refreshIdentityBlock();
|
||||
},
|
||||
|
||||
/**
|
||||
* Attempt to provide proper IDN treatment for host names
|
||||
*/
|
||||
|
@ -7095,10 +7077,6 @@ var gIdentityHandler = {
|
|||
* Updates the identity block user interface with the data from this object.
|
||||
*/
|
||||
refreshIdentityBlock() {
|
||||
if (!this._identityBox) {
|
||||
return;
|
||||
}
|
||||
|
||||
let icon_label = "";
|
||||
let tooltip = "";
|
||||
let icon_country_label = "";
|
||||
|
@ -7167,11 +7145,6 @@ var gIdentityHandler = {
|
|||
this._identityBox.classList.add("weakCipher");
|
||||
}
|
||||
}
|
||||
if (LoginManagerParent.hasInsecureLoginForms(gBrowser.selectedBrowser)) {
|
||||
// Insecure login forms can only be present on "unknown identity"
|
||||
// pages, either already insecure or with mixed active content loaded.
|
||||
this._identityBox.classList.add("insecureLoginForms");
|
||||
}
|
||||
tooltip = gNavigatorBundle.getString("identity.unknown.tooltip");
|
||||
}
|
||||
|
||||
|
@ -7209,12 +7182,6 @@ var gIdentityHandler = {
|
|||
connection = "secure";
|
||||
}
|
||||
|
||||
// Determine if there are insecure login forms.
|
||||
let loginforms = "secure";
|
||||
if (LoginManagerParent.hasInsecureLoginForms(gBrowser.selectedBrowser)) {
|
||||
loginforms = "insecure";
|
||||
}
|
||||
|
||||
// Determine the mixed content state.
|
||||
let mixedcontent = [];
|
||||
if (this._isMixedPassiveContentLoaded) {
|
||||
|
@ -7252,7 +7219,6 @@ var gIdentityHandler = {
|
|||
for (let id of elementIDs) {
|
||||
let element = document.getElementById(id);
|
||||
updateAttribute(element, "connection", connection);
|
||||
updateAttribute(element, "loginforms", loginforms);
|
||||
updateAttribute(element, "ciphers", ciphers);
|
||||
updateAttribute(element, "mixedcontent", mixedcontent);
|
||||
updateAttribute(element, "isbroken", this._isBroken);
|
||||
|
|
|
@ -268,7 +268,7 @@ tags = mcb
|
|||
tags = mcb
|
||||
[browser_bug906190.js]
|
||||
tags = mcb
|
||||
skip-if = buildapp == "mulet" || e10s || os == "linux" # Bug 1093642 - test manipulates content and relies on content focus, Bug 1212520 - Re-enable on Linux
|
||||
skip-if = buildapp == "mulet" || e10s # Bug 1093642 - test manipulates content and relies on content focus
|
||||
[browser_mixedContentFromOnunload.js]
|
||||
tags = mcb
|
||||
[browser_mixedContentFramesOnHttp.js]
|
||||
|
@ -322,7 +322,6 @@ skip-if = e10s # Bug 863514 - no gesture support.
|
|||
[browser_homeDrop.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_identity_UI.js]
|
||||
[browser_insecureLoginForms.js]
|
||||
[browser_keywordBookmarklets.js]
|
||||
skip-if = e10s # Bug 1102025 - different principals for the bookmarklet only in e10s mode (unclear if test or 'real' issue)
|
||||
[browser_keywordSearch.js]
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Load directly from the browser-chrome support files of login tests.
|
||||
const testUrlPath =
|
||||
"://example.com/browser/toolkit/components/passwordmgr/test/browser/";
|
||||
|
||||
/**
|
||||
* Waits for the given number of occurrences of InsecureLoginFormsStateChange
|
||||
* on the given browser element.
|
||||
*/
|
||||
function waitForInsecureLoginFormsStateChange(browser, count) {
|
||||
return BrowserTestUtils.waitForEvent(browser, "InsecureLoginFormsStateChange",
|
||||
false, () => --count == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the insecure login forms logic for the identity block.
|
||||
*/
|
||||
add_task(function* test_simple() {
|
||||
for (let scheme of ["http", "https"]) {
|
||||
let tab = gBrowser.addTab(scheme + testUrlPath + "form_basic.html");
|
||||
let browser = tab.linkedBrowser;
|
||||
yield Promise.all([
|
||||
BrowserTestUtils.switchTab(gBrowser, tab),
|
||||
BrowserTestUtils.browserLoaded(browser),
|
||||
// One event is triggered by pageshow and one by DOMFormHasPassword.
|
||||
waitForInsecureLoginFormsStateChange(browser, 2),
|
||||
]);
|
||||
|
||||
let { gIdentityHandler } = gBrowser.ownerGlobal;
|
||||
gIdentityHandler._identityBox.click();
|
||||
document.getElementById("identity-popup-security-expander").click();
|
||||
|
||||
if (scheme == "http") {
|
||||
let identityBoxImage = gBrowser.ownerGlobal
|
||||
.getComputedStyle(document.getElementById("page-proxy-favicon"), "")
|
||||
.getPropertyValue("list-style-image");
|
||||
let securityViewBG = gBrowser.ownerGlobal
|
||||
.getComputedStyle(document.getElementById("identity-popup-securityView"), "")
|
||||
.getPropertyValue("background-image");
|
||||
let securityContentBG = gBrowser.ownerGlobal
|
||||
.getComputedStyle(document.getElementById("identity-popup-security-content"), "")
|
||||
.getPropertyValue("background-image");
|
||||
is(identityBoxImage,
|
||||
"url(\"chrome://browser/skin/identity-mixed-active-loaded.svg\")",
|
||||
"Using expected icon image in the identity block");
|
||||
is(securityViewBG,
|
||||
"url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
|
||||
"Using expected icon image in the Control Center main view");
|
||||
is(securityContentBG,
|
||||
"url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
|
||||
"Using expected icon image in the Control Center subview");
|
||||
}
|
||||
|
||||
// Messages should be visible when the scheme is HTTP, and invisible when
|
||||
// the scheme is HTTPS.
|
||||
is(Array.every(document.querySelectorAll("[when-loginforms=insecure]"),
|
||||
element => !is_hidden(element)),
|
||||
scheme == "http",
|
||||
"The relevant messages should visible or hidden.");
|
||||
|
||||
gIdentityHandler._identityPopup.hidden = true;
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks that the insecure login forms logic does not regress mixed content
|
||||
* blocking messages when mixed active content is loaded.
|
||||
*/
|
||||
add_task(function* test_mixedcontent() {
|
||||
yield new Promise(resolve => SpecialPowers.pushPrefEnv({
|
||||
"set": [["security.mixed_content.block_active_content", false]],
|
||||
}, resolve));
|
||||
|
||||
// Load the page with the subframe in a new tab.
|
||||
let tab = gBrowser.addTab("https" + testUrlPath + "insecure_test.html");
|
||||
let browser = tab.linkedBrowser;
|
||||
yield Promise.all([
|
||||
BrowserTestUtils.switchTab(gBrowser, tab),
|
||||
BrowserTestUtils.browserLoaded(browser),
|
||||
// Two events are triggered by pageshow and one by DOMFormHasPassword.
|
||||
waitForInsecureLoginFormsStateChange(browser, 3),
|
||||
]);
|
||||
|
||||
assertMixedContentBlockingState(browser, { activeLoaded: true,
|
||||
activeBlocked: false,
|
||||
passiveLoaded: false });
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
|
@ -890,13 +890,6 @@ function assertMixedContentBlockingState(tabbrowser, states = {}) {
|
|||
}
|
||||
}
|
||||
|
||||
if (activeLoaded || activeBlocked || passiveLoaded) {
|
||||
doc.getElementById("identity-popup-security-expander").click();
|
||||
is(Array.filter(doc.querySelectorAll("[observes=identity-popup-mcb-learn-more]"),
|
||||
element => !is_hidden(element)).length, 1,
|
||||
"The 'Learn more' link should be visible once.");
|
||||
}
|
||||
|
||||
gIdentityHandler._identityPopup.hidden = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
<description when-mixedcontent="active-loaded">&identity.activeLoaded;</description>
|
||||
<description class="identity-popup-warning-yellow"
|
||||
when-ciphers="weak">&identity.weakEncryption;</description>
|
||||
<description when-loginforms="insecure">&identity.insecureLoginForms;</description>
|
||||
</vbox>
|
||||
</vbox>
|
||||
<button id="identity-popup-security-expander"
|
||||
|
@ -117,11 +116,7 @@
|
|||
when-connection="secure secure-ev"/>
|
||||
|
||||
<!-- Connection is Not Secure -->
|
||||
<description when-connection="not-secure"
|
||||
and-when-loginforms="secure">&identity.description.insecure;</description>
|
||||
|
||||
<!-- Insecure login forms -->
|
||||
<description when-loginforms="insecure">&identity.description.insecureLoginForms;</description>
|
||||
<description when-connection="not-secure">&identity.description.insecure;</description>
|
||||
|
||||
<!-- Weak Cipher -->
|
||||
<description when-ciphers="weak">&identity.description.weakCipher;</description>
|
||||
|
@ -143,14 +138,8 @@
|
|||
class="identity-popup-warning-yellow">&identity.description.passiveLoaded3; <label observes="identity-popup-mcb-learn-more"/></description>
|
||||
|
||||
<!-- Active Mixed Content Blocking Disabled -->
|
||||
<description when-mixedcontent="active-loaded"
|
||||
and-when-loginforms="secure">&identity.description.activeLoaded;</description>
|
||||
<description when-mixedcontent="active-loaded"
|
||||
and-when-loginforms="secure">&identity.description.activeLoaded2; <label observes="identity-popup-mcb-learn-more"/></description>
|
||||
<!-- Show only the first message when there are insecure login forms,
|
||||
and make sure the Learn More link is included. -->
|
||||
<description when-mixedcontent="active-loaded"
|
||||
and-when-loginforms="insecure">&identity.description.activeLoaded; <label observes="identity-popup-mcb-learn-more"/></description>
|
||||
<description when-mixedcontent="active-loaded">&identity.description.activeLoaded;</description>
|
||||
<description when-mixedcontent="active-loaded">&identity.description.activeLoaded2; <label observes="identity-popup-mcb-learn-more"/></description>
|
||||
|
||||
<!-- Buttons to enable/disable mixed content blocking. -->
|
||||
<button when-mixedcontent="active-blocked"
|
||||
|
|
|
@ -61,20 +61,6 @@ function createLazy(fn) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given storage event was triggered by changes
|
||||
* to the sessionStorage object and not the local or globalStorage.
|
||||
*/
|
||||
function isSessionStorageEvent(event) {
|
||||
try {
|
||||
return event.storageArea == event.target.sessionStorage;
|
||||
} catch (ex if ex instanceof Ci.nsIException && ex.result == Cr.NS_ERROR_NOT_AVAILABLE) {
|
||||
// This page does not have a DOMSessionStorage
|
||||
// (this is typically the case for about: pages)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for and handles content events that we need for the
|
||||
* session store service to be notified of state changes in content.
|
||||
|
@ -520,7 +506,7 @@ var DocShellCapabilitiesListener = {
|
|||
*/
|
||||
var SessionStorageListener = {
|
||||
init: function () {
|
||||
addEventListener("MozStorageChanged", this, true);
|
||||
addEventListener("MozSessionStorageChanged", this, true);
|
||||
Services.obs.addObserver(this, "browser:purge-domain-data", false);
|
||||
gFrameTree.addObserver(this);
|
||||
},
|
||||
|
@ -530,8 +516,7 @@ var SessionStorageListener = {
|
|||
},
|
||||
|
||||
handleEvent: function (event) {
|
||||
// Ignore events triggered by localStorage or globalStorage changes.
|
||||
if (gFrameTree.contains(event.target) && isSessionStorageEvent(event)) {
|
||||
if (gFrameTree.contains(event.target)) {
|
||||
this.collect();
|
||||
}
|
||||
},
|
||||
|
|
|
@ -550,13 +550,13 @@ function modifySessionStorage(browser, data, options = {}) {
|
|||
let storage = frame.sessionStorage;
|
||||
|
||||
return new Promise(resolve => {
|
||||
addEventListener("MozStorageChanged", function onStorageChanged(event) {
|
||||
addEventListener("MozSessionStorageChanged", function onStorageChanged(event) {
|
||||
if (event.storageArea == storage) {
|
||||
keys.delete(event.key);
|
||||
}
|
||||
|
||||
if (keys.size == 0) {
|
||||
removeEventListener("MozStorageChanged", onStorageChanged, true);
|
||||
removeEventListener("MozSessionStorageChanged", onStorageChanged, true);
|
||||
resolve();
|
||||
}
|
||||
}, true);
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"clang_version": "r247539"
|
||||
},
|
||||
{
|
||||
"size": 93197192,
|
||||
"digest": "6ebd8994ac76cf6694c3d9054104219836f47578223c799cb9ba9669cfdee00112e9de56aea9d1e6d9d50ee94a201970555de19794b5fbb7546f58fdf8e59a99",
|
||||
"size": 105219872,
|
||||
"digest": "aa8de2fa535d0667e079019c475c631ea008f1bb5228505510867255b4d9c30663e2c97e579220a575a5887aa3bcf250021b50f76b90c2fa8c65a7aa19270066",
|
||||
"algorithm": "sha512",
|
||||
"filename": "clang.tar.xz",
|
||||
"unpack": true,
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"clang_version": "r247539"
|
||||
},
|
||||
{
|
||||
"size": 93197192,
|
||||
"digest": "6ebd8994ac76cf6694c3d9054104219836f47578223c799cb9ba9669cfdee00112e9de56aea9d1e6d9d50ee94a201970555de19794b5fbb7546f58fdf8e59a99",
|
||||
"size": 105219872,
|
||||
"digest": "aa8de2fa535d0667e079019c475c631ea008f1bb5228505510867255b4d9c30663e2c97e579220a575a5887aa3bcf250021b50f76b90c2fa8c65a7aa19270066",
|
||||
"algorithm": "sha512",
|
||||
"filename": "clang.tar.xz",
|
||||
"unpack": true
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"clang_version": "r247539"
|
||||
},
|
||||
{
|
||||
"size": 93197192,
|
||||
"digest": "6ebd8994ac76cf6694c3d9054104219836f47578223c799cb9ba9669cfdee00112e9de56aea9d1e6d9d50ee94a201970555de19794b5fbb7546f58fdf8e59a99",
|
||||
"size": 105219872,
|
||||
"digest": "aa8de2fa535d0667e079019c475c631ea008f1bb5228505510867255b4d9c30663e2c97e579220a575a5887aa3bcf250021b50f76b90c2fa8c65a7aa19270066",
|
||||
"algorithm": "sha512",
|
||||
"filename": "clang.tar.xz",
|
||||
"unpack": true
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"clang_version": "r247539"
|
||||
},
|
||||
{
|
||||
"size": 97314461,
|
||||
"digest": "9a74670fa917f760a4767923485d5166bbd258a8023c8aeb899b8c4d22f2847be76508ac5f26d7d2193318a2bb368a71bc62888d1bfe9d81eb45329a60451aa4",
|
||||
"size": 121393888,
|
||||
"digest": "6ae4e651e545538e6de326a7fb8b44b6e6d0b3acdb6a969ecb2b6f63b9995bbad2111cabf044ba575464f17f1f948d78ec92ad3a6922a7bfdf3ad6b6b2cad050",
|
||||
"algorithm": "sha512",
|
||||
"filename": "clang.tar.bz2",
|
||||
"unpack": true
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"clang_version": "r247539"
|
||||
},
|
||||
{
|
||||
"size": 93197192,
|
||||
"digest": "6ebd8994ac76cf6694c3d9054104219836f47578223c799cb9ba9669cfdee00112e9de56aea9d1e6d9d50ee94a201970555de19794b5fbb7546f58fdf8e59a99",
|
||||
"size": 105219872,
|
||||
"digest": "aa8de2fa535d0667e079019c475c631ea008f1bb5228505510867255b4d9c30663e2c97e579220a575a5887aa3bcf250021b50f76b90c2fa8c65a7aa19270066",
|
||||
"algorithm": "sha512",
|
||||
"filename": "clang.tar.xz",
|
||||
"unpack": true
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
%endif
|
||||
|
||||
/* Hide all conditional elements by default. */
|
||||
:-moz-any([when-connection],[when-mixedcontent],[when-ciphers],[when-loginforms]) {
|
||||
:-moz-any([when-connection],[when-mixedcontent],[when-ciphers]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,6 @@
|
|||
#identity-popup[connection=secure] [when-connection~=secure],
|
||||
#identity-popup[connection=chrome] [when-connection~=chrome],
|
||||
#identity-popup[connection=file] [when-connection~=file],
|
||||
/* Show insecure login forms messages when needed. */
|
||||
#identity-popup[loginforms=insecure] [when-loginforms=insecure],
|
||||
/* Show weak cipher messages when needed. */
|
||||
#identity-popup[ciphers=weak] [when-ciphers~=weak],
|
||||
/* Show mixed content warnings when needed */
|
||||
|
@ -30,14 +28,6 @@
|
|||
display: inherit;
|
||||
}
|
||||
|
||||
/* Hide redundant messages based on insecure login forms presence. */
|
||||
#identity-popup[loginforms=secure] [and-when-loginforms=insecure] {
|
||||
display: none;
|
||||
}
|
||||
#identity-popup[loginforms=insecure] [and-when-loginforms=secure] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide 'not secure' message in subview when weak cipher or mixed content messages are shown. */
|
||||
#identity-popup-securityView-body:-moz-any([mixedcontent],[ciphers]) > description[when-connection=not-secure],
|
||||
/* Hide 'passive-loaded (only)' message when there is mixed passive content loaded and active blocked. */
|
||||
|
@ -234,8 +224,6 @@
|
|||
background-image: url(chrome://browser/skin/controlcenter/conn-degraded.svg);
|
||||
}
|
||||
|
||||
#identity-popup[loginforms=insecure] #identity-popup-securityView,
|
||||
#identity-popup[loginforms=insecure] #identity-popup-security-content,
|
||||
#identity-popup[mixedcontent~=active-loaded][isbroken] #identity-popup-securityView,
|
||||
#identity-popup[mixedcontent~=active-loaded][isbroken] #identity-popup-security-content {
|
||||
background-image: url(chrome://browser/skin/controlcenter/mcb-disabled.svg);
|
||||
|
|
|
@ -123,7 +123,6 @@
|
|||
list-style-image: url(chrome://browser/skin/identity-secure.svg);
|
||||
}
|
||||
|
||||
.insecureLoginForms > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
|
||||
.mixedActiveContent > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://browser/skin/identity-mixed-active-loaded.svg);
|
||||
}
|
||||
|
|
|
@ -170,6 +170,7 @@ def build_one_stage_aux(src_dir, stage_dir, build_libcxx,
|
|||
"-DPYTHON_EXECUTABLE=%s" % python_path,
|
||||
"-DCMAKE_INSTALL_PREFIX=%s" % inst_dir,
|
||||
"-DLLVM_TOOL_LIBCXX_BUILD=%s" % ("ON" if build_libcxx else "OFF"),
|
||||
"-DLIBCXX_LIBCPPABI_VERSION=\"\"",
|
||||
src_dir];
|
||||
build_package(build_dir, run_cmake, cmake_args)
|
||||
|
||||
|
|
|
@ -2512,20 +2512,25 @@ nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed)
|
|||
// Assume false until we determine otherwise...
|
||||
*aFullscreenAllowed = false;
|
||||
|
||||
// For non-browsers/apps, check that the enclosing iframe element
|
||||
// has the allowfullscreen attribute set to true. If any ancestor
|
||||
// iframe does not have mozallowfullscreen=true, then fullscreen is
|
||||
// prohibited.
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindow();
|
||||
if (!win) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
|
||||
if (frameElement &&
|
||||
frameElement->IsHTMLElement(nsGkAtoms::iframe) &&
|
||||
!frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) &&
|
||||
!frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)) {
|
||||
return NS_OK;
|
||||
if (frameElement && !frameElement->IsXULElement()) {
|
||||
// We do not allow document inside any containing element other
|
||||
// than iframe to enter fullscreen.
|
||||
if (!frameElement->IsHTMLElement(nsGkAtoms::iframe)) {
|
||||
return NS_OK;
|
||||
}
|
||||
// If any ancestor iframe does not have allowfullscreen attribute
|
||||
// set, then fullscreen is not allowed.
|
||||
if (!frameElement->HasAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::allowfullscreen) &&
|
||||
!frameElement->HasAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::mozallowfullscreen)) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have no parent then we're the root docshell; no ancestor of the
|
||||
|
@ -11818,8 +11823,6 @@ nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel,
|
|||
nsAutoString srcdoc;
|
||||
inStrmChan->GetSrcdocData(srcdoc);
|
||||
entry->SetSrcdocData(srcdoc);
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
|
||||
nsCOMPtr<nsIURI> baseURI;
|
||||
inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
|
||||
entry->SetBaseURI(baseURI);
|
||||
|
@ -13940,8 +13943,7 @@ nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNavigate,
|
||||
nsContentPolicyType aLoadContentType,
|
||||
nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNonSubresourceRequest,
|
||||
bool* aShouldIntercept)
|
||||
{
|
||||
*aShouldIntercept = false;
|
||||
|
@ -13970,7 +13972,8 @@ nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNavigate,
|
|||
do_GetService(THIRDPARTYUTIL_CONTRACTID, &result);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
|
||||
if (mCurrentURI) {
|
||||
if (mCurrentURI &&
|
||||
nsContentUtils::CookiesBehavior() == nsICookieService::BEHAVIOR_REJECT_FOREIGN) {
|
||||
nsAutoCString uriSpec;
|
||||
mCurrentURI->GetSpec(uriSpec);
|
||||
if (!(uriSpec.EqualsLiteral("about:blank"))) {
|
||||
|
@ -13985,16 +13988,13 @@ nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNavigate,
|
|||
return result;
|
||||
}
|
||||
|
||||
if (isThirdPartyURI &&
|
||||
(Preferences::GetInt("network.cookie.cookieBehavior",
|
||||
nsICookieService::BEHAVIOR_ACCEPT) ==
|
||||
nsICookieService::BEHAVIOR_REJECT_FOREIGN)) {
|
||||
if (isThirdPartyURI) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aIsNavigate || nsContentUtils::IsWorkerLoad(aLoadContentType)) {
|
||||
if (aIsNonSubresourceRequest) {
|
||||
OriginAttributes attrs(GetAppId(), GetIsInBrowserElement());
|
||||
*aShouldIntercept = swm->IsAvailable(attrs, aURI);
|
||||
return NS_OK;
|
||||
|
@ -14070,18 +14070,13 @@ nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool isNavigation = false;
|
||||
nsresult rv = aChannel->GetIsNavigation(&isNavigation);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsContentPolicyType loadType;
|
||||
rv = aChannel->GetInternalContentPolicyType(&loadType);
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
nsresult rv = aChannel->GetChannel(getter_AddRefs(channel));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIDocument> doc;
|
||||
|
||||
bool isSubresourceLoad = !isNavigation &&
|
||||
!nsContentUtils::IsWorkerLoad(loadType);
|
||||
bool isSubresourceLoad = !nsContentUtils::IsNonSubresourceRequest(channel);
|
||||
if (isSubresourceLoad) {
|
||||
doc = GetDocument();
|
||||
if (!doc) {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/DataSurfaceHelpers.h"
|
||||
#include "mozilla/layers/AsyncCanvasRenderer.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
@ -165,6 +166,7 @@ public:
|
|||
mSize,
|
||||
mImage,
|
||||
nullptr,
|
||||
nullptr,
|
||||
getter_AddRefs(stream),
|
||||
mEncoder);
|
||||
|
||||
|
@ -178,6 +180,7 @@ public:
|
|||
mSize,
|
||||
mImage,
|
||||
nullptr,
|
||||
nullptr,
|
||||
getter_AddRefs(stream),
|
||||
mEncoder);
|
||||
}
|
||||
|
@ -234,6 +237,7 @@ ImageEncoder::ExtractData(nsAString& aType,
|
|||
const nsAString& aOptions,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
layers::AsyncCanvasRenderer* aRenderer,
|
||||
nsIInputStream** aStream)
|
||||
{
|
||||
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
|
||||
|
@ -242,10 +246,9 @@ ImageEncoder::ExtractData(nsAString& aType,
|
|||
}
|
||||
|
||||
return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize, nullptr,
|
||||
aContext, aStream, encoder);
|
||||
aContext, aRenderer, aStream, encoder);
|
||||
}
|
||||
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
ImageEncoder::ExtractDataFromLayersImageAsync(nsAString& aType,
|
||||
|
@ -341,6 +344,7 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType,
|
|||
const nsIntSize aSize,
|
||||
layers::Image* aImage,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
layers::AsyncCanvasRenderer* aRenderer,
|
||||
nsIInputStream** aStream,
|
||||
imgIEncoder* aEncoder)
|
||||
{
|
||||
|
@ -366,6 +370,11 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType,
|
|||
rv = aContext->GetInputStream(encoderType.get(),
|
||||
nsPromiseFlatString(aOptions).get(),
|
||||
getter_AddRefs(imgStream));
|
||||
} else if (aRenderer) {
|
||||
NS_ConvertUTF16toUTF8 encoderType(aType);
|
||||
rv = aRenderer->GetInputStream(encoderType.get(),
|
||||
nsPromiseFlatString(aOptions).get(),
|
||||
getter_AddRefs(imgStream));
|
||||
} else if (aImage) {
|
||||
// It is safe to convert PlanarYCbCr format from YUV to RGB off-main-thread.
|
||||
// Other image formats could have problem to convert format off-main-thread.
|
||||
|
|
|
@ -19,6 +19,7 @@ class nsICanvasRenderingContextInternal;
|
|||
namespace mozilla {
|
||||
|
||||
namespace layers {
|
||||
class AsyncCanvasRenderer;
|
||||
class Image;
|
||||
} // namespace layers
|
||||
|
||||
|
@ -40,6 +41,7 @@ public:
|
|||
const nsAString& aOptions,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
layers::AsyncCanvasRenderer* aRenderer,
|
||||
nsIInputStream** aStream);
|
||||
|
||||
// Extracts data asynchronously. aType may change to "image/png" if we had to
|
||||
|
@ -84,7 +86,7 @@ public:
|
|||
nsIInputStream** aStream);
|
||||
|
||||
private:
|
||||
// When called asynchronously, aContext is null.
|
||||
// When called asynchronously, aContext and aRenderer are null.
|
||||
static nsresult
|
||||
ExtractDataInternal(const nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
|
@ -93,6 +95,7 @@ private:
|
|||
const nsIntSize aSize,
|
||||
layers::Image* aImage,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
layers::AsyncCanvasRenderer* aRenderer,
|
||||
nsIInputStream** aStream,
|
||||
imgIEncoder* aEncoder);
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "mozilla/dom/StructuredClone.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/OffscreenCanvas.h"
|
||||
#include "mozilla/dom/OffscreenCanvasBinding.h"
|
||||
#include "mozilla/dom/PMessagePort.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
#include "mozilla/dom/SubtleCryptoBinding.h"
|
||||
|
@ -1014,6 +1016,25 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_CANVAS) {
|
||||
MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
|
||||
mSupportedContext == SameProcessDifferentThread);
|
||||
MOZ_ASSERT(aContent);
|
||||
OffscreenCanvasCloneData* data =
|
||||
static_cast<OffscreenCanvasCloneData*>(aContent);
|
||||
nsRefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(data);
|
||||
delete data;
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!GetOrCreateDOMReflector(aCx, canvas, &value)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
aReturnObject.set(&value.toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1045,6 +1066,24 @@ StructuredCloneHolder::CustomWriteTransferHandler(JSContext* aCx,
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mSupportedContext == SameProcessSameThread ||
|
||||
mSupportedContext == SameProcessDifferentThread) {
|
||||
OffscreenCanvas* canvas = nullptr;
|
||||
rv = UNWRAP_OBJECT(OffscreenCanvas, aObj, canvas);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MOZ_ASSERT(canvas);
|
||||
|
||||
*aExtraData = 0;
|
||||
*aTag = SCTAG_DOM_CANVAS;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = canvas->ToCloneData();
|
||||
MOZ_ASSERT(*aContent);
|
||||
canvas->SetNeutered();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1062,6 +1101,17 @@ StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag,
|
|||
MOZ_ASSERT(!aContent);
|
||||
MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
|
||||
MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_CANVAS) {
|
||||
MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
|
||||
mSupportedContext == SameProcessDifferentThread);
|
||||
MOZ_ASSERT(aContent);
|
||||
OffscreenCanvasCloneData* data =
|
||||
static_cast<OffscreenCanvasCloneData*>(aContent);
|
||||
delete data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,9 @@ enum StructuredCloneTags {
|
|||
|
||||
SCTAG_DOM_FORMDATA,
|
||||
|
||||
// This tag is for OffscreenCanvas.
|
||||
SCTAG_DOM_CANVAS,
|
||||
|
||||
SCTAG_DOM_MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -147,3 +147,4 @@ DOM4_MSG_DEF(AbortError, "A request was aborted, for example through a call to F
|
|||
DOM4_MSG_DEF(QuotaExceededError, "The current file handle exceeded its quota limitations.", NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR)
|
||||
|
||||
DOM_MSG_DEF(NS_ERROR_DOM_JS_EXCEPTION, "A callback threw an exception")
|
||||
DOM_MSG_DEF(NS_ERROR_DOM_DOMEXCEPTION, "A DOMException was thrown")
|
||||
|
|
|
@ -727,7 +727,7 @@ DragDataProducer::AddString(DataTransfer* aDataTransfer,
|
|||
const nsAString& aData,
|
||||
nsIPrincipal* aPrincipal)
|
||||
{
|
||||
nsRefPtr<nsVariant> variant = new nsVariant();
|
||||
nsRefPtr<nsVariantCC> variant = new nsVariantCC();
|
||||
variant->SetAsAString(aData);
|
||||
aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal);
|
||||
}
|
||||
|
@ -783,7 +783,7 @@ DragDataProducer::AddStringsToDataTransfer(nsIContent* aDragNode,
|
|||
// a new flavor so as not to confuse anyone who is really registered
|
||||
// for image/gif or image/jpg.
|
||||
if (mImage) {
|
||||
nsRefPtr<nsVariant> variant = new nsVariant();
|
||||
nsRefPtr<nsVariantCC> variant = new nsVariantCC();
|
||||
variant->SetAsISupports(mImage);
|
||||
aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kNativeImageMime),
|
||||
variant, 0, principal);
|
||||
|
@ -795,7 +795,7 @@ DragDataProducer::AddStringsToDataTransfer(nsIContent* aDragNode,
|
|||
nsCOMPtr<nsIFlavorDataProvider> dataProvider =
|
||||
new nsContentAreaDragDropDataProvider();
|
||||
if (dataProvider) {
|
||||
nsRefPtr<nsVariant> variant = new nsVariant();
|
||||
nsRefPtr<nsVariantCC> variant = new nsVariantCC();
|
||||
variant->SetAsISupports(dataProvider);
|
||||
aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kFilePromiseMime),
|
||||
variant, 0, principal);
|
||||
|
|
|
@ -8101,10 +8101,21 @@ nsContentUtils::PushEnabled(JSContext* aCx, JSObject* aObj)
|
|||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::IsWorkerLoad(nsContentPolicyType aType)
|
||||
nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel)
|
||||
{
|
||||
return aType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
|
||||
aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
|
||||
nsLoadFlags loadFlags = 0;
|
||||
aChannel->GetLoadFlags(&loadFlags);
|
||||
if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
|
||||
if (!loadInfo) {
|
||||
return false;
|
||||
}
|
||||
nsContentPolicyType type = loadInfo->InternalContentPolicyType();
|
||||
return type == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
|
||||
type == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
|
||||
}
|
||||
|
||||
// static, public
|
||||
|
|
|
@ -2522,7 +2522,12 @@ public:
|
|||
|
||||
static bool PushEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
static bool IsWorkerLoad(nsContentPolicyType aLoadType);
|
||||
static bool IsNonSubresourceRequest(nsIChannel* aChannel);
|
||||
|
||||
static uint32_t CookiesBehavior()
|
||||
{
|
||||
return sCookiesBehavior;
|
||||
}
|
||||
|
||||
// The order of these entries matters, as we use std::min for total ordering
|
||||
// of permissions. Private Browsing is considered to be more limiting
|
||||
|
|
|
@ -12027,18 +12027,15 @@ nsDocument::IsFullScreenEnabled(bool aCallerIsChrome, bool aLogFailure)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Ensure that all ancestor <iframe> elements have the allowfullscreen
|
||||
// boolean attribute set.
|
||||
// Ensure that all containing elements are <iframe> and have
|
||||
// allowfullscreen attribute set.
|
||||
nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
|
||||
bool allowed = false;
|
||||
if (docShell) {
|
||||
docShell->GetFullscreenAllowed(&allowed);
|
||||
}
|
||||
if (!allowed) {
|
||||
LogFullScreenDenied(aLogFailure, "FullScreenDeniedIframeNotAllowed", this);
|
||||
if (!docShell || !docShell->GetFullscreenAllowed()) {
|
||||
LogFullScreenDenied(aLogFailure, "FullScreenDeniedContainerNotAllowed", this);
|
||||
return false;
|
||||
}
|
||||
|
||||
return allowed;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t
|
||||
|
|
|
@ -563,7 +563,7 @@ nsTimeout::HasRefCntOne()
|
|||
static already_AddRefed<nsIVariant>
|
||||
CreateVoidVariant()
|
||||
{
|
||||
nsRefPtr<nsVariant> writable = new nsVariant();
|
||||
nsRefPtr<nsVariantCC> writable = new nsVariantCC();
|
||||
writable->SetAsVoid();
|
||||
return writable.forget();
|
||||
}
|
||||
|
@ -11837,6 +11837,8 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
nsCOMPtr<nsIDOMStorage> istorage = changingStorage.get();
|
||||
|
||||
bool fireMozStorageChanged = false;
|
||||
nsAutoString eventType;
|
||||
eventType.AssignLiteral("storage");
|
||||
principal = GetPrincipal();
|
||||
if (!principal) {
|
||||
return NS_OK;
|
||||
|
@ -11872,6 +11874,9 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
}
|
||||
|
||||
fireMozStorageChanged = mSessionStorage == changingStorage;
|
||||
if (fireMozStorageChanged) {
|
||||
eventType.AssignLiteral("MozSessionStorageChanged");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -11889,6 +11894,9 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
return NS_OK;
|
||||
|
||||
fireMozStorageChanged = mLocalStorage == changingStorage;
|
||||
if (fireMozStorageChanged) {
|
||||
eventType.AssignLiteral("MozLocalStorageChanged");
|
||||
};
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -11898,11 +11906,8 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
// Clone the storage event included in the observer notification. We want
|
||||
// to dispatch clones rather than the original event.
|
||||
ErrorResult error;
|
||||
nsRefPtr<StorageEvent> newEvent =
|
||||
CloneStorageEvent(fireMozStorageChanged ?
|
||||
NS_LITERAL_STRING("MozStorageChanged") :
|
||||
NS_LITERAL_STRING("storage"),
|
||||
event, error);
|
||||
nsRefPtr<StorageEvent> newEvent = CloneStorageEvent(eventType,
|
||||
event, error);
|
||||
if (error.Failed()) {
|
||||
return error.StealNSResult();
|
||||
}
|
||||
|
|
|
@ -1718,6 +1718,7 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
|
|||
nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
|
||||
|
||||
nsSecurityFlags secFlags = nsILoadInfo::SEC_NORMAL;
|
||||
nsLoadFlags loadFlags = nsIRequest::LOAD_BACKGROUND;
|
||||
if (IsSystemXHR()) {
|
||||
// Don't give this document the system principal. We need to keep track of
|
||||
// mPrincipal being system because we use it for various security checks
|
||||
|
@ -1725,6 +1726,9 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
|
|||
// principal. Hence we set the sandbox flag in loadinfo, so that
|
||||
// GetChannelResultPrincipal will give us the nullprincipal.
|
||||
secFlags |= nsILoadInfo::SEC_SANDBOXED;
|
||||
|
||||
//For a XHR, disable interception
|
||||
loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
|
||||
} else {
|
||||
secFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
|
||||
}
|
||||
|
@ -1738,7 +1742,7 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
|
|||
nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
|
||||
loadGroup,
|
||||
nullptr, // aCallbacks
|
||||
nsIRequest::LOAD_BACKGROUND);
|
||||
loadFlags);
|
||||
} else {
|
||||
//otherwise use the principal
|
||||
rv = NS_NewChannel(getter_AddRefs(mChannel),
|
||||
|
@ -1748,7 +1752,7 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
|
|||
nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
|
||||
loadGroup,
|
||||
nullptr, // aCallbacks
|
||||
nsIRequest::LOAD_BACKGROUND);
|
||||
loadFlags);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
|
|
@ -1225,11 +1225,7 @@ function test41()
|
|||
ok(true, "test 41c close");
|
||||
|
||||
// clean up the STS state
|
||||
const Cc = SpecialPowers.Cc;
|
||||
const Ci = SpecialPowers.Ci;
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
|
||||
var thehost = ios.newURI("http://example.com", null, null);
|
||||
var sss = Cc["@mozilla.org/ssservice;1"].getService(Ci.nsISiteSecurityService);
|
||||
var loadContext = SpecialPowers.wrap(window)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
|
@ -1237,7 +1233,7 @@ function test41()
|
|||
var flags = 0;
|
||||
if (loadContext.usePrivateBrowsing)
|
||||
flags |= Ci.nsISocketProvider.NO_PERMANENT_STORAGE;
|
||||
sss.removeState(Ci.nsISiteSecurityService.HEADER_HSTS, thehost, flags);
|
||||
SpecialPowers.cleanUpSTSData("http://example.com", flags);
|
||||
doTest(42);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
#include "mozilla/dom/DOMErrorBinding.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/dom/ElementBinding.h"
|
||||
#include "mozilla/dom/HTMLObjectElement.h"
|
||||
#include "mozilla/dom/HTMLObjectElementBinding.h"
|
||||
|
@ -142,6 +143,10 @@ ThrowMethodFailed(JSContext* cx, ErrorResult& rv)
|
|||
rv.ReportJSException(cx);
|
||||
return false;
|
||||
}
|
||||
if (rv.IsDOMException()) {
|
||||
rv.ReportDOMException(cx);
|
||||
return false;
|
||||
}
|
||||
rv.ReportGenericError(cx);
|
||||
return false;
|
||||
}
|
||||
|
@ -169,9 +174,6 @@ struct ErrorResult::Message {
|
|||
nsTArray<nsString>&
|
||||
ErrorResult::CreateErrorMessageHelper(const dom::ErrNum errorNumber, nsresult errorType)
|
||||
{
|
||||
if (IsErrorWithMessage()) {
|
||||
delete mMessage;
|
||||
}
|
||||
mResult = errorType;
|
||||
|
||||
mMessage = new Message();
|
||||
|
@ -183,8 +185,8 @@ void
|
|||
ErrorResult::SerializeMessage(IPC::Message* aMsg) const
|
||||
{
|
||||
using namespace IPC;
|
||||
MOZ_ASSERT(mUnionState == HasMessage);
|
||||
MOZ_ASSERT(mMessage);
|
||||
MOZ_ASSERT(mHasMessage);
|
||||
WriteParam(aMsg, mMessage->mArgs);
|
||||
WriteParam(aMsg, mMessage->mErrorNumber);
|
||||
}
|
||||
|
@ -202,11 +204,11 @@ ErrorResult::DeserializeMessage(const IPC::Message* aMsg, void** aIter)
|
|||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mHasMessage);
|
||||
MOZ_ASSERT(mUnionState == HasNothing);
|
||||
mMessage = readMessage.forget();
|
||||
#ifdef DEBUG
|
||||
mHasMessage = true;
|
||||
#endif
|
||||
mUnionState = HasMessage;
|
||||
#endif // DEBUG
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -214,7 +216,7 @@ void
|
|||
ErrorResult::ReportErrorWithMessage(JSContext* aCx)
|
||||
{
|
||||
MOZ_ASSERT(mMessage, "ReportErrorWithMessage() can be called only once");
|
||||
MOZ_ASSERT(mHasMessage);
|
||||
MOZ_ASSERT(mUnionState == HasMessage);
|
||||
|
||||
Message* message = mMessage;
|
||||
MOZ_RELEASE_ASSERT(message->HasCorrectNumberOfArguments());
|
||||
|
@ -239,8 +241,8 @@ ErrorResult::ClearMessage()
|
|||
delete mMessage;
|
||||
mMessage = nullptr;
|
||||
#ifdef DEBUG
|
||||
mHasMessage = false;
|
||||
#endif
|
||||
mUnionState = HasNothing;
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -249,12 +251,7 @@ ErrorResult::ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn)
|
|||
MOZ_ASSERT(mMightHaveUnreportedJSException,
|
||||
"Why didn't you tell us you planned to throw a JS exception?");
|
||||
|
||||
if (IsErrorWithMessage()) {
|
||||
delete mMessage;
|
||||
#ifdef DEBUG
|
||||
mHasMessage = false;
|
||||
#endif
|
||||
}
|
||||
ClearUnionData();
|
||||
|
||||
// Make sure mJSException is initialized _before_ we try to root it. But
|
||||
// don't set it to exn yet, because we don't want to do that until after we
|
||||
|
@ -267,6 +264,9 @@ ErrorResult::ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn)
|
|||
} else {
|
||||
mJSException = exn;
|
||||
mResult = NS_ERROR_DOM_JS_EXCEPTION;
|
||||
#ifdef DEBUG
|
||||
mUnionState = HasJSException;
|
||||
#endif // DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,6 +275,7 @@ ErrorResult::ReportJSException(JSContext* cx)
|
|||
{
|
||||
MOZ_ASSERT(!mMightHaveUnreportedJSException,
|
||||
"Why didn't you tell us you planned to handle JS exceptions?");
|
||||
MOZ_ASSERT(mUnionState == HasJSException);
|
||||
|
||||
JS::Rooted<JS::Value> exception(cx, mJSException);
|
||||
if (JS_WrapValue(cx, &exception)) {
|
||||
|
@ -288,6 +289,9 @@ ErrorResult::ReportJSException(JSContext* cx)
|
|||
// We no longer have a useful exception but we do want to signal that an error
|
||||
// occured.
|
||||
mResult = NS_ERROR_FAILURE;
|
||||
#ifdef DEBUG
|
||||
mUnionState = HasNothing;
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -297,10 +301,107 @@ ErrorResult::StealJSException(JSContext* cx,
|
|||
MOZ_ASSERT(!mMightHaveUnreportedJSException,
|
||||
"Must call WouldReportJSException unconditionally in all codepaths that might call StealJSException");
|
||||
MOZ_ASSERT(IsJSException(), "No exception to steal");
|
||||
MOZ_ASSERT(mUnionState == HasJSException);
|
||||
|
||||
value.set(mJSException);
|
||||
js::RemoveRawValueRoot(cx, &mJSException);
|
||||
mResult = NS_OK;
|
||||
#ifdef DEBUG
|
||||
mUnionState = HasNothing;
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
struct ErrorResult::DOMExceptionInfo {
|
||||
DOMExceptionInfo(nsresult rv, const nsACString& message)
|
||||
: mMessage(message)
|
||||
, mRv(rv)
|
||||
{}
|
||||
|
||||
nsCString mMessage;
|
||||
nsresult mRv;
|
||||
};
|
||||
|
||||
void
|
||||
ErrorResult::SerializeDOMExceptionInfo(IPC::Message* aMsg) const
|
||||
{
|
||||
using namespace IPC;
|
||||
MOZ_ASSERT(mDOMExceptionInfo);
|
||||
MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
|
||||
WriteParam(aMsg, mDOMExceptionInfo->mMessage);
|
||||
WriteParam(aMsg, mDOMExceptionInfo->mRv);
|
||||
}
|
||||
|
||||
bool
|
||||
ErrorResult::DeserializeDOMExceptionInfo(const IPC::Message* aMsg, void** aIter)
|
||||
{
|
||||
using namespace IPC;
|
||||
nsCString message;
|
||||
nsresult rv;
|
||||
if (!ReadParam(aMsg, aIter, &message) ||
|
||||
!ReadParam(aMsg, aIter, &rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mUnionState == HasNothing);
|
||||
MOZ_ASSERT(IsDOMException());
|
||||
mDOMExceptionInfo = new DOMExceptionInfo(rv, message);
|
||||
#ifdef DEBUG
|
||||
mUnionState = HasDOMExceptionInfo;
|
||||
#endif // DEBUG
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ErrorResult::ThrowDOMException(nsresult rv, const nsACString& message)
|
||||
{
|
||||
ClearUnionData();
|
||||
|
||||
mResult = NS_ERROR_DOM_DOMEXCEPTION;
|
||||
mDOMExceptionInfo = new DOMExceptionInfo(rv, message);
|
||||
#ifdef DEBUG
|
||||
mUnionState = HasDOMExceptionInfo;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ErrorResult::ReportDOMException(JSContext* cx)
|
||||
{
|
||||
MOZ_ASSERT(mDOMExceptionInfo, "ReportDOMException() can be called only once");
|
||||
MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
|
||||
|
||||
dom::Throw(cx, mDOMExceptionInfo->mRv, mDOMExceptionInfo->mMessage);
|
||||
|
||||
ClearDOMExceptionInfo();
|
||||
}
|
||||
|
||||
void
|
||||
ErrorResult::ClearDOMExceptionInfo()
|
||||
{
|
||||
MOZ_ASSERT(IsDOMException());
|
||||
MOZ_ASSERT(mUnionState == HasDOMExceptionInfo || !mDOMExceptionInfo);
|
||||
delete mDOMExceptionInfo;
|
||||
mDOMExceptionInfo = nullptr;
|
||||
#ifdef DEBUG
|
||||
mUnionState = HasNothing;
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
void
|
||||
ErrorResult::ClearUnionData()
|
||||
{
|
||||
if (IsJSException()) {
|
||||
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
|
||||
MOZ_ASSERT(cx);
|
||||
mJSException.setUndefined();
|
||||
js::RemoveRawValueRoot(cx, &mJSException);
|
||||
#ifdef DEBUG
|
||||
mUnionState = HasNothing;
|
||||
#endif // DEBUG
|
||||
} else if (IsErrorWithMessage()) {
|
||||
ClearMessage();
|
||||
} else if (IsDOMException()) {
|
||||
ClearDOMExceptionInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -308,12 +409,17 @@ ErrorResult::ReportGenericError(JSContext* cx)
|
|||
{
|
||||
MOZ_ASSERT(!IsErrorWithMessage());
|
||||
MOZ_ASSERT(!IsJSException());
|
||||
MOZ_ASSERT(!IsDOMException());
|
||||
dom::Throw(cx, ErrorCode());
|
||||
}
|
||||
|
||||
ErrorResult&
|
||||
ErrorResult::operator=(ErrorResult&& aRHS)
|
||||
{
|
||||
// Clear out any union members we may have right now, before we
|
||||
// start writing to it.
|
||||
ClearUnionData();
|
||||
|
||||
#ifdef DEBUG
|
||||
mMightHaveUnreportedJSException = aRHS.mMightHaveUnreportedJSException;
|
||||
aRHS.mMightHaveUnreportedJSException = false;
|
||||
|
@ -321,10 +427,6 @@ ErrorResult::operator=(ErrorResult&& aRHS)
|
|||
if (aRHS.IsErrorWithMessage()) {
|
||||
mMessage = aRHS.mMessage;
|
||||
aRHS.mMessage = nullptr;
|
||||
#ifdef DEBUG
|
||||
mHasMessage = aRHS.mHasMessage;
|
||||
aRHS.mHasMessage = false;
|
||||
#endif
|
||||
} else if (aRHS.IsJSException()) {
|
||||
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
|
||||
MOZ_ASSERT(cx);
|
||||
|
@ -335,13 +437,19 @@ ErrorResult::operator=(ErrorResult&& aRHS)
|
|||
mJSException = aRHS.mJSException;
|
||||
aRHS.mJSException.setUndefined();
|
||||
js::RemoveRawValueRoot(cx, &aRHS.mJSException);
|
||||
} else if (aRHS.IsDOMException()) {
|
||||
mDOMExceptionInfo = aRHS.mDOMExceptionInfo;
|
||||
aRHS.mDOMExceptionInfo = nullptr;
|
||||
} else {
|
||||
// Null out the union on both sides for hygiene purposes.
|
||||
mMessage = aRHS.mMessage = nullptr;
|
||||
#ifdef DEBUG
|
||||
mHasMessage = aRHS.mHasMessage = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
mUnionState = aRHS.mUnionState;
|
||||
aRHS.mUnionState = HasNothing;
|
||||
#endif // DEBUG
|
||||
|
||||
// Note: It's important to do this last, since this affects the condition
|
||||
// checks above!
|
||||
mResult = aRHS.mResult;
|
||||
|
@ -353,15 +461,7 @@ void
|
|||
ErrorResult::SuppressException()
|
||||
{
|
||||
WouldReportJSException();
|
||||
if (IsErrorWithMessage()) {
|
||||
ClearMessage();
|
||||
} else if (IsJSException()) {
|
||||
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
|
||||
// Just steal it into a stack value (unrooting it in the process)
|
||||
// that we then allow to die.
|
||||
JS::Rooted<JS::Value> temp(cx);
|
||||
StealJSException(cx, &temp);
|
||||
}
|
||||
ClearUnionData();
|
||||
// We don't use AssignErrorCode, because we want to override existing error
|
||||
// states, which AssignErrorCode is not allowed to do.
|
||||
mResult = NS_OK;
|
||||
|
|
|
@ -3111,6 +3111,14 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool succeeded;
|
||||
if (!JS_SetImmutablePrototype(aCx, aGlobal, &succeeded)) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(succeeded,
|
||||
"making a fresh global object's [[Prototype]] immutable can "
|
||||
"internally fail, but it should never be unsuccessful");
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
|
|
|
@ -2953,9 +2953,32 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
else:
|
||||
unforgeableHolderSetup = None
|
||||
|
||||
if (self.descriptor.interface.isOnGlobalProtoChain() and
|
||||
needInterfacePrototypeObject):
|
||||
makeProtoPrototypeImmutable = CGGeneric(fill(
|
||||
"""
|
||||
if (*${protoCache}) {
|
||||
bool succeeded;
|
||||
JS::Handle<JSObject*> prot = GetProtoObjectHandle(aCx, aGlobal);
|
||||
if (!JS_SetImmutablePrototype(aCx, prot, &succeeded)) {
|
||||
$*{failureCode}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(succeeded,
|
||||
"making a fresh prototype object's [[Prototype]] "
|
||||
"immutable can internally fail, but it should "
|
||||
"never be unsuccessful");
|
||||
}
|
||||
""",
|
||||
protoCache=protoCache,
|
||||
failureCode=failureCode))
|
||||
else:
|
||||
makeProtoPrototypeImmutable = None
|
||||
|
||||
return CGList(
|
||||
[getParentProto, CGGeneric(getConstructorProto), initIds,
|
||||
prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup],
|
||||
prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup,
|
||||
makeProtoPrototypeImmutable],
|
||||
"\n").define()
|
||||
|
||||
|
||||
|
|
|
@ -42,8 +42,11 @@ struct ParamTraits<mozilla::ErrorResult>
|
|||
|
||||
WriteParam(aMsg, aParam.mResult);
|
||||
WriteParam(aMsg, aParam.IsErrorWithMessage());
|
||||
WriteParam(aMsg, aParam.IsDOMException());
|
||||
if (aParam.IsErrorWithMessage()) {
|
||||
aParam.SerializeMessage(aMsg);
|
||||
} else if (aParam.IsDOMException()) {
|
||||
aParam.SerializeDOMExceptionInfo(aMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,8 +60,19 @@ struct ParamTraits<mozilla::ErrorResult>
|
|||
if (!ReadParam(aMsg, aIter, &hasMessage)) {
|
||||
return false;
|
||||
}
|
||||
bool hasDOMExceptionInfo = false;
|
||||
if (!ReadParam(aMsg, aIter, &hasDOMExceptionInfo)) {
|
||||
return false;
|
||||
}
|
||||
if (hasMessage && hasDOMExceptionInfo) {
|
||||
// Shouldn't have both!
|
||||
return false;
|
||||
}
|
||||
if (hasMessage && !readValue.DeserializeMessage(aMsg, aIter)) {
|
||||
return false;
|
||||
} else if (hasDOMExceptionInfo &&
|
||||
!readValue.DeserializeDOMExceptionInfo(aMsg, aIter)) {
|
||||
return false;
|
||||
}
|
||||
*aResult = Move(readValue);
|
||||
return true;
|
||||
|
|
|
@ -76,24 +76,29 @@ struct StringArrayAppender
|
|||
|
||||
class ErrorResult {
|
||||
public:
|
||||
ErrorResult() {
|
||||
mResult = NS_OK;
|
||||
|
||||
ErrorResult()
|
||||
: mResult(NS_OK)
|
||||
#ifdef DEBUG
|
||||
mMightHaveUnreportedJSException = false;
|
||||
mHasMessage = false;
|
||||
, mMightHaveUnreportedJSException(false)
|
||||
, mUnionState(HasNothing)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
~ErrorResult() {
|
||||
MOZ_ASSERT_IF(IsErrorWithMessage(), !mMessage);
|
||||
MOZ_ASSERT_IF(IsDOMException(), !mDOMExceptionInfo);
|
||||
MOZ_ASSERT(!mMightHaveUnreportedJSException);
|
||||
MOZ_ASSERT(!mHasMessage);
|
||||
MOZ_ASSERT(mUnionState == HasNothing);
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG
|
||||
|
||||
ErrorResult(ErrorResult&& aRHS)
|
||||
// Initialize mResult and whatever else we need to default-initialize, so
|
||||
// the ClearUnionData call in our operator= will do the right thing
|
||||
// (nothing).
|
||||
: ErrorResult()
|
||||
{
|
||||
*this = Move(aRHS);
|
||||
}
|
||||
|
@ -153,6 +158,15 @@ public:
|
|||
void ReportJSException(JSContext* cx);
|
||||
bool IsJSException() const { return ErrorCode() == NS_ERROR_DOM_JS_EXCEPTION; }
|
||||
|
||||
// Facilities for throwing a DOMException. If an empty message string is
|
||||
// passed to ThrowDOMException, the default message string for the given
|
||||
// nsresult will be used. The passed-in string must be UTF-8. The nsresult
|
||||
// passed in must be one we create DOMExceptions for; otherwise you may get an
|
||||
// XPConnect Exception.
|
||||
void ThrowDOMException(nsresult rv, const nsACString& message = EmptyCString());
|
||||
void ReportDOMException(JSContext* cx);
|
||||
bool IsDOMException() const { return ErrorCode() == NS_ERROR_DOM_DOMEXCEPTION; }
|
||||
|
||||
// Report a generic error. This should only be used if we're not
|
||||
// some more specific exception type.
|
||||
void ReportGenericError(JSContext* cx);
|
||||
|
@ -213,10 +227,22 @@ protected:
|
|||
}
|
||||
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
enum UnionState {
|
||||
HasMessage,
|
||||
HasDOMExceptionInfo,
|
||||
HasJSException,
|
||||
HasNothing
|
||||
};
|
||||
#endif // DEBUG
|
||||
|
||||
friend struct IPC::ParamTraits<ErrorResult>;
|
||||
void SerializeMessage(IPC::Message* aMsg) const;
|
||||
bool DeserializeMessage(const IPC::Message* aMsg, void** aIter);
|
||||
|
||||
void SerializeDOMExceptionInfo(IPC::Message* aMsg) const;
|
||||
bool DeserializeDOMExceptionInfo(const IPC::Message* aMsg, void** aIter);
|
||||
|
||||
// Helper method that creates a new Message for this ErrorResult,
|
||||
// and returns the arguments array from that Message.
|
||||
nsTArray<nsString>& CreateErrorMessageHelper(const dom::ErrNum errorNumber, nsresult errorType);
|
||||
|
@ -229,19 +255,14 @@ private:
|
|||
"Pass in the right number of arguments");
|
||||
#endif
|
||||
|
||||
if (IsJSException()) {
|
||||
// We have rooted our mJSException, and we don't have the info
|
||||
// needed to unroot here, so just bail.
|
||||
MOZ_ASSERT(false,
|
||||
"Ignoring ThrowErrorWithMessage call because we have a JS exception");
|
||||
return;
|
||||
}
|
||||
ClearUnionData();
|
||||
|
||||
nsTArray<nsString>& messageArgsArray = CreateErrorMessageHelper(errorNumber, errorType);
|
||||
uint16_t argCount = dom::GetErrorArgCount(errorNumber);
|
||||
dom::StringArrayAppender::Append(messageArgsArray, argCount, messageArgs...);
|
||||
#ifdef DEBUG
|
||||
mHasMessage = true;
|
||||
#endif
|
||||
mUnionState = HasMessage;
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
void AssignErrorCode(nsresult aRv) {
|
||||
|
@ -250,31 +271,55 @@ private:
|
|||
MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
|
||||
MOZ_ASSERT(aRv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
|
||||
MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
|
||||
MOZ_ASSERT(aRv != NS_ERROR_DOM_DOMEXCEPTION, "Use ThrowDOMException()");
|
||||
MOZ_ASSERT(!IsDOMException(), "Don't overwrite DOM exceptions");
|
||||
MOZ_ASSERT(aRv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "May need to bring back ThrowNotEnoughArgsError");
|
||||
mResult = aRv;
|
||||
}
|
||||
|
||||
void ClearMessage();
|
||||
void ClearDOMExceptionInfo();
|
||||
|
||||
// ClearUnionData will try to clear the data in our
|
||||
// mMessage/mJSException/mDOMExceptionInfo union. After this the union may be
|
||||
// in an uninitialized state (e.g. mMessage or mDOMExceptionInfo may be
|
||||
// pointing to deleted memory) and the caller must either reinitialize it or
|
||||
// change mResult to something that will not involve us touching the union
|
||||
// anymore.
|
||||
void ClearUnionData();
|
||||
|
||||
// Special values of mResult:
|
||||
// NS_ERROR_TYPE_ERR -- ThrowTypeError() called on us.
|
||||
// NS_ERROR_RANGE_ERR -- ThrowRangeError() called on us.
|
||||
// NS_ERROR_DOM_JS_EXCEPTION -- ThrowJSException() called on us.
|
||||
// NS_ERROR_UNCATCHABLE_EXCEPTION -- ThrowUncatchableException called on us.
|
||||
// NS_ERROR_DOM_DOMEXCEPTION -- ThrowDOMException() called on us.
|
||||
nsresult mResult;
|
||||
|
||||
struct Message;
|
||||
// mMessage is set by ThrowErrorWithMessage and cleared (and deallocated) by
|
||||
struct DOMExceptionInfo;
|
||||
// mMessage is set by ThrowErrorWithMessage and reported (and deallocated) by
|
||||
// ReportErrorWithMessage.
|
||||
// mJSException is set (and rooted) by ThrowJSException and unrooted
|
||||
// by ReportJSException.
|
||||
// mJSException is set (and rooted) by ThrowJSException and reported
|
||||
// (and unrooted) by ReportJSException.
|
||||
// mDOMExceptionInfo is set by ThrowDOMException and reported
|
||||
// (and deallocated) by ReportDOMException.
|
||||
union {
|
||||
Message* mMessage; // valid when IsErrorWithMessage()
|
||||
JS::Value mJSException; // valid when IsJSException()
|
||||
DOMExceptionInfo* mDOMExceptionInfo; // valid when IsDOMException()
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
// Used to keep track of codepaths that might throw JS exceptions,
|
||||
// for assertion purposes.
|
||||
bool mMightHaveUnreportedJSException;
|
||||
// Used to keep track of whether mMessage has ever been assigned to.
|
||||
// We need to check this in order to ensure that not attempting to
|
||||
// delete mMessage in DeserializeMessage doesn't leak memory.
|
||||
bool mHasMessage;
|
||||
|
||||
// Used to keep track of what's stored in our union right now. Note
|
||||
// that this may be set to HasNothing even if our mResult suggests
|
||||
// we should have something, if we have already cleaned up the
|
||||
// something.
|
||||
UnionState mUnionState;
|
||||
#endif
|
||||
|
||||
// Not to be implemented, to make sure people always pass this by
|
||||
|
|
|
@ -88,7 +88,7 @@ ThrowExceptionObject(JSContext* aCx, Exception* aException)
|
|||
}
|
||||
|
||||
bool
|
||||
Throw(JSContext* aCx, nsresult aRv, const char* aMessage)
|
||||
Throw(JSContext* aCx, nsresult aRv, const nsACString& aMessage)
|
||||
{
|
||||
if (aRv == NS_ERROR_UNCATCHABLE_EXCEPTION) {
|
||||
// Nuke any existing exception on aCx, to make sure we're uncatchable.
|
||||
|
@ -134,7 +134,7 @@ Throw(JSContext* aCx, nsresult aRv, const char* aMessage)
|
|||
}
|
||||
|
||||
void
|
||||
ThrowAndReport(nsPIDOMWindow* aWindow, nsresult aRv, const char* aMessage)
|
||||
ThrowAndReport(nsPIDOMWindow* aWindow, nsresult aRv)
|
||||
{
|
||||
MOZ_ASSERT(aRv != NS_ERROR_UNCATCHABLE_EXCEPTION,
|
||||
"Doesn't make sense to report uncatchable exceptions!");
|
||||
|
@ -144,11 +144,11 @@ ThrowAndReport(nsPIDOMWindow* aWindow, nsresult aRv, const char* aMessage)
|
|||
}
|
||||
jsapi.TakeOwnershipOfErrorReporting();
|
||||
|
||||
Throw(jsapi.cx(), aRv, aMessage);
|
||||
Throw(jsapi.cx(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Exception>
|
||||
CreateException(JSContext* aCx, nsresult aRv, const char* aMessage)
|
||||
CreateException(JSContext* aCx, nsresult aRv, const nsACString& aMessage)
|
||||
{
|
||||
// Do we use DOM exceptions for this error code?
|
||||
switch (NS_ERROR_GET_MODULE(aRv)) {
|
||||
|
@ -158,16 +158,17 @@ CreateException(JSContext* aCx, nsresult aRv, const char* aMessage)
|
|||
case NS_ERROR_MODULE_DOM_INDEXEDDB:
|
||||
case NS_ERROR_MODULE_DOM_FILEHANDLE:
|
||||
case NS_ERROR_MODULE_DOM_BLUETOOTH:
|
||||
return DOMException::Create(aRv);
|
||||
if (aMessage.IsEmpty()) {
|
||||
return DOMException::Create(aRv);
|
||||
}
|
||||
return DOMException::Create(aRv, aMessage);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// If not, use the default.
|
||||
// aMessage can be null, so we can't use nsDependentCString on it.
|
||||
nsRefPtr<Exception> exception =
|
||||
new Exception(nsCString(aMessage), aRv,
|
||||
EmptyCString(), nullptr, nullptr);
|
||||
new Exception(aMessage, aRv, EmptyCString(), nullptr, nullptr);
|
||||
return exception.forget();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <stdint.h>
|
||||
#include "jspubtd.h"
|
||||
#include "nsIException.h"
|
||||
#include "nsStringGlue.h"
|
||||
|
||||
class nsIStackFrame;
|
||||
class nsPIDOMWindow;
|
||||
|
@ -23,13 +24,14 @@ namespace dom {
|
|||
|
||||
class Exception;
|
||||
|
||||
// If we're throwing a DOMException and message is empty, the default
|
||||
// message for the nsresult in question will be used.
|
||||
bool
|
||||
Throw(JSContext* cx, nsresult rv, const char* sz = nullptr);
|
||||
Throw(JSContext* cx, nsresult rv, const nsACString& message = EmptyCString());
|
||||
|
||||
// Create, throw and report an exception to a given window.
|
||||
void
|
||||
ThrowAndReport(nsPIDOMWindow* aWindow, nsresult aRv,
|
||||
const char* aMessage = nullptr);
|
||||
ThrowAndReport(nsPIDOMWindow* aWindow, nsresult aRv);
|
||||
|
||||
bool
|
||||
ThrowExceptionObject(JSContext* aCx, Exception* aException);
|
||||
|
@ -37,10 +39,14 @@ ThrowExceptionObject(JSContext* aCx, Exception* aException);
|
|||
bool
|
||||
ThrowExceptionObject(JSContext* aCx, nsIException* aException);
|
||||
|
||||
// Create an exception object for the given nsresult and message but
|
||||
// don't set it pending on aCx. This never returns null.
|
||||
// Create an exception object for the given nsresult and message but don't set
|
||||
// it pending on aCx. If we're throwing a DOMException and aMessage is empty,
|
||||
// the default message for the nsresult in question will be used.
|
||||
//
|
||||
// This never returns null.
|
||||
already_AddRefed<Exception>
|
||||
CreateException(JSContext* aCx, nsresult aRv, const char* aMessage = nullptr);
|
||||
CreateException(JSContext* aCx, nsresult aRv,
|
||||
const nsACString& aMessage = EmptyCString());
|
||||
|
||||
already_AddRefed<nsIStackFrame>
|
||||
GetCurrentJSStack();
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
/* -*- 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 "CanvasRenderingContextHelper.h"
|
||||
#include "ImageEncoder.h"
|
||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "WebGL1Context.h"
|
||||
#include "WebGL2Context.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
void
|
||||
CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
|
||||
nsIGlobalObject* aGlobal,
|
||||
FileCallback& 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;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* imageBuffer = nullptr;
|
||||
int32_t format = 0;
|
||||
if (mCurrentContext) {
|
||||
mCurrentContext->GetImageBuffer(&imageBuffer, &format);
|
||||
}
|
||||
|
||||
// Encoder callback when encoding is complete.
|
||||
class EncodeCallback : public EncodeCompleteCallback
|
||||
{
|
||||
public:
|
||||
EncodeCallback(nsIGlobalObject* aGlobal, FileCallback* aCallback)
|
||||
: mGlobal(aGlobal)
|
||||
, mFileCallback(aCallback) {}
|
||||
|
||||
// This is called on main thread.
|
||||
nsresult ReceiveBlob(already_AddRefed<Blob> aBlob)
|
||||
{
|
||||
nsRefPtr<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);
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
|
||||
|
||||
mFileCallback->Call(*newBlob, rv);
|
||||
|
||||
mGlobal = nullptr;
|
||||
mFileCallback = nullptr;
|
||||
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
nsRefPtr<FileCallback> mFileCallback;
|
||||
};
|
||||
|
||||
nsRefPtr<EncodeCompleteCallback> callback =
|
||||
new EncodeCallback(aGlobal, &aCallback);
|
||||
|
||||
aRv = ImageEncoder::ExtractDataAsync(type,
|
||||
params,
|
||||
usingCustomParseOptions,
|
||||
imageBuffer,
|
||||
format,
|
||||
GetWidthHeight(),
|
||||
callback);
|
||||
}
|
||||
|
||||
already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
CanvasRenderingContextHelper::CreateContext(CanvasContextType aContextType)
|
||||
{
|
||||
MOZ_ASSERT(aContextType != CanvasContextType::NoContext);
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> ret;
|
||||
|
||||
switch (aContextType) {
|
||||
case CanvasContextType::NoContext:
|
||||
break;
|
||||
|
||||
case CanvasContextType::Canvas2D:
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
|
||||
ret = new CanvasRenderingContext2D();
|
||||
break;
|
||||
|
||||
case CanvasContextType::WebGL1:
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
|
||||
|
||||
ret = WebGL1Context::Create();
|
||||
if (!ret)
|
||||
return nullptr;
|
||||
|
||||
break;
|
||||
|
||||
case CanvasContextType::WebGL2:
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
|
||||
|
||||
ret = WebGL2Context::Create();
|
||||
if (!ret)
|
||||
return nullptr;
|
||||
|
||||
break;
|
||||
}
|
||||
MOZ_ASSERT(ret);
|
||||
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
CanvasRenderingContextHelper::GetContext(JSContext* aCx,
|
||||
const nsAString& aContextId,
|
||||
JS::Handle<JS::Value> aContextOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
CanvasContextType contextType;
|
||||
if (!CanvasUtils::GetCanvasContextType(aContextId, &contextType))
|
||||
return nullptr;
|
||||
|
||||
if (!mCurrentContext) {
|
||||
// This canvas doesn't have a context yet.
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> context;
|
||||
context = CreateContext(contextType);
|
||||
if (!context) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure that the context participates in CC. Note that returning a
|
||||
// CC participant from QI doesn't addref.
|
||||
nsXPCOMCycleCollectionParticipant* cp = nullptr;
|
||||
CallQueryInterface(context, &cp);
|
||||
if (!cp) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mCurrentContext = context.forget();
|
||||
mCurrentContextType = contextType;
|
||||
|
||||
aRv = UpdateContext(aCx, aContextOptions);
|
||||
if (aRv.Failed()) {
|
||||
aRv = NS_OK; // See bug 645792
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
// We already have a context of some type.
|
||||
if (contextType != mCurrentContextType)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICanvasRenderingContextInternal> context = mCurrentContext;
|
||||
return context.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
CanvasRenderingContextHelper::UpdateContext(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aNewContextOptions)
|
||||
{
|
||||
if (!mCurrentContext)
|
||||
return NS_OK;
|
||||
|
||||
nsIntSize sz = GetWidthHeight();
|
||||
|
||||
nsCOMPtr<nsICanvasRenderingContextInternal> currentContext = mCurrentContext;
|
||||
|
||||
nsresult rv = currentContext->SetIsOpaque(GetOpaqueAttr());
|
||||
if (NS_FAILED(rv)) {
|
||||
mCurrentContext = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = currentContext->SetContextOptions(aCx, aNewContextOptions);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCurrentContext = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = currentContext->SetDimensions(sz.width, sz.height);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCurrentContext = nullptr;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CanvasRenderingContextHelper::ParseParams(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
nsAString& outParams,
|
||||
bool* const outUsingCustomParseOptions)
|
||||
{
|
||||
// Quality parameter is only valid for the image/jpeg MIME type
|
||||
if (aType.EqualsLiteral("image/jpeg")) {
|
||||
if (aEncoderOptions.isNumber()) {
|
||||
double quality = aEncoderOptions.toNumber();
|
||||
// Quality must be between 0.0 and 1.0, inclusive
|
||||
if (quality >= 0.0 && quality <= 1.0) {
|
||||
outParams.AppendLiteral("quality=");
|
||||
outParams.AppendInt(NS_lround(quality * 100.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't parsed the aParams check for proprietary options.
|
||||
// The proprietary option -moz-parse-options will take a image lib encoder
|
||||
// parse options string as is and pass it to the encoder.
|
||||
*outUsingCustomParseOptions = false;
|
||||
if (outParams.Length() == 0 && aEncoderOptions.isString()) {
|
||||
NS_NAMED_LITERAL_STRING(mozParseOptions, "-moz-parse-options:");
|
||||
nsAutoJSString paramString;
|
||||
if (!paramString.init(aCx, aEncoderOptions.toString())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (StringBeginsWith(paramString, mozParseOptions)) {
|
||||
nsDependentSubstring parseOptions = Substring(paramString,
|
||||
mozParseOptions.Length(),
|
||||
paramString.Length() -
|
||||
mozParseOptions.Length());
|
||||
outParams.Append(parseOptions);
|
||||
*outUsingCustomParseOptions = true;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,71 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
|
||||
#define MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
|
||||
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsSize.h"
|
||||
|
||||
class nsICanvasRenderingContextInternal;
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class FileCallback;
|
||||
|
||||
enum class CanvasContextType : uint8_t {
|
||||
NoContext,
|
||||
Canvas2D,
|
||||
WebGL1,
|
||||
WebGL2
|
||||
};
|
||||
|
||||
/**
|
||||
* Povides common RenderingContext functionality used by both OffscreenCanvas
|
||||
* and HTMLCanvasElement.
|
||||
*/
|
||||
class CanvasRenderingContextHelper
|
||||
{
|
||||
public:
|
||||
virtual already_AddRefed<nsISupports>
|
||||
GetContext(JSContext* aCx,
|
||||
const nsAString& aContextId,
|
||||
JS::Handle<JS::Value> aContextOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual bool GetOpaqueAttr() = 0;
|
||||
|
||||
protected:
|
||||
virtual nsresult UpdateContext(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aNewContextOptions);
|
||||
|
||||
virtual nsresult ParseParams(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
nsAString& outParams,
|
||||
bool* const outCustomParseOptions);
|
||||
|
||||
void ToBlob(JSContext* aCx, nsIGlobalObject* global, FileCallback& aCallback,
|
||||
const nsAString& aType, JS::Handle<JS::Value> aParams,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
CreateContext(CanvasContextType aContextType);
|
||||
|
||||
virtual nsIntSize GetWidthHeight() = 0;
|
||||
|
||||
CanvasContextType mCurrentContextType;
|
||||
nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
|
|
@ -23,12 +23,47 @@
|
|||
|
||||
#include "CanvasUtils.h"
|
||||
#include "mozilla/gfx/Matrix.h"
|
||||
#include "WebGL2Context.h"
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
namespace CanvasUtils {
|
||||
|
||||
bool
|
||||
GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type)
|
||||
{
|
||||
if (str.EqualsLiteral("2d")) {
|
||||
*out_type = dom::CanvasContextType::Canvas2D;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (str.EqualsLiteral("experimental-webgl")) {
|
||||
*out_type = dom::CanvasContextType::WebGL1;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WEBGL_CONFORMANT
|
||||
if (str.EqualsLiteral("webgl")) {
|
||||
/* WebGL 1.0, $2.1 "Context Creation":
|
||||
* If the user agent supports both the webgl and experimental-webgl
|
||||
* canvas context types, they shall be treated as aliases.
|
||||
*/
|
||||
*out_type = dom::CanvasContextType::WebGL1;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (WebGL2Context::IsSupported()) {
|
||||
if (str.EqualsLiteral("webgl2")) {
|
||||
*out_type = dom::CanvasContextType::WebGL2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This security check utility might be called from an source that never taints
|
||||
* others. For example, while painting a CanvasPattern, which is created from an
|
||||
|
|
|
@ -21,6 +21,7 @@ class HTMLCanvasElement;
|
|||
|
||||
namespace CanvasUtils {
|
||||
|
||||
bool GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type);
|
||||
|
||||
// Check that the rectangle [x,y,w,h] is a subrectangle of [0,0,realWidth,realHeight]
|
||||
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "OffscreenCanvas.h"
|
||||
|
||||
#include "mozilla/dom/OffscreenCanvasBinding.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/layers/AsyncCanvasRenderer.h"
|
||||
#include "mozilla/layers/CanvasClient.h"
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "CanvasRenderingContext2D.h"
|
||||
#include "CanvasUtils.h"
|
||||
#include "GLScreenBuffer.h"
|
||||
#include "WebGL1Context.h"
|
||||
#include "WebGL2Context.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
OffscreenCanvasCloneData::OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
|
||||
uint32_t aWidth, uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
bool aNeutered)
|
||||
: mRenderer(aRenderer)
|
||||
, mWidth(aWidth)
|
||||
, mHeight(aHeight)
|
||||
, mCompositorBackendType(aCompositorBackend)
|
||||
, mNeutered(aNeutered)
|
||||
{
|
||||
}
|
||||
|
||||
OffscreenCanvasCloneData::~OffscreenCanvasCloneData()
|
||||
{
|
||||
}
|
||||
|
||||
OffscreenCanvas::OffscreenCanvas(uint32_t aWidth,
|
||||
uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
layers::AsyncCanvasRenderer* aRenderer)
|
||||
: mAttrDirty(false)
|
||||
, mNeutered(false)
|
||||
, mWidth(aWidth)
|
||||
, mHeight(aHeight)
|
||||
, mCompositorBackendType(aCompositorBackend)
|
||||
, mCanvasClient(nullptr)
|
||||
, mCanvasRenderer(aRenderer)
|
||||
{}
|
||||
|
||||
OffscreenCanvas::~OffscreenCanvas()
|
||||
{
|
||||
ClearResources();
|
||||
}
|
||||
|
||||
OffscreenCanvas*
|
||||
OffscreenCanvas::GetParentObject() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
OffscreenCanvas::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return OffscreenCanvasBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
OffscreenCanvas::ClearResources()
|
||||
{
|
||||
if (mCanvasClient) {
|
||||
mCanvasClient->Clear();
|
||||
ImageBridgeChild::DispatchReleaseCanvasClient(mCanvasClient);
|
||||
mCanvasClient = nullptr;
|
||||
|
||||
if (mCanvasRenderer) {
|
||||
nsCOMPtr<nsIThread> activeThread = mCanvasRenderer->GetActiveThread();
|
||||
MOZ_RELEASE_ASSERT(activeThread);
|
||||
MOZ_RELEASE_ASSERT(activeThread == NS_GetCurrentThread());
|
||||
mCanvasRenderer->SetCanvasClient(nullptr);
|
||||
mCanvasRenderer->mContext = nullptr;
|
||||
mCanvasRenderer->mGLContext = nullptr;
|
||||
mCanvasRenderer->ResetActiveThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
OffscreenCanvas::GetContext(JSContext* aCx,
|
||||
const nsAString& aContextId,
|
||||
JS::Handle<JS::Value> aContextOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (mNeutered) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We only support WebGL in workers for now
|
||||
CanvasContextType contextType;
|
||||
if (!CanvasUtils::GetCanvasContextType(aContextId, &contextType)) {
|
||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!(contextType == CanvasContextType::WebGL1 ||
|
||||
contextType == CanvasContextType::WebGL2))
|
||||
{
|
||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports> result =
|
||||
CanvasRenderingContextHelper::GetContext(aCx,
|
||||
aContextId,
|
||||
aContextOptions,
|
||||
aRv);
|
||||
|
||||
if (!mCurrentContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mCanvasRenderer) {
|
||||
WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
|
||||
gl::GLContext* gl = webGL->GL();
|
||||
mCanvasRenderer->mContext = mCurrentContext;
|
||||
mCanvasRenderer->SetActiveThread();
|
||||
mCanvasRenderer->mGLContext = gl;
|
||||
mCanvasRenderer->SetIsAlphaPremultiplied(webGL->IsPremultAlpha() || !gl->Caps().alpha);
|
||||
|
||||
if (ImageBridgeChild::IsCreated()) {
|
||||
TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
|
||||
mCanvasClient = ImageBridgeChild::GetSingleton()->
|
||||
CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take();
|
||||
mCanvasRenderer->SetCanvasClient(mCanvasClient);
|
||||
|
||||
gl::GLScreenBuffer* screen = gl->Screen();
|
||||
gl::SurfaceCaps caps = screen->mCaps;
|
||||
auto forwarder = mCanvasClient->GetForwarder();
|
||||
|
||||
UniquePtr<gl::SurfaceFactory> factory =
|
||||
gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
|
||||
|
||||
if (factory)
|
||||
screen->Morph(Move(factory));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
OffscreenCanvas::CreateContext(CanvasContextType aContextType)
|
||||
{
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> ret =
|
||||
CanvasRenderingContextHelper::CreateContext(aContextType);
|
||||
|
||||
ret->SetOffscreenCanvas(this);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
void
|
||||
OffscreenCanvas::CommitFrameToCompositor()
|
||||
{
|
||||
// The attributes has changed, we have to notify main
|
||||
// thread to change canvas size.
|
||||
if (mAttrDirty) {
|
||||
if (mCanvasRenderer) {
|
||||
mCanvasRenderer->SetWidth(mWidth);
|
||||
mCanvasRenderer->SetHeight(mHeight);
|
||||
mCanvasRenderer->NotifyElementAboutAttributesChanged();
|
||||
}
|
||||
mAttrDirty = false;
|
||||
}
|
||||
|
||||
if (mCurrentContext) {
|
||||
static_cast<WebGLContext*>(mCurrentContext.get())->PresentScreenBuffer();
|
||||
}
|
||||
|
||||
if (mCanvasRenderer && mCanvasRenderer->mGLContext) {
|
||||
mCanvasRenderer->NotifyElementAboutInvalidation();
|
||||
ImageBridgeChild::GetSingleton()->
|
||||
UpdateAsyncCanvasRenderer(mCanvasRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
OffscreenCanvasCloneData*
|
||||
OffscreenCanvas::ToCloneData()
|
||||
{
|
||||
return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth, mHeight,
|
||||
mCompositorBackendType, mNeutered);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<OffscreenCanvas>
|
||||
OffscreenCanvas::CreateFromCloneData(OffscreenCanvasCloneData* aData)
|
||||
{
|
||||
MOZ_ASSERT(aData);
|
||||
nsRefPtr<OffscreenCanvas> wc =
|
||||
new OffscreenCanvas(aData->mWidth, aData->mHeight,
|
||||
aData->mCompositorBackendType, aData->mRenderer);
|
||||
if (aData->mNeutered) {
|
||||
wc->SetNeutered();
|
||||
}
|
||||
return wc.forget();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
OffscreenCanvas::PrefEnabled(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return Preferences::GetBool("gfx.offscreencanvas.enabled");
|
||||
} else {
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
return workerPrivate->OffscreenCanvasEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
OffscreenCanvas::PrefEnabledOnWorkerThread(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return PrefEnabled(aCx, aObj);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(OffscreenCanvas, DOMEventTargetHelper, mCurrentContext)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OffscreenCanvas)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,179 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 MOZILLA_DOM_OFFSCREENCANVAS_H_
|
||||
#define MOZILLA_DOM_OFFSCREENCANVAS_H_
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "CanvasRenderingContextHelper.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
struct JSContext;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace layers {
|
||||
class AsyncCanvasRenderer;
|
||||
class CanvasClient;
|
||||
} // namespace layers
|
||||
|
||||
namespace dom {
|
||||
|
||||
// This is helper class for transferring OffscreenCanvas to worker thread.
|
||||
// Because OffscreenCanvas is not thread-safe. So we cannot pass Offscreen-
|
||||
// Canvas to worker thread directly. Thus, we create this helper class and
|
||||
// store necessary data in it then pass it to worker thread.
|
||||
struct OffscreenCanvasCloneData final
|
||||
{
|
||||
OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
|
||||
uint32_t aWidth, uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
bool aNeutered);
|
||||
~OffscreenCanvasCloneData();
|
||||
|
||||
RefPtr<layers::AsyncCanvasRenderer> mRenderer;
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
layers::LayersBackend mCompositorBackendType;
|
||||
bool mNeutered;
|
||||
};
|
||||
|
||||
class OffscreenCanvas final : public DOMEventTargetHelper
|
||||
, public CanvasRenderingContextHelper
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
|
||||
|
||||
OffscreenCanvas(uint32_t aWidth,
|
||||
uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
layers::AsyncCanvasRenderer* aRenderer);
|
||||
|
||||
OffscreenCanvas* GetParentObject() const;
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void ClearResources();
|
||||
|
||||
uint32_t Width() const
|
||||
{
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
uint32_t Height() const
|
||||
{
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
void SetWidth(uint32_t aWidth, ErrorResult& aRv)
|
||||
{
|
||||
if (mNeutered) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mWidth != aWidth) {
|
||||
mWidth = aWidth;
|
||||
CanvasAttrChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void SetHeight(uint32_t aHeight, ErrorResult& aRv)
|
||||
{
|
||||
if (mNeutered) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mHeight != aHeight) {
|
||||
mHeight = aHeight;
|
||||
CanvasAttrChanged();
|
||||
}
|
||||
}
|
||||
|
||||
nsICanvasRenderingContextInternal* GetContext() const
|
||||
{
|
||||
return mCurrentContext;
|
||||
}
|
||||
|
||||
static already_AddRefed<OffscreenCanvas>
|
||||
CreateFromCloneData(OffscreenCanvasCloneData* aData);
|
||||
|
||||
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
// Return true on main-thread, and return gfx.offscreencanvas.enabled
|
||||
// on worker thread.
|
||||
static bool PrefEnabledOnWorkerThread(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
OffscreenCanvasCloneData* ToCloneData();
|
||||
|
||||
void CommitFrameToCompositor();
|
||||
|
||||
virtual bool GetOpaqueAttr() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual nsIntSize GetWidthHeight() override
|
||||
{
|
||||
return nsIntSize(mWidth, mHeight);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
CreateContext(CanvasContextType aContextType) override;
|
||||
|
||||
virtual already_AddRefed<nsISupports>
|
||||
GetContext(JSContext* aCx,
|
||||
const nsAString& aContextId,
|
||||
JS::Handle<JS::Value> aContextOptions,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
void SetNeutered()
|
||||
{
|
||||
mNeutered = true;
|
||||
}
|
||||
|
||||
bool IsNeutered() const
|
||||
{
|
||||
return mNeutered;
|
||||
}
|
||||
|
||||
layers::LayersBackend GetCompositorBackendType() const
|
||||
{
|
||||
return mCompositorBackendType;
|
||||
}
|
||||
|
||||
private:
|
||||
~OffscreenCanvas();
|
||||
|
||||
void CanvasAttrChanged()
|
||||
{
|
||||
mAttrDirty = true;
|
||||
UpdateContext(nullptr, JS::NullHandleValue);
|
||||
}
|
||||
|
||||
bool mAttrDirty;
|
||||
bool mNeutered;
|
||||
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
|
||||
layers::LayersBackend mCompositorBackendType;
|
||||
|
||||
layers::CanvasClient* mCanvasClient;
|
||||
RefPtr<layers::AsyncCanvasRenderer> mCanvasRenderer;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_DOM_OFFSCREENCANVAS_H_
|
|
@ -22,6 +22,7 @@
|
|||
#include "ImageEncoder.h"
|
||||
#include "Layers.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/HTMLVideoElement.h"
|
||||
#include "mozilla/dom/ImageData.h"
|
||||
#include "mozilla/EnumeratedArrayCycleCollection.h"
|
||||
|
@ -79,125 +80,6 @@ using namespace mozilla::gfx;
|
|||
using namespace mozilla::gl;
|
||||
using namespace mozilla::layers;
|
||||
|
||||
WebGLObserver::WebGLObserver(WebGLContext* webgl)
|
||||
: mWebGL(webgl)
|
||||
{
|
||||
}
|
||||
|
||||
WebGLObserver::~WebGLObserver()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
WebGLObserver::Destroy()
|
||||
{
|
||||
UnregisterMemoryPressureEvent();
|
||||
UnregisterVisibilityChangeEvent();
|
||||
mWebGL = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLObserver::RegisterVisibilityChangeEvent()
|
||||
{
|
||||
if (!mWebGL)
|
||||
return;
|
||||
|
||||
HTMLCanvasElement* canvas = mWebGL->GetCanvas();
|
||||
MOZ_ASSERT(canvas);
|
||||
|
||||
if (canvas) {
|
||||
nsIDocument* document = canvas->OwnerDoc();
|
||||
|
||||
document->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
||||
this, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGLObserver::UnregisterVisibilityChangeEvent()
|
||||
{
|
||||
if (!mWebGL)
|
||||
return;
|
||||
|
||||
HTMLCanvasElement* canvas = mWebGL->GetCanvas();
|
||||
|
||||
if (canvas) {
|
||||
nsIDocument* document = canvas->OwnerDoc();
|
||||
|
||||
document->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
||||
this, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGLObserver::RegisterMemoryPressureEvent()
|
||||
{
|
||||
if (!mWebGL)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
|
||||
MOZ_ASSERT(observerService);
|
||||
|
||||
if (observerService)
|
||||
observerService->AddObserver(this, "memory-pressure", false);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLObserver::UnregisterMemoryPressureEvent()
|
||||
{
|
||||
if (!mWebGL)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
|
||||
// Do not assert on observerService here. This might be triggered by
|
||||
// the cycle collector at a late enough time, that XPCOM services are
|
||||
// no longer available. See bug 1029504.
|
||||
if (observerService)
|
||||
observerService->RemoveObserver(this, "memory-pressure");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLObserver::Observe(nsISupports*, const char* topic, const char16_t*)
|
||||
{
|
||||
if (!mWebGL || strcmp(topic, "memory-pressure")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool wantToLoseContext = mWebGL->mLoseContextOnMemoryPressure;
|
||||
|
||||
if (!mWebGL->mCanLoseContextInForeground &&
|
||||
ProcessPriorityManager::CurrentProcessIsForeground())
|
||||
{
|
||||
wantToLoseContext = false;
|
||||
}
|
||||
|
||||
if (wantToLoseContext)
|
||||
mWebGL->ForceLoseContext();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLObserver::HandleEvent(nsIDOMEvent* event)
|
||||
{
|
||||
nsAutoString type;
|
||||
event->GetType(type);
|
||||
if (!mWebGL || !type.EqualsLiteral("visibilitychange"))
|
||||
return NS_OK;
|
||||
|
||||
HTMLCanvasElement* canvas = mWebGL->GetCanvas();
|
||||
MOZ_ASSERT(canvas);
|
||||
|
||||
if (canvas && !canvas->OwnerDoc()->Hidden())
|
||||
mWebGL->ForceRestoreContext();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
WebGLContextOptions::WebGLContextOptions()
|
||||
: alpha(true)
|
||||
, depth(true)
|
||||
|
@ -208,7 +90,7 @@ WebGLContextOptions::WebGLContextOptions()
|
|||
, failIfMajorPerformanceCaveat(false)
|
||||
{
|
||||
// Set default alpha state based on preference.
|
||||
if (Preferences::GetBool("webgl.default-no-alpha", false))
|
||||
if (gfxPrefs::WebGLDefaultNoAlpha())
|
||||
alpha = false;
|
||||
}
|
||||
|
||||
|
@ -282,7 +164,10 @@ WebGLContext::WebGLContext()
|
|||
mPixelStorePackAlignment = 4;
|
||||
mPixelStoreUnpackAlignment = 4;
|
||||
|
||||
WebGLMemoryTracker::AddWebGLContext(this);
|
||||
if (NS_IsMainThread()) {
|
||||
// XXX mtseng: bug 709490, not thread safe
|
||||
WebGLMemoryTracker::AddWebGLContext(this);
|
||||
}
|
||||
|
||||
mAllowContextRestore = true;
|
||||
mLastLossWasSimulated = false;
|
||||
|
@ -296,15 +181,12 @@ WebGLContext::WebGLContext()
|
|||
mAlreadyWarnedAboutFakeVertexAttrib0 = false;
|
||||
mAlreadyWarnedAboutViewportLargerThanDest = false;
|
||||
|
||||
mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32);
|
||||
mMaxWarnings = gfxPrefs::WebGLMaxWarningsPerContext();
|
||||
if (mMaxWarnings < -1) {
|
||||
GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
|
||||
mMaxWarnings = 0;
|
||||
}
|
||||
|
||||
mContextObserver = new WebGLObserver(this);
|
||||
MOZ_RELEASE_ASSERT(mContextObserver, "Can't alloc WebGLContextObserver");
|
||||
|
||||
mLastUseIndex = 0;
|
||||
|
||||
InvalidateBufferFetching();
|
||||
|
@ -319,10 +201,12 @@ WebGLContext::WebGLContext()
|
|||
WebGLContext::~WebGLContext()
|
||||
{
|
||||
RemovePostRefreshObserver();
|
||||
mContextObserver->Destroy();
|
||||
|
||||
DestroyResourcesAndContext();
|
||||
WebGLMemoryTracker::RemoveWebGLContext(this);
|
||||
if (NS_IsMainThread()) {
|
||||
// XXX mtseng: bug 709490, not thread safe
|
||||
WebGLMemoryTracker::RemoveWebGLContext(this);
|
||||
}
|
||||
|
||||
mContextLossHandler->DisableTimer();
|
||||
mContextLossHandler = nullptr;
|
||||
|
@ -331,8 +215,6 @@ WebGLContext::~WebGLContext()
|
|||
void
|
||||
WebGLContext::DestroyResourcesAndContext()
|
||||
{
|
||||
mContextObserver->UnregisterMemoryPressureEvent();
|
||||
|
||||
if (!gl)
|
||||
return;
|
||||
|
||||
|
@ -431,6 +313,35 @@ WebGLContext::Invalidate()
|
|||
mCanvasElement->InvalidateCanvasContent(nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::OnVisibilityChange()
|
||||
{
|
||||
if (!IsContextLost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mRestoreWhenVisible || mLastLossWasSimulated) {
|
||||
return;
|
||||
}
|
||||
|
||||
ForceRestoreContext();
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::OnMemoryPressure()
|
||||
{
|
||||
bool shouldLoseContext = mLoseContextOnMemoryPressure;
|
||||
|
||||
if (!mCanLoseContextInForeground &&
|
||||
ProcessPriorityManager::CurrentProcessIsForeground())
|
||||
{
|
||||
shouldLoseContext = false;
|
||||
}
|
||||
|
||||
if (shouldLoseContext)
|
||||
ForceLoseContext();
|
||||
}
|
||||
|
||||
//
|
||||
// nsICanvasRenderingContextInternal
|
||||
//
|
||||
|
@ -513,7 +424,7 @@ static bool
|
|||
IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature)
|
||||
{
|
||||
int32_t status;
|
||||
if (!NS_SUCCEEDED(gfxInfo->GetFeatureStatus(feature, &status)))
|
||||
if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature, &status)))
|
||||
return false;
|
||||
|
||||
return status != nsIGfxInfo::FEATURE_STATUS_OK;
|
||||
|
@ -524,19 +435,29 @@ HasAcceleratedLayers(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
|
|||
{
|
||||
int32_t status;
|
||||
|
||||
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, &status);
|
||||
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
|
||||
nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
|
||||
&status);
|
||||
if (status)
|
||||
return true;
|
||||
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS, &status);
|
||||
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
|
||||
nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
|
||||
&status);
|
||||
if (status)
|
||||
return true;
|
||||
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS, &status);
|
||||
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
|
||||
nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
|
||||
&status);
|
||||
if (status)
|
||||
return true;
|
||||
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status);
|
||||
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
|
||||
nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
|
||||
&status);
|
||||
if (status)
|
||||
return true;
|
||||
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_OPENGL_LAYERS, &status);
|
||||
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
|
||||
nsIGfxInfo::FEATURE_OPENGL_LAYERS,
|
||||
&status);
|
||||
if (status)
|
||||
return true;
|
||||
|
||||
|
@ -593,11 +514,14 @@ BaseCaps(const WebGLContextOptions& options, WebGLContext* webgl)
|
|||
// we should really have this behind a
|
||||
// |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
|
||||
// for now it's just behind a pref for testing/evaluation.
|
||||
baseCaps.bpp16 = Preferences::GetBool("webgl.prefer-16bpp", false);
|
||||
baseCaps.bpp16 = gfxPrefs::WebGLPrefer16bpp();
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
do {
|
||||
auto canvasElement = webgl->GetCanvas();
|
||||
if (!canvasElement)
|
||||
break;
|
||||
|
||||
auto ownerDoc = canvasElement->OwnerDoc();
|
||||
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(ownerDoc);
|
||||
if (!docWidget)
|
||||
|
@ -618,7 +542,7 @@ BaseCaps(const WebGLContextOptions& options, WebGLContext* webgl)
|
|||
|
||||
// Done with baseCaps construction.
|
||||
|
||||
bool forceAllowAA = Preferences::GetBool("webgl.msaa-force", false);
|
||||
bool forceAllowAA = gfxPrefs::WebGLForceMSAA();
|
||||
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
|
||||
if (!forceAllowAA &&
|
||||
IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA))
|
||||
|
@ -738,7 +662,7 @@ bool
|
|||
WebGLContext::CreateAndInitGL(bool forceEnabled)
|
||||
{
|
||||
bool preferEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
|
||||
bool disableANGLE = Preferences::GetBool("webgl.disable-angle", false);
|
||||
bool disableANGLE = gfxPrefs::WebGLDisableANGLE();
|
||||
|
||||
if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL"))
|
||||
disableANGLE = true;
|
||||
|
@ -819,10 +743,6 @@ WebGLContext::ResizeBackbuffer(uint32_t requestedWidth,
|
|||
NS_IMETHODIMP
|
||||
WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
|
||||
{
|
||||
// Early error return cases
|
||||
if (!GetCanvas())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (signedWidth < 0 || signedHeight < 0) {
|
||||
GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -832,7 +752,10 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
|
|||
uint32_t height = signedHeight;
|
||||
|
||||
// Early success return cases
|
||||
GetCanvas()->InvalidateCanvas();
|
||||
|
||||
// May have a OffscreenCanvas instead of an HTMLCanvasElement
|
||||
if (GetCanvas())
|
||||
GetCanvas()->InvalidateCanvas();
|
||||
|
||||
// Zero-sized surfaces can cause problems.
|
||||
if (width == 0)
|
||||
|
@ -905,10 +828,7 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
|
|||
// pick up the old generation.
|
||||
++mGeneration;
|
||||
|
||||
// Get some prefs for some preferred/overriden things
|
||||
NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
|
||||
|
||||
bool disabled = Preferences::GetBool("webgl.disabled", false);
|
||||
bool disabled = gfxPrefs::WebGLDisabled();
|
||||
|
||||
// TODO: When we have software webgl support we should use that instead.
|
||||
disabled |= gfxPlatform::InSafeMode();
|
||||
|
@ -931,7 +851,7 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
|
|||
}
|
||||
|
||||
// Alright, now let's start trying.
|
||||
bool forceEnabled = Preferences::GetBool("webgl.force-enabled", false);
|
||||
bool forceEnabled = gfxPrefs::WebGLForceEnabled();
|
||||
ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
|
||||
|
||||
MOZ_ASSERT(!gl);
|
||||
|
@ -1052,6 +972,11 @@ WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
|
|||
#endif
|
||||
MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
// XXX mtseng: bug 709490, WebGLMemoryTracker is not thread safe.
|
||||
return;
|
||||
}
|
||||
|
||||
// it's important to update the index on a new context before losing old contexts,
|
||||
// otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
|
||||
// when choosing which one to lose first.
|
||||
|
@ -1139,32 +1064,8 @@ WebGLContext::GetImageBuffer(uint8_t** out_imageBuffer, int32_t* out_format)
|
|||
|
||||
RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map))
|
||||
return;
|
||||
|
||||
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
|
||||
if (!imageBuffer) {
|
||||
dataSurface->Unmap();
|
||||
return;
|
||||
}
|
||||
memcpy(imageBuffer, map.mData, mWidth * mHeight * 4);
|
||||
|
||||
dataSurface->Unmap();
|
||||
|
||||
int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
|
||||
if (!mOptions.premultipliedAlpha) {
|
||||
// We need to convert to INPUT_FORMAT_RGBA, otherwise
|
||||
// we are automatically considered premult, and unpremult'd.
|
||||
// Yes, it is THAT silly.
|
||||
// Except for different lossy conversions by color,
|
||||
// we could probably just change the label, and not change the data.
|
||||
gfxUtils::ConvertBGRAtoRGBA(imageBuffer, mWidth * mHeight * 4);
|
||||
format = imgIEncoder::INPUT_FORMAT_RGBA;
|
||||
}
|
||||
|
||||
*out_imageBuffer = imageBuffer;
|
||||
*out_format = format;
|
||||
return gfxUtils::GetImageBuffer(dataSurface, mOptions.premultipliedAlpha,
|
||||
out_imageBuffer, out_format);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -1176,20 +1077,18 @@ WebGLContext::GetInputStream(const char* mimeType,
|
|||
if (!gl)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCString enccid("@mozilla.org/image/encoder;2?type=");
|
||||
enccid += mimeType;
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
|
||||
if (!encoder)
|
||||
// Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
|
||||
bool premult;
|
||||
RefPtr<SourceSurface> snapshot =
|
||||
GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
|
||||
if (!snapshot)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsAutoArrayPtr<uint8_t> imageBuffer;
|
||||
int32_t format = 0;
|
||||
GetImageBuffer(getter_Transfers(imageBuffer), &format);
|
||||
if (!imageBuffer)
|
||||
return NS_ERROR_FAILURE;
|
||||
MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
|
||||
|
||||
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
|
||||
encoder, encoderOptions, out_stream);
|
||||
RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
|
||||
return gfxUtils::GetInputStream(dataSurface, mOptions.premultipliedAlpha, mimeType,
|
||||
encoderOptions, out_stream);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1268,25 +1167,26 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
|
|||
}
|
||||
|
||||
WebGLContextUserData* userData = nullptr;
|
||||
if (builder->IsPaintingToWindow()) {
|
||||
// Make the layer tell us whenever a transaction finishes (including
|
||||
// the current transaction), so we can clear our invalidation state and
|
||||
// start invalidating again. We need to do this for the layer that is
|
||||
// being painted to a window (there shouldn't be more than one at a time,
|
||||
// and if there is, flushing the invalidation state more often than
|
||||
// necessary is harmless).
|
||||
if (builder->IsPaintingToWindow() && mCanvasElement) {
|
||||
// Make the layer tell us whenever a transaction finishes (including
|
||||
// the current transaction), so we can clear our invalidation state and
|
||||
// start invalidating again. We need to do this for the layer that is
|
||||
// being painted to a window (there shouldn't be more than one at a time,
|
||||
// and if there is, flushing the invalidation state more often than
|
||||
// necessary is harmless).
|
||||
|
||||
// The layer will be destroyed when we tear down the presentation
|
||||
// (at the latest), at which time this userData will be destroyed,
|
||||
// releasing the reference to the element.
|
||||
// The userData will receive DidTransactionCallbacks, which flush the
|
||||
// the invalidation state to indicate that the canvas is up to date.
|
||||
userData = new WebGLContextUserData(mCanvasElement);
|
||||
canvasLayer->SetDidTransactionCallback(
|
||||
WebGLContextUserData::DidTransactionCallback, userData);
|
||||
canvasLayer->SetPreTransactionCallback(
|
||||
WebGLContextUserData::PreTransactionCallback, userData);
|
||||
// The layer will be destroyed when we tear down the presentation
|
||||
// (at the latest), at which time this userData will be destroyed,
|
||||
// releasing the reference to the element.
|
||||
// The userData will receive DidTransactionCallbacks, which flush the
|
||||
// the invalidation state to indicate that the canvas is up to date.
|
||||
userData = new WebGLContextUserData(mCanvasElement);
|
||||
canvasLayer->SetDidTransactionCallback(
|
||||
WebGLContextUserData::DidTransactionCallback, userData);
|
||||
canvasLayer->SetPreTransactionCallback(
|
||||
WebGLContextUserData::PreTransactionCallback, userData);
|
||||
}
|
||||
|
||||
canvasLayer->SetUserData(&gWebGLLayerUserData, userData);
|
||||
|
||||
CanvasLayer::Data data;
|
||||
|
@ -1308,14 +1208,36 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
|
|||
layers::LayersBackend
|
||||
WebGLContext::GetCompositorBackendType() const
|
||||
{
|
||||
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
|
||||
if (docWidget) {
|
||||
layers::LayerManager* layerManager = docWidget->GetLayerManager();
|
||||
return layerManager->GetCompositorBackendType();
|
||||
if (mCanvasElement) {
|
||||
return mCanvasElement->GetCompositorBackendType();
|
||||
} else if (mOffscreenCanvas) {
|
||||
return mOffscreenCanvas->GetCompositorBackendType();
|
||||
}
|
||||
|
||||
return LayersBackend::LAYERS_NONE;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::Commit()
|
||||
{
|
||||
if (mOffscreenCanvas) {
|
||||
mOffscreenCanvas->CommitFrameToCompositor();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval)
|
||||
{
|
||||
if (mCanvasElement) {
|
||||
MOZ_RELEASE_ASSERT(!mOffscreenCanvas);
|
||||
retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement;
|
||||
} else if (mOffscreenCanvas) {
|
||||
retval.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas;
|
||||
} else {
|
||||
retval.SetNull();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval)
|
||||
{
|
||||
|
@ -1624,7 +1546,7 @@ WebGLContext::RunContextLossTimer()
|
|||
mContextLossHandler->RunTimer();
|
||||
}
|
||||
|
||||
class UpdateContextLossStatusTask : public nsRunnable
|
||||
class UpdateContextLossStatusTask : public nsCancelableRunnable
|
||||
{
|
||||
nsRefPtr<WebGLContext> mWebGL;
|
||||
|
||||
|
@ -1635,10 +1557,16 @@ public:
|
|||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mWebGL->UpdateContextLossStatus();
|
||||
if (mWebGL)
|
||||
mWebGL->UpdateContextLossStatus();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Cancel() {
|
||||
mWebGL = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
|
@ -1665,7 +1593,7 @@ WebGLContext::EnqueueUpdateContextLossStatus()
|
|||
void
|
||||
WebGLContext::UpdateContextLossStatus()
|
||||
{
|
||||
if (!mCanvasElement) {
|
||||
if (!mCanvasElement && !mOffscreenCanvas) {
|
||||
// the canvas is gone. That happens when the page was closed before we got
|
||||
// this timer event. In this case, there's nothing to do here, just don't crash.
|
||||
return;
|
||||
|
@ -1693,12 +1621,23 @@ WebGLContext::UpdateContextLossStatus()
|
|||
// callback, so do that now.
|
||||
|
||||
bool useDefaultHandler;
|
||||
nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
|
||||
NS_LITERAL_STRING("webglcontextlost"),
|
||||
true,
|
||||
true,
|
||||
&useDefaultHandler);
|
||||
|
||||
if (mCanvasElement) {
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
mCanvasElement->OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
|
||||
NS_LITERAL_STRING("webglcontextlost"),
|
||||
true,
|
||||
true,
|
||||
&useDefaultHandler);
|
||||
} else {
|
||||
// OffscreenCanvas case
|
||||
nsRefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
|
||||
event->InitEvent(NS_LITERAL_STRING("webglcontextlost"), true, true);
|
||||
event->SetTrusted(true);
|
||||
mOffscreenCanvas->DispatchEvent(event, &useDefaultHandler);
|
||||
}
|
||||
|
||||
// We sent the callback, so we're just 'regular lost' now.
|
||||
mContextStatus = ContextLost;
|
||||
// If we're told to use the default handler, it means the script
|
||||
|
@ -1750,11 +1689,22 @@ WebGLContext::UpdateContextLossStatus()
|
|||
|
||||
// Revival!
|
||||
mContextStatus = ContextNotLost;
|
||||
nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
|
||||
NS_LITERAL_STRING("webglcontextrestored"),
|
||||
true,
|
||||
true);
|
||||
|
||||
if (mCanvasElement) {
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
mCanvasElement->OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
|
||||
NS_LITERAL_STRING("webglcontextrestored"),
|
||||
true,
|
||||
true);
|
||||
} else {
|
||||
nsRefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
|
||||
event->InitEvent(NS_LITERAL_STRING("webglcontextrestored"), true, true);
|
||||
event->SetTrusted(true);
|
||||
bool unused;
|
||||
mOffscreenCanvas->DispatchEvent(event, &unused);
|
||||
}
|
||||
|
||||
mEmitContextLostErrorOnce = true;
|
||||
return;
|
||||
}
|
||||
|
@ -1772,12 +1722,6 @@ WebGLContext::ForceLoseContext(bool simulateLosing)
|
|||
DestroyResourcesAndContext();
|
||||
mLastLossWasSimulated = simulateLosing;
|
||||
|
||||
// Register visibility change observer to defer the context restoring.
|
||||
// Restore the context when the app is visible.
|
||||
if (mRestoreWhenVisible && !mLastLossWasSimulated) {
|
||||
mContextObserver->RegisterVisibilityChangeEvent();
|
||||
}
|
||||
|
||||
// Queue up a task, since we know the status changed.
|
||||
EnqueueUpdateContextLossStatus();
|
||||
}
|
||||
|
@ -1789,8 +1733,6 @@ WebGLContext::ForceRestoreContext()
|
|||
mContextStatus = ContextLostAwaitingRestore;
|
||||
mAllowContextRestore = true; // Hey, you did say 'force'.
|
||||
|
||||
mContextObserver->UnregisterVisibilityChangeEvent();
|
||||
|
||||
// Queue up a task, since we know the status changed.
|
||||
EnqueueUpdateContextLossStatus();
|
||||
}
|
||||
|
@ -1925,6 +1867,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
|
||||
mCanvasElement,
|
||||
mOffscreenCanvas,
|
||||
mExtensions,
|
||||
mBound2DTextures,
|
||||
mBoundCubeMapTextures,
|
||||
|
|
|
@ -40,7 +40,11 @@
|
|||
// Generated
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIDOMWebGLRenderingContext.h"
|
||||
#include "nsICanvasRenderingContextInternal.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
||||
|
||||
class nsIDocShell;
|
||||
|
@ -80,7 +84,6 @@ class WebGLContextLossHandler;
|
|||
class WebGLBuffer;
|
||||
class WebGLExtensionBase;
|
||||
class WebGLFramebuffer;
|
||||
class WebGLObserver;
|
||||
class WebGLProgram;
|
||||
class WebGLQuery;
|
||||
class WebGLRenderbuffer;
|
||||
|
@ -95,6 +98,7 @@ class WebGLVertexArray;
|
|||
namespace dom {
|
||||
class Element;
|
||||
class ImageData;
|
||||
class OwningHTMLCanvasElementOrOffscreenCanvas;
|
||||
struct WebGLContextAttributes;
|
||||
template<typename> struct Nullable;
|
||||
} // namespace dom
|
||||
|
@ -184,7 +188,6 @@ class WebGLContext
|
|||
friend class WebGLExtensionLoseContext;
|
||||
friend class WebGLExtensionVertexArray;
|
||||
friend class WebGLMemoryTracker;
|
||||
friend class WebGLObserver;
|
||||
|
||||
enum {
|
||||
UNPACK_FLIP_Y_WEBGL = 0x9240,
|
||||
|
@ -214,6 +217,9 @@ public:
|
|||
|
||||
NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
|
||||
|
||||
virtual void OnVisibilityChange() override;
|
||||
virtual void OnMemoryPressure() override;
|
||||
|
||||
// nsICanvasRenderingContextInternal
|
||||
virtual int32_t GetWidth() const override;
|
||||
virtual int32_t GetHeight() const override;
|
||||
|
@ -361,8 +367,11 @@ public:
|
|||
void AssertCachedBindings();
|
||||
void AssertCachedState();
|
||||
|
||||
// WebIDL WebGLRenderingContext API
|
||||
dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
|
||||
|
||||
// WebIDL WebGLRenderingContext API
|
||||
void Commit();
|
||||
void GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval);
|
||||
GLsizei DrawingBufferWidth() const { return IsContextLost() ? 0 : mWidth; }
|
||||
GLsizei DrawingBufferHeight() const {
|
||||
return IsContextLost() ? 0 : mHeight;
|
||||
|
@ -1508,8 +1517,6 @@ protected:
|
|||
ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper;
|
||||
#endif
|
||||
|
||||
nsRefPtr<WebGLObserver> mContextObserver;
|
||||
|
||||
public:
|
||||
// console logging helpers
|
||||
void GenerateWarning(const char* fmt, ...);
|
||||
|
@ -1614,32 +1621,6 @@ WebGLContext::ValidateObject(const char* info, ObjectType* object)
|
|||
return ValidateObjectAssumeNonNull(info, object);
|
||||
}
|
||||
|
||||
// Listen visibilitychange and memory-pressure event for context lose/restore
|
||||
class WebGLObserver final
|
||||
: public nsIObserver
|
||||
, public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
explicit WebGLObserver(WebGLContext* webgl);
|
||||
|
||||
void Destroy();
|
||||
|
||||
void RegisterVisibilityChangeEvent();
|
||||
void UnregisterVisibilityChangeEvent();
|
||||
|
||||
void RegisterMemoryPressureEvent();
|
||||
void UnregisterMemoryPressureEvent();
|
||||
|
||||
private:
|
||||
~WebGLObserver();
|
||||
|
||||
WebGLContext* mWebGL;
|
||||
};
|
||||
|
||||
size_t RoundUpToMultipleOf(size_t value, size_t multiple);
|
||||
|
||||
bool
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "WebGLContext.h"
|
||||
#include "WebGLContextUtils.h"
|
||||
#include "WebGLExtensions.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "GLContext.h"
|
||||
|
||||
#include "nsString.h"
|
||||
|
@ -74,12 +75,15 @@ bool WebGLContext::IsExtensionSupported(JSContext* cx,
|
|||
|
||||
// Chrome contexts need access to debug information even when
|
||||
// webgl.disable-extensions is set. This is used in the graphics
|
||||
// section of about:support.
|
||||
if (xpc::AccessCheck::isChrome(js::GetContextCompartment(cx)))
|
||||
// section of about:support
|
||||
if (NS_IsMainThread() &&
|
||||
xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
|
||||
allowPrivilegedExts = true;
|
||||
}
|
||||
|
||||
if (Preferences::GetBool("webgl.enable-privileged-extensions", false))
|
||||
if (gfxPrefs::WebGLPrivilegedExtensionsEnabled()) {
|
||||
allowPrivilegedExts = true;
|
||||
}
|
||||
|
||||
if (allowPrivilegedExts) {
|
||||
switch (ext) {
|
||||
|
@ -181,9 +185,7 @@ WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
|
|||
break;
|
||||
}
|
||||
|
||||
if (Preferences::GetBool("webgl.enable-draft-extensions", false) ||
|
||||
IsWebGL2())
|
||||
{
|
||||
if (gfxPrefs::WebGLDraftExtensionsEnabled() || IsWebGL2()) {
|
||||
switch (ext) {
|
||||
case WebGLExtensionID::EXT_disjoint_timer_query:
|
||||
return WebGLExtensionDisjointTimerQuery::IsSupported(this);
|
||||
|
|
|
@ -1390,7 +1390,10 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (mCanvasElement->IsWriteOnly() && !nsContentUtils::IsCallerChrome()) {
|
||||
if (mCanvasElement &&
|
||||
mCanvasElement->IsWriteOnly() &&
|
||||
!nsContentUtils::IsCallerChrome())
|
||||
{
|
||||
GenerateWarning("readPixels: Not allowed");
|
||||
return rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
}
|
||||
|
|
|
@ -8,15 +8,103 @@
|
|||
#include "nsITimer.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Begin worker specific code
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// On workers we can only dispatch CancelableRunnables, so we have to wrap the
|
||||
// timer's EventTarget to use our own cancelable runnable
|
||||
|
||||
class ContextLossWorkerEventTarget final : public nsIEventTarget
|
||||
{
|
||||
public:
|
||||
explicit ContextLossWorkerEventTarget(nsIEventTarget* aEventTarget)
|
||||
: mEventTarget(aEventTarget)
|
||||
{
|
||||
MOZ_ASSERT(aEventTarget);
|
||||
}
|
||||
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
protected:
|
||||
~ContextLossWorkerEventTarget() {}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIEventTarget> mEventTarget;
|
||||
};
|
||||
|
||||
class ContextLossWorkerRunnable final : public nsICancelableRunnable
|
||||
{
|
||||
public:
|
||||
explicit ContextLossWorkerRunnable(nsIRunnable* aRunnable)
|
||||
: mRunnable(aRunnable)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_NSICANCELABLERUNNABLE
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
NS_FORWARD_NSIRUNNABLE(mRunnable->)
|
||||
|
||||
protected:
|
||||
~ContextLossWorkerRunnable() {}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ContextLossWorkerEventTarget, nsIEventTarget,
|
||||
nsISupports)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContextLossWorkerEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> event(aEvent);
|
||||
return Dispatch(event.forget(), aFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContextLossWorkerEventTarget::Dispatch(already_AddRefed<nsIRunnable>&& aEvent,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> eventRef(aEvent);
|
||||
nsRefPtr<ContextLossWorkerRunnable> wrappedEvent =
|
||||
new ContextLossWorkerRunnable(eventRef);
|
||||
return mEventTarget->Dispatch(wrappedEvent, aFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContextLossWorkerEventTarget::IsOnCurrentThread(bool* aResult)
|
||||
{
|
||||
return mEventTarget->IsOnCurrentThread(aResult);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(ContextLossWorkerRunnable, nsICancelableRunnable,
|
||||
nsIRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContextLossWorkerRunnable::Cancel()
|
||||
{
|
||||
mRunnable = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// End worker-specific code
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
WebGLContextLossHandler::WebGLContextLossHandler(WebGLContext* webgl)
|
||||
: mWeakWebGL(webgl)
|
||||
, mTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
|
||||
, mIsTimerRunning(false)
|
||||
, mShouldRunTimerAgain(false)
|
||||
, mIsDisabled(false)
|
||||
, mFeatureAdded(false)
|
||||
#ifdef DEBUG
|
||||
, mThread(NS_GetCurrentThread())
|
||||
#endif
|
||||
|
@ -90,6 +178,17 @@ WebGLContextLossHandler::RunTimer()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
dom::workers::WorkerPrivate* workerPrivate =
|
||||
dom::workers::GetCurrentThreadWorkerPrivate();
|
||||
nsCOMPtr<nsIEventTarget> target = workerPrivate->GetEventTarget();
|
||||
mTimer->SetTarget(new ContextLossWorkerEventTarget(target));
|
||||
if (!mFeatureAdded) {
|
||||
workerPrivate->AddFeature(workerPrivate->GetJSContext(), this);
|
||||
mFeatureAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
StartTimer(1000);
|
||||
|
||||
mIsTimerRunning = true;
|
||||
|
@ -104,6 +203,14 @@ WebGLContextLossHandler::DisableTimer()
|
|||
|
||||
mIsDisabled = true;
|
||||
|
||||
if (mFeatureAdded) {
|
||||
dom::workers::WorkerPrivate* workerPrivate =
|
||||
dom::workers::GetCurrentThreadWorkerPrivate();
|
||||
MOZ_RELEASE_ASSERT(workerPrivate);
|
||||
workerPrivate->RemoveFeature(workerPrivate->GetJSContext(), this);
|
||||
mFeatureAdded = false;
|
||||
}
|
||||
|
||||
// We can't just Cancel() the timer, as sometimes we end up
|
||||
// receiving a callback after calling Cancel(). This could cause us
|
||||
// to receive the callback after object destruction.
|
||||
|
@ -116,4 +223,16 @@ WebGLContextLossHandler::DisableTimer()
|
|||
mTimer->SetDelay(0);
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContextLossHandler::Notify(JSContext* aCx, dom::workers::Status aStatus)
|
||||
{
|
||||
bool isWorkerRunning = aStatus < dom::workers::Closing;
|
||||
if (!isWorkerRunning && mIsTimerRunning) {
|
||||
mIsTimerRunning = false;
|
||||
this->Release();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "WorkerFeature.h"
|
||||
|
||||
class nsIThread;
|
||||
class nsITimer;
|
||||
|
@ -17,13 +18,14 @@ class nsITimer;
|
|||
namespace mozilla {
|
||||
class WebGLContext;
|
||||
|
||||
class WebGLContextLossHandler
|
||||
class WebGLContextLossHandler : public dom::workers::WorkerFeature
|
||||
{
|
||||
WeakPtr<WebGLContext> mWeakWebGL;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
bool mIsTimerRunning;
|
||||
bool mShouldRunTimerAgain;
|
||||
bool mIsDisabled;
|
||||
bool mFeatureAdded;
|
||||
DebugOnly<nsIThread*> mThread;
|
||||
|
||||
public:
|
||||
|
@ -33,6 +35,7 @@ public:
|
|||
|
||||
void RunTimer();
|
||||
void DisableTimer();
|
||||
bool Notify(JSContext* aCx, dom::workers::Status aStatus) override;
|
||||
|
||||
protected:
|
||||
~WebGLContextLossHandler();
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
|
||||
nsISupports* data, bool)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <algorithm>
|
||||
#include "angle/ShaderLang.h"
|
||||
#include "CanvasUtils.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "GLContext.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
@ -1665,11 +1666,11 @@ WebGLContext::InitAndValidateGL()
|
|||
return false;
|
||||
}
|
||||
|
||||
mMinCapability = Preferences::GetBool("webgl.min_capability_mode", false);
|
||||
mDisableExtensions = Preferences::GetBool("webgl.disable-extensions", false);
|
||||
mLoseContextOnMemoryPressure = Preferences::GetBool("webgl.lose-context-on-memory-pressure", false);
|
||||
mCanLoseContextInForeground = Preferences::GetBool("webgl.can-lose-context-in-foreground", true);
|
||||
mRestoreWhenVisible = Preferences::GetBool("webgl.restore-context-when-visible", true);
|
||||
mMinCapability = gfxPrefs::WebGLMinCapabilityMode();
|
||||
mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
|
||||
mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
|
||||
mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
|
||||
mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();
|
||||
|
||||
if (MinCapabilityMode())
|
||||
mDisableFragHighP = true;
|
||||
|
@ -1878,10 +1879,7 @@ WebGLContext::InitAndValidateGL()
|
|||
#endif
|
||||
|
||||
// Check the shader validator pref
|
||||
NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
|
||||
|
||||
mBypassShaderValidation = Preferences::GetBool("webgl.bypass-shader-validation",
|
||||
mBypassShaderValidation);
|
||||
mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator();
|
||||
|
||||
// initialize shader translator
|
||||
if (!ShInitialize()) {
|
||||
|
@ -1937,9 +1935,6 @@ WebGLContext::InitAndValidateGL()
|
|||
mDefaultVertexArray->BindVertexArray();
|
||||
}
|
||||
|
||||
if (mLoseContextOnMemoryPressure)
|
||||
mContextObserver->RegisterMemoryPressureEvent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
|
||||
nsISupports* data, bool)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "WebGLShaderValidator.h"
|
||||
|
||||
#include "angle/ShaderLang.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "GLContext.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "MurmurHash3.h"
|
||||
|
@ -43,7 +44,7 @@ ChooseValidatorCompileOptions(const ShBuiltInResources& resources,
|
|||
options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
|
||||
}
|
||||
|
||||
if (Preferences::GetBool("webgl.all-angle-options", false)) {
|
||||
if (gfxPrefs::WebGLAllANGLEOptions()) {
|
||||
return options |
|
||||
SH_VALIDATE_LOOP_INDEXING |
|
||||
SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX |
|
||||
|
|
|
@ -28,10 +28,12 @@ EXPORTS.mozilla.dom += [
|
|||
'CanvasPath.h',
|
||||
'CanvasPattern.h',
|
||||
'CanvasRenderingContext2D.h',
|
||||
'CanvasRenderingContextHelper.h',
|
||||
'CanvasUtils.h',
|
||||
'ImageBitmap.h',
|
||||
'ImageBitmapSource.h',
|
||||
'ImageData.h',
|
||||
'OffscreenCanvas.h',
|
||||
'TextMetrics.h',
|
||||
'WebGLVertexArrayObject.h',
|
||||
]
|
||||
|
@ -40,11 +42,13 @@ EXPORTS.mozilla.dom += [
|
|||
UNIFIED_SOURCES += [
|
||||
'CanvasImageCache.cpp',
|
||||
'CanvasRenderingContext2D.cpp',
|
||||
'CanvasRenderingContextHelper.cpp',
|
||||
'CanvasUtils.cpp',
|
||||
'DocumentRendererChild.cpp',
|
||||
'DocumentRendererParent.cpp',
|
||||
'ImageBitmap.cpp',
|
||||
'ImageData.cpp',
|
||||
'OffscreenCanvas.cpp',
|
||||
]
|
||||
|
||||
# WebGL Sources
|
||||
|
@ -150,6 +154,7 @@ LOCAL_INCLUDES += [
|
|||
'/dom/base',
|
||||
'/dom/html',
|
||||
'/dom/svg',
|
||||
'/dom/workers',
|
||||
'/dom/xul',
|
||||
'/gfx/gl',
|
||||
'/image',
|
||||
|
|
|
@ -12,11 +12,12 @@
|
|||
#include "nsIDocShell.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "mozilla/dom/OffscreenCanvas.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
#define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
|
||||
{ 0x3cc9e801, 0x1806, 0x4ff6, \
|
||||
{ 0x86, 0x14, 0xf9, 0xd0, 0xf4, 0xfb, 0x3b, 0x08 } }
|
||||
{ 0xb84f2fed, 0x9d4b, 0x430b, \
|
||||
{ 0xbd, 0xfb, 0x85, 0x57, 0x8a, 0xc2, 0xb4, 0x4b } }
|
||||
|
||||
class gfxASurface;
|
||||
class nsDisplayListBuilder;
|
||||
|
@ -79,6 +80,11 @@ public:
|
|||
return mCanvasElement;
|
||||
}
|
||||
|
||||
void SetOffscreenCanvas(mozilla::dom::OffscreenCanvas* aOffscreenCanvas)
|
||||
{
|
||||
mOffscreenCanvas = aOffscreenCanvas;
|
||||
}
|
||||
|
||||
// Dimensions of the canvas, in pixels.
|
||||
virtual int32_t GetWidth() const = 0;
|
||||
virtual int32_t GetHeight() const = 0;
|
||||
|
@ -151,6 +157,10 @@ public:
|
|||
// Given a point, return hit region ID if it exists or an empty string if it doesn't
|
||||
virtual nsString GetHitRegion(const mozilla::gfx::Point& point) { return nsString(); }
|
||||
|
||||
virtual void OnVisibilityChange() {}
|
||||
|
||||
virtual void OnMemoryPressure() {}
|
||||
|
||||
//
|
||||
// shmem support
|
||||
//
|
||||
|
@ -163,6 +173,7 @@ public:
|
|||
|
||||
protected:
|
||||
nsRefPtr<mozilla::dom::HTMLCanvasElement> mCanvasElement;
|
||||
nsRefPtr<mozilla::dom::OffscreenCanvas> mOffscreenCanvas;
|
||||
nsRefPtr<nsRefreshDriver> mRefreshDriver;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,10 @@ support-files =
|
|||
imagebitmap_on_worker.js
|
||||
imagebitmap_structuredclone.js
|
||||
imagebitmap_structuredclone_iframe.html
|
||||
offscreencanvas.js
|
||||
offscreencanvas_mask.svg
|
||||
offscreencanvas_neuter.js
|
||||
offscreencanvas_serviceworker_inner.html
|
||||
|
||||
[test_2d.clearRect.image.offscreen.html]
|
||||
[test_2d.clip.winding.html]
|
||||
|
@ -261,3 +265,22 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965
|
|||
[test_createPattern_broken.html]
|
||||
[test_setlinedash.html]
|
||||
[test_filter.html]
|
||||
[test_offscreencanvas_basic_webgl.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_dynamic_fallback.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_sharedworker.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_serviceworker.html]
|
||||
tags = offscreencanvas
|
||||
skip-if = buildapp == 'b2g'
|
||||
[test_offscreencanvas_neuter.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_many.html]
|
||||
tags = offscreencanvas
|
||||
skip-if = (toolkit == 'android' || toolkit == 'gonk' || toolkit == 'windows' || toolkit == 'gtk2' || toolkit == 'gtk3')
|
||||
[test_offscreencanvas_sizechange.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_subworker.html]
|
||||
tags = offscreencanvas
|
||||
skip-if = (toolkit == 'android' || toolkit == 'gonk' || toolkit == 'windows' || toolkit == 'gtk2' || toolkit == 'gtk3')
|
||||
|
|
|
@ -0,0 +1,299 @@
|
|||
/* WebWorker for test_offscreencanvas_*.html */
|
||||
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 finish() {
|
||||
if (port) {
|
||||
port.postMessage({type: "finish"});
|
||||
} else {
|
||||
postMessage({type: "finish"});
|
||||
}
|
||||
}
|
||||
|
||||
function drawCount(count) {
|
||||
if (port) {
|
||||
port.postMessage({type: "draw", count: count});
|
||||
} else {
|
||||
postMessage({type: "draw", count: count});
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// WebGL Drawing Functions
|
||||
//--------------------------------------------------------------------
|
||||
function createDrawFunc(canvas) {
|
||||
var gl;
|
||||
|
||||
try {
|
||||
gl = canvas.getContext("experimental-webgl");
|
||||
} catch (e) {}
|
||||
|
||||
if (!gl) {
|
||||
ok(false, "WebGL is unavailable");
|
||||
return null;
|
||||
}
|
||||
|
||||
var vertSrc = "attribute vec2 position; \
|
||||
void main(void) { \
|
||||
gl_Position = vec4(position, 0.0, 1.0); \
|
||||
}";
|
||||
|
||||
var fragSrc = "precision mediump float; \
|
||||
void main(void) { \
|
||||
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); \
|
||||
}";
|
||||
|
||||
// Returns a valid shader, or null on errors.
|
||||
var createShader = function(src, t) {
|
||||
var shader = gl.createShader(t);
|
||||
|
||||
gl.shaderSource(shader, src);
|
||||
gl.compileShader(shader);
|
||||
|
||||
return shader;
|
||||
};
|
||||
|
||||
var createProgram = function(vsSrc, fsSrc) {
|
||||
var vs = createShader(vsSrc, gl.VERTEX_SHADER);
|
||||
var fs = createShader(fsSrc, gl.FRAGMENT_SHADER);
|
||||
|
||||
var prog = gl.createProgram();
|
||||
gl.attachShader(prog, vs);
|
||||
gl.attachShader(prog, fs);
|
||||
gl.linkProgram(prog);
|
||||
|
||||
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
|
||||
var str = "Shader program linking failed:";
|
||||
str += "\nShader program info log:\n" + gl.getProgramInfoLog(prog);
|
||||
str += "\n\nVert shader log:\n" + gl.getShaderInfoLog(vs);
|
||||
str += "\n\nFrag shader log:\n" + gl.getShaderInfoLog(fs);
|
||||
console.log(str);
|
||||
ok(false, "Shader program linking failed");
|
||||
return null;
|
||||
}
|
||||
|
||||
return prog;
|
||||
};
|
||||
|
||||
gl.disable(gl.DEPTH_TEST);
|
||||
|
||||
var program = createProgram(vertSrc, fragSrc);
|
||||
ok(program, "Creating shader program");
|
||||
|
||||
program.positionAttr = gl.getAttribLocation(program, "position");
|
||||
ok(program.positionAttr >= 0, "position attribute should be valid");
|
||||
|
||||
var vertCoordArr = new Float32Array([
|
||||
-1, -1,
|
||||
1, -1,
|
||||
-1, 1,
|
||||
1, 1,
|
||||
]);
|
||||
var vertCoordBuff = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertCoordBuff);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertCoordArr, gl.STATIC_DRAW);
|
||||
|
||||
var checkGLError = function(prefix, refValue) {
|
||||
if (!refValue) {
|
||||
refValue = 0;
|
||||
}
|
||||
|
||||
var error = gl.getError();
|
||||
ok(error == refValue,
|
||||
prefix + 'gl.getError should be 0x' + refValue.toString(16) +
|
||||
', was 0x' + error.toString(16) + '.');
|
||||
};
|
||||
|
||||
var testPixel = function(x, y, refData, infoString) {
|
||||
var pixel = new Uint8Array(4);
|
||||
gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
|
||||
|
||||
var pixelMatches = pixel[0] == refData[0] &&
|
||||
pixel[1] == refData[1] &&
|
||||
pixel[2] == refData[2] &&
|
||||
pixel[3] == refData[3];
|
||||
ok(pixelMatches, infoString);
|
||||
};
|
||||
|
||||
var preDraw = function(prefix) {
|
||||
gl.clearColor(1.0, 0.0, 0.0, 1.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
testPixel(0, 0, [255, 0, 0, 255], prefix + 'Should be red before drawing.');
|
||||
};
|
||||
|
||||
var postDraw = function(prefix) {
|
||||
testPixel(0, 0, [0, 255, 0, 255], prefix + 'Should be green after drawing.');
|
||||
};
|
||||
|
||||
gl.useProgram(program);
|
||||
gl.enableVertexAttribArray(program.position);
|
||||
gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
// Start drawing
|
||||
checkGLError('after setup');
|
||||
|
||||
return function(prefix) {
|
||||
if (prefix) {
|
||||
prefix = "[" + prefix + "] ";
|
||||
} else {
|
||||
prefix = "";
|
||||
}
|
||||
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
|
||||
preDraw(prefix);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
postDraw(prefix);
|
||||
gl.commit();
|
||||
checkGLError(prefix);
|
||||
};
|
||||
}
|
||||
|
||||
/* entry point */
|
||||
function entryFunction(testStr, subtests, offscreenCanvas) {
|
||||
var test = testStr;
|
||||
var canvas = offscreenCanvas;
|
||||
|
||||
if (test != "subworker") {
|
||||
ok(canvas, "Canvas successfully transfered to worker");
|
||||
ok(canvas.getContext, "Canvas has getContext");
|
||||
|
||||
ok(canvas.width == 64, "OffscreenCanvas width should be 64");
|
||||
ok(canvas.height == 64, "OffscreenCanvas height should be 64");
|
||||
}
|
||||
|
||||
var draw;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Basic WebGL test
|
||||
//------------------------------------------------------------------------
|
||||
if (test == "webgl") {
|
||||
draw = createDrawFunc(canvas);
|
||||
if (!draw) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
var iid = setInterval(function() {
|
||||
if (count++ > 20) {
|
||||
clearInterval(iid);
|
||||
ok(true, "Worker is done");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
draw("loop " +count);
|
||||
}, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
// Test dynamic fallback
|
||||
//------------------------------------------------------------------------
|
||||
else if (test == "webgl_fallback") {
|
||||
draw = createDrawFunc(canvas);
|
||||
if (!draw) {
|
||||
return;
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
var iid = setInterval(function() {
|
||||
++count;
|
||||
draw("loop " + count);
|
||||
drawCount(count);
|
||||
}, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
// Canvas Size Change from Worker
|
||||
//------------------------------------------------------------------------
|
||||
else if (test == "webgl_changesize") {
|
||||
draw = createDrawFunc(canvas);
|
||||
if (!draw) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
draw("64x64");
|
||||
|
||||
setTimeout(function() {
|
||||
canvas.width = 128;
|
||||
canvas.height = 128;
|
||||
draw("Increased to 128x128");
|
||||
|
||||
setTimeout(function() {
|
||||
canvas.width = 32;
|
||||
canvas.width = 32;
|
||||
draw("Decreased to 32x32");
|
||||
|
||||
setTimeout(function() {
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
draw("Increased to 64x64");
|
||||
|
||||
ok(true, "Worker is done");
|
||||
finish();
|
||||
}, 0);
|
||||
}, 0);
|
||||
}, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
// Using OffscreenCanvas from sub workers
|
||||
//------------------------------------------------------------------------
|
||||
else if (test == "subworker") {
|
||||
/* subworker tests take a list of tests to run on children */
|
||||
var stillRunning = 0;
|
||||
subtests.forEach(function (subtest) {
|
||||
++stillRunning;
|
||||
var subworker = new Worker('offscreencanvas.js');
|
||||
subworker.onmessage = function(evt) {
|
||||
/* report finish to parent when all children are finished */
|
||||
if (evt.data.type == "finish") {
|
||||
subworker.terminate();
|
||||
if (--stillRunning == 0) {
|
||||
ok(true, "Worker is done");
|
||||
finish();
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* relay all other messages to parent */
|
||||
postMessage(evt.data);
|
||||
};
|
||||
|
||||
var findTransferables = function(t) {
|
||||
if (t.test == "subworker") {
|
||||
var result = [];
|
||||
t.subtests.forEach(function(test) {
|
||||
result = result.concat(findTransferables(test));
|
||||
});
|
||||
|
||||
return result;
|
||||
} else {
|
||||
return [t.canvas];
|
||||
}
|
||||
};
|
||||
|
||||
subworker.postMessage(subtest, findTransferables(subtest));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onmessage = function(evt) {
|
||||
port = evt.ports[0];
|
||||
entryFunction(evt.data.test, evt.data.subtests, evt.data.canvas);
|
||||
};
|
||||
|
||||
onconnect = function(evt) {
|
||||
port = evt.ports[0];
|
||||
|
||||
port.addEventListener('message', function(evt) {
|
||||
entryFunction(evt.data.test, evt.data.subtests, evt.data.canvas);
|
||||
});
|
||||
|
||||
port.start();
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||
<mask id="fade_mask_both" maskUnits="objectBoundingBox" maskContentUnits="objectBoundingBox">
|
||||
<linearGradient id="fade_gradient_both" gradientUnits="objectBoundingBox" x2="0" y2="1">
|
||||
<stop stop-color="white" stop-opacity="0" offset="0"></stop>
|
||||
<stop stop-color="white" stop-opacity="1" offset="0.2"></stop>
|
||||
<stop stop-color="white" stop-opacity="1" offset="0.8"></stop>
|
||||
<stop stop-color="white" stop-opacity="0" offset="1"></stop>
|
||||
</linearGradient>
|
||||
<rect x="0" y="0" width="1" height="1" fill="url(#fade_gradient_both)"></rect>
|
||||
</mask>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 638 B |
|
@ -0,0 +1 @@
|
|||
/* empty worker for test_offscreencanvas_disable.html */
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebGL in OffscreenCanvas</title>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="c" width="64" height="64"></canvas>
|
||||
<script>
|
||||
function ok(expect, msg) {
|
||||
parent.postMessage({type: "test", result: !!expect, name: msg}, "*");
|
||||
}
|
||||
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
|
||||
ok(htmlCanvas, "Should have HTML canvas element");
|
||||
|
||||
var messageChannel = new MessageChannel();
|
||||
messageChannel.port1.onmessage = function(evt) {
|
||||
parent.postMessage(evt.data, "*");
|
||||
}
|
||||
|
||||
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
|
||||
|
||||
navigator.serviceWorker.ready.then(function() {
|
||||
navigator.serviceWorker.controller.postMessage({test: 'webgl', canvas: offscreenCanvas}, [offscreenCanvas, messageChannel.port2]);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,62 @@
|
|||
<!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="c-ref" width="64" height="64"></canvas>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function testToDataURL() {
|
||||
// testing toDataURL
|
||||
// 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");
|
||||
ok(c.toDataURL() == htmlCanvas.toDataURL(), "toDataURL should return a 64x64 green square");
|
||||
}
|
||||
|
||||
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 == "finish") {
|
||||
testToDataURL();
|
||||
worker.terminate();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
|
||||
|
||||
worker.postMessage({test: 'webgl', canvas: offscreenCanvas}, [offscreenCanvas]);
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,80 @@
|
|||
<!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>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function createCanvas(initWithMask) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
document.body.appendChild(canvas);
|
||||
if (initWithMask) {
|
||||
canvas.style.mask = "url('offscreencanvas_mask.svg#fade_mask_both')";
|
||||
}
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function getRefSnapshot(initWithMask) {
|
||||
var refCanvas = createCanvas(!initWithMask);
|
||||
var ctx = refCanvas.getContext("2d");
|
||||
ctx.rect(0, 0, 64, 64);
|
||||
ctx.fillStyle = "#00FF00";
|
||||
ctx.fill();
|
||||
var result = snapshotWindow(window);
|
||||
document.body.removeChild(refCanvas);
|
||||
return result;
|
||||
}
|
||||
|
||||
function runTest(initWithMask) {
|
||||
var htmlCanvas = createCanvas(initWithMask);
|
||||
var worker = new Worker("offscreencanvas.js");
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "draw") {
|
||||
if (msg.count === 10) {
|
||||
// Change the fallback state dynamically when drawing count reaches 10.
|
||||
if (initWithMask) {
|
||||
htmlCanvas.style.mask = "";
|
||||
} else {
|
||||
htmlCanvas.style.mask = "url('offscreencanvas_mask.svg#fade_mask_both')";
|
||||
}
|
||||
} else if (msg.count === 20) {
|
||||
var snapshotFallback = snapshotWindow(window);
|
||||
worker.terminate();
|
||||
document.body.removeChild(htmlCanvas);
|
||||
|
||||
var results = compareSnapshots(snapshotFallback, getRefSnapshot(initWithMask), true);
|
||||
ok(results[0], "after dynamic fallback, screenshots should be the same");
|
||||
|
||||
if (initWithMask) {
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
runTest(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
|
||||
worker.postMessage({test: 'webgl_fallback', canvas: offscreenCanvas}, [offscreenCanvas]);
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest.bind(false));
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,67 @@
|
|||
<!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>
|
||||
<!--
|
||||
This test needs several workers run offscreen canvas simultaneously.
|
||||
So we choose 8 workers, 4 of them run basic webgl drawing test and
|
||||
others run size changing test.
|
||||
-->
|
||||
<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 stillRunning = 0;
|
||||
|
||||
var startWorker = function(canvas, test) {
|
||||
stillRunning++;
|
||||
var worker = new Worker("offscreencanvas.js");
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "test") {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "finish") {
|
||||
worker.terminate();
|
||||
if (--stillRunning == 0)
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
var offscreenCanvas = canvas.transferControlToOffscreen();
|
||||
worker.postMessage({test: test, canvas: offscreenCanvas}, [offscreenCanvas]);
|
||||
}
|
||||
|
||||
/* create 4 workers that do the regular drawing test and 4 workers
|
||||
that do the size change test */
|
||||
for (var i = 0; i < 4; i++) {
|
||||
startWorker(createCanvas(), 'webgl');
|
||||
}
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
startWorker(createCanvas(), 'webgl_changesize');
|
||||
}
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true]
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,78 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>OffscreenCanvas: Test neutering</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>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
var worker = new Worker("offscreencanvas_neuter.js");
|
||||
|
||||
ok(htmlCanvas, "Should have HTML canvas element");
|
||||
ok(worker, "Web worker successfully created");
|
||||
|
||||
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
|
||||
|
||||
/* check html canvas is neuterd */
|
||||
is(htmlCanvas.width, 64, "HTML canvas has correct width");
|
||||
SimpleTest.doesThrow(
|
||||
function() { htmlCanvas.width = 128; },
|
||||
"Can't change html canvas' width after transferControlToOffscreen");
|
||||
|
||||
SimpleTest.doesThrow(
|
||||
function() { htmlCanvas.height = 128; },
|
||||
"Can't change html canvas' height after transferControlToOffscreen");
|
||||
|
||||
ok(!htmlCanvas.getContext("2d"), "Can't getContext after transferControlToOffscreen");
|
||||
ok(!htmlCanvas.getContext("webgl"), "Can't getContext after transferControlToOffscreen");
|
||||
ok(!htmlCanvas.getContext("webgl2"), "Can't getContext after transferControlToOffscreen");
|
||||
|
||||
worker.postMessage(offscreenCanvas, [offscreenCanvas]);
|
||||
|
||||
/* check parent offscreencanvas is neutered after being transfered */
|
||||
SimpleTest.doesThrow(
|
||||
function() { offscreenCanvas.width = 128; },
|
||||
"Can't change transfered worker canvas width");
|
||||
|
||||
SimpleTest.doesThrow(
|
||||
function() { offscreenCanvas.height = 128; },
|
||||
"Can't change transfered worker canvas height");
|
||||
|
||||
SimpleTest.doesThrow(
|
||||
function() { offscreenCanvas.getContext("2d") },
|
||||
"Can't getContext on transfered worker canvas");
|
||||
|
||||
SimpleTest.doesThrow(
|
||||
function() { offscreenCanvas.getContext("webgl") },
|
||||
"Can't getContext on transfered worker canvas");
|
||||
|
||||
SimpleTest.doesThrow(
|
||||
function() { offscreenCanvas.getContext("webgl2") },
|
||||
"Can't getContext on transfered worker canvas");
|
||||
|
||||
// Transfer a neutered offscreencanvas should be ok.
|
||||
worker.postMessage(offscreenCanvas, [offscreenCanvas]);
|
||||
|
||||
worker.terminate();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,46 @@
|
|||
<!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>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
window.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "test") {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "finish") {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
navigator.serviceWorker.register('offscreencanvas.js', { scope: "."})
|
||||
// Wait until the service worker is active.
|
||||
.then(navigator.serviceWorker.ready)
|
||||
// ...and then show the interface for the commands once it's ready.
|
||||
.then(function() {
|
||||
iframe = document.createElement("iframe");
|
||||
iframe.setAttribute('src', "offscreencanvas_serviceworker_inner.html");
|
||||
document.body.appendChild(iframe);
|
||||
})
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true],
|
||||
["dom.serviceWorkers.interception.enabled", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true]
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,47 @@
|
|||
<!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>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
var worker = new SharedWorker("offscreencanvas.js");
|
||||
|
||||
ok(htmlCanvas, "Should have HTML canvas element");
|
||||
ok(worker, "Web worker successfully created");
|
||||
|
||||
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
|
||||
|
||||
worker.port.start();
|
||||
|
||||
// We don't support transferring OffscreenCanvas via shared worker.
|
||||
SimpleTest.doesThrow(
|
||||
function() {
|
||||
worker.port.postMessage({test: 'webgl', canvas: offscreenCanvas}, [offscreenCanvas]);
|
||||
},
|
||||
"OffscreenCanvas cannot transfer to shared worker"
|
||||
);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,41 @@
|
|||
<!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>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
var worker = new Worker("offscreencanvas.js");
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "test") {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "finish") {
|
||||
worker.terminate();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
worker.postMessage({test: 'webgl_changesize', canvas: offscreenCanvas}, [offscreenCanvas]);
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,90 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>OffscreenCanvas: Test subworkers</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<!--
|
||||
We want to test offscreen canvas works well when it running on worker
|
||||
and nested worker simultaneously. So we create 10 canvas and dispatch
|
||||
it to different workers and sub-workers.
|
||||
-->
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function createCanvas() {
|
||||
var htmlCanvas = document.createElement('canvas');
|
||||
htmlCanvas.width = 64;
|
||||
htmlCanvas.height = 64;
|
||||
document.body.appendChild(htmlCanvas);
|
||||
return htmlCanvas.transferControlToOffscreen();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
|
||||
var worker = new Worker("offscreencanvas.js");
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "test") {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "finish") {
|
||||
worker.terminate();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
var findTransferables = function(t) {
|
||||
if (t.test == "subworker") {
|
||||
var result = [];
|
||||
t.subtests.forEach(function(test) {
|
||||
result = result.concat(findTransferables(test));
|
||||
});
|
||||
|
||||
return result;
|
||||
} else {
|
||||
return [t.canvas];
|
||||
}
|
||||
};
|
||||
|
||||
var testData =
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'webgl', canvas: createCanvas()},
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'webgl', canvas: createCanvas()},
|
||||
{test: 'webgl_changesize', canvas: createCanvas()},
|
||||
{test: 'webgl', canvas: createCanvas()}
|
||||
]},
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'webgl', canvas: createCanvas()},
|
||||
{test: 'webgl_changesize', canvas: createCanvas()},
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'webgl_changesize', canvas: createCanvas()},
|
||||
{test: 'webgl', canvas: createCanvas()}
|
||||
]},
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'webgl_changesize', canvas: createCanvas()},
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'webgl_changesize', canvas: createCanvas()}
|
||||
]}
|
||||
]}
|
||||
]},
|
||||
]}
|
||||
]};
|
||||
|
||||
worker.postMessage(testData, findTransferables(testData));
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1145,8 +1145,7 @@ private:
|
|||
nsresult rv;
|
||||
if (mSign) {
|
||||
ScopedSECItem signature(::SECITEM_AllocItem(nullptr, nullptr, 0));
|
||||
ScopedSGNContext ctx(SGN_NewContext(mOidTag, mPrivKey));
|
||||
if (!signature.get() || !ctx.get()) {
|
||||
if (!signature.get()) {
|
||||
return NS_ERROR_DOM_OPERATION_ERR;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,11 +35,21 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
inline void
|
||||
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
|
||||
TransferItem& aField,
|
||||
const char* aName,
|
||||
uint32_t aFlags = 0)
|
||||
{
|
||||
ImplCycleCollectionTraverse(aCallback, aField.mData, aName, aFlags);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
|
@ -47,6 +57,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
|
@ -435,7 +446,7 @@ void
|
|||
DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<nsVariant> variant = new nsVariant();
|
||||
nsRefPtr<nsVariantCC> variant = new nsVariantCC();
|
||||
variant->SetAsAString(aData);
|
||||
|
||||
aRv = MozSetDataAt(aFormat, variant, 0);
|
||||
|
@ -1345,7 +1356,7 @@ DataTransfer::FillInExternalData(TransferItem& aItem, uint32_t aIndex)
|
|||
if (!data)
|
||||
return;
|
||||
|
||||
nsRefPtr<nsVariant> variant = new nsVariant();
|
||||
nsRefPtr<nsVariantCC> variant = new nsVariantCC();
|
||||
|
||||
nsCOMPtr<nsISupportsString> supportsstr = do_QueryInterface(data);
|
||||
if (supportsstr) {
|
||||
|
|
|
@ -218,8 +218,6 @@ IMEContentObserver::Init(nsIWidget* aWidget,
|
|||
nsIContent* aContent,
|
||||
nsIEditor* aEditor)
|
||||
{
|
||||
MOZ_ASSERT(aEditor, "aEditor must not be null");
|
||||
|
||||
State state = GetState();
|
||||
if (NS_WARN_IF(state == eState_Observing)) {
|
||||
return; // Nothing to do.
|
||||
|
@ -231,10 +229,7 @@ IMEContentObserver::Init(nsIWidget* aWidget,
|
|||
// should be registered again for simpler implementation.
|
||||
UnregisterObservers();
|
||||
// Clear members which may not be initialized again.
|
||||
mRootContent = nullptr;
|
||||
mEditor = nullptr;
|
||||
mSelection = nullptr;
|
||||
mDocShell = nullptr;
|
||||
Clear();
|
||||
}
|
||||
|
||||
mESM = aPresContext->EventStateManager();
|
||||
|
@ -242,51 +237,17 @@ IMEContentObserver::Init(nsIWidget* aWidget,
|
|||
|
||||
mWidget = aWidget;
|
||||
|
||||
mEditableNode =
|
||||
IMEStateManager::GetRootEditableNode(aPresContext, aContent);
|
||||
if (!mEditableNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEditor = aEditor;
|
||||
|
||||
nsIPresShell* presShell = aPresContext->PresShell();
|
||||
|
||||
// get selection and root content
|
||||
nsCOMPtr<nsISelectionController> selCon;
|
||||
if (mEditableNode->IsNodeOfType(nsINode::eCONTENT)) {
|
||||
nsIFrame* frame =
|
||||
static_cast<nsIContent*>(mEditableNode.get())->GetPrimaryFrame();
|
||||
NS_ENSURE_TRUE_VOID(frame);
|
||||
|
||||
frame->GetSelectionController(aPresContext,
|
||||
getter_AddRefs(selCon));
|
||||
if (aWidget->GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) {
|
||||
if (!InitWithPlugin(aPresContext, aContent)) {
|
||||
Clear();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// mEditableNode is a document
|
||||
selCon = do_QueryInterface(presShell);
|
||||
if (!InitWithEditor(aPresContext, aContent, aEditor)) {
|
||||
Clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
NS_ENSURE_TRUE_VOID(selCon);
|
||||
|
||||
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
|
||||
getter_AddRefs(mSelection));
|
||||
NS_ENSURE_TRUE_VOID(mSelection);
|
||||
|
||||
nsCOMPtr<nsIDOMRange> selDomRange;
|
||||
if (NS_SUCCEEDED(mSelection->GetRangeAt(0, getter_AddRefs(selDomRange)))) {
|
||||
nsRange* selRange = static_cast<nsRange*>(selDomRange.get());
|
||||
NS_ENSURE_TRUE_VOID(selRange && selRange->GetStartParent());
|
||||
|
||||
mRootContent = selRange->GetStartParent()->
|
||||
GetSelectionRootContent(presShell);
|
||||
} else {
|
||||
mRootContent = mEditableNode->GetSelectionRootContent(presShell);
|
||||
}
|
||||
if (!mRootContent && mEditableNode->IsNodeOfType(nsINode::eDOCUMENT)) {
|
||||
// The document node is editable, but there are no contents, this document
|
||||
// is not editable.
|
||||
return;
|
||||
}
|
||||
NS_ENSURE_TRUE_VOID(mRootContent);
|
||||
|
||||
if (firstInitialization) {
|
||||
MaybeNotifyIMEOfFocusSet();
|
||||
|
@ -307,8 +268,6 @@ IMEContentObserver::Init(nsIWidget* aWidget,
|
|||
}
|
||||
}
|
||||
|
||||
mDocShell = aPresContext->GetDocShell();
|
||||
|
||||
ObserveEditableNode();
|
||||
|
||||
// Some change events may wait to notify IME because this was being
|
||||
|
@ -316,16 +275,138 @@ IMEContentObserver::Init(nsIWidget* aWidget,
|
|||
FlushMergeableNotifications();
|
||||
}
|
||||
|
||||
bool
|
||||
IMEContentObserver::InitWithEditor(nsPresContext* aPresContext,
|
||||
nsIContent* aContent,
|
||||
nsIEditor* aEditor)
|
||||
{
|
||||
MOZ_ASSERT(aEditor);
|
||||
|
||||
mEditableNode =
|
||||
IMEStateManager::GetRootEditableNode(aPresContext, aContent);
|
||||
if (NS_WARN_IF(!mEditableNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mEditor = aEditor;
|
||||
if (NS_WARN_IF(!mEditor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIPresShell* presShell = aPresContext->PresShell();
|
||||
|
||||
// get selection and root content
|
||||
nsCOMPtr<nsISelectionController> selCon;
|
||||
if (mEditableNode->IsNodeOfType(nsINode::eCONTENT)) {
|
||||
nsIFrame* frame =
|
||||
static_cast<nsIContent*>(mEditableNode.get())->GetPrimaryFrame();
|
||||
if (NS_WARN_IF(!frame)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
frame->GetSelectionController(aPresContext,
|
||||
getter_AddRefs(selCon));
|
||||
} else {
|
||||
// mEditableNode is a document
|
||||
selCon = do_QueryInterface(presShell);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!selCon)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
|
||||
getter_AddRefs(mSelection));
|
||||
if (NS_WARN_IF(!mSelection)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMRange> selDomRange;
|
||||
if (NS_SUCCEEDED(mSelection->GetRangeAt(0, getter_AddRefs(selDomRange)))) {
|
||||
nsRange* selRange = static_cast<nsRange*>(selDomRange.get());
|
||||
if (NS_WARN_IF(!selRange) || NS_WARN_IF(!selRange->GetStartParent())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mRootContent = selRange->GetStartParent()->
|
||||
GetSelectionRootContent(presShell);
|
||||
} else {
|
||||
mRootContent = mEditableNode->GetSelectionRootContent(presShell);
|
||||
}
|
||||
if (!mRootContent && mEditableNode->IsNodeOfType(nsINode::eDOCUMENT)) {
|
||||
// The document node is editable, but there are no contents, this document
|
||||
// is not editable.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!mRootContent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mDocShell = aPresContext->GetDocShell();
|
||||
if (NS_WARN_IF(!mDocShell)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IMEContentObserver::InitWithPlugin(nsPresContext* aPresContext,
|
||||
nsIContent* aContent)
|
||||
{
|
||||
if (NS_WARN_IF(!aContent) ||
|
||||
NS_WARN_IF(aContent->GetDesiredIMEState().mEnabled != IMEState::PLUGIN)) {
|
||||
return false;
|
||||
}
|
||||
nsIFrame* frame = aContent->GetPrimaryFrame();
|
||||
if (NS_WARN_IF(!frame)) {
|
||||
return false;
|
||||
}
|
||||
nsCOMPtr<nsISelectionController> selCon;
|
||||
frame->GetSelectionController(aPresContext, getter_AddRefs(selCon));
|
||||
if (NS_WARN_IF(!selCon)) {
|
||||
return false;
|
||||
}
|
||||
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
|
||||
getter_AddRefs(mSelection));
|
||||
if (NS_WARN_IF(!mSelection)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mEditor = nullptr;
|
||||
mEditableNode = aContent;
|
||||
mRootContent = aContent;
|
||||
|
||||
mDocShell = aPresContext->GetDocShell();
|
||||
if (NS_WARN_IF(!mDocShell)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::Clear()
|
||||
{
|
||||
mEditor = nullptr;
|
||||
mSelection = nullptr;
|
||||
mEditableNode = nullptr;
|
||||
mRootContent = nullptr;
|
||||
mDocShell = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::ObserveEditableNode()
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mEditor);
|
||||
MOZ_RELEASE_ASSERT(mSelection);
|
||||
MOZ_RELEASE_ASSERT(mRootContent);
|
||||
MOZ_RELEASE_ASSERT(GetState() != eState_Observing);
|
||||
|
||||
mIsObserving = true;
|
||||
mEditor->AddEditorObserver(this);
|
||||
if (mEditor) {
|
||||
mEditor->AddEditorObserver(this);
|
||||
}
|
||||
|
||||
mUpdatePreference = mWidget->GetIMEUpdatePreference();
|
||||
if (mUpdatePreference.WantSelectionChange()) {
|
||||
|
@ -430,13 +511,9 @@ IMEContentObserver::Destroy()
|
|||
|
||||
NotifyIMEOfBlur();
|
||||
UnregisterObservers();
|
||||
Clear();
|
||||
|
||||
mEditor = nullptr;
|
||||
mWidget = nullptr;
|
||||
mSelection = nullptr;
|
||||
mRootContent = nullptr;
|
||||
mEditableNode = nullptr;
|
||||
mDocShell = nullptr;
|
||||
mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING;
|
||||
|
||||
if (mESM) {
|
||||
|
@ -492,8 +569,10 @@ bool
|
|||
IMEContentObserver::IsObservingContent(nsPresContext* aPresContext,
|
||||
nsIContent* aContent) const
|
||||
{
|
||||
return mEditableNode == IMEStateManager::GetRootEditableNode(aPresContext,
|
||||
aContent);
|
||||
return IsInitializedWithPlugin() ?
|
||||
mRootContent == aContent && mRootContent != nullptr :
|
||||
mEditableNode == IMEStateManager::GetRootEditableNode(aPresContext,
|
||||
aContent);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -113,6 +113,11 @@ private:
|
|||
eState_Observing
|
||||
};
|
||||
State GetState() const;
|
||||
bool InitWithEditor(nsPresContext* aPresContext, nsIContent* aContent,
|
||||
nsIEditor* aEditor);
|
||||
bool InitWithPlugin(nsPresContext* aPresContext, nsIContent* aContent);
|
||||
bool IsInitializedWithPlugin() const { return !mEditor; }
|
||||
void Clear();
|
||||
bool IsObservingContent(nsPresContext* aPresContext,
|
||||
nsIContent* aContent) const;
|
||||
bool IsReflowLocked() const;
|
||||
|
|
|
@ -510,8 +510,12 @@ IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
|
|||
sPresContext = aPresContext;
|
||||
sContent = aContent;
|
||||
|
||||
// Don't call CreateIMEContentObserver() here, it should be called from
|
||||
// focus event handler of editor.
|
||||
// Don't call CreateIMEContentObserver() here except when a plugin gets
|
||||
// focus because it will be called from the focus event handler of focused
|
||||
// editor.
|
||||
if (newState.mEnabled == IMEState::PLUGIN) {
|
||||
CreateIMEContentObserver(nullptr);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1515,7 +1519,7 @@ IMEStateManager::GetRootEditableNode(nsPresContext* aPresContext,
|
|||
bool
|
||||
IMEStateManager::IsIMEObserverNeeded(const IMEState& aState)
|
||||
{
|
||||
return aState.IsEditable();
|
||||
return aState.MaybeEditable();
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
@ -19,8 +19,10 @@
|
|||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/HTMLCanvasElementBinding.h"
|
||||
#include "mozilla/dom/MouseEvent.h"
|
||||
#include "mozilla/dom/OffscreenCanvas.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/layers/AsyncCanvasRenderer.h"
|
||||
#include "mozilla/MouseEvents.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
@ -239,18 +241,135 @@ HTMLCanvasPrintState::NotifyDone()
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
HTMLCanvasElementObserver::HTMLCanvasElementObserver(HTMLCanvasElement* aElement)
|
||||
: mElement(aElement)
|
||||
{
|
||||
RegisterVisibilityChangeEvent();
|
||||
RegisterMemoryPressureEvent();
|
||||
}
|
||||
|
||||
HTMLCanvasElementObserver::~HTMLCanvasElementObserver()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElementObserver::Destroy()
|
||||
{
|
||||
UnregisterMemoryPressureEvent();
|
||||
UnregisterVisibilityChangeEvent();
|
||||
mElement = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElementObserver::RegisterVisibilityChangeEvent()
|
||||
{
|
||||
if (!mElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIDocument* document = mElement->OwnerDoc();
|
||||
document->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
||||
this, true, false);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElementObserver::UnregisterVisibilityChangeEvent()
|
||||
{
|
||||
if (!mElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIDocument* document = mElement->OwnerDoc();
|
||||
document->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
||||
this, true);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElementObserver::RegisterMemoryPressureEvent()
|
||||
{
|
||||
if (!mElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
|
||||
MOZ_ASSERT(observerService);
|
||||
|
||||
if (observerService)
|
||||
observerService->AddObserver(this, "memory-pressure", false);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElementObserver::UnregisterMemoryPressureEvent()
|
||||
{
|
||||
if (!mElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
|
||||
// Do not assert on observerService here. This might be triggered by
|
||||
// the cycle collector at a late enough time, that XPCOM services are
|
||||
// no longer available. See bug 1029504.
|
||||
if (observerService)
|
||||
observerService->RemoveObserver(this, "memory-pressure");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HTMLCanvasElementObserver::Observe(nsISupports*, const char* aTopic, const char16_t*)
|
||||
{
|
||||
if (!mElement || strcmp(aTopic, "memory-pressure")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mElement->OnMemoryPressure();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HTMLCanvasElementObserver::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
nsAutoString type;
|
||||
aEvent->GetType(type);
|
||||
if (!mElement || !type.EqualsLiteral("visibilitychange")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mElement->OnVisibilityChange();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver, nsIObserver)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
HTMLCanvasElement::HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
||||
: nsGenericHTMLElement(aNodeInfo),
|
||||
mResetLayer(true) ,
|
||||
mWriteOnly(false)
|
||||
{
|
||||
}
|
||||
|
||||
HTMLCanvasElement::~HTMLCanvasElement()
|
||||
{
|
||||
if (mContextObserver) {
|
||||
mContextObserver->Destroy();
|
||||
mContextObserver = nullptr;
|
||||
}
|
||||
|
||||
ResetPrintCallback();
|
||||
if (mRequestedFrameRefreshObserver) {
|
||||
mRequestedFrameRefreshObserver->DetachFromRefreshDriver();
|
||||
}
|
||||
|
||||
if (mAsyncCanvasRenderer) {
|
||||
mAsyncCanvasRenderer->mHTMLCanvasElement = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement, nsGenericHTMLElement,
|
||||
|
@ -272,6 +391,22 @@ HTMLCanvasElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|||
return HTMLCanvasElementBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
HTMLCanvasElement::CreateContext(CanvasContextType aContextType)
|
||||
{
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> ret =
|
||||
CanvasRenderingContextHelper::CreateContext(aContextType);
|
||||
|
||||
// Add Observer for webgl canvas.
|
||||
if (aContextType == CanvasContextType::WebGL1 ||
|
||||
aContextType == CanvasContextType::WebGL2) {
|
||||
mContextObserver = new HTMLCanvasElementObserver(this);
|
||||
}
|
||||
|
||||
ret->SetCanvasElement(this);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
nsIntSize
|
||||
HTMLCanvasElement::GetWidthHeight()
|
||||
{
|
||||
|
@ -556,51 +691,10 @@ HTMLCanvasElement::ExtractData(nsAString& aType,
|
|||
aOptions,
|
||||
GetSize(),
|
||||
mCurrentContext,
|
||||
mAsyncCanvasRenderer,
|
||||
aStream);
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::ParseParams(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
nsAString& aParams,
|
||||
bool* usingCustomParseOptions)
|
||||
{
|
||||
// Quality parameter is only valid for the image/jpeg MIME type
|
||||
if (aType.EqualsLiteral("image/jpeg")) {
|
||||
if (aEncoderOptions.isNumber()) {
|
||||
double quality = aEncoderOptions.toNumber();
|
||||
// Quality must be between 0.0 and 1.0, inclusive
|
||||
if (quality >= 0.0 && quality <= 1.0) {
|
||||
aParams.AppendLiteral("quality=");
|
||||
aParams.AppendInt(NS_lround(quality * 100.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't parsed the aParams check for proprietary options.
|
||||
// The proprietary option -moz-parse-options will take a image lib encoder
|
||||
// parse options string as is and pass it to the encoder.
|
||||
*usingCustomParseOptions = false;
|
||||
if (aParams.Length() == 0 && aEncoderOptions.isString()) {
|
||||
NS_NAMED_LITERAL_STRING(mozParseOptions, "-moz-parse-options:");
|
||||
nsAutoJSString paramString;
|
||||
if (!paramString.init(aCx, aEncoderOptions.toString())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (StringBeginsWith(paramString, mozParseOptions)) {
|
||||
nsDependentSubstring parseOptions = Substring(paramString,
|
||||
mozParseOptions.Length(),
|
||||
paramString.Length() -
|
||||
mozParseOptions.Length());
|
||||
aParams.Append(parseOptions);
|
||||
*usingCustomParseOptions = true;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
|
||||
const nsAString& aMimeType,
|
||||
|
@ -659,84 +753,38 @@ HTMLCanvasElement::ToBlob(JSContext* aCx,
|
|||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* imageBuffer = nullptr;
|
||||
int32_t format = 0;
|
||||
if (mCurrentContext) {
|
||||
mCurrentContext->GetImageBuffer(&imageBuffer, &format);
|
||||
}
|
||||
|
||||
// Encoder callback when encoding is complete.
|
||||
class EncodeCallback : public EncodeCompleteCallback
|
||||
{
|
||||
public:
|
||||
EncodeCallback(nsIGlobalObject* aGlobal, FileCallback* aCallback)
|
||||
: mGlobal(aGlobal)
|
||||
, mFileCallback(aCallback) {}
|
||||
|
||||
// This is called on main thread.
|
||||
nsresult ReceiveBlob(already_AddRefed<Blob> aBlob)
|
||||
{
|
||||
nsRefPtr<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);
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
|
||||
|
||||
mFileCallback->Call(*newBlob, rv);
|
||||
|
||||
mGlobal = nullptr;
|
||||
mFileCallback = nullptr;
|
||||
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
nsRefPtr<FileCallback> mFileCallback;
|
||||
};
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
|
||||
MOZ_ASSERT(global);
|
||||
nsRefPtr<EncodeCompleteCallback> callback = new EncodeCallback(global, &aCallback);
|
||||
aRv = ImageEncoder::ExtractDataAsync(type,
|
||||
params,
|
||||
usingCustomParseOptions,
|
||||
imageBuffer,
|
||||
format,
|
||||
GetSize(),
|
||||
callback);
|
||||
|
||||
CanvasRenderingContextHelper::ToBlob(aCx, global, aCallback, aType,
|
||||
aParams, aRv);
|
||||
|
||||
}
|
||||
|
||||
OffscreenCanvas*
|
||||
HTMLCanvasElement::TransferControlToOffscreen(ErrorResult& aRv)
|
||||
{
|
||||
if (mCurrentContext) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mOffscreenCanvas) {
|
||||
nsIntSize sz = GetWidthHeight();
|
||||
nsRefPtr<AsyncCanvasRenderer> renderer = GetAsyncCanvasRenderer();
|
||||
renderer->SetWidth(sz.width);
|
||||
renderer->SetHeight(sz.height);
|
||||
|
||||
mOffscreenCanvas = new OffscreenCanvas(sz.width,
|
||||
sz.height,
|
||||
GetCompositorBackendType(),
|
||||
renderer);
|
||||
mContextObserver = new HTMLCanvasElementObserver(this);
|
||||
} else {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
|
||||
return mOffscreenCanvas;
|
||||
}
|
||||
|
||||
already_AddRefed<File>
|
||||
|
@ -807,76 +855,6 @@ HTMLCanvasElement::MozGetAsBlobImpl(const nsAString& aName,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetCanvasContextType(const nsAString& str, CanvasContextType* const out_type)
|
||||
{
|
||||
if (str.EqualsLiteral("2d")) {
|
||||
*out_type = CanvasContextType::Canvas2D;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (str.EqualsLiteral("experimental-webgl")) {
|
||||
*out_type = CanvasContextType::WebGL1;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WEBGL_CONFORMANT
|
||||
if (str.EqualsLiteral("webgl")) {
|
||||
/* WebGL 1.0, $2.1 "Context Creation":
|
||||
* If the user agent supports both the webgl and experimental-webgl
|
||||
* canvas context types, they shall be treated as aliases.
|
||||
*/
|
||||
*out_type = CanvasContextType::WebGL1;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (WebGL2Context::IsSupported()) {
|
||||
if (str.EqualsLiteral("webgl2")) {
|
||||
*out_type = CanvasContextType::WebGL2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
CreateContextForCanvas(CanvasContextType contextType, HTMLCanvasElement* canvas)
|
||||
{
|
||||
MOZ_ASSERT(contextType != CanvasContextType::NoContext);
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> ret;
|
||||
|
||||
switch (contextType) {
|
||||
case CanvasContextType::NoContext:
|
||||
break;
|
||||
case CanvasContextType::Canvas2D:
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
|
||||
ret = new CanvasRenderingContext2D();
|
||||
break;
|
||||
|
||||
case CanvasContextType::WebGL1:
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
|
||||
|
||||
ret = WebGL1Context::Create();
|
||||
if (!ret)
|
||||
return nullptr;
|
||||
break;
|
||||
|
||||
case CanvasContextType::WebGL2:
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
|
||||
|
||||
ret = WebGL2Context::Create();
|
||||
if (!ret)
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
MOZ_ASSERT(ret);
|
||||
|
||||
ret->SetCanvasElement(canvas);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::GetContext(const nsAString& aContextId,
|
||||
nsISupports** aContext)
|
||||
|
@ -890,45 +868,14 @@ already_AddRefed<nsISupports>
|
|||
HTMLCanvasElement::GetContext(JSContext* aCx,
|
||||
const nsAString& aContextId,
|
||||
JS::Handle<JS::Value> aContextOptions,
|
||||
ErrorResult& rv)
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
CanvasContextType contextType;
|
||||
if (!GetCanvasContextType(aContextId, &contextType))
|
||||
if (mOffscreenCanvas) {
|
||||
return nullptr;
|
||||
|
||||
if (!mCurrentContext) {
|
||||
// This canvas doesn't have a context yet.
|
||||
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> context;
|
||||
context = CreateContextForCanvas(contextType, this);
|
||||
if (!context)
|
||||
return nullptr;
|
||||
|
||||
// Ensure that the context participates in CC. Note that returning a
|
||||
// CC participant from QI doesn't addref.
|
||||
nsXPCOMCycleCollectionParticipant* cp = nullptr;
|
||||
CallQueryInterface(context, &cp);
|
||||
if (!cp) {
|
||||
rv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mCurrentContext = context.forget();
|
||||
mCurrentContextType = contextType;
|
||||
|
||||
rv = UpdateContext(aCx, aContextOptions);
|
||||
if (rv.Failed()) {
|
||||
rv = NS_OK; // See bug 645792
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
// We already have a context of some type.
|
||||
if (contextType != mCurrentContextType)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICanvasRenderingContextInternal> context = mCurrentContext;
|
||||
return context.forget();
|
||||
return CanvasRenderingContextHelper::GetContext(aCx, aContextId,
|
||||
aContextOptions, aRv);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -950,7 +897,7 @@ HTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
|
|||
// This canvas doesn't have a context yet.
|
||||
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> context;
|
||||
context = CreateContextForCanvas(contextType, this);
|
||||
context = CreateContext(contextType);
|
||||
if (!context) {
|
||||
*aContext = nullptr;
|
||||
return NS_OK;
|
||||
|
@ -972,36 +919,6 @@ HTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::UpdateContext(JSContext* aCx, JS::Handle<JS::Value> aNewContextOptions)
|
||||
{
|
||||
if (!mCurrentContext)
|
||||
return NS_OK;
|
||||
|
||||
nsIntSize sz = GetWidthHeight();
|
||||
|
||||
nsCOMPtr<nsICanvasRenderingContextInternal> currentContext = mCurrentContext;
|
||||
|
||||
nsresult rv = currentContext->SetIsOpaque(HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque));
|
||||
if (NS_FAILED(rv)) {
|
||||
mCurrentContext = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = currentContext->SetContextOptions(aCx, aNewContextOptions);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCurrentContext = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = currentContext->SetDimensions(sz.width, sz.height);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCurrentContext = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsIntSize
|
||||
HTMLCanvasElement::GetSize()
|
||||
|
@ -1105,6 +1022,12 @@ HTMLCanvasElement::GetIsOpaque()
|
|||
return mCurrentContext->GetIsOpaque();
|
||||
}
|
||||
|
||||
return GetOpaqueAttr();
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLCanvasElement::GetOpaqueAttr()
|
||||
{
|
||||
return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
|
||||
}
|
||||
|
||||
|
@ -1113,16 +1036,57 @@ HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
|
|||
CanvasLayer *aOldLayer,
|
||||
LayerManager *aManager)
|
||||
{
|
||||
if (!mCurrentContext)
|
||||
return nullptr;
|
||||
// The address of sOffscreenCanvasLayerUserDataDummy is used as the user
|
||||
// data key for retained LayerManagers managed by FrameLayerBuilder.
|
||||
// We don't much care about what value in it, so just assign a dummy
|
||||
// value for it.
|
||||
static uint8_t sOffscreenCanvasLayerUserDataDummy = 0;
|
||||
|
||||
return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
|
||||
if (mCurrentContext) {
|
||||
return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
|
||||
}
|
||||
|
||||
if (mOffscreenCanvas) {
|
||||
if (!mResetLayer &&
|
||||
aOldLayer && aOldLayer->HasUserData(&sOffscreenCanvasLayerUserDataDummy)) {
|
||||
nsRefPtr<CanvasLayer> ret = aOldLayer;
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
nsRefPtr<CanvasLayer> layer = aManager->CreateCanvasLayer();
|
||||
if (!layer) {
|
||||
NS_WARNING("CreateCanvasLayer failed!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LayerUserData* userData = nullptr;
|
||||
layer->SetUserData(&sOffscreenCanvasLayerUserDataDummy, userData);
|
||||
|
||||
CanvasLayer::Data data;
|
||||
data.mRenderer = GetAsyncCanvasRenderer();
|
||||
data.mSize = GetWidthHeight();
|
||||
layer->Initialize(data);
|
||||
|
||||
layer->Updated();
|
||||
return layer.forget();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLCanvasElement::ShouldForceInactiveLayer(LayerManager *aManager)
|
||||
HTMLCanvasElement::ShouldForceInactiveLayer(LayerManager* aManager)
|
||||
{
|
||||
return !mCurrentContext || mCurrentContext->ShouldForceInactiveLayer(aManager);
|
||||
if (mCurrentContext) {
|
||||
return mCurrentContext->ShouldForceInactiveLayer(aManager);
|
||||
}
|
||||
|
||||
if (mOffscreenCanvas) {
|
||||
// TODO: We should handle offscreen canvas case.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1237,5 +1201,155 @@ HTMLCanvasElement::GetSurfaceSnapshot(bool* aPremultAlpha)
|
|||
return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha);
|
||||
}
|
||||
|
||||
AsyncCanvasRenderer*
|
||||
HTMLCanvasElement::GetAsyncCanvasRenderer()
|
||||
{
|
||||
if (!mAsyncCanvasRenderer) {
|
||||
mAsyncCanvasRenderer = new AsyncCanvasRenderer();
|
||||
mAsyncCanvasRenderer->mHTMLCanvasElement = this;
|
||||
}
|
||||
|
||||
return mAsyncCanvasRenderer;
|
||||
}
|
||||
|
||||
layers::LayersBackend
|
||||
HTMLCanvasElement::GetCompositorBackendType() const
|
||||
{
|
||||
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(OwnerDoc());
|
||||
if (docWidget) {
|
||||
layers::LayerManager* layerManager = docWidget->GetLayerManager();
|
||||
return layerManager->GetCompositorBackendType();
|
||||
}
|
||||
|
||||
return LayersBackend::LAYERS_NONE;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElement::OnVisibilityChange()
|
||||
{
|
||||
if (OwnerDoc()->Hidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mOffscreenCanvas) {
|
||||
class Runnable final : public nsCancelableRunnable
|
||||
{
|
||||
public:
|
||||
explicit Runnable(AsyncCanvasRenderer* aRenderer)
|
||||
: mRenderer(aRenderer)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (mRenderer && mRenderer->mContext) {
|
||||
mRenderer->mContext->OnVisibilityChange();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Revoke()
|
||||
{
|
||||
mRenderer = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<AsyncCanvasRenderer> mRenderer;
|
||||
};
|
||||
|
||||
nsRefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
|
||||
nsCOMPtr<nsIThread> activeThread = mAsyncCanvasRenderer->GetActiveThread();
|
||||
if (activeThread) {
|
||||
activeThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentContext) {
|
||||
mCurrentContext->OnVisibilityChange();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElement::OnMemoryPressure()
|
||||
{
|
||||
if (mOffscreenCanvas) {
|
||||
class Runnable final : public nsCancelableRunnable
|
||||
{
|
||||
public:
|
||||
explicit Runnable(AsyncCanvasRenderer* aRenderer)
|
||||
: mRenderer(aRenderer)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (mRenderer && mRenderer->mContext) {
|
||||
mRenderer->mContext->OnMemoryPressure();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Revoke()
|
||||
{
|
||||
mRenderer = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<AsyncCanvasRenderer> mRenderer;
|
||||
};
|
||||
|
||||
nsRefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
|
||||
nsCOMPtr<nsIThread> activeThread = mAsyncCanvasRenderer->GetActiveThread();
|
||||
if (activeThread) {
|
||||
activeThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentContext) {
|
||||
mCurrentContext->OnMemoryPressure();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer)
|
||||
{
|
||||
HTMLCanvasElement *element = aRenderer->mHTMLCanvasElement;
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (element->GetWidthHeight() == aRenderer->GetSize()) {
|
||||
return;
|
||||
}
|
||||
|
||||
gfx::IntSize asyncCanvasSize = aRenderer->GetSize();
|
||||
|
||||
ErrorResult rv;
|
||||
element->SetUnsignedIntAttr(nsGkAtoms::width, asyncCanvasSize.width, rv);
|
||||
if (rv.Failed()) {
|
||||
NS_WARNING("Failed to set width attribute to a canvas element asynchronously.");
|
||||
}
|
||||
|
||||
element->SetUnsignedIntAttr(nsGkAtoms::height, asyncCanvasSize.height, rv);
|
||||
if (rv.Failed()) {
|
||||
NS_WARNING("Failed to set height attribute to a canvas element asynchronously.");
|
||||
}
|
||||
|
||||
element->mResetLayer = true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer)
|
||||
{
|
||||
HTMLCanvasElement *element = aRenderer->mHTMLCanvasElement;
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
element->InvalidateCanvasContent(nullptr);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -8,20 +8,27 @@
|
|||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIDOMHTMLCanvasElement.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsSize.h"
|
||||
#include "nsError.h"
|
||||
|
||||
#include "mozilla/dom/CanvasRenderingContextHelper.h"
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
|
||||
class nsICanvasRenderingContextInternal;
|
||||
class nsITimerCallback;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class WebGLContext;
|
||||
|
||||
namespace layers {
|
||||
class AsyncCanvasRenderer;
|
||||
class CanvasLayer;
|
||||
class Image;
|
||||
class LayerManager;
|
||||
|
@ -35,14 +42,33 @@ class CanvasCaptureMediaStream;
|
|||
class File;
|
||||
class FileCallback;
|
||||
class HTMLCanvasPrintState;
|
||||
class OffscreenCanvas;
|
||||
class PrintCallback;
|
||||
class RequestedFrameRefreshObserver;
|
||||
|
||||
enum class CanvasContextType : uint8_t {
|
||||
NoContext,
|
||||
Canvas2D,
|
||||
WebGL1,
|
||||
WebGL2
|
||||
// Listen visibilitychange and memory-pressure event and inform
|
||||
// context when event is fired.
|
||||
class HTMLCanvasElementObserver final : public nsIObserver
|
||||
, public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
explicit HTMLCanvasElementObserver(HTMLCanvasElement* aElement);
|
||||
void Destroy();
|
||||
|
||||
void RegisterVisibilityChangeEvent();
|
||||
void UnregisterVisibilityChangeEvent();
|
||||
|
||||
void RegisterMemoryPressureEvent();
|
||||
void UnregisterMemoryPressureEvent();
|
||||
|
||||
private:
|
||||
~HTMLCanvasElementObserver();
|
||||
|
||||
HTMLCanvasElement* mElement;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -84,13 +110,15 @@ protected:
|
|||
};
|
||||
|
||||
class HTMLCanvasElement final : public nsGenericHTMLElement,
|
||||
public nsIDOMHTMLCanvasElement
|
||||
public nsIDOMHTMLCanvasElement,
|
||||
public CanvasRenderingContextHelper
|
||||
{
|
||||
enum {
|
||||
DEFAULT_CANVAS_WIDTH = 300,
|
||||
DEFAULT_CANVAS_HEIGHT = 150
|
||||
};
|
||||
|
||||
typedef layers::AsyncCanvasRenderer AsyncCanvasRenderer;
|
||||
typedef layers::CanvasLayer CanvasLayer;
|
||||
typedef layers::LayerManager LayerManager;
|
||||
|
||||
|
@ -116,6 +144,11 @@ public:
|
|||
}
|
||||
void SetHeight(uint32_t aHeight, ErrorResult& aRv)
|
||||
{
|
||||
if (mOffscreenCanvas) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
SetUnsignedIntAttr(nsGkAtoms::height, aHeight, aRv);
|
||||
}
|
||||
uint32_t Width()
|
||||
|
@ -124,30 +157,45 @@ public:
|
|||
}
|
||||
void SetWidth(uint32_t aWidth, ErrorResult& aRv)
|
||||
{
|
||||
if (mOffscreenCanvas) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
SetUnsignedIntAttr(nsGkAtoms::width, aWidth, aRv);
|
||||
}
|
||||
already_AddRefed<nsISupports>
|
||||
|
||||
virtual already_AddRefed<nsISupports>
|
||||
GetContext(JSContext* aCx, const nsAString& aContextId,
|
||||
JS::Handle<JS::Value> aContextOptions,
|
||||
ErrorResult& aRv);
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
void ToDataURL(JSContext* aCx, const nsAString& aType,
|
||||
JS::Handle<JS::Value> aParams,
|
||||
nsAString& aDataURL, ErrorResult& aRv)
|
||||
{
|
||||
aRv = ToDataURL(aType, aParams, aCx, aDataURL);
|
||||
}
|
||||
|
||||
void ToBlob(JSContext* aCx,
|
||||
FileCallback& aCallback,
|
||||
const nsAString& aType,
|
||||
JS::Handle<JS::Value> aParams,
|
||||
ErrorResult& aRv);
|
||||
|
||||
OffscreenCanvas* TransferControlToOffscreen(ErrorResult& aRv);
|
||||
|
||||
bool MozOpaque() const
|
||||
{
|
||||
return GetBoolAttr(nsGkAtoms::moz_opaque);
|
||||
}
|
||||
void SetMozOpaque(bool aValue, ErrorResult& aRv)
|
||||
{
|
||||
if (mOffscreenCanvas) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
SetHTMLBoolAttr(nsGkAtoms::moz_opaque, aValue, aRv);
|
||||
}
|
||||
already_AddRefed<File> MozGetAsFile(const nsAString& aName,
|
||||
|
@ -204,6 +252,7 @@ public:
|
|||
* across its entire area.
|
||||
*/
|
||||
bool GetIsOpaque();
|
||||
virtual bool GetOpaqueAttr() override;
|
||||
|
||||
virtual already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr);
|
||||
|
||||
|
@ -282,19 +331,25 @@ public:
|
|||
|
||||
nsresult GetContext(const nsAString& aContextId, nsISupports** aContext);
|
||||
|
||||
layers::LayersBackend GetCompositorBackendType() const;
|
||||
|
||||
void OnVisibilityChange();
|
||||
|
||||
void OnMemoryPressure();
|
||||
|
||||
static void SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer);
|
||||
static void InvalidateFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer);
|
||||
|
||||
protected:
|
||||
virtual ~HTMLCanvasElement();
|
||||
|
||||
virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsIntSize GetWidthHeight();
|
||||
virtual nsIntSize GetWidthHeight() override;
|
||||
|
||||
virtual already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
CreateContext(CanvasContextType aContextType) override;
|
||||
|
||||
nsresult UpdateContext(JSContext* aCx, JS::Handle<JS::Value> options);
|
||||
nsresult ParseParams(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
nsAString& aParams,
|
||||
bool* usingCustomParseOptions);
|
||||
nsresult ExtractData(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
nsIInputStream** aStream);
|
||||
|
@ -307,13 +362,17 @@ protected:
|
|||
nsISupports** aResult);
|
||||
void CallPrintCallback();
|
||||
|
||||
CanvasContextType mCurrentContextType;
|
||||
AsyncCanvasRenderer* GetAsyncCanvasRenderer();
|
||||
|
||||
bool mResetLayer;
|
||||
nsRefPtr<HTMLCanvasElement> mOriginalCanvas;
|
||||
nsRefPtr<PrintCallback> mPrintCallback;
|
||||
nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
|
||||
nsRefPtr<HTMLCanvasPrintState> mPrintState;
|
||||
nsTArray<WeakPtr<FrameCaptureListener>> mRequestedFrameListeners;
|
||||
nsRefPtr<RequestedFrameRefreshObserver> mRequestedFrameRefreshObserver;
|
||||
nsRefPtr<AsyncCanvasRenderer> mAsyncCanvasRenderer;
|
||||
nsRefPtr<OffscreenCanvas> mOffscreenCanvas;
|
||||
nsRefPtr<HTMLCanvasElementObserver> mContextObserver;
|
||||
|
||||
public:
|
||||
// Record whether this canvas should be write-only or not.
|
||||
|
|
|
@ -808,7 +808,7 @@ UploadLastDir::StoreLastUsedDirectory(nsIDocument* aDoc, nsIFile* aDir)
|
|||
aDir->GetPath(unicodePath);
|
||||
if (unicodePath.IsEmpty()) // nothing to do
|
||||
return NS_OK;
|
||||
nsRefPtr<nsVariant> prefValue = new nsVariant();
|
||||
nsRefPtr<nsVariantCC> prefValue = new nsVariantCC();
|
||||
prefValue->SetAsAString(unicodePath);
|
||||
|
||||
// Use the document's current load context to ensure that the content pref
|
||||
|
|
|
@ -215,7 +215,7 @@ TextTrackManager::UpdateCueDisplay()
|
|||
mTextTracks->UpdateAndGetShowingCues(activeCues);
|
||||
|
||||
if (activeCues.Length() > 0) {
|
||||
nsRefPtr<nsVariant> jsCues = new nsVariant();
|
||||
nsRefPtr<nsVariantCC> jsCues = new nsVariantCC();
|
||||
|
||||
jsCues->SetAsArray(nsIDataType::VTYPE_INTERFACE,
|
||||
&NS_GET_IID(nsIDOMEventTarget),
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body onload='foo();'>
|
||||
<script>
|
||||
function foo() {
|
||||
document.addEventListener('mozfullscreenerror',
|
||||
function() {
|
||||
parent.ok(true, "Request from an iframe without allowfullscreen should be denied");
|
||||
parent.finish();
|
||||
},
|
||||
false);
|
||||
document.addEventListener('mozfullscreenchange',
|
||||
function() {
|
||||
parent.ok(false, "Request from an iframe without allowfullscreen should be denied, but was granted!");
|
||||
parent.finish();
|
||||
},
|
||||
false);
|
||||
parent.is(document.mozFullScreenEnabled, false, "Full-screen should not be enabled, coz allowfullscreen isn't present.");
|
||||
document.body.mozRequestFullScreen();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body onload="doRequestFullscreen()">
|
||||
<script>
|
||||
function doRequestFullscreen() {
|
||||
function handler(evt) {
|
||||
document.removeEventListener("mozfullscreenchange", handler);
|
||||
document.removeEventListener("mozfullscreenerror", handler);
|
||||
parent.is(evt.type, "mozfullscreenerror", "Request from " +
|
||||
`document inside ${parent.testTargetName} should be denied`);
|
||||
parent.continueTest();
|
||||
}
|
||||
parent.ok(!document.mozFullScreenEnabled, "Fullscreen " +
|
||||
`should not be enabled in ${parent.testTargetName}`);
|
||||
document.addEventListener("mozfullscreenchange", handler);
|
||||
document.addEventListener("mozfullscreenerror", handler);
|
||||
document.documentElement.mozRequestFullScreen();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=545812
|
||||
|
||||
Test DOM full-screen API.
|
||||
Test DOM fullscreen API.
|
||||
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 545812</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="file_fullscreen-utils.js"></script>
|
||||
<style>
|
||||
|
@ -30,66 +31,99 @@ function is(a, b, msg) {
|
|||
opener.is(a, b, "[denied] " + msg);
|
||||
}
|
||||
|
||||
var gotFullScreenChange = false;
|
||||
|
||||
function begin() {
|
||||
document.addEventListener("mozfullscreenchange",
|
||||
function() {
|
||||
ok(false, "Should never receive a mozfullscreenchange event in the main window.");
|
||||
gotFullScreenChange = true;
|
||||
},
|
||||
false);
|
||||
|
||||
// Request full-screen from a non trusted context (this script isn't a user
|
||||
// generated event!).
|
||||
SpecialPowers.pushPrefEnv({"set":[["full-screen-api.allow-trusted-requests-only", true]]}, startTest);
|
||||
function startTest() {
|
||||
addFullscreenErrorContinuation(
|
||||
function() {
|
||||
ok(!document.mozFullScreen, "Should not grant request in non-trusted context");
|
||||
// Test requesting full-screen mode in a long-running user-generated event handler.
|
||||
// The request in the key handler should not be granted.
|
||||
window.addEventListener("keypress", keyHandler, false);
|
||||
synthesizeKey("VK_A", {});
|
||||
});
|
||||
document.body.mozRequestFullScreen();
|
||||
}
|
||||
const INNER_FILE = "file_fullscreen-denied-inner.html";
|
||||
function setupForInnerTest(targetName, callback) {
|
||||
window.testTargetName = targetName;
|
||||
window.continueTest = () => {
|
||||
delete window.testTargetName;
|
||||
delete window.continueTest;
|
||||
callback();
|
||||
};
|
||||
}
|
||||
|
||||
function keyHandler(event) {
|
||||
window.removeEventListener("keypress", keyHandler, false);
|
||||
function begin() {
|
||||
document.addEventListener("mozfullscreenchange", () => {
|
||||
ok(false, "Should never receive " +
|
||||
"a fullscreenchange event in the main window.");
|
||||
});
|
||||
SimpleTest.executeSoon(testIFrameWithoutAllowFullscreen);
|
||||
}
|
||||
|
||||
// Busy loop until 2s has passed. We should then be past the 1 second threshold, and so
|
||||
// our request for full-screen mode should be rejected.
|
||||
var end = (new Date()).getTime() + 2000;
|
||||
while ((new Date()).getTime() < end) {
|
||||
; // Wait...
|
||||
function testIFrameWithoutAllowFullscreen() {
|
||||
// Create an iframe without an allowfullscreen attribute, whose
|
||||
// contents request fullscreen. The request should be denied, and
|
||||
// we should not receive a fullscreenchange event in this document.
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.src = INNER_FILE;
|
||||
setupForInnerTest("an iframe without allowfullscreen", () => {
|
||||
document.body.removeChild(iframe);
|
||||
SimpleTest.executeSoon(testFrameElement);
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
function testFrameElement() {
|
||||
var frameset = document.createElement("frameset");
|
||||
var frame = document.createElement("frame");
|
||||
frame.src = INNER_FILE;
|
||||
frameset.appendChild(frame);
|
||||
setupForInnerTest("a frame element", () => {
|
||||
document.documentElement.removeChild(frameset);
|
||||
SimpleTest.executeSoon(testObjectElement);
|
||||
});
|
||||
document.documentElement.appendChild(frameset);
|
||||
}
|
||||
|
||||
function testObjectElement() {
|
||||
var objectElem = document.createElement("object");
|
||||
objectElem.data = INNER_FILE;
|
||||
setupForInnerTest("an object element", () => {
|
||||
document.body.removeChild(objectElem);
|
||||
// In the following tests we want to test trust context requirement
|
||||
// of fullscreen request, so temporary re-enable this pref.
|
||||
SpecialPowers.pushPrefEnv({
|
||||
"set":[["full-screen-api.allow-trusted-requests-only", true]]
|
||||
}, testNonTrustContext);
|
||||
});
|
||||
document.body.appendChild(objectElem);
|
||||
}
|
||||
|
||||
function testNonTrustContext() {
|
||||
addFullscreenErrorContinuation(() => {
|
||||
ok(!document.mozFullScreen,
|
||||
"Should not grant request in non-trust context.");
|
||||
SimpleTest.executeSoon(testLongRunningEventHandler);
|
||||
});
|
||||
document.documentElement.mozRequestFullScreen();
|
||||
}
|
||||
|
||||
function testLongRunningEventHandler() {
|
||||
function longRunningHandler() {
|
||||
window.removeEventListener("keypress", longRunningHandler);
|
||||
// Busy loop until 2s has passed. We should then be past the one
|
||||
// second threshold, and so our request for fullscreen should be
|
||||
// rejected.
|
||||
var end = (new Date()).getTime() + 2000;
|
||||
while ((new Date()).getTime() < end) {
|
||||
; // Wait...
|
||||
}
|
||||
document.documentElement.mozRequestFullScreen();
|
||||
}
|
||||
addFullscreenErrorContinuation(
|
||||
function() {
|
||||
ok(!document.mozFullScreen, "Should not grant request in long-running event handler.");
|
||||
|
||||
// Disable the requirement for trusted contexts only, so the tests are easier
|
||||
// to write.
|
||||
SpecialPowers.pushPrefEnv({"set":[["full-screen-api.allow-trusted-requests-only", false]]}, function() {
|
||||
// Create an iframe without a allowfullscreen attribute, whose contents requests
|
||||
// full-screen. The request should be denied, and we should not receive a fullscreenchange
|
||||
// event in this document.
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.src = "file_fullscreen-denied-inner.html";
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
});
|
||||
document.body.mozRequestFullScreen();
|
||||
addFullscreenErrorContinuation(() => {
|
||||
ok(!document.mozFullScreen,
|
||||
"Should not grant request in long-running event handler.");
|
||||
// Restore the pref environment we changed before
|
||||
// entering testNonTrustContext.
|
||||
SpecialPowers.popPrefEnv(finish);
|
||||
});
|
||||
window.addEventListener("keypress", longRunningHandler);
|
||||
synthesizeKey("VK_A", {});
|
||||
}
|
||||
|
||||
function finish() {
|
||||
ok(!gotFullScreenChange, "Should not ever grant a fullscreen request in this doc.");
|
||||
opener.nextTest();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
<div id="full-screen-element"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,23 +1,18 @@
|
|||
// Remember the window size in non-fullscreen mode.
|
||||
var normalSize = new function() {
|
||||
this.w = window.outerWidth;
|
||||
this.h = window.outerHeight;
|
||||
}();
|
||||
|
||||
// Returns true if the window occupies the entire screen.
|
||||
// Note this only returns true once the transition from normal to
|
||||
// fullscreen mode is complete.
|
||||
function inFullscreenMode() {
|
||||
return window.outerWidth == window.screen.width &&
|
||||
window.outerHeight == window.screen.height;
|
||||
function inFullscreenMode(win) {
|
||||
return win.outerWidth == win.screen.width &&
|
||||
win.outerHeight == win.screen.height;
|
||||
}
|
||||
|
||||
// Returns true if the window is in normal mode, i.e. non fullscreen mode.
|
||||
// Note this only returns true once the transition from fullscreen back to
|
||||
// normal mode is complete.
|
||||
function inNormalMode() {
|
||||
return window.outerWidth == normalSize.w &&
|
||||
window.outerHeight == normalSize.h;
|
||||
function inNormalMode(win) {
|
||||
return win.outerWidth == win.normalSize.w &&
|
||||
win.outerHeight == win.normalSize.h;
|
||||
}
|
||||
|
||||
// Adds a listener that will be called once a fullscreen transition
|
||||
|
@ -31,17 +26,24 @@ function inNormalMode() {
|
|||
// the current document.
|
||||
function addFullscreenChangeContinuation(type, callback, inDoc) {
|
||||
var doc = inDoc || document;
|
||||
var topWin = doc.defaultView.top;
|
||||
// Remember the window size in non-fullscreen mode.
|
||||
if (!topWin.normalSize) {
|
||||
topWin.normalSize = {
|
||||
w: window.outerWidth,
|
||||
h: window.outerHeight
|
||||
};
|
||||
}
|
||||
function checkCondition() {
|
||||
if (type == "enter") {
|
||||
return inFullscreenMode();
|
||||
return inFullscreenMode(topWin);
|
||||
} else if (type == "exit") {
|
||||
// If we just revert the state to a previous fullscreen state,
|
||||
// the window won't back to the normal mode. Hence we check
|
||||
// mozFullScreenElement first here. Note that we need to check
|
||||
// the fullscreen element of the outmost document here instead
|
||||
// of the current one.
|
||||
var topDoc = doc.defaultView.top.document;
|
||||
return topDoc.mozFullScreenElement || inNormalMode();
|
||||
return topWin.document.mozFullScreenElement || inNormalMode(topWin);
|
||||
} else {
|
||||
throw "'type' must be either 'enter', or 'exit'.";
|
||||
}
|
||||
|
@ -57,14 +59,13 @@ function addFullscreenChangeContinuation(type, callback, inDoc) {
|
|||
invokeCallback(event);
|
||||
return;
|
||||
}
|
||||
var win = doc.defaultView;
|
||||
function onResize() {
|
||||
if (checkCondition()) {
|
||||
win.removeEventListener("resize", onResize, false);
|
||||
topWin.removeEventListener("resize", onResize, false);
|
||||
invokeCallback(event);
|
||||
}
|
||||
}
|
||||
win.addEventListener("resize", onResize, false);
|
||||
topWin.addEventListener("resize", onResize, false);
|
||||
}
|
||||
doc.addEventListener("mozfullscreenchange", onFullscreenChange, false);
|
||||
}
|
||||
|
|
|
@ -17411,6 +17411,25 @@ QuotaClient::PerformIdleMaintenanceOnDatabaseInternal(
|
|||
MOZ_ASSERT(!aMaintenanceInfo.mGroup.IsEmpty());
|
||||
MOZ_ASSERT(!aMaintenanceInfo.mOrigin.IsEmpty());
|
||||
|
||||
class MOZ_STACK_CLASS AutoClose final
|
||||
{
|
||||
nsCOMPtr<mozIStorageConnection> mConnection;
|
||||
|
||||
public:
|
||||
explicit AutoClose(mozIStorageConnection* aConnection)
|
||||
: mConnection(aConnection)
|
||||
{
|
||||
MOZ_ASSERT(aConnection);
|
||||
}
|
||||
|
||||
~AutoClose()
|
||||
{
|
||||
MOZ_ASSERT(mConnection);
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mConnection->Close()));
|
||||
}
|
||||
};
|
||||
|
||||
nsCOMPtr<nsIFile> databaseFile =
|
||||
GetFileForPath(aMaintenanceInfo.mDatabasePath);
|
||||
MOZ_ASSERT(databaseFile);
|
||||
|
@ -17426,6 +17445,8 @@ QuotaClient::PerformIdleMaintenanceOnDatabaseInternal(
|
|||
return;
|
||||
}
|
||||
|
||||
AutoClose autoClose(connection);
|
||||
|
||||
if (IdleMaintenanceMustEnd(aRunId)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2877,7 +2877,7 @@ ContentChild::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
|
|||
auto& items = aTransfers[i].items();
|
||||
for (uint32_t j = 0; j < items.Length(); ++j) {
|
||||
const IPCDataTransferItem& item = items[j];
|
||||
nsRefPtr<nsVariant> variant = new nsVariant();
|
||||
nsRefPtr<nsVariantCC> variant = new nsVariantCC();
|
||||
if (item.data().type() == IPCDataTransferData::TnsString) {
|
||||
const nsString& data = item.data().get_nsString();
|
||||
variant->SetAsAString(data);
|
||||
|
|
|
@ -27,21 +27,19 @@ namespace ipc {
|
|||
bool
|
||||
StructuredCloneData::Copy(const StructuredCloneData& aData)
|
||||
{
|
||||
if (!aData.mData) {
|
||||
if (!aData.Data()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t* data = static_cast<uint64_t*>(js_malloc(aData.mDataLength));
|
||||
if (!data) {
|
||||
return false;
|
||||
if (aData.SharedData()) {
|
||||
mSharedData = aData.SharedData();
|
||||
} else {
|
||||
mSharedData =
|
||||
SharedJSAllocatedData::CreateFromExternalData(aData.Data(),
|
||||
aData.DataLength());
|
||||
NS_ENSURE_TRUE(mSharedData, false);
|
||||
}
|
||||
|
||||
memcpy(data, aData.mData, aData.mDataLength);
|
||||
|
||||
mData = data;
|
||||
mDataLength = aData.mDataLength;
|
||||
mDataOwned = eJSAllocated;
|
||||
|
||||
MOZ_ASSERT(BlobImpls().IsEmpty());
|
||||
BlobImpls().AppendElements(aData.BlobImpls());
|
||||
|
||||
|
@ -55,12 +53,12 @@ StructuredCloneData::Read(JSContext* aCx,
|
|||
JS::MutableHandle<JS::Value> aValue,
|
||||
ErrorResult &aRv)
|
||||
{
|
||||
MOZ_ASSERT(mData);
|
||||
MOZ_ASSERT(Data());
|
||||
|
||||
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
|
||||
MOZ_ASSERT(global);
|
||||
|
||||
ReadFromBuffer(global, aCx, mData, mDataLength, aValue, aRv);
|
||||
ReadFromBuffer(global, aCx, Data(), DataLength(), aValue, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -68,26 +66,28 @@ StructuredCloneData::Write(JSContext* aCx,
|
|||
JS::Handle<JS::Value> aValue,
|
||||
ErrorResult &aRv)
|
||||
{
|
||||
MOZ_ASSERT(!mData);
|
||||
MOZ_ASSERT(!Data());
|
||||
|
||||
StructuredCloneHolder::Write(aCx, aValue, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
mBuffer->steal(&mData, &mDataLength);
|
||||
uint64_t* data = nullptr;
|
||||
size_t dataLength = 0;
|
||||
mBuffer->steal(&data, &dataLength);
|
||||
mBuffer = nullptr;
|
||||
mDataOwned = eJSAllocated;
|
||||
mSharedData = new SharedJSAllocatedData(data, dataLength);
|
||||
}
|
||||
|
||||
void
|
||||
StructuredCloneData::WriteIPCParams(Message* aMsg) const
|
||||
{
|
||||
WriteParam(aMsg, mDataLength);
|
||||
WriteParam(aMsg, DataLength());
|
||||
|
||||
if (mDataLength) {
|
||||
if (DataLength()) {
|
||||
// Structured clone data must be 64-bit aligned.
|
||||
aMsg->WriteBytes(mData, mDataLength, sizeof(uint64_t));
|
||||
aMsg->WriteBytes(Data(), DataLength(), sizeof(uint64_t));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,31 +95,29 @@ bool
|
|||
StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg,
|
||||
void** aIter)
|
||||
{
|
||||
MOZ_ASSERT(!mData);
|
||||
MOZ_ASSERT(!Data());
|
||||
|
||||
if (!ReadParam(aMsg, aIter, &mDataLength)) {
|
||||
size_t dataLength = 0;
|
||||
if (!ReadParam(aMsg, aIter, &dataLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mDataLength) {
|
||||
if (!dataLength) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t* dataBuffer = nullptr;
|
||||
const char** buffer =
|
||||
const_cast<const char**>(reinterpret_cast<char**>(&mData));
|
||||
const_cast<const char**>(reinterpret_cast<char**>(&dataBuffer));
|
||||
// Structured clone data must be 64-bit aligned.
|
||||
if (!aMsg->ReadBytes(aIter, buffer, mDataLength, sizeof(uint64_t))) {
|
||||
if (!aMsg->ReadBytes(aIter, buffer, dataLength, sizeof(uint64_t))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t* data = static_cast<uint64_t*>(js_malloc(mDataLength));
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
mSharedData = SharedJSAllocatedData::CreateFromExternalData(dataBuffer,
|
||||
dataLength);
|
||||
NS_ENSURE_TRUE(mSharedData, false);
|
||||
|
||||
memcpy(data, mData, mDataLength);
|
||||
mData = data;
|
||||
mDataOwned = eJSAllocated;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -127,17 +125,10 @@ bool
|
|||
StructuredCloneData::CopyExternalData(const void* aData,
|
||||
size_t aDataLength)
|
||||
{
|
||||
MOZ_ASSERT(!mData);
|
||||
uint64_t* data = static_cast<uint64_t*>(js_malloc(aDataLength));
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data, aData, aDataLength);
|
||||
mData = data;
|
||||
mDataLength = aDataLength;
|
||||
mDataOwned = eJSAllocated;
|
||||
|
||||
MOZ_ASSERT(!Data());
|
||||
mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData,
|
||||
aDataLength);
|
||||
NS_ENSURE_TRUE(mSharedData, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
#ifndef mozilla_dom_ipc_StructuredCloneData_h
|
||||
#define mozilla_dom_ipc_StructuredCloneData_h
|
||||
|
||||
#include <algorithm>
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/dom/StructuredCloneHolder.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
namespace IPC {
|
||||
class Message;
|
||||
|
@ -17,6 +20,51 @@ namespace mozilla {
|
|||
namespace dom {
|
||||
namespace ipc {
|
||||
|
||||
class SharedJSAllocatedData final
|
||||
{
|
||||
public:
|
||||
SharedJSAllocatedData(uint64_t* aData, size_t aDataLength)
|
||||
: mData(aData), mDataLength(aDataLength)
|
||||
{
|
||||
MOZ_ASSERT(mData);
|
||||
}
|
||||
|
||||
static already_AddRefed<SharedJSAllocatedData>
|
||||
CreateFromExternalData(const void* aData, size_t aDataLength)
|
||||
{
|
||||
uint64_t* data = Allocate64bitSafely(aDataLength);
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memcpy(data, aData, aDataLength);
|
||||
nsRefPtr<SharedJSAllocatedData> sharedData =
|
||||
new SharedJSAllocatedData(data, aDataLength);
|
||||
return sharedData.forget();
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(SharedJSAllocatedData)
|
||||
|
||||
uint64_t* Data() const { return mData; }
|
||||
size_t DataLength() const { return mDataLength; }
|
||||
|
||||
private:
|
||||
~SharedJSAllocatedData()
|
||||
{
|
||||
js_free(mData);
|
||||
}
|
||||
|
||||
static uint64_t*
|
||||
Allocate64bitSafely(size_t aSize)
|
||||
{
|
||||
// Structured cloning requires 64-bit aligment.
|
||||
return static_cast<uint64_t*>(js_malloc(std::max(sizeof(uint64_t), aSize)));
|
||||
}
|
||||
|
||||
uint64_t* mData;
|
||||
size_t mDataLength;
|
||||
};
|
||||
|
||||
class StructuredCloneData : public StructuredCloneHolder
|
||||
{
|
||||
public:
|
||||
|
@ -24,18 +72,15 @@ public:
|
|||
: StructuredCloneHolder(StructuredCloneHolder::CloningSupported,
|
||||
StructuredCloneHolder::TransferringNotSupported,
|
||||
StructuredCloneHolder::DifferentProcess)
|
||||
, mData(nullptr)
|
||||
, mDataLength(0)
|
||||
, mDataOwned(eNone)
|
||||
, mExternalData(nullptr)
|
||||
, mExternalDataLength(0)
|
||||
{}
|
||||
|
||||
StructuredCloneData(const StructuredCloneData&) = delete;
|
||||
|
||||
~StructuredCloneData()
|
||||
{
|
||||
if (mDataOwned == eJSAllocated) {
|
||||
js_free(mData);
|
||||
}
|
||||
MOZ_ASSERT(!(mExternalData && mSharedData));
|
||||
}
|
||||
|
||||
StructuredCloneData&
|
||||
|
@ -63,22 +108,26 @@ public:
|
|||
|
||||
void UseExternalData(uint64_t* aData, size_t aDataLength)
|
||||
{
|
||||
MOZ_ASSERT(!mData);
|
||||
mData = aData;
|
||||
mDataLength = aDataLength;
|
||||
MOZ_ASSERT(mDataOwned == eNone);
|
||||
MOZ_ASSERT(!Data());
|
||||
mExternalData = aData;
|
||||
mExternalDataLength = aDataLength;
|
||||
}
|
||||
|
||||
bool CopyExternalData(const void* aData, size_t aDataLength);
|
||||
|
||||
uint64_t* Data() const
|
||||
{
|
||||
return mData;
|
||||
return mSharedData ? mSharedData->Data() : mExternalData;
|
||||
}
|
||||
|
||||
size_t DataLength() const
|
||||
{
|
||||
return mDataLength;
|
||||
return mSharedData ? mSharedData->DataLength() : mExternalDataLength;
|
||||
}
|
||||
|
||||
SharedJSAllocatedData* SharedData() const
|
||||
{
|
||||
return mSharedData;
|
||||
}
|
||||
|
||||
// For IPC serialization
|
||||
|
@ -86,12 +135,10 @@ public:
|
|||
bool ReadIPCParams(const IPC::Message* aMessage, void** aIter);
|
||||
|
||||
private:
|
||||
uint64_t* mData;
|
||||
size_t mDataLength;
|
||||
enum {
|
||||
eNone,
|
||||
eJSAllocated,
|
||||
} mDataOwned;
|
||||
uint64_t* MOZ_NON_OWNING_REF mExternalData;
|
||||
size_t mExternalDataLength;
|
||||
|
||||
RefPtr<SharedJSAllocatedData> mSharedData;
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
|
|
|
@ -2267,14 +2267,7 @@ TabParent::RecvNotifyIMESelection(const ContentCache& aContentCache,
|
|||
return true;
|
||||
|
||||
mContentCache.AssignContent(aContentCache, &aIMENotification);
|
||||
|
||||
const nsIMEUpdatePreference updatePreference =
|
||||
widget->GetIMEUpdatePreference();
|
||||
if (updatePreference.WantSelectionChange() &&
|
||||
(updatePreference.WantChangesCausedByComposition() ||
|
||||
!aIMENotification.mSelectionChangeData.mCausedByComposition)) {
|
||||
mContentCache.MaybeNotifyIME(widget, aIMENotification);
|
||||
}
|
||||
mContentCache.MaybeNotifyIME(widget, aIMENotification);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2316,12 +2309,7 @@ TabParent::RecvNotifyIMEPositionChange(const ContentCache& aContentCache,
|
|||
}
|
||||
|
||||
mContentCache.AssignContent(aContentCache, &aIMENotification);
|
||||
|
||||
const nsIMEUpdatePreference updatePreference =
|
||||
widget->GetIMEUpdatePreference();
|
||||
if (updatePreference.WantPositionChanged()) {
|
||||
mContentCache.MaybeNotifyIME(widget, aIMENotification);
|
||||
}
|
||||
mContentCache.MaybeNotifyIME(widget, aIMENotification);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3588,7 +3576,7 @@ TabParent::AddInitialDnDDataTo(DataTransfer* aDataTransfer)
|
|||
nsTArray<DataTransferItem>& itemArray = mInitialDataTransferItems[i];
|
||||
for (uint32_t j = 0; j < itemArray.Length(); ++j) {
|
||||
DataTransferItem& item = itemArray[j];
|
||||
nsRefPtr<nsVariant> variant = new nsVariant();
|
||||
nsRefPtr<nsVariantCC> variant = new nsVariantCC();
|
||||
// Special case kFilePromiseMime so that we get the right
|
||||
// nsIFlavorDataProvider for it.
|
||||
if (item.mFlavor.EqualsLiteral(kFilePromiseMime)) {
|
||||
|
|
|
@ -67,7 +67,7 @@ nsIDOMWindowInternalWarning=Use of nsIDOMWindowInternal is deprecated. Use nsIDO
|
|||
FullScreenDeniedDisabled=Request for full-screen was denied because full-screen API is disabled by user preference.
|
||||
FullScreenDeniedFocusedPlugin=Request for full-screen was denied because a windowed plugin is focused.
|
||||
FullScreenDeniedHidden=Request for full-screen was denied because the document is no longer visible.
|
||||
FullScreenDeniedIframeNotAllowed=Request for full-screen was denied because at least one of the document's containing iframes does not have an "allowfullscreen" attribute.
|
||||
FullScreenDeniedContainerNotAllowed=Request for full-screen was denied because at least one of the document's containing element is not iframe or does not have an "allowfullscreen" attribute.
|
||||
FullScreenDeniedNotInputDriven=Request for full-screen was denied because Element.mozRequestFullScreen() was not called from inside a short running user-generated event handler.
|
||||
FullScreenDeniedNotInDocument=Request for full-screen was denied because requesting element is no longer in its document.
|
||||
FullScreenDeniedMovedDocument=Request for full-screen was denied because requesting element has moved document.
|
||||
|
|
|
@ -104,9 +104,7 @@ class MediaCache {
|
|||
public:
|
||||
friend class MediaCacheStream::BlockList;
|
||||
typedef MediaCacheStream::BlockList BlockList;
|
||||
enum {
|
||||
BLOCK_SIZE = MediaCacheStream::BLOCK_SIZE
|
||||
};
|
||||
static const int64_t BLOCK_SIZE = MediaCacheStream::BLOCK_SIZE;
|
||||
|
||||
MediaCache() : mNextResourceID(1),
|
||||
mReentrantMonitor("MediaCache.mReentrantMonitor"),
|
||||
|
@ -2224,7 +2222,7 @@ MediaCacheStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
|
|||
uint32_t streamBlock = uint32_t(mStreamOffset/BLOCK_SIZE);
|
||||
uint32_t offsetInStreamBlock =
|
||||
uint32_t(mStreamOffset - streamBlock*BLOCK_SIZE);
|
||||
int64_t size = std::min(aCount - count, BLOCK_SIZE - offsetInStreamBlock);
|
||||
int64_t size = std::min<int64_t>(aCount - count, BLOCK_SIZE - offsetInStreamBlock);
|
||||
|
||||
if (mStreamLength >= 0) {
|
||||
// Don't try to read beyond the end of the stream
|
||||
|
|
|
@ -183,10 +183,9 @@ class MediaCache;
|
|||
*/
|
||||
class MediaCacheStream {
|
||||
public:
|
||||
enum {
|
||||
// This needs to be a power of two
|
||||
BLOCK_SIZE = 32768
|
||||
};
|
||||
// This needs to be a power of two
|
||||
static const int64_t BLOCK_SIZE = 32768;
|
||||
|
||||
enum ReadMode {
|
||||
MODE_METADATA,
|
||||
MODE_PLAYBACK
|
||||
|
|
|
@ -62,13 +62,11 @@ public:
|
|||
size_t mSize;
|
||||
};
|
||||
|
||||
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder,
|
||||
TaskQueue* aBorrowedTaskQueue)
|
||||
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
|
||||
: mAudioCompactor(mAudioQueue)
|
||||
, mDecoder(aDecoder)
|
||||
, mTaskQueue(aBorrowedTaskQueue ? aBorrowedTaskQueue
|
||||
: new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
|
||||
/* aSupportsTailDispatch = */ true))
|
||||
, mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
|
||||
/* aSupportsTailDispatch = */ true))
|
||||
, mWatchManager(this, mTaskQueue)
|
||||
, mTimer(new MediaTimer())
|
||||
, mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)")
|
||||
|
@ -78,7 +76,6 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder,
|
|||
, mIgnoreAudioOutputFormat(false)
|
||||
, mHitAudioDecodeError(false)
|
||||
, mShutdown(false)
|
||||
, mTaskQueueIsBorrowed(!!aBorrowedTaskQueue)
|
||||
, mAudioDiscontinuity(false)
|
||||
, mVideoDiscontinuity(false)
|
||||
{
|
||||
|
@ -374,7 +371,7 @@ MediaDecoderReader::RequestAudioData()
|
|||
// waiting in this while loop since it somehow prevents audio EOS from
|
||||
// coming in gstreamer 1.x when there is still video buffer waiting to be
|
||||
// consumed. (|mVideoSinkBufferCount| > 0)
|
||||
if (AudioQueue().GetSize() == 0 && mTaskQueue) {
|
||||
if (AudioQueue().GetSize() == 0) {
|
||||
RefPtr<nsIRunnable> task(new ReRequestAudioTask(this));
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
return p;
|
||||
|
@ -425,22 +422,10 @@ MediaDecoderReader::Shutdown()
|
|||
|
||||
nsRefPtr<ShutdownPromise> p;
|
||||
|
||||
// Spin down the task queue if necessary. We wait until BreakCycles to null
|
||||
// out mTaskQueue, since otherwise any remaining tasks could crash when they
|
||||
// invoke OnTaskQueue().
|
||||
if (mTaskQueue && !mTaskQueueIsBorrowed) {
|
||||
// If we own our task queue, shutdown ends when the task queue is done.
|
||||
p = mTaskQueue->BeginShutdown();
|
||||
} else {
|
||||
// If we don't own our task queue, we resolve immediately (though
|
||||
// asynchronously).
|
||||
p = ShutdownPromise::CreateAndResolve(true, __func__);
|
||||
}
|
||||
|
||||
mTimer = nullptr;
|
||||
mDecoder = nullptr;
|
||||
|
||||
return p;
|
||||
return mTaskQueue->BeginShutdown();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -82,7 +82,7 @@ public:
|
|||
|
||||
// The caller must ensure that Shutdown() is called before aDecoder is
|
||||
// destroyed.
|
||||
explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder, TaskQueue* aBorrowedTaskQueue = nullptr);
|
||||
explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder);
|
||||
|
||||
// Does any spinup that needs to happen on this task queue. This runs on a
|
||||
// different thread than Init, and there should not be ordering dependencies
|
||||
|
@ -428,8 +428,6 @@ private:
|
|||
MozPromiseHolder<AudioDataPromise> mBaseAudioPromise;
|
||||
MozPromiseHolder<VideoDataPromise> mBaseVideoPromise;
|
||||
|
||||
bool mTaskQueueIsBorrowed;
|
||||
|
||||
// Flags whether a the next audio/video sample comes after a "gap" or
|
||||
// "discontinuity" in the stream. For example after a seek.
|
||||
bool mAudioDiscontinuity;
|
||||
|
|
|
@ -180,6 +180,20 @@ static uint32_t sVideoQueueDefaultSize = MAX_VIDEO_QUEUE_SIZE;
|
|||
static uint32_t sVideoQueueHWAccelSize = MIN_VIDEO_QUEUE_SIZE;
|
||||
static uint32_t sVideoQueueSendToCompositorSize = VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE;
|
||||
|
||||
static void InitVideoQueuePrefs() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
static bool sPrefInit = false;
|
||||
if (!sPrefInit) {
|
||||
sPrefInit = true;
|
||||
sVideoQueueDefaultSize = Preferences::GetUint(
|
||||
"media.video-queue.default-size", MAX_VIDEO_QUEUE_SIZE);
|
||||
sVideoQueueHWAccelSize = Preferences::GetUint(
|
||||
"media.video-queue.hw-accel-size", MIN_VIDEO_QUEUE_SIZE);
|
||||
sVideoQueueSendToCompositorSize = Preferences::GetUint(
|
||||
"media.video-queue.send-to-compositor-size", VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
MediaDecoderReader* aReader,
|
||||
bool aRealTime) :
|
||||
|
@ -269,19 +283,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
|||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::InitializationTask);
|
||||
mTaskQueue->Dispatch(r.forget());
|
||||
|
||||
static bool sPrefCacheInit = false;
|
||||
if (!sPrefCacheInit) {
|
||||
sPrefCacheInit = true;
|
||||
Preferences::AddUintVarCache(&sVideoQueueDefaultSize,
|
||||
"media.video-queue.default-size",
|
||||
MAX_VIDEO_QUEUE_SIZE);
|
||||
Preferences::AddUintVarCache(&sVideoQueueHWAccelSize,
|
||||
"media.video-queue.hw-accel-size",
|
||||
MIN_VIDEO_QUEUE_SIZE);
|
||||
Preferences::AddUintVarCache(&sVideoQueueSendToCompositorSize,
|
||||
"media.video-queue.send-to-compositor-size",
|
||||
VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE);
|
||||
}
|
||||
InitVideoQueuePrefs();
|
||||
|
||||
mBufferingWait = IsRealTime() ? 0 : 15;
|
||||
mLowDataThresholdUsecs = IsRealTime() ? 0 : detail::LOW_DATA_THRESHOLD_USECS;
|
||||
|
|
|
@ -60,9 +60,8 @@ TrackTypeToStr(TrackInfo::TrackType aTrack)
|
|||
}
|
||||
|
||||
MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
|
||||
MediaDataDemuxer* aDemuxer,
|
||||
TaskQueue* aBorrowedTaskQueue)
|
||||
: MediaDecoderReader(aDecoder, aBorrowedTaskQueue)
|
||||
MediaDataDemuxer* aDemuxer)
|
||||
: MediaDecoderReader(aDecoder)
|
||||
, mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2))
|
||||
, mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2))
|
||||
, mDemuxer(aDemuxer)
|
||||
|
|
|
@ -25,9 +25,7 @@ class MediaFormatReader final : public MediaDecoderReader
|
|||
typedef media::Interval<int64_t> ByteInterval;
|
||||
|
||||
public:
|
||||
explicit MediaFormatReader(AbstractMediaDecoder* aDecoder,
|
||||
MediaDataDemuxer* aDemuxer,
|
||||
TaskQueue* aBorrowedTaskQueue = nullptr);
|
||||
MediaFormatReader(AbstractMediaDecoder* aDecoder, MediaDataDemuxer* aDemuxer);
|
||||
|
||||
virtual ~MediaFormatReader();
|
||||
|
||||
|
|
|
@ -2204,7 +2204,8 @@ MediaManager::AnonymizeId(nsAString& aId, const nsACString& aOriginKey)
|
|||
already_AddRefed<nsIWritableVariant>
|
||||
MediaManager::ToJSArray(SourceSet& aDevices)
|
||||
{
|
||||
nsRefPtr<nsVariant> var = new nsVariant();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<nsVariantCC> var = new nsVariantCC();
|
||||
size_t len = aDevices.Length();
|
||||
if (len) {
|
||||
nsTArray<nsIMediaDevice*> tmp(len);
|
||||
|
|
|
@ -49,6 +49,8 @@ bool PDMFactory::sGonkDecoderEnabled = false;
|
|||
bool PDMFactory::sAndroidMCDecoderEnabled = false;
|
||||
bool PDMFactory::sAndroidMCDecoderPreferred = false;
|
||||
bool PDMFactory::sGMPDecoderEnabled = false;
|
||||
bool PDMFactory::sFFmpegDecoderEnabled = false;
|
||||
|
||||
bool PDMFactory::sEnableFuzzingWrapper = false;
|
||||
uint32_t PDMFactory::sVideoOutputMinimumInterval_ms = 0;
|
||||
bool PDMFactory::sDontDelayInputExhausted = false;
|
||||
|
@ -79,6 +81,8 @@ PDMFactory::Init()
|
|||
|
||||
Preferences::AddBoolVarCache(&sGMPDecoderEnabled,
|
||||
"media.fragmented-mp4.gmp.enabled", false);
|
||||
Preferences::AddBoolVarCache(&sFFmpegDecoderEnabled,
|
||||
"media.fragmented-mp4.ffmpeg.enabled", false);
|
||||
|
||||
Preferences::AddBoolVarCache(&sEnableFuzzingWrapper,
|
||||
"media.decoder.fuzzing.enabled", false);
|
||||
|
@ -204,8 +208,10 @@ PDMFactory::CreatePDMs()
|
|||
StartupPDM(m);
|
||||
#endif
|
||||
#ifdef MOZ_FFMPEG
|
||||
m = FFmpegRuntimeLinker::CreateDecoderModule();
|
||||
StartupPDM(m);
|
||||
if (sFFmpegDecoderEnabled) {
|
||||
m = FFmpegRuntimeLinker::CreateDecoderModule();
|
||||
StartupPDM(m);
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_APPLEMEDIA
|
||||
m = new AppleDecoderModule();
|
||||
|
|
|
@ -60,6 +60,7 @@ private:
|
|||
static bool sAndroidMCDecoderPreferred;
|
||||
static bool sAndroidMCDecoderEnabled;
|
||||
static bool sGMPDecoderEnabled;
|
||||
static bool sFFmpegDecoderEnabled;
|
||||
static bool sEnableFuzzingWrapper;
|
||||
static uint32_t sVideoOutputMinimumInterval_ms;
|
||||
static bool sDontDelayInputExhausted;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "AppleVDALinker.h"
|
||||
#include "AppleVTDecoder.h"
|
||||
#include "AppleVTLinker.h"
|
||||
#include "MacIOSurfaceImage.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
@ -18,10 +19,12 @@
|
|||
namespace mozilla {
|
||||
|
||||
bool AppleDecoderModule::sInitialized = false;
|
||||
bool AppleDecoderModule::sIsCoreMediaAvailable = false;
|
||||
bool AppleDecoderModule::sIsVTAvailable = false;
|
||||
bool AppleDecoderModule::sIsVTHWAvailable = false;
|
||||
bool AppleDecoderModule::sIsVDAAvailable = false;
|
||||
bool AppleDecoderModule::sForceVDA = false;
|
||||
bool AppleDecoderModule::sCanUseHardwareVideoDecoder = true;
|
||||
|
||||
AppleDecoderModule::AppleDecoderModule()
|
||||
{
|
||||
|
@ -43,19 +46,26 @@ AppleDecoderModule::Init()
|
|||
|
||||
Preferences::AddBoolVarCache(&sForceVDA, "media.apple.forcevda", false);
|
||||
|
||||
// Ensure IOSurface framework is loaded.
|
||||
MacIOSurfaceLib::LoadLibrary();
|
||||
const bool loaded = MacIOSurfaceLib::isInit();
|
||||
|
||||
// dlopen VideoDecodeAcceleration.framework if it's available.
|
||||
sIsVDAAvailable = AppleVDALinker::Link();
|
||||
sIsVDAAvailable = loaded && AppleVDALinker::Link();
|
||||
|
||||
// dlopen CoreMedia.framework if it's available.
|
||||
bool haveCoreMedia = AppleCMLinker::Link();
|
||||
sIsCoreMediaAvailable = AppleCMLinker::Link();
|
||||
// dlopen VideoToolbox.framework if it's available.
|
||||
// We must link both CM and VideoToolbox framework to allow for proper
|
||||
// paired Link/Unlink calls
|
||||
bool haveVideoToolbox = AppleVTLinker::Link();
|
||||
sIsVTAvailable = haveCoreMedia && haveVideoToolbox;
|
||||
bool haveVideoToolbox = loaded && AppleVTLinker::Link();
|
||||
sIsVTAvailable = sIsCoreMediaAvailable && haveVideoToolbox;
|
||||
|
||||
sIsVTHWAvailable = AppleVTLinker::skPropEnableHWAccel != nullptr;
|
||||
|
||||
sCanUseHardwareVideoDecoder = loaded &&
|
||||
gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding();
|
||||
|
||||
sInitialized = true;
|
||||
}
|
||||
|
||||
|
@ -109,10 +119,12 @@ AppleDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
|
|||
bool
|
||||
AppleDecoderModule::SupportsMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return aMimeType.EqualsLiteral("audio/mpeg") ||
|
||||
aMimeType.EqualsLiteral("audio/mp4a-latm") ||
|
||||
aMimeType.EqualsLiteral("video/mp4") ||
|
||||
aMimeType.EqualsLiteral("video/avc");
|
||||
return (sIsCoreMediaAvailable &&
|
||||
(aMimeType.EqualsLiteral("audio/mpeg") ||
|
||||
aMimeType.EqualsLiteral("audio/mp4a-latm"))) ||
|
||||
((sIsVTAvailable || sIsVDAAvailable) &&
|
||||
(aMimeType.EqualsLiteral("video/mp4") ||
|
||||
aMimeType.EqualsLiteral("video/avc")));
|
||||
}
|
||||
|
||||
PlatformDecoderModule::ConversionRequired
|
||||
|
|
|
@ -39,8 +39,11 @@ public:
|
|||
|
||||
static void Init();
|
||||
|
||||
static bool sCanUseHardwareVideoDecoder;
|
||||
|
||||
private:
|
||||
static bool sInitialized;
|
||||
static bool sIsCoreMediaAvailable;
|
||||
static bool sIsVTAvailable;
|
||||
static bool sIsVTHWAvailable;
|
||||
static bool sIsVDAAvailable;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <CoreFoundation/CFString.h>
|
||||
|
||||
#include "AppleDecoderModule.h"
|
||||
#include "AppleUtils.h"
|
||||
#include "AppleVDADecoder.h"
|
||||
#include "AppleVDALinker.h"
|
||||
|
@ -647,7 +648,7 @@ AppleVDADecoder::CreateVDADecoder(
|
|||
MediaDataDecoderCallback* aCallback,
|
||||
layers::ImageContainer* aImageContainer)
|
||||
{
|
||||
if (!gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding()) {
|
||||
if (!AppleDecoderModule::sCanUseHardwareVideoDecoder) {
|
||||
// This GPU is blacklisted for hardware decoding.
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <CoreFoundation/CFString.h>
|
||||
|
||||
#include "AppleCMLinker.h"
|
||||
#include "AppleDecoderModule.h"
|
||||
#include "AppleUtils.h"
|
||||
#include "AppleVTDecoder.h"
|
||||
#include "AppleVTLinker.h"
|
||||
|
@ -382,7 +383,7 @@ AppleVTDecoder::CreateDecoderSpecification()
|
|||
|
||||
const void* specKeys[] = { AppleVTLinker::skPropEnableHWAccel };
|
||||
const void* specValues[1];
|
||||
if (gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding()) {
|
||||
if (AppleDecoderModule::sCanUseHardwareVideoDecoder) {
|
||||
specValues[0] = kCFBooleanTrue;
|
||||
} else {
|
||||
// This GPU is blacklisted for hardware decoding.
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "PlatformDecoderModule.h"
|
||||
#include "FFmpegAudioDecoder.h"
|
||||
#include "FFmpegH264Decoder.h"
|
||||
#include "FFmpegRuntimeLinker.h"
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
|
@ -22,11 +21,6 @@ public:
|
|||
static already_AddRefed<PlatformDecoderModule>
|
||||
Create()
|
||||
{
|
||||
uint32_t major, minor;
|
||||
GetVersion(major, minor);
|
||||
if (major < 54 && !FFmpegRuntimeLinker::sFFmpegDecoderEnabled) {
|
||||
return nullptr;
|
||||
}
|
||||
nsRefPtr<PlatformDecoderModule> pdm = new FFmpegDecoderModule();
|
||||
|
||||
return pdm.forget();
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче