merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-10-12 11:57:06 +02:00
Родитель b340b15978 da9545f635
Коммит 2b1a321946
351 изменённых файлов: 8409 добавлений и 3161 удалений

Просмотреть файл

@ -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.
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,21 +2512,26 @@ 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)) {
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
// original docshell doesn't have a allowfullscreen attribute, so
@ -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,10 +11906,7 @@ 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"),
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:
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;
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();
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,6 +752,9 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
uint32_t height = signedHeight;
// Early success return cases
// May have a OffscreenCanvas instead of an HTMLCanvasElement
if (GetCanvas())
GetCanvas()->InvalidateCanvas();
// Zero-sized surfaces can cause problems.
@ -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,7 +1167,7 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
}
WebGLContextUserData* userData = nullptr;
if (builder->IsPaintingToWindow()) {
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
@ -1287,6 +1186,7 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
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() {
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(),
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(),
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) {
if (aWidget->GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) {
if (!InitWithPlugin(aPresContext, aContent)) {
Clear();
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));
} else {
// mEditableNode is a document
selCon = do_QueryInterface(presShell);
}
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.
if (!InitWithEditor(aPresContext, aContent, aEditor)) {
Clear();
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;
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,7 +569,9 @@ bool
IMEContentObserver::IsObservingContent(nsPresContext* aPresContext,
nsIContent* aContent) const
{
return mEditableNode == IMEStateManager::GetRootEditableNode(aPresContext,
return IsInitializedWithPlugin() ?
mRootContent == aContent && mRootContent != nullptr :
mEditableNode == IMEStateManager::GetRootEditableNode(aPresContext,
aContent);
}

Просмотреть файл

@ -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))
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);
if (mOffscreenCanvas) {
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;
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)
{
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,21 +1,23 @@
<!DOCTYPE html>
<html>
<body onload='foo();'>
<head>
<meta charset="UTF-8">
</head>
<body onload="doRequestFullscreen()">
<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();
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>

Просмотреть файл

@ -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;
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 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.addEventListener("mozfullscreenchange", () => {
ok(false, "Should never receive " +
"a fullscreenchange event in the main window.");
});
document.body.mozRequestFullScreen();
}
SimpleTest.executeSoon(testIFrameWithoutAllowFullscreen);
}
function keyHandler(event) {
window.removeEventListener("keypress", keyHandler, false);
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);
}
// 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.
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...
}
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.documentElement.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);
});
});
document.body.mozRequestFullScreen();
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);
}
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);
}
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
};
static const int64_t BLOCK_SIZE = 32768;
enum ReadMode {
MODE_METADATA,
MODE_PLAYBACK

Просмотреть файл

@ -62,12 +62,10 @@ 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),
, mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
/* aSupportsTailDispatch = */ true))
, mWatchManager(this, mTaskQueue)
, mTimer(new MediaTimer())
@ -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
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();

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше