Merge m-c to b2g-i on a CLOSED TREE

This commit is contained in:
Carsten "Tomcat" Book 2015-10-05 13:55:18 +02:00
commit bc7e34a613
465 changed files with 18353 additions and 13558 deletions

View File

@ -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 1205242 - Changed toolchain, needs clobber
Bug 1182727 - Changed toolchain, needs clobber again :(

View File

@ -1115,6 +1115,10 @@ GetInterfacesForProxy(ProxyAccessible* aProxy, uint32_t aInterfaces)
if (aInterfaces & Interfaces::DOCUMENT)
interfaces |= 1 << MAI_INTERFACE_DOCUMENT;
if (aInterfaces & Interfaces::SELECTION) {
interfaces |= 1 << MAI_INTERFACE_SELECTION;
}
return interfaces;
}

View File

@ -43,6 +43,10 @@ InterfacesFor(Accessible* aAcc)
if (aAcc->IsDoc())
interfaces |= Interfaces::DOCUMENT;
if (aAcc->IsSelect()) {
interfaces |= Interfaces::SELECTION;
}
return interfaces;
}

View File

@ -1000,6 +1000,14 @@ ProxyAccessible::IndexOfEmbeddedChild(const ProxyAccessible* aChild)
ProxyAccessible*
ProxyAccessible::EmbeddedChildAt(size_t aChildIdx)
{
// For an outer doc the only child is a document, which is of course an
// embedded child. Further asking the child process for the id of the child
// document won't work because the id of the child doc will be 0, which we
// would interpret as being our parent document.
if (mOuterDoc) {
return ChildAt(aChildIdx);
}
uint64_t childID;
unused << mDoc->SendEmbeddedChildAt(mID, aChildIdx, &childID);
return mDoc->GetAccessible(childID);

View File

@ -391,6 +391,7 @@ enum Interfaces
TABLE = 16,
TABLECELL = 32,
DOCUMENT = 64,
SELECTION = 128,
};
}

View File

@ -144,7 +144,7 @@ skip-if = true
[test-timer.js]
[test-traceback.js]
[test-ui-action-button.js]
skip-if = (os == "linux" || os == "win") && debug
skip-if = debug || asan # Bug 1208727
[test-ui-frame.js]
[test-ui-id.js]
[test-ui-sidebar-private-browsing.js]

View File

@ -1066,19 +1066,6 @@ pref("dom.wakelock.enabled", true);
// Enable webapps add-ons
pref("dom.apps.customization.enabled", true);
#ifdef MOZ_GRAPHENE
// Enable touch caret by default
pref("touchcaret.enabled", true);
// Enable selection caret by default
pref("selectioncaret.enabled", true);
#else
// Original caret implementation on collapsed selection.
pref("touchcaret.enabled", false);
// Original caret implementation on non-collapsed selection.
pref("selectioncaret.enabled", false);
#endif
// New implementation to unify touch-caret and selection-carets.
pref("layout.accessiblecaret.enabled", true);

View File

@ -666,7 +666,7 @@ var shell = {
let manifestURI = Services.io.newURI(manifest, null, documentURI);
let updateService = Cc['@mozilla.org/offlinecacheupdate-service;1']
.getService(Ci.nsIOfflineCacheUpdateService);
updateService.scheduleUpdate(manifestURI, documentURI, window);
updateService.scheduleUpdate(manifestURI, documentURI, principal, window);
} catch (e) {
dump('Error while creating offline cache: ' + e + '\n');
}

View File

@ -27,10 +27,8 @@ pref("browser.autofocus", true);
pref("layers.async-pan-zoom.enabled", false);
pref("network.predictor.enabled", true);
// Not touch caret
// No AccessibleCaret
pref("layout.accessiblecaret.enabled", false);
pref("selectioncaret.enabled", false);
pref("touchcaret.enabled", false);
pref("gfx.vsync.hw-vsync.enabled", true);
pref("gfx.vsync.compositor", true);

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1443217898000">
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1443743586000">
<emItems>
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
<versionRange minVersion="0" maxVersion="*">
@ -1282,6 +1282,12 @@
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i1030" id="support@todoist.com">
<versionRange minVersion="0" maxVersion="3.9" severity="1">
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i65" id="activity@facebook.com">
<versionRange minVersion="0" maxVersion="*">
@ -1810,6 +1816,12 @@
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i1022" id="g99hiaoekjoasiijdkoleabsy278djasi@jetpack">
<versionRange minVersion="0" maxVersion="*" severity="3">
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i746" id="{58d2a791-6199-482f-a9aa-9b725ec61362}">
<versionRange minVersion="0" maxVersion="*" severity="1">
@ -2085,6 +2097,16 @@
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i1024" id="{458fb825-2370-4973-bf66-9d7142141847}">
<versionRange minVersion="0" maxVersion="*" severity="1">
</versionRange>
<prefs>
<pref>app.update.auto</pref>
<pref>app.update.enabled</pref>
<pref>app.update.interval</pref>
<pref>app.update.url</pref>
</prefs>
</emItem>
<emItem blockID="i476" id="mbroctone@facebook.com">
<versionRange minVersion="0" maxVersion="*" severity="3">
@ -3205,6 +3227,14 @@
<match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="13.0" maxVersion="13.*" severity="0" vulnerabilitystatus="1"></versionRange>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
</pluginItem>
<pluginItem blockID="p1026">
<match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="18.0.0.204" maxVersion="18.0.0.232" severity="0" vulnerabilitystatus="1"></versionRange>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
</pluginItem>
<pluginItem os="Linux" blockID="p1028">
<match name="filename" exp="libflashplayer\.so" /> <versionRange minVersion="11.2.202.482" maxVersion="11.2.202.508" severity="0" vulnerabilitystatus="1"></versionRange>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
</pluginItem>
</pluginItems>
<gfxItems>

View File

@ -29,9 +29,8 @@ FINAL_TARGET_FILES.defaults.profile += ['profile/prefs.js']
DEFINES['APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
for var in ('MOZILLA_OFFICIAL', 'LIBXUL_SDK'):
if CONFIG[var]:
DEFINES[var] = True
if CONFIG['LIBXUL_SDK']:
DEFINES['LIBXUL_SDK'] = True
GENERATED_INCLUDES += [
'/build',

View File

@ -21,7 +21,7 @@ const DATABASE_NAME = "abouthome";
const DATABASE_VERSION = 1;
const DATABASE_STORAGE = "persistent";
const SNIPPETS_OBJECTSTORE_NAME = "snippets";
var searchText, findKey;
var searchText;
// This global tracks if the page has been set up before, to prevent double inits
var gInitialized = false;
@ -55,14 +55,20 @@ window.addEventListener("pagehide", function() {
window.removeEventListener("resize", fitToWidth);
});
// make Accel+f focus the search box
window.addEventListener("keypress", ev => {
// Make Ctrl/Cmd+f focus the search box.
let modifiers = ev.ctrlKey + ev.altKey + ev.shiftKey + ev.metaKey;
if (ev.getModifierState("Accel") && modifiers == 1 && ev.key == findKey) {
searchText.focus();
ev.preventDefault();
}
// focus the search-box on keypress
if (document.activeElement.id == "searchText") // unless already focussed
return;
let modifiers = ev.ctrlKey + ev.altKey + ev.metaKey;
// ignore Ctrl/Cmd/Alt, but not Shift
// also ignore Tab, Insert, PageUp, etc., and Space
if (modifiers != 0 || ev.charCode == 0 || ev.charCode == 32)
return;
searchText.focus();
// need to send the first keypress outside the search-box manually to it
searchText.value += ev.key;
});
// This object has the same interface as Map and is used to store and retrieve
@ -195,7 +201,6 @@ function setupSearch()
searchText.removeEventListener("blur", searchText_onBlur);
searchText.removeAttribute("autofocus");
});
findKey = searchText.dataset.findkey;
if (!gContentSearchController) {
gContentSearchController =

View File

@ -43,7 +43,7 @@
<div id="searchIcon"/>
<input type="text" name="q" value="" id="searchText" maxlength="256"
aria-label="&contentSearchInput.label;" autofocus="autofocus"
dir="auto" data-findkey="&find.commandkey;"/>
dir="auto"/>
<input id="searchSubmit" type="button" value="" onclick="onSearchSubmit(event)"
aria-label="&contentSearchSubmit.label;"/>
</div>

View File

@ -6287,7 +6287,8 @@ var OfflineApps = {
var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
getService(Ci.nsIOfflineCacheUpdateService);
updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject, window);
updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject,
aDocument.nodePrincipal, window);
},
/////////////////////////////////////////////////////////////////////////////

View File

@ -420,7 +420,7 @@ var gTests = [
}
},
{
desc: "Cmd+f should focus the search box in the page",
desc: "Pressing any key should focus the search box in the page, and send the key to it",
setup: function () {},
run: Task.async(function* () {
let doc = gBrowser.selectedBrowser.contentDocument;
@ -430,9 +430,10 @@ var gTests = [
EventUtils.synthesizeMouseAtCenter(logo, {});
isnot(searchInput, doc.activeElement, "Search input should not be the active element.");
EventUtils.synthesizeKey("f", { accelKey: true });
EventUtils.synthesizeKey("a", {});
yield promiseWaitForCondition(() => doc.activeElement === searchInput);
is(searchInput, doc.activeElement, "Search input should be the active element.");
is(searchInput.value, "a", "Search input should be 'a'.");
})
},
{
@ -491,6 +492,25 @@ var gTests = [
is(result.pane, "paneSync", "openPreferences should be called with paneSync");
is(result.params.urlParams.entrypoint, "abouthome", "openPreferences should be called with abouthome entrypoint");
})
},
{
desc: "Pressing Space while the Addons button is focussed should activate it",
setup: function () {},
run: Task.async(function* () {
// Skip this test on Mac, because Space doesn't activate the button there.
if (navigator.platform.indexOf("Mac") == 0) {
return Promise.resolve();
}
info("Waiting for about:addons tab to open...");
let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons");
let addOnsButton = gBrowser.selectedBrowser.contentDocument.getElementById("addons");
addOnsButton.focus();
EventUtils.synthesizeKey(" ", {});
let tab = yield promiseTabOpened;
is(tab.linkedBrowser.currentURI.spec, "about:addons", "Should have seen the about:addons tab");
yield BrowserTestUtils.removeTab(tab);
})
}
];

View File

@ -26,6 +26,7 @@ gDirectorySource = "data:application/json," + JSON.stringify({
function runTests() {
requestLongerTimeout(4);
let origEnhanced = NewTabUtils.allPages.enhanced;
let origCompareLinks = NewTabUtils.links.compareLinks;
registerCleanupFunction(() => {

View File

@ -757,9 +757,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
}
.context-url-view-wrapper {
/* 18px for indent of .text-chat-arrow, 1px for border of .text-chat-entry > p,
0.5rem for padding of .text-chat-entry > p */
padding: calc(18px - 1px - 0.5rem);
padding: 12px;
margin-bottom: 0.5em;
background-color: #dbf7ff;
}
@ -1182,32 +1180,44 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
background: white;
}
.text-chat-box {
flex: 0 0 auto;
max-height: 40px;
min-height: 40px;
width: 100%;
}
.text-chat-entries {
overflow: auto;
}
.text-chat-entry {
.text-chat-entry,
.text-chat-header {
display: flex;
flex-direction: row;
margin-right: .2em;
margin-bottom: .5em;
text-align: start;
flex-wrap: nowrap;
justify-content: flex-start;
align-content: stretch;
align-items: flex-start;
}
html[dir="rtl"] .text-chat-entry {
margin-right: auto;
margin-left: .2em;
.text-chat-entry {
/* aligns paragraph to side where reading starts from */
text-align: start;
}
/* Sent text chat entries should be on the right */
.text-chat-entry.sent {
/* aligns paragraph to right side */
justify-content: flex-end;
margin-left: 0;
margin-right: 5px;
}
.text-chat-entry.received {
margin-left: 4px;
margin-right: 0;
}
html[dir="rtl"] .text-chat-entry.sent {
margin-left: 5px;
margin-right: 0;
}
html[dir="rtl"] .text-chat-entry.received {
margin-left: 0;
margin-right: 5px;
}
/* If you change this entry, check it doesn't affect the "special" text
@ -1217,57 +1227,34 @@ html[dir="rtl"] .text-chat-entry {
z-index: 10;
/* Drop the default margins from the 'p' element. */
margin: 0;
padding: .5rem;
padding: .8rem;
/* leave some room for the chat bubble arrow */
max-width: 80%;
border-width: 1px;
border-style: solid;
border-color: #5cccee;
max-width: 70%;
border-radius: 15px;
border: 1px solid #5cccee;
background: #fff;
word-wrap: break-word;
flex: 0 1 auto;
align-self: auto;
}
.text-chat-entry.sent > p,
.text-chat-entry.received > p {
background: #fff;
order: 1;
}
.text-chat-entry.sent > p {
border-radius: 15px;
border-bottom-right-radius: 0;
}
.text-chat-entry.received > p {
border-radius: 15px;
border-top-left-radius: 0;
border-color: #d8d8d8;
}
html[dir="rtl"] .text-chat-entry.sent > p {
border-radius: 15px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 15px;
}
html[dir="rtl"] .text-chat-entry.received > p {
border-radius: 15px;
border-top-right-radius: 0;
}
.text-chat-entry.received > p {
order: 1;
}
.text-chat-entry.sent > p {
order: 1;
}
.text-chat-entry.received {
text-align: start;
}
.text-chat-entry.received > p {
border-color: #d8d8d8;
border-top-left-radius: 15px;
}
/* Text chat entry timestamp */
@ -1280,38 +1267,34 @@ html[dir="rtl"] .text-chat-entry.received > p {
align-self: center;
}
/* Sent text chat entries should be on the right */
.text-chat-entry.sent {
justify-content: flex-end;
}
.received > .text-chat-entry-timestamp {
order: 2;
}
/* Pseudo element used to cover part between chat bubble and chat arrow. */
/* Pseudo element used to cover part between chat bubble and chat arrow.
dimensions may change for each position */
.text-chat-entry > p:after {
position: absolute;
background: #fff;
content: "";
/* default dimensions */
width: 6px;
height: 7px;
}
.text-chat-entry.sent > p:after {
right: -2px;
right: -1px;
bottom: 0;
width: 15px;
height: 9px;
border-top-left-radius: 15px;
border-top-right-radius: 22px;
width: 7px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.text-chat-entry.received > p:after {
top: 0;
left: -2px;
width: 15px;
height: 9px;
border-bottom-left-radius: 22px;
border-bottom-right-radius: 15px;
left: -1px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
html[dir="rtl"] .text-chat-entry.sent > p:after {
@ -1319,16 +1302,13 @@ html[dir="rtl"] .text-chat-entry.sent > p:after {
right: auto;
left: -1px;
bottom: 0;
width: 15px;
height: 9px;
}
html[dir="rtl"] .text-chat-entry.received > p:after {
/* Reset */
left: auto;
top: 0;
right: -1px;
width: 15px;
width: 9px;
height: 6px;
}
@ -1374,7 +1354,7 @@ html[dir="rtl"] .text-chat-entry.received .text-chat-arrow {
margin-left: -10px;
}
.text-chat-entry.special.room-name {
.text-chat-header.special.room-name {
color: black;
font-weight: bold;
text-align: start;
@ -1383,7 +1363,7 @@ html[dir="rtl"] .text-chat-entry.received .text-chat-arrow {
margin-right: 0;
}
.text-chat-entry.special.room-name p {
.text-chat-header.special.room-name p {
background: #dbf7ff;
max-width: 100%;
/* 18px for indent of .text-chat-arrow, 1px for border of .text-chat-entry > p,
@ -1391,12 +1371,16 @@ html[dir="rtl"] .text-chat-entry.received .text-chat-arrow {
padding: calc(18px - 1px - 0.5rem);
}
.text-chat-entry.special > p {
.text-chat-header.special > p {
border: none;
}
.text-chat-box {
margin: auto;
flex: 0 0 auto;
max-height: 40px;
min-height: 40px;
width: 100%;
}
.text-chat-box > form > input {

View File

@ -81,7 +81,7 @@ loop.shared.views.chat = (function(mozL10n) {
render: function() {
return (
React.createElement("div", {className: "text-chat-entry special room-name"},
React.createElement("div", {className: "text-chat-header special room-name"},
React.createElement("p", null, mozL10n.get("rooms_welcome_title", {conversationName: this.props.message}))
)
);

View File

@ -81,7 +81,7 @@ loop.shared.views.chat = (function(mozL10n) {
render: function() {
return (
<div className="text-chat-entry special room-name">
<div className="text-chat-header special room-name">
<p>{mozL10n.get("rooms_welcome_title", {conversationName: this.props.message})}</p>
</div>
);

View File

@ -580,7 +580,7 @@ describe("loop.shared.views.TextChatView", function () {
var node = view.getDOMNode();
expect(node.querySelector(".text-chat-entries")).to.not.eql(null);
var entries = node.querySelectorAll(".text-chat-entry");
var entries = node.querySelectorAll(".text-chat-header");
expect(entries.length).eql(1);
expect(entries[0].classList.contains("special")).eql(true);
expect(entries[0].classList.contains("room-name")).eql(true);

View File

@ -32,6 +32,8 @@ var gContentPane = {
setEventListener("font.language.group", "change",
gContentPane._rebuildFonts);
setEventListener("notificationsPolicyButton", "command",
gContentPane.showNotificationExceptions);
setEventListener("popupPolicyButton", "command",
gContentPane.showPopupExceptions);
setEventListener("advancedFonts", "command",
@ -83,6 +85,28 @@ var gContentPane = {
* - true if popups are blocked by default, false otherwise
*/
// NOTIFICATIONS
/**
* Displays the notifications exceptions dialog where specific site notification
* preferences can be set.
*/
showNotificationExceptions()
{
let bundlePreferences = document.getElementById("bundlePreferences");
let params = { blockVisible: true,
sessionVisible: false,
allowVisible: true,
prefilledHost: "",
permissionType: "desktop-notification" };
params.windowTitle = bundlePreferences.getString("notificationspermissionstitle");
params.introText = bundlePreferences.getString("notificationspermissionstext");
gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
"resizable=yes", params);
},
// POP-UPS
/**

View File

@ -60,6 +60,27 @@
</grid>
</groupbox>
<groupbox id="notificationsGroup" data-category="paneContent" hidden="true">
<caption><label>&notificationsPolicy.label;</label></caption>
<grid>
<columns>
<column flex="1"/>
<column/>
</columns>
<rows>
<row id="notificationsPolicyRow">
<vbox align="start">
<label id="notificationsPolicy">&notificationsPolicyDesc.label;</label>
</vbox>
<hbox pack="end">
<button id="notificationsPolicyButton" label="&notificationsPolicyButton.label;"
accesskey="&notificationsPolicyButton.accesskey;"/>
</hbox>
</row>
</rows>
</grid>
</groupbox>
<groupbox id="miscGroup" data-category="paneContent" hidden="true">
<caption><label>&popups.label;</label></caption>
<grid id="contentGrid">

View File

@ -6,34 +6,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
function whenNewWindowLoaded(aOptions, aCallback) {
let win = OpenBrowserWindow(aOptions);
let gotLoad = false;
let gotActivate = Services.focus.activeWindow == win;
function maybeRunCallback() {
if (gotLoad && gotActivate) {
executeSoon(function() { aCallback(win); });
}
}
if (!gotActivate) {
win.addEventListener("activate", function onActivate() {
info("Got activate.");
win.removeEventListener("activate", onActivate, false);
gotActivate = true;
maybeRunCallback();
}, false);
} else {
info("Was activated.");
}
Services.obs.addObserver(function observer(aSubject, aTopic) {
if (win == aSubject) {
info("Delayed startup finished");
Services.obs.removeObserver(observer, aTopic);
gotLoad = true;
maybeRunCallback();
}
}, "browser-delayed-startup-finished", false);
let focused = SimpleTest.promiseFocus(win);
let startupFinished = TestUtils.topicObserved("browser-delayed-startup-finished",
subject => subject == win).then(() => win);
Promise.all([focused, startupFinished])
.then(results => executeSoon(() => aCallback(results[1])));
return win;
}

View File

@ -37,7 +37,3 @@
<!ENTITY abouthome.appsButton2.label "Apps">
<!ENTITY abouthome.downloadsButton.label "Downloads">
<!ENTITY abouthome.syncButton.label "&syncBrand.shortName.label;">
<!-- LOCALIZATION NOTE (find.commandkey): This is the key to use in
conjunction with accel (Command on Mac or Ctrl on other platforms) to find -->
<!ENTITY find.commandkey "f">

View File

@ -6,6 +6,12 @@
<!ENTITY blockPopups.label "Block pop-up windows">
<!ENTITY blockPopups.accesskey "B">
<!ENTITY notificationsPolicy.label "Notifications">
<!ENTITY notificationsPolicyDesc.label "Choose which sites are allowed to show notifications">
<!ENTITY notificationsPolicyButton.accesskey "h">
<!ENTITY notificationsPolicyButton.label "Choose…">
<!ENTITY popupExceptions.label "Exceptions…">
<!ENTITY popupExceptions.accesskey "E">

View File

@ -25,6 +25,8 @@ addonspermissionstext=You can specify which websites are allowed to install add-
addons_permissions_title=Allowed Sites - Add-ons Installation
popuppermissionstext=You can specify which websites are allowed to open pop-up windows. Type the exact address of the site you want to allow and then click Allow.
popuppermissionstitle=Allowed Sites - Pop-ups
notificationspermissionstext=You can specify which websites are always or never allowed to show notifications. Type the exact address of the site you want to manage and then click Block or Allow.
notificationspermissionstitle=Notification Permissions
invalidURI=Please enter a valid hostname
invalidURITitle=Invalid Hostname Entered

View File

@ -62,6 +62,3 @@ if CONFIG['NIGHTLY_BUILD']:
EXTRA_PP_COMPONENTS += [
'browsermodules.manifest',
]
if CONFIG['MOZILLA_OFFICIAL']:
DEFINES['MOZILLA_OFFICIAL'] = 1

View File

@ -103,6 +103,11 @@ private:
virtual void run(const MatchFinder::MatchResult &Result);
};
class RefCountedCopyConstructorChecker : public MatchFinder::MatchCallback {
public:
virtual void run(const MatchFinder::MatchResult &Result);
};
ScopeChecker scopeChecker;
ArithmeticArgChecker arithmeticArgChecker;
TrivialCtorDtorChecker trivialCtorDtorChecker;
@ -116,6 +121,7 @@ private:
ExplicitImplicitChecker explicitImplicitChecker;
NoAutoTypeChecker noAutoTypeChecker;
NoExplicitMoveConstructorChecker noExplicitMoveConstructorChecker;
RefCountedCopyConstructorChecker refCountedCopyConstructorChecker;
MatchFinder astMatcher;
};
@ -615,8 +621,6 @@ AST_MATCHER(MemberExpr, isAddRefOrRelease) {
}
/// This matcher will select classes which are refcounted.
AST_MATCHER(QualType, isRefCounted) { return isClassRefCounted(Node); }
AST_MATCHER(CXXRecordDecl, hasRefCntMember) {
return isClassRefCounted(&Node) && getClassRefCntMember(&Node);
}
@ -672,6 +676,10 @@ AST_MATCHER(QualType, autoNonAutoableType) {
AST_MATCHER(CXXConstructorDecl, isExplicitMoveConstructor) {
return Node.isExplicit() && Node.isMoveConstructor();
}
AST_MATCHER(CXXConstructorDecl, isCompilerProvidedCopyConstructor) {
return !Node.isUserProvided() && Node.isCopyConstructor();
}
}
}
@ -950,6 +958,12 @@ DiagnosticsMatcher::DiagnosticsMatcher() {
astMatcher.addMatcher(constructorDecl(isExplicitMoveConstructor()).bind("node"),
&noExplicitMoveConstructorChecker);
astMatcher.addMatcher(constructExpr(hasDeclaration(
constructorDecl(
isCompilerProvidedCopyConstructor(),
ofClass(hasRefCntMember())))).bind("node"),
&refCountedCopyConstructorChecker);
}
// These enum variants determine whether an allocation has occured in the code.
@ -1374,6 +1388,28 @@ void DiagnosticsMatcher::NoExplicitMoveConstructorChecker::run(
Diag.Report(D->getLocation(), ErrorID);
}
void DiagnosticsMatcher::RefCountedCopyConstructorChecker::run(
const MatchFinder::MatchResult &Result) {
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "Invalid use of compiler-provided copy constructor "
"on refcounted type");
unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "The default copy constructor also copies the "
"default mRefCnt property, leading to reference "
"count imbalance issues. Please provide your own "
"copy constructor which only copies the fields which "
"need to be copied");
// Everything we needed to know was checked in the matcher - we just report
// the error here
const CXXConstructExpr *E =
Result.Nodes.getNodeAs<CXXConstructExpr>("node");
Diag.Report(E->getLocation(), ErrorID);
Diag.Report(E->getLocation(), NoteID);
}
class MozCheckAction : public PluginASTAction {
public:
ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI,

View File

@ -4,7 +4,7 @@
# Build without any warning flags, and with clang verify flag for a
# syntax-only build (no codegen), without a limit on the number of errors.
OS_CFLAGS := $(filter-out -W%,$(OS_CFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0
OS_CFLAGS := $(filter-out -W%,$(OS_CFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0 -std=c11
OS_CXXFLAGS := $(filter-out -W%,$(OS_CXXFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,25 @@
// Implicit copy construct which is unused
class RC1 {
void AddRef();
void Release();
int mRefCnt;
};
// Explicit copy constructor which is used
class RC2 {
public:
RC2();
RC2(const RC2&);
private:
void AddRef();
void Release();
int mRefCnt;
};
void f() {
RC1* r1 = new RC1();
RC1* r1p = new RC1(*r1); // expected-error {{Invalid use of compiler-provided copy constructor on refcounted type}} expected-note {{The default copy constructor also copies the default mRefCnt property, leading to reference count imbalance issues. Please provide your own copy constructor which only copies the fields which need to be copied}}
RC2* r2 = new RC2();
RC2* r2p = new RC2(*r2);
}

View File

@ -26,6 +26,7 @@ SOURCES += [
'TestNonMemMovable.cpp',
'TestNonTemporaryClass.cpp',
'TestNoRefcountedInsideLambdas.cpp',
'TestRefCountedCopyConstructor.cpp',
'TestStackClass.cpp',
'TestTrivialCtorDtor.cpp',
]

View File

@ -0,0 +1,34 @@
build-clang.py
==============
A script to build clang from source.
```
usage: build-clang.py [-h] -c CONFIG [--clean]
optional arguments:
-h, --help show this help message and exit
-c CONFIG, --config CONFIG
Clang configuration file
--clean Clean the build directory
```
Config file format
------------------
build-clang.py accepts a JSON config format with the following fields:
* llvm_revision: The LLVM SVN revision to build.
* stages: Use 1, 2, or 3 to select different compiler stages. The default is 3.
* llvm_repo: SVN path to the LLVM repo.
* clang_repo: SVN path to the Clang repo.
* compiler_repo: SVN path to the compiler-rt repo.
* libcxx_repo: SVN path to the libcxx repo.
* python_path: Path to the Python 2.7 installation on the machine building clang.
* gcc_dir: Path to the gcc toolchain installation, only required on Linux.
* cc: Path to the bootsraping C Compiler.
* cxx: Path to the bootsraping C++ Compiler.
* patches: Optional list of patches to apply per platform. Supported platforms: macosx64, linux32, linux64. The default is Release.
* build_type: The type of build to make. Supported types: Release, Debug, RelWithDebInfo or MinSizeRel.
* build_libcxx: Whether to build with libcxx. The default is false.
* assertions: Whether to enable LLVM assertions. The default is false.

View File

@ -14,19 +14,27 @@ import tempfile
import glob
import errno
from contextlib import contextmanager
import sys
centOS6 = False
DEBUG = os.getenv("DEBUG")
def check_run(args):
global DEBUG
if DEBUG:
print >> sys.stderr, ' '.join(args)
r = subprocess.call(args)
assert r == 0
def run_in(path, args):
d = os.getcwd()
global DEBUG
if DEBUG:
print >> sys.stderr, 'cd "%s"' % path
os.chdir(path)
check_run(args)
if DEBUG:
print >> sys.stderr, 'cd "%s"' % d
os.chdir(d)
@ -36,14 +44,12 @@ def patch(patch, srcdir):
'-s'])
def build_package(package_source_dir, package_build_dir, configure_args,
make_args):
def build_package(package_build_dir, run_cmake, cmake_args):
if not os.path.exists(package_build_dir):
os.mkdir(package_build_dir)
run_in(package_build_dir,
["%s/configure" % package_source_dir] + configure_args)
run_in(package_build_dir, ["make", "-j4"] + make_args)
run_in(package_build_dir, ["make", "install"])
if run_cmake:
run_in(package_build_dir, ["cmake"] + cmake_args)
run_in(package_build_dir, ["ninja", "install"])
@contextmanager
@ -57,7 +63,9 @@ def updated_env(env):
def build_tar_package(tar, name, base, directory):
name = os.path.realpath(name)
run_in(base, [tar, "-cJf", name, directory])
run_in(base, [tar,
"-c -%s -f" % "J" if ".xz" in name else "j",
name, directory])
def copy_dir_contents(src, dest):
@ -115,9 +123,15 @@ def svn_co(url, directory, revision):
check_run(["svn", "co", "-r", revision, url, directory])
def build_one_stage(env, stage_dir, llvm_source_dir):
def svn_update(directory, revision):
run_in(directory, ["svn", "update", "-r", revision])
def build_one_stage(env, src_dir, stage_dir, build_libcxx, build_type, assertions,
python_path):
with updated_env(env):
build_one_stage_aux(stage_dir, llvm_source_dir)
build_one_stage_aux(src_dir, stage_dir, build_libcxx, build_type,
assertions, python_path)
def get_platform():
@ -137,34 +151,27 @@ def is_darwin():
return platform.system() == "Darwin"
def build_one_stage_aux(stage_dir, llvm_source_dir):
os.mkdir(stage_dir)
def build_one_stage_aux(src_dir, stage_dir, build_libcxx,
build_type, assertions, python_path):
if not os.path.exists(stage_dir):
os.mkdir(stage_dir)
build_dir = stage_dir + "/build"
inst_dir = stage_dir + "/clang"
targets = ["x86", "x86_64"]
# The Darwin equivalents of binutils appear to have intermittent problems
# with objects in compiler-rt that are compiled for arm. Since the arm
# support is only necessary for iOS (which we don't support), only enable
# arm support on Linux.
if not is_darwin():
targets.append("arm")
run_cmake = True
if os.path.exists(build_dir):
run_cmake = False
global centOS6
if centOS6:
python_path = "/usr/bin/python2.7"
else:
python_path = "/usr/local/bin/python2.7"
configure_opts = ["--enable-optimized",
"--enable-targets=" + ",".join(targets),
"--disable-assertions",
"--disable-libedit",
"--with-python=%s" % python_path,
"--prefix=%s" % inst_dir,
"--disable-compiler-version-checks"]
build_package(llvm_source_dir, build_dir, configure_opts, [])
cmake_args = ["-GNinja",
"-DCMAKE_BUILD_TYPE=%s" % build_type,
"-DLLVM_TARGETS_TO_BUILD=X86;ARM",
"-DLLVM_ENABLE_ASSERTIONS=%s" % ("ON" if assertions else "OFF"),
"-DPYTHON_EXECUTABLE=%s" % python_path,
"-DCMAKE_INSTALL_PREFIX=%s" % inst_dir,
"-DLLVM_TOOL_LIBCXX_BUILD=%s" % ("ON" if build_libcxx else "OFF"),
src_dir];
build_package(build_dir, run_cmake, cmake_args)
if __name__ == "__main__":
# The directories end up in the debug info, so the easy way of getting
@ -181,12 +188,6 @@ if __name__ == "__main__":
compiler_rt_source_dir = source_dir + "/compiler-rt"
libcxx_source_dir = source_dir + "/libcxx"
global centOS6
if centOS6:
gcc_dir = "/home/worker/workspace/build/src/gcc"
else:
gcc_dir = "/tools/gcc-4.7.3-0moz1"
if is_darwin():
os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.7'
@ -194,14 +195,67 @@ if __name__ == "__main__":
parser.add_argument('-c', '--config', required=True,
type=argparse.FileType('r'),
help="Clang configuration file")
parser.add_argument('--clean', required=False,
action='store_true',
help="Clean the build directory")
args = parser.parse_args()
config = json.load(args.config)
if args.clean:
shutil.rmtree(build_dir)
os.sys.exit(0)
llvm_revision = config["llvm_revision"]
llvm_repo = config["llvm_repo"]
clang_repo = config["clang_repo"]
compiler_repo = config["compiler_repo"]
libcxx_repo = config["libcxx_repo"]
stages = 3
if "stages" in config:
stages = int(config["stages"])
if stages not in (1, 2, 3):
raise ValueError("We only know how to build 1, 2, or 3 stages")
build_type = "Release"
if "build_type" in config:
build_type = config["build_type"]
if build_type not in ("Release", "Debug", "RelWithDebInfo", "MinSizeRel"):
raise ValueError("We only know how to do Release, Debug, RelWithDebInfo or MinSizeRel builds")
build_libcxx = False
if "build_libcxx" in config:
build_libcxx = config["build_libcxx"]
if build_libcxx not in (True, False):
raise ValueError("Only boolean values are accepted for build_libcxx.")
assertions = False
if "assertions" in config:
assertions = config["assertions"]
if assertions not in (True, False):
raise ValueError("Only boolean values are accepted for assertions.")
python_path = None
if "python_path" not in config:
raise ValueError("Config file needs to set python_path")
python_path = config["python_path"]
gcc_dir = None
if "gcc_dir" in config:
gcc_dir = config["gcc_dir"]
if not os.path.exists(gcc_dir):
raise ValueError("gcc_dir must point to an existing path")
if not is_darwin() and gcc_dir is None:
raise ValueError("Config file needs to set gcc_dir")
cc = None
if "cc" in config:
cc = config["cc"]
if not os.path.exists(cc):
raise ValueError("cc must point to an existing path")
else:
raise ValueError("Config file needs to set cc")
cxx = None
if "cxx" in config:
cxx = config["cxx"]
if not os.path.exists(cxx):
raise ValueError("cxx must point to an existing path")
else:
raise ValueError("Config file needs to set cxx")
if not os.path.exists(source_dir):
os.makedirs(source_dir)
@ -216,50 +270,69 @@ if __name__ == "__main__":
llvm_source_dir + "/projects/libcxx")
for p in config.get("patches", {}).get(get_platform(), []):
patch(p, source_dir)
else:
svn_update(llvm_source_dir, llvm_revision)
svn_update(clang_source_dir, llvm_revision)
svn_update(compiler_rt_source_dir, llvm_revision)
svn_update(libcxx_source_dir, llvm_revision)
if os.path.exists(build_dir):
shutil.rmtree(build_dir)
os.makedirs(build_dir)
if not os.path.exists(build_dir):
os.makedirs(build_dir)
stage1_dir = build_dir + '/stage1'
stage1_inst_dir = stage1_dir + '/clang'
final_stage_dir = stage1_dir
if is_darwin():
extra_cflags = ""
extra_cxxflags = "-stdlib=libc++"
extra_cflags2 = ""
extra_cxxflags2 = "-stdlib=libc++"
cc = "/usr/bin/clang"
cxx = "/usr/bin/clang++"
else:
extra_cflags = ""
extra_cxxflags = ""
extra_cflags2 = "-static-libgcc --gcc-toolchain=%s" % gcc_dir
extra_cxxflags2 = "-static-libgcc -static-libstdc++ --gcc-toolchain=%s" % gcc_dir
cc = gcc_dir + "/bin/gcc"
cxx = gcc_dir + "/bin/g++"
extra_cflags = "-static-libgcc"
extra_cxxflags = "-static-libgcc -static-libstdc++"
extra_cflags2 = "-fPIC --gcc-toolchain=%s" % gcc_dir
extra_cxxflags2 = "-fPIC --gcc-toolchain=%s" % gcc_dir
if os.environ.has_key('LD_LIBRARY_PATH'):
os.environ['LD_LIBRARY_PATH'] = '%s/lib64/:%s' % (gcc_dir, os.environ['LD_LIBRARY_PATH']);
else:
os.environ['LD_LIBRARY_PATH'] = '%s/lib64/' % gcc_dir
if os.environ.has_key('LD_LIBRARY_PATH'):
os.environ['LD_LIBRARY_PATH'] = '%s/lib64/:%s' % (gcc_dir, os.environ['LD_LIBRARY_PATH']);
else:
os.environ['LD_LIBRARY_PATH'] = '%s/lib64/' % gcc_dir
build_one_stage(
{"CC": cc + " %s" % extra_cflags,
"CXX": cxx + " %s" % extra_cxxflags},
stage1_dir, llvm_source_dir)
llvm_source_dir, stage1_dir, build_libcxx,
build_type, assertions, python_path)
stage2_dir = build_dir + '/stage2'
build_one_stage(
{"CC": stage1_inst_dir + "/bin/clang %s" % extra_cflags2,
"CXX": stage1_inst_dir + "/bin/clang++ %s" % extra_cxxflags2},
stage2_dir, llvm_source_dir)
if stages > 1:
stage2_dir = build_dir + '/stage2'
stage2_inst_dir = stage2_dir + '/clang'
final_stage_dir = stage2_dir
build_one_stage(
{"CC": stage1_inst_dir + "/bin/clang %s" % extra_cflags2,
"CXX": stage1_inst_dir + "/bin/clang++ %s" % extra_cxxflags2},
llvm_source_dir, stage2_dir, build_libcxx,
build_type, assertions, python_path)
if stages > 2:
stage3_dir = build_dir + '/stage3'
final_stage_dir = stage3_dir
build_one_stage(
{"CC": stage2_inst_dir + "/bin/clang %s" % extra_cflags2,
"CXX": stage2_inst_dir + "/bin/clang++ %s" % extra_cxxflags2},
llvm_source_dir, stage3_dir, build_libcxx,
build_type, assertions, python_path)
if not is_darwin():
stage2_inst_dir = stage2_dir + '/clang'
final_stage_inst_dir = final_stage_dir + '/clang'
build_and_use_libgcc(
{"CC": cc + " %s" % extra_cflags,
"CXX": cxx + " %s" % extra_cxxflags},
stage2_inst_dir)
final_stage_inst_dir)
build_tar_package("tar", "clang.tar.xz", stage2_dir, "clang")
if is_darwin():
build_tar_package("tar", "clang.tar.bz2", final_stage_dir, "clang")
else:
build_tar_package("tar", "clang.tar.xz", final_stage_dir, "clang")

View File

@ -1,11 +0,0 @@
{
"llvm_revision": "183744",
"llvm_repo": "http://llvm.org/svn/llvm-project/llvm/branches/release_33",
"clang_repo": "http://llvm.org/svn/llvm-project/cfe/branches/release_33",
"compiler_repo": "http://llvm.org/svn/llvm-project/compiler-rt/branches/release_33",
"patches": {
"macosx64": ["llvm-debug-frame.patch"],
"linux64": ["llvm-debug-frame.patch", "no-sse-on-linux.patch"],
"linux32": ["llvm-debug-frame.patch", "no-sse-on-linux.patch"]
}
}

View File

@ -1,8 +0,0 @@
{
"llvm_revision": "241773",
"llvm_repo": "http://llvm.org/svn/llvm-project/llvm/branches/release_36",
"clang_repo": "http://llvm.org/svn/llvm-project/cfe/branches/release_36",
"compiler_repo": "http://llvm.org/svn/llvm-project/compiler-rt/branches/release_36",
"patches": {
}
}

View File

@ -0,0 +1,20 @@
{
"llvm_revision": "247539",
"stages": "3",
"build_libcxx": false,
"build_type": "Release",
"assertions": false,
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
"python_path": "/usr/bin/python2.7",
"gcc_dir": "/home/worker/workspace/build/src/gcc",
"cc": "/home/worker/workspace/build/src/gcc/bin/gcc",
"cxx": "/home/worker/workspace/build/src/gcc/bin/g++",
"patches": {
"macosx64": ["llvm-debug-frame.patch"],
"linux64": ["llvm-debug-frame.patch"],
"linux32": ["llvm-debug-frame.patch"]
}
}

View File

@ -0,0 +1,20 @@
{
"llvm_revision": "247539",
"stages": "3",
"build_libcxx": false,
"build_type": "Release",
"assertions": false,
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
"python_path": "/usr/local/bin/python2.7",
"gcc_dir": "/tools/gcc-4.7.3-0moz1",
"cc": "/home/worker/workspace/build/src/gcc/bin/gcc",
"cxx": "/home/worker/workspace/build/src/gcc/bin/g++",
"patches": {
"macosx64": ["llvm-debug-frame.patch"],
"linux64": ["llvm-debug-frame.patch"],
"linux32": ["llvm-debug-frame.patch"]
}
}

View File

@ -1,9 +1,16 @@
{
"llvm_revision": "247539",
"stages": "3",
"build_libcxx": true,
"build_type": "Release",
"assertions": false,
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
"python_path": "/usr/local/bin/python2.7",
"cc": "/usr/bin/clang",
"cxx": "/usr/bin/clang++",
"patches": {
"macosx64": ["llvm-debug-frame.patch"],
"linux64": ["llvm-debug-frame.patch"],

View File

@ -1,13 +0,0 @@
Index: lib/Driver/Tools.cpp
===================================================================
--- a/clang/lib/Driver/Tools.cpp (revision 181515)
+++ b/clang/lib/Driver/Tools.cpp (working copy)
@@ -1245,7 +1245,7 @@
// All x86 devices running Android have core2 as their common
// denominator. This makes a better choice than pentium4.
if (Triple.getEnvironment() == llvm::Triple::Android)
- return "core2";
+ return "686";
// Fallback to p4.
return "pentium4";

View File

@ -1,4 +0,0 @@
#!/bin/sh
rm -rf clang
tar -xjf clang.tar.bz2

View File

@ -269,6 +269,44 @@ BasePrincipal::SubsumesConsideringDomain(nsIPrincipal *aOther, bool *aResult)
return NS_OK;
}
NS_IMETHODIMP
BasePrincipal::CheckMayLoad(nsIURI* aURI, bool aReport, bool aAllowIfInheritsPrincipal)
{
// Check the internal method first, which allows us to quickly approve loads
// for the System Principal.
if (MayLoadInternal(aURI)) {
return NS_OK;
}
nsresult rv;
if (aAllowIfInheritsPrincipal) {
// If the caller specified to allow loads of URIs that inherit
// our principal, allow the load if this URI inherits its principal.
bool doesInheritSecurityContext;
rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
&doesInheritSecurityContext);
if (NS_SUCCEEDED(rv) && doesInheritSecurityContext) {
return NS_OK;
}
}
bool fetchableByAnyone;
rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_FETCHABLE_BY_ANYONE, &fetchableByAnyone);
if (NS_SUCCEEDED(rv) && fetchableByAnyone) {
return NS_OK;
}
if (aReport) {
nsCOMPtr<nsIURI> prinURI;
rv = GetURI(getter_AddRefs(prinURI));
if (NS_SUCCEEDED(rv) && prinURI) {
nsScriptSecurityManager::ReportError(nullptr, NS_LITERAL_STRING("CheckSameOriginError"), prinURI, aURI);
}
}
return NS_ERROR_DOM_BAD_URI;
}
NS_IMETHODIMP
BasePrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
{

View File

@ -18,6 +18,8 @@ class nsILoadContext;
class nsIObjectOutputStream;
class nsIObjectInputStream;
class nsExpandedPrincipal;
namespace mozilla {
class OriginAttributes : public dom::OriginAttributesDictionary
@ -141,6 +143,7 @@ public:
NS_IMETHOD EqualsConsideringDomain(nsIPrincipal* other, bool* _retval) final;
NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval) final;
NS_IMETHOD SubsumesConsideringDomain(nsIPrincipal* other, bool* _retval) final;
NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal) final;
NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
NS_IMETHOD GetCspJSON(nsAString& outCSPinJSON) override;
@ -174,6 +177,12 @@ protected:
virtual nsresult GetOriginInternal(nsACString& aOrigin) = 0;
virtual bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsider) = 0;
// Internal, side-effect-free check to determine whether the concrete
// principal would allow the load ignoring any common behavior implemented in
// BasePrincipal::CheckMayLoad.
virtual bool MayLoadInternal(nsIURI* aURI) = 0;
friend class ::nsExpandedPrincipal;
// Helper to check whether this principal is associated with an addon that
// allows unprivileged code to load aURI.
bool AddonAllowsLoad(nsIURI* aURI);

View File

@ -107,15 +107,9 @@ nsNullPrincipal::GetOriginInternal(nsACString& aOrigin)
return mURI->GetSpec(aOrigin);
}
NS_IMETHODIMP
nsNullPrincipal::CheckMayLoad(nsIURI* aURI, bool aReport, bool aAllowIfInheritsPrincipal)
{
if (aAllowIfInheritsPrincipal) {
if (nsPrincipal::IsPrincipalInherited(aURI)) {
return NS_OK;
}
}
bool
nsNullPrincipal::MayLoadInternal(nsIURI* aURI)
{
// Also allow the load if we are the principal of the URI being checked.
nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(aURI);
if (uriPrinc) {
@ -123,16 +117,11 @@ nsNullPrincipal::CheckMayLoad(nsIURI* aURI, bool aReport, bool aAllowIfInheritsP
uriPrinc->GetPrincipal(getter_AddRefs(principal));
if (principal == this) {
return NS_OK;
return true;
}
}
if (aReport) {
nsScriptSecurityManager::ReportError(
nullptr, NS_LITERAL_STRING("CheckSameOriginError"), mURI, aURI);
}
return NS_ERROR_DOM_BAD_URI;
return false;
}
NS_IMETHODIMP

View File

@ -44,7 +44,6 @@ public:
NS_IMETHOD GetURI(nsIURI** aURI) override;
NS_IMETHOD GetDomain(nsIURI** aDomain) override;
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal) override;
NS_IMETHOD GetIsNullPrincipal(bool* aIsNullPrincipal) override;
NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
nsresult GetOriginInternal(nsACString& aOrigin) override;
@ -68,6 +67,8 @@ public:
return aOther == this;
}
bool MayLoadInternal(nsIURI* aURI) override;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
};

View File

@ -238,17 +238,9 @@ nsPrincipal::GetURI(nsIURI** aURI)
return NS_EnsureSafeToReturn(mCodebase, aURI);
}
NS_IMETHODIMP
nsPrincipal::CheckMayLoad(nsIURI* aURI, bool aReport, bool aAllowIfInheritsPrincipal)
bool
nsPrincipal::MayLoadInternal(nsIURI* aURI)
{
if (aAllowIfInheritsPrincipal) {
// If the caller specified to allow loads of URIs that inherit
// our principal, allow the load if this URI inherits its principal
if (nsPrincipal::IsPrincipalInherited(aURI)) {
return NS_OK;
}
}
// See if aURI is something like a Blob URI that is actually associated with
// a principal.
nsCOMPtr<nsIURIWithPrincipal> uriWithPrin = do_QueryInterface(aURI);
@ -257,17 +249,17 @@ nsPrincipal::CheckMayLoad(nsIURI* aURI, bool aReport, bool aAllowIfInheritsPrinc
uriWithPrin->GetPrincipal(getter_AddRefs(uriPrin));
}
if (uriPrin && nsIPrincipal::Subsumes(uriPrin)) {
return NS_OK;
return true;
}
// If this principal is associated with an addon, check whether that addon
// has been given permission to load from this domain.
if (AddonAllowsLoad(aURI)) {
return NS_OK;
return true;
}
if (nsScriptSecurityManager::SecurityCompareURIs(mCodebase, aURI)) {
return NS_OK;
return true;
}
// If strict file origin policy is in effect, local files will always fail
@ -276,13 +268,10 @@ nsPrincipal::CheckMayLoad(nsIURI* aURI, bool aReport, bool aAllowIfInheritsPrinc
if (nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
NS_URIIsLocalFile(aURI) &&
NS_RelaxStrictFileOriginPolicy(aURI, mCodebase)) {
return NS_OK;
return true;
}
if (aReport) {
nsScriptSecurityManager::ReportError(nullptr, NS_LITERAL_STRING("CheckSameOriginError"), mCodebase, aURI);
}
return NS_ERROR_DOM_BAD_URI;
return false;
}
void
@ -758,17 +747,16 @@ nsExpandedPrincipal::SubsumesInternal(nsIPrincipal* aOther,
return false;
}
NS_IMETHODIMP
nsExpandedPrincipal::CheckMayLoad(nsIURI* uri, bool aReport, bool aAllowIfInheritsPrincipal)
bool
nsExpandedPrincipal::MayLoadInternal(nsIURI* uri)
{
nsresult rv;
for (uint32_t i = 0; i < mPrincipals.Length(); ++i){
rv = mPrincipals[i]->CheckMayLoad(uri, aReport, aAllowIfInheritsPrincipal);
if (NS_SUCCEEDED(rv))
return rv;
if (BasePrincipal::Cast(mPrincipals[i])->MayLoadInternal(uri)) {
return true;
}
}
return NS_ERROR_DOM_BAD_URI;
return false;
}
NS_IMETHODIMP

View File

@ -26,7 +26,6 @@ public:
NS_IMETHOD GetURI(nsIURI** aURI) override;
NS_IMETHOD GetDomain(nsIURI** aDomain) override;
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal) override;
NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
virtual bool IsOnCSSUnprefixingWhitelist() override;
bool IsCodebasePrincipal() const override { return true; }
@ -40,23 +39,6 @@ public:
virtual void GetScriptLocation(nsACString& aStr) override;
void SetURI(nsIURI* aURI);
static bool IsPrincipalInherited(nsIURI* aURI) {
// return true if the loadee URI has
// the URI_INHERITS_SECURITY_CONTEXT flag set.
bool doesInheritSecurityContext;
nsresult rv =
NS_URIChainHasFlags(aURI,
nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
&doesInheritSecurityContext);
if (NS_SUCCEEDED(rv) && doesInheritSecurityContext) {
return true;
}
return false;
}
/**
* Computes the puny-encoded origin of aURI.
*/
@ -79,6 +61,7 @@ protected:
virtual ~nsPrincipal();
bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
bool MayLoadInternal(nsIURI* aURI) override;
};
class nsExpandedPrincipal : public nsIExpandedPrincipal, public mozilla::BasePrincipal
@ -95,7 +78,6 @@ public:
NS_IMETHOD GetURI(nsIURI** aURI) override;
NS_IMETHOD GetDomain(nsIURI** aDomain) override;
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal) override;
NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
virtual bool IsOnCSSUnprefixingWhitelist() override;
virtual void GetScriptLocation(nsACString &aStr) override;
@ -105,6 +87,7 @@ protected:
virtual ~nsExpandedPrincipal();
bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
bool MayLoadInternal(nsIURI* aURI) override;
private:
nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals;

View File

@ -41,12 +41,6 @@ nsSystemPrincipal::GetScriptLocation(nsACString &aStr)
// Methods implementing nsIPrincipal //
///////////////////////////////////////
NS_IMETHODIMP
nsSystemPrincipal::CheckMayLoad(nsIURI* uri, bool aReport, bool aAllowIfInheritsPrincipal)
{
return NS_OK;
}
NS_IMETHODIMP
nsSystemPrincipal::GetHashValue(uint32_t *result)
{

View File

@ -29,7 +29,6 @@ public:
NS_IMETHOD GetURI(nsIURI** aURI) override;
NS_IMETHOD GetDomain(nsIURI** aDomain) override;
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal) override;
NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
@ -46,6 +45,11 @@ protected:
{
return true;
}
bool MayLoadInternal(nsIURI* aURI) override
{
return true;
}
};
#endif // nsSystemPrincipal_h__

View File

@ -121,15 +121,33 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1161831
return p;
}
function testXHR(url, shouldThrow) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", () => { ok(!shouldThrow, "XHR succeeded for " + url); resolve(); });
xhr.addEventListener("error", () => { ok(false, "Unexpected XHR error: " + url); resolve(); });
try {
xhr.open("GET", url, true);
xhr.send();
} catch (e) {
ok(shouldThrow, "XHR threw for " + url);
resolve();
}
});
}
//
// Perform some loads and make sure they work correctly.
//
testLoad.bind(null, 'moz-extension://cherise', navigateFromChromeWithLocation)()
.then(testLoad.bind(null, 'moz-extension://cherise', navigateFromChromeWithWebNav))
.then(testLoad.bind(null, 'moz-extension://cherise', navigateWithLocation, /* shouldThrow = */ true))
.then(testXHR.bind(null, 'moz-extension://cherise', /* shouldThrow = */ true))
.then(setWhitelistCallback.bind(null, /cherise/))
.then(testLoad.bind(null, 'moz-extension://cherise', navigateWithLocation))
.then(testXHR.bind(null, 'moz-extension://cherise'))
.then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithLocation, /* shouldThrow = */ true))
.then(testXHR.bind(null, 'moz-extension://liebchen', /* shouldThrow = */ true))
.then(setWhitelistCallback.bind(null, /cherise|liebchen/))
.then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithLocation))
.then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithSrc))

View File

@ -8816,9 +8816,9 @@ AC_SUBST(MOZ_PKG_SPECIAL)
AC_SUBST(MOZ_SIMPLE_PACKAGE_NAME)
AC_SUBST(MOZILLA_OFFICIAL)
# Build revisions should always be present in official builds
if test "$MOZILLA_OFFICIAL"; then
AC_DEFINE(MOZILLA_OFFICIAL)
# Build revisions should always be present in official builds
MOZ_INCLUDE_SOURCE_INFO=1
fi

View File

@ -99,6 +99,8 @@ const TEST_DATA = [{
}]
}];
requestLongerTimeout(2);
add_task(function*() {
let {inspector} = yield addTab(TEST_URL).then(openInspector);

View File

@ -195,10 +195,10 @@ const STORE_ID_PENDING_PREFIX = "#unknownID#";
this.DOMApplicationRegistry = {
// pseudo-constants for the different application kinds.
get kPackaged() "packaged",
get kHosted() "hosted",
get kHostedAppcache() "hosted-appcache",
get kAndroid() "android-native",
get kPackaged() { return "packaged"; },
get kHosted() { return "hosted"; },
get kHostedAppcache() { return "hosted-appcache"; },
get kAndroid() { return "android-native"; },
// Path to the webapps.json file where we store the registry data.
appsFile: null,
@ -1989,8 +1989,12 @@ this.DOMApplicationRegistry = {
},
id: aApp.id
});
let appURI = NetUtil.newURI(aApp.origin, null, null);
let principal =
Services.scriptSecurityManager.createCodebasePrincipal(appURI,
{appId: aApp.localId});
let cacheUpdate = updateSvc.scheduleAppUpdate(
appcacheURI, docURI, aApp.localId, false, aProfileDir);
appcacheURI, docURI, principal, aApp.localId, false, aProfileDir);
// We save the download details for potential further usage like
// cancelling it.
@ -2139,8 +2143,12 @@ this.DOMApplicationRegistry = {
new ManifestHelper(manifest, aData.origin, aData.manifestURL);
debug("onlyCheckAppCache - launch updateSvc.checkForUpdate for " +
helper.fullAppcachePath());
let appURI = NetUtil.newURI(aApp.origin, null, null);
let principal =
Services.scriptSecurityManager.createCodebasePrincipal(appURI,
{appId: aApp.localId});
updateSvc.checkForUpdate(Services.io.newURI(helper.fullAppcachePath(), null, null),
app.localId, false, updateObserver);
principal, app.localId, false, updateObserver);
});
return;
}
@ -2409,9 +2417,13 @@ this.DOMApplicationRegistry = {
manifest.fullAppcachePath());
let updateDeferred = Promise.defer();
let appURI = NetUtil.newURI(aApp.origin, null, null);
let principal =
Services.scriptSecurityManager.createCodebasePrincipal(appURI,
{appId: aApp.localId});
updateSvc.checkForUpdate(Services.io.newURI(manifest.fullAppcachePath(), null, null),
aApp.localId, false,
principal, aApp.localId, false,
(aSubject, aTopic, aData) => updateDeferred.resolve(aTopic));
let topic = yield updateDeferred.promise;

View File

@ -158,7 +158,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=826058
ok(app, "App is non-null");
if (app.installState == "pending") {
ok(true, "App is pending. Waiting for progress");
app.onprogress = function() ok(true, "Got download progress");
app.onprogress = () => ok(true, "Got download progress");
app.ondownloadsuccess = continueTest;
app.ondownloaderror = mozAppsError;
yield undefined;
@ -173,8 +173,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=826058
// manifest, so there should always be an update.
var lastCheck = app.lastUpdateCheck;
ok(true, "Setting callbacks");
app.ondownloadapplied = function() ok(true, "downloadapplied fired.");
app.ondownloadavailable = function() ok(false, "downloadavailable fired");
app.ondownloadapplied = () => ok(true, "downloadapplied fired.");
app.ondownloadavailable = () => ok(false, "downloadavailable fired");
ok(true, "Checking for updates");
var request = app.checkForUpdate();
request.onerror = mozAppsError;

View File

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

View File

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

View File

@ -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"
@ -1064,6 +1066,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;
}
@ -1095,6 +1116,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;
@ -1112,6 +1151,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;
}
}

View File

@ -48,6 +48,9 @@ enum StructuredCloneTags {
SCTAG_DOM_FORMDATA,
// This tag is for OffscreenCanvas.
SCTAG_DOM_CANVAS,
SCTAG_DOM_MAX
};

View File

@ -1171,7 +1171,8 @@ nsContentSink::ProcessOfflineManifest(const nsAString& aManifestSpec)
if (updateService) {
nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(mDocument);
updateService->ScheduleOnDocumentStop(manifestURI, mDocumentURI, domdoc);
updateService->ScheduleOnDocumentStop(manifestURI, mDocumentURI,
mDocument->NodePrincipal(), domdoc);
}
break;
}

View File

@ -2710,6 +2710,7 @@ nsContentUtils::SubjectPrincipal()
MOZ_ASSERT(NS_IsMainThread());
JSContext* cx = GetCurrentJSContext();
if (!cx) {
Telemetry::Accumulate(Telemetry::SUBJECT_PRINCIPAL_ACCESSED_WITHOUT_SCRIPT_ON_STACK, true);
return GetSystemPrincipal();
}

File diff suppressed because it is too large Load Diff

View File

@ -5040,7 +5040,7 @@ nsDocument::MozSetImageElement(const nsAString& aImageElementId,
if (entry) {
entry->SetImageElement(aElement);
if (entry->IsEmpty()) {
mIdentifierMap.RemoveEntry(aImageElementId);
mIdentifierMap.RemoveEntry(entry);
}
}
}
@ -7838,9 +7838,7 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
// pixel an integer, and we want the adjusted value.
float fullZoom = context ? context->DeviceContext()->GetFullZoom() : 1.0;
fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom;
CSSToLayoutDeviceScale layoutDeviceScale(
(float)nsPresContext::AppUnitsPerCSSPixel() /
context->AppUnitsPerDevPixel());
CSSToLayoutDeviceScale layoutDeviceScale = context->CSSToDevPixelScale();
CSSToScreenScale defaultScale = layoutDeviceScale
* LayoutDeviceToScreenScale(1.0);
@ -8052,7 +8050,7 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
// We need to perform a conversion, but only if the initial or maximum
// scale were set explicitly by the user.
if (mValidScaleFloat) {
if (mValidScaleFloat && scaleFloat >= scaleMinFloat && scaleFloat <= scaleMaxFloat) {
CSSSize displaySize = ScreenSize(aDisplaySize) / scaleFloat;
size.width = std::max(size.width, displaySize.width);
size.height = std::max(size.height, displaySize.height);
@ -9405,7 +9403,7 @@ nsDocument::ForgetLink(Link* aLink)
NS_ASSERTION(entry || mStyledLinksCleared,
"Document knows nothing about this Link!");
#endif
(void)mStyledLinks.RemoveEntry(aLink);
mStyledLinks.RemoveEntry(aLink);
}
void
@ -10124,11 +10122,14 @@ nsIDocument::RegisterActivityObserver(nsISupports* aSupports)
bool
nsIDocument::UnregisterActivityObserver(nsISupports* aSupports)
{
if (!mActivityObservers)
if (!mActivityObservers) {
return false;
if (!mActivityObservers->GetEntry(aSupports))
}
nsPtrHashKey<nsISupports>* entry = mActivityObservers->GetEntry(aSupports);
if (!entry) {
return false;
mActivityObservers->RemoveEntry(aSupports);
}
mActivityObservers->RemoveEntry(entry);
return true;
}

View File

@ -4411,7 +4411,7 @@ nsGlobalWindow::GetApplicationCache(ErrorResult& aError)
if (!mApplicationCache) {
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell()));
if (!webNav) {
if (!webNav || !mDoc) {
aError.Throw(NS_ERROR_FAILURE);
return nullptr;
}
@ -4426,7 +4426,7 @@ nsGlobalWindow::GetApplicationCache(ErrorResult& aError)
nsContentUtils::GetOfflineAppManifest(mDoc, getter_AddRefs(manifestURI));
nsRefPtr<nsDOMOfflineResourceList> applicationCache =
new nsDOMOfflineResourceList(manifestURI, uri, this);
new nsDOMOfflineResourceList(manifestURI, uri, mDoc->NodePrincipal(), this);
applicationCache->Init();

View File

@ -10,13 +10,6 @@
using namespace mozilla;
void
nsViewportInfo::SetDefaultZoom(const CSSToScreenScale& aDefaultZoom)
{
MOZ_ASSERT(aDefaultZoom.scale >= 0.0f);
mDefaultZoom = aDefaultZoom;
}
void
nsViewportInfo::ConstrainViewportValues()
{
@ -24,6 +17,12 @@ nsViewportInfo::ConstrainViewportValues()
// dev.w3.org/csswg/css-device-adapt section 6.2
mMaxZoom = std::max(mMinZoom, mMaxZoom);
mDefaultZoom = mDefaultZoom < mMaxZoom ? mDefaultZoom : mMaxZoom;
mDefaultZoom = mDefaultZoom > mMinZoom ? mDefaultZoom : mMinZoom;
if (mDefaultZoom > mMaxZoom) {
mDefaultZoomValid = false;
mDefaultZoom = mMaxZoom;
}
if (mDefaultZoom < mMinZoom) {
mDefaultZoomValid = false;
mDefaultZoom = mMinZoom;
}
}

View File

@ -12,7 +12,7 @@
/**
* Default values for the nsViewportInfo class.
*/
static const mozilla::LayoutDeviceToScreenScale kViewportMinScale(0.0f);
static const mozilla::LayoutDeviceToScreenScale kViewportMinScale(0.1f);
static const mozilla::LayoutDeviceToScreenScale kViewportMaxScale(10.0f);
static const mozilla::CSSIntSize kViewportMinSize(200, 40);
static const mozilla::CSSIntSize kViewportMaxSize(10000, 10000);
@ -27,6 +27,7 @@ class MOZ_STACK_CLASS nsViewportInfo
nsViewportInfo(const mozilla::ScreenIntSize& aDisplaySize,
const mozilla::CSSToScreenScale& aDefaultZoom,
bool aAllowZoom) :
mDefaultZoomValid(true),
mDefaultZoom(aDefaultZoom),
mAutoSize(true),
mAllowZoom(aAllowZoom)
@ -44,6 +45,7 @@ class MOZ_STACK_CLASS nsViewportInfo
const mozilla::CSSSize& aSize,
bool aAutoSize,
bool aAllowZoom) :
mDefaultZoomValid(true),
mDefaultZoom(aDefaultZoom),
mMinZoom(aMinZoom),
mMaxZoom(aMaxZoom),
@ -54,8 +56,8 @@ class MOZ_STACK_CLASS nsViewportInfo
ConstrainViewportValues();
}
bool IsDefaultZoomValid() const { return mDefaultZoomValid; }
mozilla::CSSToScreenScale GetDefaultZoom() const { return mDefaultZoom; }
void SetDefaultZoom(const mozilla::CSSToScreenScale& aDefaultZoom);
mozilla::CSSToScreenScale GetMinZoom() const { return mMinZoom; }
mozilla::CSSToScreenScale GetMaxZoom() const { return mMaxZoom; }
@ -73,6 +75,10 @@ class MOZ_STACK_CLASS nsViewportInfo
*/
void ConstrainViewportValues();
// If the default zoom was specified and was between the min and max
// zoom values.
bool mDefaultZoomValid;
// Default zoom indicates the level at which the display is 'zoomed in'
// initially for the user, upon loading of the page.
mozilla::CSSToScreenScale mDefaultZoom;

View File

@ -9,6 +9,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=416317
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script>
SimpleTest.waitForExplicitFinish();
SimpleTest.requestLongerTimeout(3);
</script>
</head>
<body>

View File

@ -18,7 +18,7 @@
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
addLoadEvent(function () testCopyPaste(false));
addLoadEvent(() => testCopyPaste(false));
</script>
</pre>

View File

@ -32,7 +32,7 @@ function modifySelectionDiv12() {
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(function () testCopyPaste(true));
addLoadEvent(() => testCopyPaste(true));
]]>
</script>

View File

@ -16,12 +16,16 @@
let tests = [];
function fuzzeq(a, b, msg) {
ok(Math.abs(a - b) < 1e-6, msg);
}
tests.push(function test1() {
SpecialPowers.pushPrefEnv(scaleRatio(1.0),
function() {
let info = getViewportInfo(800, 480);
is(info.defaultZoom, 0, "initial scale is unspecified");
is(info.minZoom, 0, "minumum scale defaults to the absolute minumum");
fuzzeq(info.defaultZoom, 0.1, "initial scale is unspecified");
fuzzeq(info.minZoom, 0.1, "minimum scale defaults to the absolute minimum");
is(info.maxZoom, 10, "maximum scale defaults to the absolute maximum");
is(info.width, 980, "width is the default width");
is(info.height, 588, "height is proportional to displayHeight");

View File

@ -510,6 +510,6 @@
[ 'addToDoc' ],
[ 'displayInherit' ]
)});
runWhenDone(function() SimpleTest.finish());
runWhenDone(() => SimpleTest.finish());
}
</script>

View File

@ -2689,6 +2689,26 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
# if we don't need to create anything, why are we generating this?
assert needInterfaceObject or needInterfacePrototypeObject
getParentProto = fill(
"""
JS::${type}<JSObject*> parentProto(${getParentProto});
if (!parentProto) {
return;
}
""",
type=parentProtoType,
getParentProto=getParentProto)
getConstructorProto = fill(
"""
JS::${type}<JSObject*> constructorProto(${getConstructorProto});
if (!constructorProto) {
return;
}
""",
type=constructorProtoType,
getConstructorProto=getConstructorProto)
idsToInit = []
# There is no need to init any IDs in bindings that don't want Xrays.
if self.descriptor.wantsXrays:
@ -2728,75 +2748,6 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
else:
prefCache = None
if self.descriptor.hasUnforgeableMembers:
assert needInterfacePrototypeObject
# We want to use the same JSClass and prototype as the object we'll
# end up defining the unforgeable properties on in the end, so that
# we can use JS_InitializePropertiesFromCompatibleNativeObject to do
# a fast copy. In the case of proxies that's null, because the
# expando object is a vanilla object, but in the case of other DOM
# objects it's whatever our class is.
#
# Also, for a global we can't use the global's class; just use
# nullpr and when we do the copy off the holder we'll take a slower
# path. This also means that we don't need to worry about matching
# the prototype.
if self.descriptor.proxy or self.descriptor.isGlobal():
holderClass = "nullptr"
holderProto = "nullptr"
else:
holderClass = "Class.ToJSClass()"
holderProto = "*protoCache"
failureCode = dedent(
"""
*protoCache = nullptr;
if (interfaceCache) {
*interfaceCache = nullptr;
}
return;
""")
createUnforgeableHolder = CGGeneric(fill(
"""
JS::Rooted<JSObject*> unforgeableHolder(aCx);
{
JS::Rooted<JSObject*> holderProto(aCx, ${holderProto});
unforgeableHolder = JS_NewObjectWithoutMetadata(aCx, ${holderClass}, holderProto);
if (!unforgeableHolder) {
$*{failureCode}
}
}
""",
holderProto=holderProto,
holderClass=holderClass,
failureCode=failureCode))
defineUnforgeables = InitUnforgeablePropertiesOnHolder(self.descriptor,
self.properties,
failureCode)
createUnforgeableHolder = CGList(
[createUnforgeableHolder, defineUnforgeables])
else:
createUnforgeableHolder = None
getParentProto = fill(
"""
JS::${type}<JSObject*> parentProto(${getParentProto});
if (!parentProto) {
return;
}
""",
type=parentProtoType,
getParentProto=getParentProto)
getConstructorProto = fill(
"""
JS::${type}<JSObject*> constructorProto(${getConstructorProto});
if (!constructorProto) {
return;
}
""",
type=constructorProtoType,
getConstructorProto=getConstructorProto)
if (needInterfaceObject and
self.descriptor.needsConstructHookHolder()):
constructHookHolder = "&" + CONSTRUCT_HOOK_NAME + "_holder"
@ -2869,18 +2820,6 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
chromeProperties=chromeProperties,
name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr")
if self.descriptor.hasUnforgeableMembers:
assert needInterfacePrototypeObject
setUnforgeableHolder = CGGeneric(dedent(
"""
if (*protoCache) {
js::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE,
JS::ObjectValue(*unforgeableHolder));
}
"""))
else:
setUnforgeableHolder = None
aliasedMembers = [m for m in self.descriptor.interface.members if m.isMethod() and m.aliases]
if aliasedMembers:
assert needInterfacePrototypeObject
@ -2938,9 +2877,70 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
else:
defineAliases = None
if self.descriptor.hasUnforgeableMembers:
assert needInterfacePrototypeObject
# We want to use the same JSClass and prototype as the object we'll
# end up defining the unforgeable properties on in the end, so that
# we can use JS_InitializePropertiesFromCompatibleNativeObject to do
# a fast copy. In the case of proxies that's null, because the
# expando object is a vanilla object, but in the case of other DOM
# objects it's whatever our class is.
#
# Also, for a global we can't use the global's class; just use
# nullpr and when we do the copy off the holder we'll take a slower
# path. This also means that we don't need to worry about matching
# the prototype.
if self.descriptor.proxy or self.descriptor.isGlobal():
holderClass = "nullptr"
holderProto = "nullptr"
else:
holderClass = "Class.ToJSClass()"
holderProto = "*protoCache"
failureCode = dedent(
"""
*protoCache = nullptr;
if (interfaceCache) {
*interfaceCache = nullptr;
}
return;
""")
createUnforgeableHolder = CGGeneric(fill(
"""
JS::Rooted<JSObject*> unforgeableHolder(aCx);
{
JS::Rooted<JSObject*> holderProto(aCx, ${holderProto});
unforgeableHolder = JS_NewObjectWithoutMetadata(aCx, ${holderClass}, holderProto);
if (!unforgeableHolder) {
$*{failureCode}
}
}
""",
holderProto=holderProto,
holderClass=holderClass,
failureCode=failureCode))
defineUnforgeables = InitUnforgeablePropertiesOnHolder(self.descriptor,
self.properties,
failureCode)
createUnforgeableHolder = CGList(
[createUnforgeableHolder, defineUnforgeables])
installUnforgeableHolder = CGGeneric(dedent(
"""
if (*protoCache) {
js::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE,
JS::ObjectValue(*unforgeableHolder));
}
"""))
unforgeableHolderSetup = CGList(
[createUnforgeableHolder, installUnforgeableHolder], "\n")
else:
unforgeableHolderSetup = None
return CGList(
[getParentProto, CGGeneric(getConstructorProto), initIds,
prefCache, CGGeneric(call), defineAliases, createUnforgeableHolder, setUnforgeableHolder],
prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup],
"\n").define()

View File

@ -29,7 +29,7 @@ function runTest() {
});
// Force the iframe to repaint.
SimpleTest.executeSoon(function () iframe.src += '#next');
SimpleTest.executeSoon(() => iframe.src += '#next');
});
// Remove the first listener to make sure it's not called.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,6 +22,7 @@
#include "ImageEncoder.h"
#include "Layers.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/ImageData.h"
#include "mozilla/EnumeratedArrayCycleCollection.h"
@ -79,125 +80,6 @@ using namespace mozilla::gfx;
using namespace mozilla::gl;
using namespace mozilla::layers;
WebGLObserver::WebGLObserver(WebGLContext* webgl)
: mWebGL(webgl)
{
}
WebGLObserver::~WebGLObserver()
{
}
void
WebGLObserver::Destroy()
{
UnregisterMemoryPressureEvent();
UnregisterVisibilityChangeEvent();
mWebGL = nullptr;
}
void
WebGLObserver::RegisterVisibilityChangeEvent()
{
if (!mWebGL)
return;
HTMLCanvasElement* canvas = mWebGL->GetCanvas();
MOZ_ASSERT(canvas);
if (canvas) {
nsIDocument* document = canvas->OwnerDoc();
document->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this, true, false);
}
}
void
WebGLObserver::UnregisterVisibilityChangeEvent()
{
if (!mWebGL)
return;
HTMLCanvasElement* canvas = mWebGL->GetCanvas();
if (canvas) {
nsIDocument* document = canvas->OwnerDoc();
document->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this, true);
}
}
void
WebGLObserver::RegisterMemoryPressureEvent()
{
if (!mWebGL)
return;
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
MOZ_ASSERT(observerService);
if (observerService)
observerService->AddObserver(this, "memory-pressure", false);
}
void
WebGLObserver::UnregisterMemoryPressureEvent()
{
if (!mWebGL)
return;
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
// Do not assert on observerService here. This might be triggered by
// the cycle collector at a late enough time, that XPCOM services are
// no longer available. See bug 1029504.
if (observerService)
observerService->RemoveObserver(this, "memory-pressure");
}
NS_IMETHODIMP
WebGLObserver::Observe(nsISupports*, const char* topic, const char16_t*)
{
if (!mWebGL || strcmp(topic, "memory-pressure")) {
return NS_OK;
}
bool wantToLoseContext = mWebGL->mLoseContextOnMemoryPressure;
if (!mWebGL->mCanLoseContextInForeground &&
ProcessPriorityManager::CurrentProcessIsForeground())
{
wantToLoseContext = false;
}
if (wantToLoseContext)
mWebGL->ForceLoseContext();
return NS_OK;
}
NS_IMETHODIMP
WebGLObserver::HandleEvent(nsIDOMEvent* event)
{
nsAutoString type;
event->GetType(type);
if (!mWebGL || !type.EqualsLiteral("visibilitychange"))
return NS_OK;
HTMLCanvasElement* canvas = mWebGL->GetCanvas();
MOZ_ASSERT(canvas);
if (canvas && !canvas->OwnerDoc()->Hidden())
mWebGL->ForceRestoreContext();
return NS_OK;
}
WebGLContextOptions::WebGLContextOptions()
: alpha(true)
, depth(true)
@ -208,7 +90,7 @@ WebGLContextOptions::WebGLContextOptions()
, failIfMajorPerformanceCaveat(false)
{
// Set default alpha state based on preference.
if (Preferences::GetBool("webgl.default-no-alpha", false))
if (gfxPrefs::WebGLDefaultNoAlpha())
alpha = false;
}
@ -282,7 +164,10 @@ WebGLContext::WebGLContext()
mPixelStorePackAlignment = 4;
mPixelStoreUnpackAlignment = 4;
WebGLMemoryTracker::AddWebGLContext(this);
if (NS_IsMainThread()) {
// XXX mtseng: bug 709490, not thread safe
WebGLMemoryTracker::AddWebGLContext(this);
}
mAllowContextRestore = true;
mLastLossWasSimulated = false;
@ -296,15 +181,12 @@ WebGLContext::WebGLContext()
mAlreadyWarnedAboutFakeVertexAttrib0 = false;
mAlreadyWarnedAboutViewportLargerThanDest = false;
mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32);
mMaxWarnings = gfxPrefs::WebGLMaxWarningsPerContext();
if (mMaxWarnings < -1) {
GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
mMaxWarnings = 0;
}
mContextObserver = new WebGLObserver(this);
MOZ_RELEASE_ASSERT(mContextObserver, "Can't alloc WebGLContextObserver");
mLastUseIndex = 0;
InvalidateBufferFetching();
@ -319,10 +201,12 @@ WebGLContext::WebGLContext()
WebGLContext::~WebGLContext()
{
RemovePostRefreshObserver();
mContextObserver->Destroy();
DestroyResourcesAndContext();
WebGLMemoryTracker::RemoveWebGLContext(this);
if (NS_IsMainThread()) {
// XXX mtseng: bug 709490, not thread safe
WebGLMemoryTracker::RemoveWebGLContext(this);
}
mContextLossHandler->DisableTimer();
mContextLossHandler = nullptr;
@ -331,8 +215,6 @@ WebGLContext::~WebGLContext()
void
WebGLContext::DestroyResourcesAndContext()
{
mContextObserver->UnregisterMemoryPressureEvent();
if (!gl)
return;
@ -431,6 +313,35 @@ WebGLContext::Invalidate()
mCanvasElement->InvalidateCanvasContent(nullptr);
}
void
WebGLContext::OnVisibilityChange()
{
if (!IsContextLost()) {
return;
}
if (!mRestoreWhenVisible || mLastLossWasSimulated) {
return;
}
ForceRestoreContext();
}
void
WebGLContext::OnMemoryPressure()
{
bool shouldLoseContext = mLoseContextOnMemoryPressure;
if (!mCanLoseContextInForeground &&
ProcessPriorityManager::CurrentProcessIsForeground())
{
shouldLoseContext = false;
}
if (shouldLoseContext)
ForceLoseContext();
}
//
// nsICanvasRenderingContextInternal
//
@ -513,7 +424,7 @@ static bool
IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature)
{
int32_t status;
if (!NS_SUCCEEDED(gfxInfo->GetFeatureStatus(feature, &status)))
if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature, &status)))
return false;
return status != nsIGfxInfo::FEATURE_STATUS_OK;
@ -524,19 +435,29 @@ HasAcceleratedLayers(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
{
int32_t status;
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, &status);
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
&status);
if (status)
return true;
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS, &status);
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
&status);
if (status)
return true;
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS, &status);
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
&status);
if (status)
return true;
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status);
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
&status);
if (status)
return true;
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_OPENGL_LAYERS, &status);
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
nsIGfxInfo::FEATURE_OPENGL_LAYERS,
&status);
if (status)
return true;
@ -593,11 +514,14 @@ BaseCaps(const WebGLContextOptions& options, WebGLContext* webgl)
// we should really have this behind a
// |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
// for now it's just behind a pref for testing/evaluation.
baseCaps.bpp16 = Preferences::GetBool("webgl.prefer-16bpp", false);
baseCaps.bpp16 = gfxPrefs::WebGLPrefer16bpp();
#ifdef MOZ_WIDGET_GONK
do {
auto canvasElement = webgl->GetCanvas();
if (!canvasElement)
break;
auto ownerDoc = canvasElement->OwnerDoc();
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(ownerDoc);
if (!docWidget)
@ -618,7 +542,7 @@ BaseCaps(const WebGLContextOptions& options, WebGLContext* webgl)
// Done with baseCaps construction.
bool forceAllowAA = Preferences::GetBool("webgl.msaa-force", false);
bool forceAllowAA = gfxPrefs::WebGLForceMSAA();
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
if (!forceAllowAA &&
IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA))
@ -738,7 +662,7 @@ bool
WebGLContext::CreateAndInitGL(bool forceEnabled)
{
bool preferEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
bool disableANGLE = Preferences::GetBool("webgl.disable-angle", false);
bool disableANGLE = gfxPrefs::WebGLDisableANGLE();
if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL"))
disableANGLE = true;
@ -819,10 +743,6 @@ WebGLContext::ResizeBackbuffer(uint32_t requestedWidth,
NS_IMETHODIMP
WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
{
// Early error return cases
if (!GetCanvas())
return NS_ERROR_FAILURE;
if (signedWidth < 0 || signedHeight < 0) {
GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
return NS_ERROR_OUT_OF_MEMORY;
@ -832,7 +752,10 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
uint32_t height = signedHeight;
// Early success return cases
GetCanvas()->InvalidateCanvas();
// May have a OffscreenCanvas instead of an HTMLCanvasElement
if (GetCanvas())
GetCanvas()->InvalidateCanvas();
// Zero-sized surfaces can cause problems.
if (width == 0)
@ -905,10 +828,7 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
// pick up the old generation.
++mGeneration;
// Get some prefs for some preferred/overriden things
NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
bool disabled = Preferences::GetBool("webgl.disabled", false);
bool disabled = gfxPrefs::WebGLDisabled();
// TODO: When we have software webgl support we should use that instead.
disabled |= gfxPlatform::InSafeMode();
@ -931,7 +851,7 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
}
// Alright, now let's start trying.
bool forceEnabled = Preferences::GetBool("webgl.force-enabled", false);
bool forceEnabled = gfxPrefs::WebGLForceEnabled();
ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
MOZ_ASSERT(!gl);
@ -1052,6 +972,11 @@ WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
#endif
MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
if (!NS_IsMainThread()) {
// XXX mtseng: bug 709490, WebGLMemoryTracker is not thread safe.
return;
}
// it's important to update the index on a new context before losing old contexts,
// otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
// when choosing which one to lose first.
@ -1139,32 +1064,8 @@ WebGLContext::GetImageBuffer(uint8_t** out_imageBuffer, int32_t* out_format)
RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
DataSourceSurface::MappedSurface map;
if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map))
return;
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
if (!imageBuffer) {
dataSurface->Unmap();
return;
}
memcpy(imageBuffer, map.mData, mWidth * mHeight * 4);
dataSurface->Unmap();
int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
if (!mOptions.premultipliedAlpha) {
// We need to convert to INPUT_FORMAT_RGBA, otherwise
// we are automatically considered premult, and unpremult'd.
// Yes, it is THAT silly.
// Except for different lossy conversions by color,
// we could probably just change the label, and not change the data.
gfxUtils::ConvertBGRAtoRGBA(imageBuffer, mWidth * mHeight * 4);
format = imgIEncoder::INPUT_FORMAT_RGBA;
}
*out_imageBuffer = imageBuffer;
*out_format = format;
return gfxUtils::GetImageBuffer(dataSurface, mOptions.premultipliedAlpha,
out_imageBuffer, out_format);
}
NS_IMETHODIMP
@ -1176,20 +1077,18 @@ WebGLContext::GetInputStream(const char* mimeType,
if (!gl)
return NS_ERROR_FAILURE;
nsCString enccid("@mozilla.org/image/encoder;2?type=");
enccid += mimeType;
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
if (!encoder)
// Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
bool premult;
RefPtr<SourceSurface> snapshot =
GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
if (!snapshot)
return NS_ERROR_FAILURE;
nsAutoArrayPtr<uint8_t> imageBuffer;
int32_t format = 0;
GetImageBuffer(getter_Transfers(imageBuffer), &format);
if (!imageBuffer)
return NS_ERROR_FAILURE;
MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
encoder, encoderOptions, out_stream);
RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
return gfxUtils::GetInputStream(dataSurface, mOptions.premultipliedAlpha, mimeType,
encoderOptions, out_stream);
}
void
@ -1268,25 +1167,26 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
}
WebGLContextUserData* userData = nullptr;
if (builder->IsPaintingToWindow()) {
// Make the layer tell us whenever a transaction finishes (including
// the current transaction), so we can clear our invalidation state and
// start invalidating again. We need to do this for the layer that is
// being painted to a window (there shouldn't be more than one at a time,
// and if there is, flushing the invalidation state more often than
// necessary is harmless).
if (builder->IsPaintingToWindow() && mCanvasElement) {
// Make the layer tell us whenever a transaction finishes (including
// the current transaction), so we can clear our invalidation state and
// start invalidating again. We need to do this for the layer that is
// being painted to a window (there shouldn't be more than one at a time,
// and if there is, flushing the invalidation state more often than
// necessary is harmless).
// The layer will be destroyed when we tear down the presentation
// (at the latest), at which time this userData will be destroyed,
// releasing the reference to the element.
// The userData will receive DidTransactionCallbacks, which flush the
// the invalidation state to indicate that the canvas is up to date.
userData = new WebGLContextUserData(mCanvasElement);
canvasLayer->SetDidTransactionCallback(
WebGLContextUserData::DidTransactionCallback, userData);
canvasLayer->SetPreTransactionCallback(
WebGLContextUserData::PreTransactionCallback, userData);
// The layer will be destroyed when we tear down the presentation
// (at the latest), at which time this userData will be destroyed,
// releasing the reference to the element.
// The userData will receive DidTransactionCallbacks, which flush the
// the invalidation state to indicate that the canvas is up to date.
userData = new WebGLContextUserData(mCanvasElement);
canvasLayer->SetDidTransactionCallback(
WebGLContextUserData::DidTransactionCallback, userData);
canvasLayer->SetPreTransactionCallback(
WebGLContextUserData::PreTransactionCallback, userData);
}
canvasLayer->SetUserData(&gWebGLLayerUserData, userData);
CanvasLayer::Data data;
@ -1308,14 +1208,36 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
layers::LayersBackend
WebGLContext::GetCompositorBackendType() const
{
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
if (docWidget) {
layers::LayerManager* layerManager = docWidget->GetLayerManager();
return layerManager->GetCompositorBackendType();
if (mCanvasElement) {
return mCanvasElement->GetCompositorBackendType();
} else if (mOffscreenCanvas) {
return mOffscreenCanvas->GetCompositorBackendType();
}
return LayersBackend::LAYERS_NONE;
}
void
WebGLContext::Commit()
{
if (mOffscreenCanvas) {
mOffscreenCanvas->CommitFrameToCompositor();
}
}
void
WebGLContext::GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval)
{
if (mCanvasElement) {
MOZ_RELEASE_ASSERT(!mOffscreenCanvas);
retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement;
} else if (mOffscreenCanvas) {
retval.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas;
} else {
retval.SetNull();
}
}
void
WebGLContext::GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval)
{
@ -1624,7 +1546,7 @@ WebGLContext::RunContextLossTimer()
mContextLossHandler->RunTimer();
}
class UpdateContextLossStatusTask : public nsRunnable
class UpdateContextLossStatusTask : public nsCancelableRunnable
{
nsRefPtr<WebGLContext> mWebGL;
@ -1635,10 +1557,16 @@ public:
}
NS_IMETHOD Run() {
mWebGL->UpdateContextLossStatus();
if (mWebGL)
mWebGL->UpdateContextLossStatus();
return NS_OK;
}
NS_IMETHOD Cancel() {
mWebGL = nullptr;
return NS_OK;
}
};
void
@ -1665,7 +1593,7 @@ WebGLContext::EnqueueUpdateContextLossStatus()
void
WebGLContext::UpdateContextLossStatus()
{
if (!mCanvasElement) {
if (!mCanvasElement && !mOffscreenCanvas) {
// the canvas is gone. That happens when the page was closed before we got
// this timer event. In this case, there's nothing to do here, just don't crash.
return;
@ -1693,12 +1621,23 @@ WebGLContext::UpdateContextLossStatus()
// callback, so do that now.
bool useDefaultHandler;
nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
NS_LITERAL_STRING("webglcontextlost"),
true,
true,
&useDefaultHandler);
if (mCanvasElement) {
nsContentUtils::DispatchTrustedEvent(
mCanvasElement->OwnerDoc(),
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
NS_LITERAL_STRING("webglcontextlost"),
true,
true,
&useDefaultHandler);
} else {
// OffscreenCanvas case
nsRefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
event->InitEvent(NS_LITERAL_STRING("webglcontextlost"), true, true);
event->SetTrusted(true);
mOffscreenCanvas->DispatchEvent(event, &useDefaultHandler);
}
// We sent the callback, so we're just 'regular lost' now.
mContextStatus = ContextLost;
// If we're told to use the default handler, it means the script
@ -1750,11 +1689,22 @@ WebGLContext::UpdateContextLossStatus()
// Revival!
mContextStatus = ContextNotLost;
nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
NS_LITERAL_STRING("webglcontextrestored"),
true,
true);
if (mCanvasElement) {
nsContentUtils::DispatchTrustedEvent(
mCanvasElement->OwnerDoc(),
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
NS_LITERAL_STRING("webglcontextrestored"),
true,
true);
} else {
nsRefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
event->InitEvent(NS_LITERAL_STRING("webglcontextrestored"), true, true);
event->SetTrusted(true);
bool unused;
mOffscreenCanvas->DispatchEvent(event, &unused);
}
mEmitContextLostErrorOnce = true;
return;
}
@ -1772,12 +1722,6 @@ WebGLContext::ForceLoseContext(bool simulateLosing)
DestroyResourcesAndContext();
mLastLossWasSimulated = simulateLosing;
// Register visibility change observer to defer the context restoring.
// Restore the context when the app is visible.
if (mRestoreWhenVisible && !mLastLossWasSimulated) {
mContextObserver->RegisterVisibilityChangeEvent();
}
// Queue up a task, since we know the status changed.
EnqueueUpdateContextLossStatus();
}
@ -1789,8 +1733,6 @@ WebGLContext::ForceRestoreContext()
mContextStatus = ContextLostAwaitingRestore;
mAllowContextRestore = true; // Hey, you did say 'force'.
mContextObserver->UnregisterVisibilityChangeEvent();
// Queue up a task, since we know the status changed.
EnqueueUpdateContextLossStatus();
}
@ -1925,6 +1867,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
mCanvasElement,
mOffscreenCanvas,
mExtensions,
mBound2DTextures,
mBoundCubeMapTextures,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,8 +9,6 @@
namespace mozilla {
NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
NS_IMETHODIMP
WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
nsISupports* data, bool)

View File

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

View File

@ -16,8 +16,6 @@
namespace mozilla {
NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
NS_IMETHODIMP
WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
nsISupports* data, bool)

View File

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

View File

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

View File

@ -12,12 +12,13 @@
#include "nsIDocShell.h"
#include "nsRefreshDriver.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/OffscreenCanvas.h"
#include "GraphicsFilter.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;
@ -80,6 +81,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;
@ -152,6 +158,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
//
@ -164,6 +174,7 @@ public:
protected:
nsRefPtr<mozilla::dom::HTMLCanvasElement> mCanvasElement;
nsRefPtr<mozilla::dom::OffscreenCanvas> mOffscreenCanvas;
nsRefPtr<nsRefreshDriver> mRefreshDriver;
};

View File

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

View File

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

View File

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

After

Width:  |  Height:  |  Size: 638 B

View File

@ -0,0 +1 @@
/* empty worker for test_offscreencanvas_disable.html */

View File

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

View File

@ -7468,25 +7468,25 @@ var _thrown = undefined; try {
ctx.createImageData(Infinity, Infinity);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.createImageData({valueOf:function() Infinity}, 10);
ctx.createImageData({valueOf:() => Infinity}, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.createImageData({valueOf:function() -Infinity}, 10);
ctx.createImageData({valueOf:() => -Infinity}, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.createImageData({valueOf:function() NaN}, 10);
ctx.createImageData({valueOf:() => NaN}, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.createImageData(10, {valueOf:function() Infinity});
ctx.createImageData(10, {valueOf:() => Infinity});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.createImageData(10, {valueOf:function() -Infinity});
ctx.createImageData(10, {valueOf:() => -Infinity});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.createImageData(10, {valueOf:function() NaN});
ctx.createImageData(10, {valueOf:() => NaN});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.createImageData({valueOf:function() Infinity}, {valueOf:function() Infinity});
ctx.createImageData({valueOf:() => Infinity}, {valueOf:() => Infinity});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
@ -7774,73 +7774,73 @@ var _thrown = undefined; try {
ctx.getImageData(10, 10, Infinity, Infinity);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData({valueOf:function() Infinity}, 10, 10, 10);
ctx.getImageData({valueOf:() => Infinity}, 10, 10, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData({valueOf:function() -Infinity}, 10, 10, 10);
ctx.getImageData({valueOf:() => -Infinity}, 10, 10, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData({valueOf:function() NaN}, 10, 10, 10);
ctx.getImageData({valueOf:() => NaN}, 10, 10, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, {valueOf:function() Infinity}, 10, 10);
ctx.getImageData(10, {valueOf:() => Infinity}, 10, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, {valueOf:function() -Infinity}, 10, 10);
ctx.getImageData(10, {valueOf:() => -Infinity}, 10, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, {valueOf:function() NaN}, 10, 10);
ctx.getImageData(10, {valueOf:() => NaN}, 10, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, 10, {valueOf:function() Infinity}, 10);
ctx.getImageData(10, 10, {valueOf:() => Infinity}, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, 10, {valueOf:function() -Infinity}, 10);
ctx.getImageData(10, 10, {valueOf:() => -Infinity}, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, 10, {valueOf:function() NaN}, 10);
ctx.getImageData(10, 10, {valueOf:() => NaN}, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, 10, 10, {valueOf:function() Infinity});
ctx.getImageData(10, 10, 10, {valueOf:() => Infinity});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, 10, 10, {valueOf:function() -Infinity});
ctx.getImageData(10, 10, 10, {valueOf:() => -Infinity});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, 10, 10, {valueOf:function() NaN});
ctx.getImageData(10, 10, 10, {valueOf:() => NaN});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData({valueOf:function() Infinity}, {valueOf:function() Infinity}, 10, 10);
ctx.getImageData({valueOf:() => Infinity}, {valueOf:() => Infinity}, 10, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData({valueOf:function() Infinity}, {valueOf:function() Infinity}, {valueOf:function() Infinity}, 10);
ctx.getImageData({valueOf:() => Infinity}, {valueOf:() => Infinity}, {valueOf:() => Infinity}, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData({valueOf:function() Infinity}, {valueOf:function() Infinity}, {valueOf:function() Infinity}, {valueOf:function() Infinity});
ctx.getImageData({valueOf:() => Infinity}, {valueOf:() => Infinity}, {valueOf:() => Infinity}, {valueOf:() => Infinity});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData({valueOf:function() Infinity}, {valueOf:function() Infinity}, 10, {valueOf:function() Infinity});
ctx.getImageData({valueOf:() => Infinity}, {valueOf:() => Infinity}, 10, {valueOf:() => Infinity});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData({valueOf:function() Infinity}, 10, {valueOf:function() Infinity}, 10);
ctx.getImageData({valueOf:() => Infinity}, 10, {valueOf:() => Infinity}, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData({valueOf:function() Infinity}, 10, {valueOf:function() Infinity}, {valueOf:function() Infinity});
ctx.getImageData({valueOf:() => Infinity}, 10, {valueOf:() => Infinity}, {valueOf:() => Infinity});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData({valueOf:function() Infinity}, 10, 10, {valueOf:function() Infinity});
ctx.getImageData({valueOf:() => Infinity}, 10, 10, {valueOf:() => Infinity});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, {valueOf:function() Infinity}, {valueOf:function() Infinity}, 10);
ctx.getImageData(10, {valueOf:() => Infinity}, {valueOf:() => Infinity}, 10);
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, {valueOf:function() Infinity}, {valueOf:function() Infinity}, {valueOf:function() Infinity});
ctx.getImageData(10, {valueOf:() => Infinity}, {valueOf:() => Infinity}, {valueOf:() => Infinity});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, {valueOf:function() Infinity}, 10, {valueOf:function() Infinity});
ctx.getImageData(10, {valueOf:() => Infinity}, 10, {valueOf:() => Infinity});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");
var _thrown = undefined; try {
ctx.getImageData(10, 10, {valueOf:function() Infinity}, {valueOf:function() Infinity});
ctx.getImageData(10, 10, {valueOf:() => Infinity}, {valueOf:() => Infinity});
} catch (e) { _thrown = e }; ok(_thrown && _thrown instanceof TypeError, "should throw TypeError");

View File

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

View File

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

View File

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

View File

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

View File

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

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