Merge branch 'mozilla-central'

This commit is contained in:
Jacek Caban 2016-02-16 10:38:25 +01:00
commit 33db3dfa02
348 changed files with 4182 additions and 7569 deletions

10
.gitignore vendored
View File

@ -54,6 +54,16 @@ parser/html/java/javaparser/
.cproject .cproject
.settings/ .settings/
# Ignore the files and directory that JetBrains IDEs create.
/.idea/
*.iml
# Gradle cache.
/.gradle/
# Local Gradle configuration properties.
/local.properties
# Python virtualenv artifacts. # Python virtualenv artifacts.
python/psutil/**/*.so python/psutil/**/*.so
python/psutil/**/*.pyd python/psutil/**/*.pyd

View File

@ -821,9 +821,17 @@ Accessible::XULElmName(DocAccessible* aDocument,
nsIContent *bindingParent = aElm->GetBindingParent(); nsIContent *bindingParent = aElm->GetBindingParent();
nsIContent* parent = nsIContent* parent =
bindingParent? bindingParent->GetParent() : aElm->GetParent(); bindingParent? bindingParent->GetParent() : aElm->GetParent();
nsAutoString ancestorTitle;
while (parent) { while (parent) {
if (parent->IsXULElement(nsGkAtoms::toolbaritem) && if (parent->IsXULElement(nsGkAtoms::toolbaritem) &&
parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) { parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, ancestorTitle)) {
// Before returning this, check if the element itself has a tooltip:
if (aElm->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
aName.CompressWhitespace();
return;
}
aName.Assign(ancestorTitle);
aName.CompressWhitespace(); aName.CompressWhitespace();
return; return;
} }

View File

@ -13,4 +13,5 @@ support-files =
[test_list.html] [test_list.html]
[test_markup.html] [test_markup.html]
[test_svg.html] [test_svg.html]
[test_toolbaritem.xul]
[test_tree.xul] [test_tree.xul]

View File

@ -0,0 +1,84 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<?xml-stylesheet href="general.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Accessibility Name Calculating Test.">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../role.js"></script>
<script type="application/javascript"
src="../name.js"></script>
<script type="application/javascript">
<![CDATA[
var gQueue = null;
function doTest() {
let ids = [];
for (let item of ["button", "textbox"]) {
ids.push(item + "withtooltip");
ids.push(item + "withouttooltip");
ids.push("nested" + item + "withtooltip");
ids.push("nested" + item + "withouttooltip");
}
for (let id of ids) {
if (id.endsWith("withtooltip")) {
testName(id, id, id + " should have individual name from its tooltip - ");
} else {
testName(id, "Toolbaritem title", id + " should have toolbaritem's title for a name - ");
}
}
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
]]>
</script>
<hbox flex="1" style="overflow: auto;">
<body xmlns="http://www.w3.org/1999/xhtml">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1216478"
title="Items with tooltips inside items with a label should use their own tooltip as an accessible name, not the ancestor's label">
Mozilla Bug 1216478
</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
<vbox flex="1">
<toolbox>
<toolbar>
<toolbaritem title="Toolbaritem title">
<toolbarbutton id="buttonwithtooltip" tooltiptext="buttonwithtooltip"/>
<toolbarbutton id="buttonwithouttooltip"/>
<textbox id="textboxwithtooltip" tooltiptext="textboxwithtooltip"/>
<textbox id="textboxwithouttooltip"/>
<vbox>
<toolbarbutton id="nestedbuttonwithtooltip" tooltiptext="nestedbuttonwithtooltip"/>
<toolbarbutton id="nestedbuttonwithouttooltip"/>
<textbox id="nestedtextboxwithtooltip" tooltiptext="nestedtextboxwithtooltip"/>
<textbox id="nestedtextboxwithouttooltip"/>
</vbox>
</toolbaritem>
</toolbar>
</toolbox>
</vbox> <!-- close tests area -->
</hbox> <!-- close main area -->
</window>

View File

@ -57,7 +57,29 @@
<menuitem id="context-openlinkintab" <menuitem id="context-openlinkintab"
label="&openLinkCmdInTab.label;" label="&openLinkCmdInTab.label;"
accesskey="&openLinkCmdInTab.accesskey;" accesskey="&openLinkCmdInTab.accesskey;"
oncommand="gContextMenu.openLinkInTab();"/> usercontextid="0"
oncommand="gContextMenu.openLinkInTab(event);"/>
<menu id="context-openlinkinusercontext-menu"
label="&openLinkCmdInContainerTab.label;"
accesskey="&openLinkCmdInContainerTab.accesskey;"
hidden="true">
<menupopup oncommand="gContextMenu.openLinkInTab(event);">
<menuitem label="&userContextPersonal.label;"
usercontextid="1"
accesskey="&userContextPersonal.accesskey;"/>
<menuitem label="&userContextWork.label;"
usercontextid="2"
accesskey="&userContextWork.accesskey;"/>
<menuitem label="&userContextBanking.label;"
usercontextid="3"
accesskey="&userContextBanking.accesskey;"/>
<menuitem label="&userContextShopping.label;"
usercontextid="4"
accesskey="&userContextShopping.accesskey;"/>
</menupopup>
</menu>
<menuitem id="context-openlink" <menuitem id="context-openlink"
label="&openLinkCmd.label;" label="&openLinkCmd.label;"
accesskey="&openLinkCmd.accesskey;" accesskey="&openLinkCmd.accesskey;"

View File

@ -451,6 +451,10 @@
class="show-only-for-keyboard" class="show-only-for-keyboard"
command="Browser:BookmarkAllTabs" command="Browser:BookmarkAllTabs"
key="bookmarkAllTabsKb"/> key="bookmarkAllTabsKb"/>
<menuseparator/>
<menuitem label="&recentBookmarks.label;"
disabled="true"/>
<vbox id="menu_recentBookmarks"/>
<menuseparator id="bookmarksToolbarSeparator"/> <menuseparator id="bookmarksToolbarSeparator"/>
<menu id="bookmarksToolbarFolderMenu" <menu id="bookmarksToolbarFolderMenu"
class="menu-iconic bookmark-item" class="menu-iconic bookmark-item"

View File

@ -1305,6 +1305,9 @@ var BookmarkingUI = {
return; return;
} }
this._updateRecentBookmarks(document.getElementById("BMB_recentBookmarks"),
"subviewbutton");
if (!this._popupNeedsUpdate) if (!this._popupNeedsUpdate)
return; return;
this._popupNeedsUpdate = false; this._popupNeedsUpdate = false;
@ -1338,6 +1341,60 @@ var BookmarkingUI = {
}); });
}, },
_updateRecentBookmarks: function(container, extraCSSClass = "") {
const kMaxResults = 5;
let options = PlacesUtils.history.getNewQueryOptions();
options.excludeQueries = true;
options.queryType = options.QUERY_TYPE_BOOKMARKS;
options.sortingMode = options.SORT_BY_DATE_DESCENDING;
options.maxResults = kMaxResults;
let query = PlacesUtils.history.getNewQuery();
while (container.firstChild) {
container.firstChild.remove();
}
PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
.asyncExecuteLegacyQueries([query], 1, options, {
handleResult: function (aResultSet) {
let onItemClick = function (aEvent) {
let item = aEvent.target;
openUILink(item.getAttribute("targetURI"), aEvent);
CustomizableUI.hidePanelForNode(item);
};
let fragment = document.createDocumentFragment();
let row;
while ((row = aResultSet.getNextRow())) {
let uri = row.getResultByIndex(1);
let title = row.getResultByIndex(2);
let icon = row.getResultByIndex(6);
let item =
document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
"menuitem");
item.setAttribute("label", title || uri);
item.setAttribute("targetURI", uri);
item.setAttribute("class", "menuitem-iconic menuitem-with-favicon bookmark-item " +
extraCSSClass);
item.addEventListener("click", onItemClick);
if (icon) {
let iconURL = "moz-anno:favicon:" + icon;
item.setAttribute("image", iconURL);
}
fragment.appendChild(item);
}
container.appendChild(fragment);
},
handleError: function (aError) {
Cu.reportError("Error while attempting to show recent bookmarks: " + aError);
},
handleCompletion: function (aReason) {
},
});
},
/** /**
* Handles star styling based on page proxy state changes. * Handles star styling based on page proxy state changes.
*/ */
@ -1550,6 +1607,7 @@ var BookmarkingUI = {
onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) { onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) {
this._updateBookmarkPageMenuItem(); this._updateBookmarkPageMenuItem();
PlacesCommandHook.updateBookmarkAllTabsCommand(); PlacesCommandHook.updateBookmarkAllTabsCommand();
this._updateRecentBookmarks(document.getElementById("menu_recentBookmarks"));
}, },
_showBookmarkedNotification: function BUI_showBookmarkedNotification() { _showBookmarkedNotification: function BUI_showBookmarkedNotification() {

View File

@ -8,9 +8,14 @@
var gSafeBrowsing = { var gSafeBrowsing = {
setReportPhishingMenu: function() { setReportPhishingMenu: function() {
// A phishing page will have a specific about:blocked content documentURI // In order to detect whether or not we're at the phishing warning
var uri = gBrowser.currentURI; // page, we have to check the documentURI instead of the currentURI.
var isPhishingPage = uri && uri.spec.startsWith("about:blocked?e=phishingBlocked"); // This is because when the DocShell loads an error page, the
// currentURI stays at the original target, while the documentURI
// will point to the internal error page we loaded instead.
var docURI = gBrowser.selectedBrowser.documentURI;
var isPhishingPage =
docURI && docURI.spec.startsWith("about:blocked?e=phishingBlocked");
// Show/hide the appropriate menu item. // Show/hide the appropriate menu item.
document.getElementById("menu_HelpPopup_reportPhishingtoolmenu") document.getElementById("menu_HelpPopup_reportPhishingtoolmenu")
@ -26,6 +31,9 @@ var gSafeBrowsing = {
if (!broadcaster) if (!broadcaster)
return; return;
// Now look at the currentURI to learn which page we were trying
// to browse to.
let uri = gBrowser.currentURI;
if (uri && (uri.schemeIs("http") || uri.schemeIs("https"))) if (uri && (uri.schemeIs("http") || uri.schemeIs("https")))
broadcaster.removeAttribute("disabled"); broadcaster.removeAttribute("disabled");
else else

View File

@ -812,6 +812,11 @@
command="Browser:ShowAllBookmarks" command="Browser:ShowAllBookmarks"
key="manBookmarkKb"/> key="manBookmarkKb"/>
<menuseparator/> <menuseparator/>
<menuitem label="&recentBookmarks.label;"
disabled="true"
class="subviewbutton"/>
<vbox id="BMB_recentBookmarks"/>
<menuseparator/>
<menu id="BMB_bookmarksToolbar" <menu id="BMB_bookmarksToolbar"
class="menu-iconic bookmark-item subviewbutton" class="menu-iconic bookmark-item subviewbutton"
label="&personalbarCmd.label;" label="&personalbarCmd.label;"

View File

@ -144,9 +144,11 @@ nsContextMenu.prototype = {
var shouldShow = this.onSaveableLink || isMailtoInternal || this.onPlainTextLink; var shouldShow = this.onSaveableLink || isMailtoInternal || this.onPlainTextLink;
var isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window); var isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
var showContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
this.showItem("context-openlink", shouldShow && !isWindowPrivate); this.showItem("context-openlink", shouldShow && !isWindowPrivate);
this.showItem("context-openlinkprivate", shouldShow); this.showItem("context-openlinkprivate", shouldShow);
this.showItem("context-openlinkintab", shouldShow); this.showItem("context-openlinkintab", shouldShow);
this.showItem("context-openlinkinusercontext-menu", shouldShow && showContainers);
this.showItem("context-openlinkincurrent", this.onPlainTextLink); this.showItem("context-openlinkincurrent", this.onPlainTextLink);
this.showItem("context-sep-open", shouldShow); this.showItem("context-sep-open", shouldShow);
}, },
@ -960,7 +962,7 @@ nsContextMenu.prototype = {
}, },
// Open linked-to URL in a new tab. // Open linked-to URL in a new tab.
openLinkInTab: function() { openLinkInTab: function(event) {
urlSecurityCheck(this.linkURL, this.principal); urlSecurityCheck(this.linkURL, this.principal);
let referrerURI = gContextMenuContentData.documentURIObject; let referrerURI = gContextMenuContentData.documentURIObject;
@ -981,6 +983,7 @@ nsContextMenu.prototype = {
let params = this._openLinkInParameters({ let params = this._openLinkInParameters({
allowMixedContent: persistAllowMixedContentInChildTab, allowMixedContent: persistAllowMixedContentInChildTab,
userContextId: event.target.getAttribute('usercontextid'),
}); });
openLinkIn(this.linkURL, "tab", params); openLinkIn(this.linkURL, "tab", params);
}, },

View File

@ -121,7 +121,7 @@ SocialErrorListener = {
.getInterface(Ci.nsIDOMWindowUtils); .getInterface(Ci.nsIDOMWindowUtils);
dwu.allowScriptsToClose(); dwu.allowScriptsToClose();
content.addEventListener("DOMWindowClose", function _mozSocialDOMWindowClose(evt) { addEventListener("DOMWindowClose", function _mozSocialDOMWindowClose(evt) {
sendAsyncMessage("DOMWindowClose"); sendAsyncMessage("DOMWindowClose");
// preventDefault stops the default window.close() function being called, // preventDefault stops the default window.close() function being called,
// which doesn't actually close anything but causes things to get into // which doesn't actually close anything but causes things to get into
@ -135,7 +135,7 @@ SocialErrorListener = {
break; break;
case "Social:ListenForEvents": case "Social:ListenForEvents":
for (let eventName of message.data.eventNames) { for (let eventName of message.data.eventNames) {
content.addEventListener(eventName, this); addEventListener(eventName, this);
} }
break; break;
case "Social:SetDocumentTitle": case "Social:SetDocumentTitle":

View File

@ -207,6 +207,16 @@
content.setAttribute("origin", this.content.getAttribute("origin")); content.setAttribute("origin", this.content.getAttribute("origin"));
content.popupnotificationanchor.className = this.content.popupnotificationanchor.className; content.popupnotificationanchor.className = this.content.popupnotificationanchor.className;
content.swapDocShells(this.content); content.swapDocShells(this.content);
// When a chat window is attached or detached, the docShell hosting
// the chat document is swapped to the newly created chat window.
// (Be it inside a popup or back inside a chatbox element attached to
// the chatbar.)
// Since a swapDocShells call does not swap the messageManager instances
// attached to a browser, we'll need to add the message listeners to
// the new messageManager. This is not a bug in swapDocShells, merely
// a design decision.
content.messageManager.addMessageListener("DOMTitleChanged", content);
]]></body> ]]></body>
</method> </method>

View File

@ -1828,6 +1828,13 @@
var uriIsAboutBlank = !aURI || aURI == "about:blank"; var uriIsAboutBlank = !aURI || aURI == "about:blank";
if (!aURI || isBlankPageURL(aURI)) {
t.setAttribute("label", this.mStringBundle.getString("tabs.emptyTabTitle"));
} else if (aURI.toLowerCase().startsWith("javascript:")) {
// This can go away when bug 672618 or bug 55696 are fixed.
t.setAttribute("label", aURI);
}
if (aUserContextId) if (aUserContextId)
t.setAttribute("usercontextid", aUserContextId); t.setAttribute("usercontextid", aUserContextId);
t.setAttribute("crop", "end"); t.setAttribute("crop", "end");
@ -1906,15 +1913,6 @@
this.mPanelContainer.appendChild(notificationbox); this.mPanelContainer.appendChild(notificationbox);
} }
// We've waited until the tab is in the DOM to set the label. This
// allows the TabLabelModified event to be properly dispatched.
if (!aURI || isBlankPageURL(aURI)) {
t.label = this.mStringBundle.getString("tabs.emptyTabTitle");
} else if (aURI.toLowerCase().startsWith("javascript:")) {
// This can go away when bug 672618 or bug 55696 are fixed.
t.label = aURI;
}
this.tabContainer.updateVisibility(); this.tabContainer.updateVisibility();
// wire up a progress listener for the new browser object. // wire up a progress listener for the new browser object.
@ -6073,8 +6071,7 @@
class="tab-icon-overlay" class="tab-icon-overlay"
role="presentation"/> role="presentation"/>
<xul:label flex="1" <xul:label flex="1"
anonid="tab-label" xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected,visuallyselected,attention"
xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected,visuallyselected,attention"
class="tab-text tab-label" class="tab-text tab-label"
role="presentation"/> role="presentation"/>
<xul:image xbl:inherits="soundplaying,pinned,muted,visuallyselected" <xul:image xbl:inherits="soundplaying,pinned,muted,visuallyselected"
@ -6140,32 +6137,6 @@
</setter> </setter>
</property> </property>
<property name="label">
<getter>
return this.getAttribute("label");
</getter>
<setter>
this.setAttribute("label", val);
let event = new CustomEvent("TabLabelModified", {
bubbles: true,
cancelable: true
});
this.dispatchEvent(event);
// Let listeners prevent synchronizing the actual label to the
// visible label (allowing them to override the visible label).
if (!event.defaultPrevented)
this.visibleLabel = val;
</setter>
</property>
<property name="visibleLabel">
<getter>
return this.getAttribute("visibleLabel");
</getter>
<setter>
this.setAttribute("visibleLabel", val);
</setter>
</property>
<property name="pinned" readonly="true"> <property name="pinned" readonly="true">
<getter> <getter>
return this.getAttribute("pinned") == "true"; return this.getAttribute("pinned") == "true";

View File

@ -8,4 +8,3 @@ support-files =
skip-if = true # Bug 1245937 - Intermittent failures/timeouts. skip-if = true # Bug 1245937 - Intermittent failures/timeouts.
[browser_focus.js] [browser_focus.js]
[browser_tearoff.js] [browser_tearoff.js]
skip-if = true # Bug 1245805 - tearing off chat windows causes a browser crash.

View File

@ -65,7 +65,7 @@ add_chat_task(function* testTearoffChat() {
Assert.equal(chatbox.getAttribute("label"), chatTitle, "window should have same title as chat"); Assert.equal(chatbox.getAttribute("label"), chatTitle, "window should have same title as chat");
yield ContentTask.spawn(chatbox.content, null, function* () { yield ContentTask.spawn(chatbox.content, null, function* () {
div = content.document.getElementById("testdiv"); let div = content.document.getElementById("testdiv");
is(div.getAttribute("test"), "1", "docshell should have been swapped"); is(div.getAttribute("test"), "1", "docshell should have been swapped");
div.setAttribute("test", "2"); div.setAttribute("test", "2");
}); });

View File

@ -491,7 +491,6 @@ skip-if = os == "linux" # Bug 1073339 - Investigate autocomplete test unreliabil
[browser_utilityOverlay.js] [browser_utilityOverlay.js]
[browser_viewSourceInTabOnViewSource.js] [browser_viewSourceInTabOnViewSource.js]
[browser_visibleFindSelection.js] [browser_visibleFindSelection.js]
[browser_visibleLabel.js]
[browser_visibleTabs.js] [browser_visibleTabs.js]
[browser_visibleTabs_bookmarkAllPages.js] [browser_visibleTabs_bookmarkAllPages.js]
skip-if = true # Bug 1005420 - fails intermittently. also with e10s enabled: bizarre problem with hidden tab having _mouseenter called, via _setPositionalAttributes, and tab not being found resulting in 'candidate is undefined' skip-if = true # Bug 1005420 - fails intermittently. also with e10s enabled: bizarre problem with hidden tab having _mouseenter called, via _setPositionalAttributes, and tab not being found resulting in 'candidate is undefined'

View File

@ -1,94 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* Tests:
* verify that the visibleLabel attribute works
* verify the TabLabelModified event works for both existing and new tabs
*/
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
gBrowser.removeCurrentTab({animate: false});
});
let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank",
{skipAnimation: true});
tab.linkedBrowser.addEventListener("load", function onLoad(event) {
event.currentTarget.removeEventListener("load", onLoad, true);
executeSoon(afterLoad);
}, true);
}
function afterLoad() {
let tab = gBrowser.selectedTab;
let xulLabel = document.getAnonymousElementByAttribute(tab, "anonid",
"tab-label");
// Verify we're starting out on the right foot
is(tab.label, "New Tab", "Initial tab label is default");
is(xulLabel.value, "New Tab", "Label element is default");
is(tab.visibleLabel, "New Tab", "visibleLabel is default");
// Check that a normal label setting works correctly
tab.label = "Hello, world!";
is(tab.label, "Hello, world!", "tab label attribute set via tab.label");
is(xulLabel.value, "Hello, world!", "xul:label set via tab.label");
is(tab.visibleLabel, "Hello, world!", "visibleLabel set via tab.label");
// Check that setting visibleLabel only affects the label element
tab.visibleLabel = "Goodnight, Irene";
is(tab.label, "Hello, world!", "Tab.label unaffected by visibleLabel setter");
is(xulLabel.value, "Goodnight, Irene",
"xul:label set by visibleLabel setter");
is(tab.visibleLabel, "Goodnight, Irene",
"visibleLabel attribute set by visibleLabel setter");
// Check that setting the label property hits everything
tab.label = "One more label";
is(tab.label, "One more label",
"Tab label set via label property after diverging from visibleLabel");
is(xulLabel.value, "One more label",
"xul:label set via label property after diverging from visibleLabel");
is(tab.visibleLabel, "One more label",
"visibleLabel set from label property after diverging from visibleLabel");
tab.addEventListener("TabLabelModified", overrideTabLabel, true);
tab.label = "This won't be the visibleLabel";
}
function overrideTabLabel(aEvent) {
aEvent.target.removeEventListener("TabLabelModified", overrideTabLabel, true);
aEvent.preventDefault();
aEvent.stopPropagation();
aEvent.target.visibleLabel = "Handler set this as the visible label";
executeSoon(checkTabLabelModified);
}
function checkTabLabelModified() {
let tab = gBrowser.selectedTab;
let xulLabel = document.getAnonymousElementByAttribute(tab, "anonid",
"tab-label");
is(tab.label, "This won't be the visibleLabel",
"Tab label set via label property that triggered event");
is(xulLabel.value, "Handler set this as the visible label",
"xul:label set by TabLabelModified handler");
is(tab.visibleLabel, "Handler set this as the visible label",
"visibleLabel set by TabLabelModified handler");
gBrowser.removeCurrentTab({animate: false});
executeSoon(checkTabLabelModifiedOnNewTab);
}
function checkTabLabelModifiedOnNewTab() {
gBrowser.tabContainer.addEventListener("TabLabelModified",
handleTabLabelModifiedOnNewTab, true);
let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank",
{skipAnimation: true});
}
function handleTabLabelModifiedOnNewTab(aEvent) {
gBrowser.tabContainer.removeEventListener("TabLabelModified",
handleTabLabelModifiedOnNewTab, true);
ok(true, "Event received from new tab default being set");
executeSoon(finish);
}

View File

@ -184,8 +184,6 @@ const CustomizableWidgets = [
let options = PlacesUtils.history.getNewQueryOptions(); let options = PlacesUtils.history.getNewQueryOptions();
options.excludeQueries = true; options.excludeQueries = true;
options.includeHidden = false;
options.resultType = options.RESULTS_AS_URI;
options.queryType = options.QUERY_TYPE_HISTORY; options.queryType = options.QUERY_TYPE_HISTORY;
options.sortingMode = options.SORT_BY_DATE_DESCENDING; options.sortingMode = options.SORT_BY_DATE_DESCENDING;
options.maxResults = kMaxResults; options.maxResults = kMaxResults;

View File

@ -5,8 +5,6 @@ support-files = head.js
[browser_bug400731.js] [browser_bug400731.js]
skip-if = e10s skip-if = e10s
[browser_bug415846.js] [browser_bug415846.js]
skip-if = true skip-if = os == "mac" || e10s
# Disabled because it seems to now touch network resources
# skip-if = os == "mac"
# Disabled on Mac because of its bizarre special-and-unique # Disabled on Mac because of its bizarre special-and-unique
# snowflake of a help menu. # snowflake of a help menu.

View File

@ -3,60 +3,84 @@ menu items.
Mac makes this astonishingly painful to test since their help menu is special magic, Mac makes this astonishingly painful to test since their help menu is special magic,
but we can at least test it on the other platforms.*/ but we can at least test it on the other platforms.*/
var menu;
function test() { const NORMAL_PAGE = "http://example.com";
waitForExplicitFinish(); const PHISH_PAGE = "http://www.itisatrap.org/firefox/its-a-trap.html";
gBrowser.selectedTab = gBrowser.addTab(); /**
* Opens a new tab and browses to some URL, tests for the existence
* of the phishing menu items, and then runs a test function to check
* the state of the menu once opened. This function will take care of
* opening and closing the menu.
*
* @param url (string)
* The URL to browse the tab to.
* @param testFn (function)
* The function to run once the menu has been opened. This
* function will be passed the "reportMenu" and "errorMenu"
* DOM nodes as arguments, in that order. This function
* should not yield anything.
* @returns Promise
*/
function check_menu_at_page(url, testFn) {
return BrowserTestUtils.withNewTab({
gBrowser,
url: "about:blank",
}, function*(browser) {
// We don't get load events when the DocShell redirects to error
// pages, but we do get DOMContentLoaded, so we'll wait for that.
let dclPromise = ContentTask.spawn(browser, null, function*() {
yield ContentTaskUtils.waitForEvent(this, "DOMContentLoaded", false);
});
browser.loadURI(url);
yield dclPromise;
// Navigate to a normal site let menu = document.getElementById("menu_HelpPopup");
gBrowser.addEventListener("DOMContentLoaded", testNormal, false); ok(menu, "Help menu should exist");
content.location = "http://example.com/";
let reportMenu =
document.getElementById("menu_HelpPopup_reportPhishingtoolmenu");
ok(reportMenu, "Report phishing menu item should exist");
let errorMenu =
document.getElementById("menu_HelpPopup_reportPhishingErrortoolmenu");
ok(errorMenu, "Report phishing error menu item should exist");
let menuOpen = BrowserTestUtils.waitForEvent(menu, "popupshown");
menu.openPopup(null, "", 0, 0, false, null);
yield menuOpen;
testFn(reportMenu, errorMenu);
let menuClose = BrowserTestUtils.waitForEvent(menu, "popuphidden");
menu.hidePopup();
yield menuClose;
});
} }
function testNormal() { /**
gBrowser.removeEventListener("DOMContentLoaded", testNormal, false); * Tests that we show the "Report this page" menu item at a normal
* page.
*/
add_task(function*() {
yield check_menu_at_page(NORMAL_PAGE, (reportMenu, errorMenu) => {
ok(!reportMenu.hidden,
"Report phishing menu should be visible on normal sites");
ok(errorMenu.hidden,
"Report error menu item should be hidden on normal sites");
});
});
// open the menu, to force it to update /**
menu = document.getElementById("menu_HelpPopup"); * Tests that we show the "Report this page is okay" menu item at
ok(menu, "Help menu should exist!"); * a reported attack site.
*/
add_task(function*() {
yield check_menu_at_page(PHISH_PAGE, (reportMenu, errorMenu) => {
ok(reportMenu.hidden,
"Report phishing menu should be hidden on phishing sites");
ok(!errorMenu.hidden,
"Report error menu item should be visible on phishing sites");
});
});
menu.addEventListener("popupshown", testNormal_PopupListener, false);
menu.openPopup(null, "", 0, 0, false, null);
}
function testNormal_PopupListener() {
menu.removeEventListener("popupshown", testNormal_PopupListener, false);
var reportMenu = document.getElementById("menu_HelpPopup_reportPhishingtoolmenu");
var errorMenu = document.getElementById("menu_HelpPopup_reportPhishingErrortoolmenu");
is(reportMenu.hidden, false, "Report phishing menu should be visible on normal sites");
is(errorMenu.hidden, true, "Report error menu item should be hidden on normal sites");
menu.hidePopup();
// Now launch the phishing test. Can't use onload here because error pages don't
// fire normal load events.
window.addEventListener("DOMContentLoaded", testPhishing, true);
content.location = "http://www.itisatrap.org/firefox/its-a-trap.html";
}
function testPhishing() {
window.removeEventListener("DOMContentLoaded", testPhishing, true);
menu.addEventListener("popupshown", testPhishing_PopupListener, false);
menu.openPopup(null, "", 0, 0, false, null);
}
function testPhishing_PopupListener() {
menu.removeEventListener("popupshown", testPhishing_PopupListener, false);
var reportMenu = document.getElementById("menu_HelpPopup_reportPhishingtoolmenu");
var errorMenu = document.getElementById("menu_HelpPopup_reportPhishingErrortoolmenu");
is(reportMenu.hidden, true, "Report phishing menu should be hidden on phishing sites");
is(errorMenu.hidden, false, "Report error menu item should be visible on phishing sites");
menu.hidePopup();
gBrowser.removeCurrentTab();
finish();
}

View File

@ -77,7 +77,7 @@ run-if = e10s
[browser_cleaner.js] [browser_cleaner.js]
[browser_cookies.js] [browser_cookies.js]
[browser_crashedTabs.js] [browser_crashedTabs.js]
skip-if = true # bug 1236414 skip-if = !e10s || !crashreporter
[browser_unrestored_crashedTabs.js] [browser_unrestored_crashedTabs.js]
skip-if = !e10s || !crashreporter skip-if = !e10s || !crashreporter
[browser_revive_crashed_bg_tabs.js] [browser_revive_crashed_bg_tabs.js]

View File

@ -3,7 +3,7 @@
"use strict"; "use strict";
requestLongerTimeout(2); requestLongerTimeout(10);
const PAGE_1 = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page."; const PAGE_1 = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
const PAGE_2 = "data:text/html,<html><body>Another%20regular,%20everyday,%20normal%20page."; const PAGE_2 = "data:text/html,<html><body>Another%20regular,%20everyday,%20normal%20page.";
@ -17,9 +17,6 @@ registerCleanupFunction(() => {
// Allow tabs to restore on demand so we can test pending states // Allow tabs to restore on demand so we can test pending states
Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand"); Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
// Running this test in ASAN is slow.
requestLongerTimeout(2);
function clickButton(browser, id) { function clickButton(browser, id) {
info("Clicking " + id); info("Clicking " + id);

View File

@ -1,3 +1,3 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js This is the pdf.js project output, https://github.com/mozilla/pdf.js
Current extension version is: 1.4.42 Current extension version is: 1.4.64

View File

@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdf = {}));
// Use strict in our context only - users might not want it // Use strict in our context only - users might not want it
'use strict'; 'use strict';
var pdfjsVersion = '1.4.42'; var pdfjsVersion = '1.4.64';
var pdfjsBuild = '4c59712'; var pdfjsBuild = '2f145d8';
var pdfjsFilePath = var pdfjsFilePath =
typeof document !== 'undefined' && document.currentScript ? typeof document !== 'undefined' && document.currentScript ?
@ -6258,9 +6258,9 @@ PDFJS.getDocument = function getDocument(src,
throw new Error('Loading aborted'); throw new Error('Loading aborted');
} }
var messageHandler = new MessageHandler(docId, workerId, worker.port); var messageHandler = new MessageHandler(docId, workerId, worker.port);
messageHandler.send('Ready', null);
var transport = new WorkerTransport(messageHandler, task, rangeTransport); var transport = new WorkerTransport(messageHandler, task, rangeTransport);
task._transport = transport; task._transport = transport;
messageHandler.send('Ready', null);
}); });
}).catch(task._capability.reject); }).catch(task._capability.reject);
@ -7102,7 +7102,7 @@ var PDFWorker = (function PDFWorkerClosure() {
_initialize: function PDFWorker_initialize() { _initialize: function PDFWorker_initialize() {
// If worker support isn't disabled explicit and the browser has worker // If worker support isn't disabled explicit and the browser has worker
// support, create a new web worker and test if it/the browser fullfills // support, create a new web worker and test if it/the browser fulfills
// all requirements to run parts of pdf.js in a web worker. // all requirements to run parts of pdf.js in a web worker.
// Right now, the requirement is, that an Uint8Array is still an // Right now, the requirement is, that an Uint8Array is still an
// Uint8Array as it arrives on the worker. (Chrome added this with v.15.) // Uint8Array as it arrives on the worker. (Chrome added this with v.15.)

View File

@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdfWorker = {}));
// Use strict in our context only - users might not want it // Use strict in our context only - users might not want it
'use strict'; 'use strict';
var pdfjsVersion = '1.4.42'; var pdfjsVersion = '1.4.64';
var pdfjsBuild = '4c59712'; var pdfjsBuild = '2f145d8';
var pdfjsFilePath = var pdfjsFilePath =
typeof document !== 'undefined' && document.currentScript ? typeof document !== 'undefined' && document.currentScript ?
@ -29687,15 +29687,21 @@ var Type1Parser = (function Type1ParserClosure() {
} }
function decrypt(data, key, discardNumber) { function decrypt(data, key, discardNumber) {
var r = key | 0, c1 = 52845, c2 = 22719; if (discardNumber >= data.length) {
var count = data.length; return new Uint8Array(0);
}
var r = key | 0, c1 = 52845, c2 = 22719, i, j;
for (i = 0; i < discardNumber; i++) {
r = ((data[i] + r) * c1 + c2) & ((1 << 16) - 1);
}
var count = data.length - discardNumber;
var decrypted = new Uint8Array(count); var decrypted = new Uint8Array(count);
for (var i = 0; i < count; i++) { for (i = discardNumber, j = 0; j < count; i++, j++) {
var value = data[i]; var value = data[i];
decrypted[i] = value ^ (r >> 8); decrypted[j] = value ^ (r >> 8);
r = ((value + r) * c1 + c2) & ((1 << 16) - 1); r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
} }
return Array.prototype.slice.call(decrypted, discardNumber); return decrypted;
} }
function decryptAscii(data, key, discardNumber) { function decryptAscii(data, key, discardNumber) {
@ -33249,15 +33255,15 @@ var ColorSpace = (function ColorSpaceClosure() {
case 'DeviceCmykCS': case 'DeviceCmykCS':
return this.singletons.cmyk; return this.singletons.cmyk;
case 'CalGrayCS': case 'CalGrayCS':
whitePoint = IR[1].WhitePoint; whitePoint = IR[1];
blackPoint = IR[1].BlackPoint; blackPoint = IR[2];
gamma = IR[1].Gamma; gamma = IR[3];
return new CalGrayCS(whitePoint, blackPoint, gamma); return new CalGrayCS(whitePoint, blackPoint, gamma);
case 'CalRGBCS': case 'CalRGBCS':
whitePoint = IR[1].WhitePoint; whitePoint = IR[1];
blackPoint = IR[1].BlackPoint; blackPoint = IR[2];
gamma = IR[1].Gamma; gamma = IR[3];
var matrix = IR[1].Matrix; var matrix = IR[4];
return new CalRGBCS(whitePoint, blackPoint, gamma, matrix); return new CalRGBCS(whitePoint, blackPoint, gamma, matrix);
case 'PatternCS': case 'PatternCS':
var basePatternCS = IR[1]; var basePatternCS = IR[1];
@ -33276,11 +33282,11 @@ var ColorSpace = (function ColorSpaceClosure() {
var tintFnIR = IR[3]; var tintFnIR = IR[3];
return new AlternateCS(numComps, ColorSpace.fromIR(alt), return new AlternateCS(numComps, ColorSpace.fromIR(alt),
PDFFunction.fromIR(tintFnIR)); PDFFunction.fromIR(tintFnIR));
case 'LabCS': case 'LabCS':
whitePoint = IR[1].WhitePoint; whitePoint = IR[1];
blackPoint = IR[1].BlackPoint; blackPoint = IR[2];
var range = IR[1].Range; var range = IR[3];
return new LabCS(whitePoint, blackPoint, range); return new LabCS(whitePoint, blackPoint, range);
default: default:
error('Unknown name ' + name); error('Unknown name ' + name);
@ -33324,7 +33330,7 @@ var ColorSpace = (function ColorSpaceClosure() {
} else if (isArray(cs)) { } else if (isArray(cs)) {
mode = xref.fetchIfRef(cs[0]).name; mode = xref.fetchIfRef(cs[0]).name;
this.mode = mode; this.mode = mode;
var numComps, params, alt; var numComps, params, alt, whitePoint, blackPoint, gamma;
switch (mode) { switch (mode) {
case 'DeviceGray': case 'DeviceGray':
@ -33337,11 +33343,18 @@ var ColorSpace = (function ColorSpaceClosure() {
case 'CMYK': case 'CMYK':
return 'DeviceCmykCS'; return 'DeviceCmykCS';
case 'CalGray': case 'CalGray':
params = xref.fetchIfRef(cs[1]).getAll(); params = xref.fetchIfRef(cs[1]);
return ['CalGrayCS', params]; whitePoint = params.get('WhitePoint');
blackPoint = params.get('BlackPoint');
gamma = params.get('Gamma');
return ['CalGrayCS', whitePoint, blackPoint, gamma];
case 'CalRGB': case 'CalRGB':
params = xref.fetchIfRef(cs[1]).getAll(); params = xref.fetchIfRef(cs[1]);
return ['CalRGBCS', params]; whitePoint = params.get('WhitePoint');
blackPoint = params.get('BlackPoint');
gamma = params.get('Gamma');
var matrix = params.get('Matrix');
return ['CalRGBCS', whitePoint, blackPoint, gamma, matrix];
case 'ICCBased': case 'ICCBased':
var stream = xref.fetchIfRef(cs[1]); var stream = xref.fetchIfRef(cs[1]);
var dict = stream.dict; var dict = stream.dict;
@ -33393,8 +33406,11 @@ var ColorSpace = (function ColorSpaceClosure() {
var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3])); var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3]));
return ['AlternateCS', numComps, alt, tintFnIR]; return ['AlternateCS', numComps, alt, tintFnIR];
case 'Lab': case 'Lab':
params = xref.fetchIfRef(cs[1]).getAll(); params = xref.fetchIfRef(cs[1]);
return ['LabCS', params]; whitePoint = params.get('WhitePoint');
blackPoint = params.get('BlackPoint');
var range = params.get('Range');
return ['LabCS', whitePoint, blackPoint, range];
default: default:
error('unimplemented color space object "' + mode + '"'); error('unimplemented color space object "' + mode + '"');
} }
@ -35949,17 +35965,19 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
processed[resources.objId] = true; processed[resources.objId] = true;
} }
var nodes = [resources]; var nodes = [resources], xref = this.xref;
while (nodes.length) { while (nodes.length) {
var key; var key, i, ii;
var node = nodes.shift(); var node = nodes.shift();
// First check the current resources for blend modes. // First check the current resources for blend modes.
var graphicStates = node.get('ExtGState'); var graphicStates = node.get('ExtGState');
if (isDict(graphicStates)) { if (isDict(graphicStates)) {
graphicStates = graphicStates.getAll(); var graphicStatesKeys = graphicStates.getKeys();
for (key in graphicStates) { for (i = 0, ii = graphicStatesKeys.length; i < ii; i++) {
var graphicState = graphicStates[key]; key = graphicStatesKeys[i];
var bm = graphicState['BM'];
var graphicState = graphicStates.get(key);
var bm = graphicState.get('BM');
if (isName(bm) && bm.name !== 'Normal') { if (isName(bm) && bm.name !== 'Normal') {
return true; return true;
} }
@ -35970,9 +35988,20 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (!isDict(xObjects)) { if (!isDict(xObjects)) {
continue; continue;
} }
xObjects = xObjects.getAll(); var xObjectsKeys = xObjects.getKeys();
for (key in xObjects) { for (i = 0, ii = xObjectsKeys.length; i < ii; i++) {
var xObject = xObjects[key]; key = xObjectsKeys[i];
var xObject = xObjects.getRaw(key);
if (isRef(xObject)) {
if (processed[xObject.toString()]) {
// The XObject has already been processed, and by avoiding a
// redundant `xref.fetch` we can *significantly* reduce the load
// time for badly generated PDF files (fixes issue6961.pdf).
continue;
}
xObject = xref.fetch(xObject);
}
if (!isStream(xObject)) { if (!isStream(xObject)) {
continue; continue;
} }
@ -36266,11 +36295,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
xref, stateManager) { xref, stateManager) {
// This array holds the converted/processed state data. // This array holds the converted/processed state data.
var gStateObj = []; var gStateObj = [];
var gStateMap = gState.map; var gStateKeys = gState.getKeys();
var self = this; var self = this;
var promise = Promise.resolve(); var promise = Promise.resolve();
for (var key in gStateMap) { for (var i = 0, ii = gStateKeys.length; i < ii; i++) {
var value = gStateMap[key]; var key = gStateKeys[i];
var value = gState.get(key);
switch (key) { switch (key) {
case 'Type': case 'Type':
break; break;
@ -36303,12 +36333,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
gStateObj.push([key, false]); gStateObj.push([key, false]);
break; break;
} }
var dict = xref.fetchIfRef(value); if (isDict(value)) {
if (isDict(dict)) { promise = promise.then(function (dict) {
promise = promise.then(function () {
return self.handleSMask(dict, resources, operatorList, return self.handleSMask(dict, resources, operatorList,
task, stateManager); task, stateManager);
}); }.bind(this, value));
gStateObj.push([key, true]); gStateObj.push([key, true]);
} else { } else {
warn('Unsupported SMask type'); warn('Unsupported SMask type');
@ -36340,7 +36369,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
} }
} }
return promise.then(function () { return promise.then(function () {
if (gStateObj.length >= 0) { if (gStateObj.length > 0) {
operatorList.addOp(OPS.setGState, [gStateObj]); operatorList.addOp(OPS.setGState, [gStateObj]);
} }
}); });
@ -37885,13 +37914,13 @@ var TranslatedFont = (function TranslatedFontClosure() {
var translatedFont = this.font; var translatedFont = this.font;
var loadCharProcsPromise = Promise.resolve(); var loadCharProcsPromise = Promise.resolve();
var charProcs = this.dict.get('CharProcs').getAll(); var charProcs = this.dict.get('CharProcs');
var fontResources = this.dict.get('Resources') || resources; var fontResources = this.dict.get('Resources') || resources;
var charProcKeys = Object.keys(charProcs); var charProcKeys = charProcs.getKeys();
var charProcOperatorList = Object.create(null); var charProcOperatorList = Object.create(null);
for (var i = 0, n = charProcKeys.length; i < n; ++i) { for (var i = 0, n = charProcKeys.length; i < n; ++i) {
loadCharProcsPromise = loadCharProcsPromise.then(function (key) { loadCharProcsPromise = loadCharProcsPromise.then(function (key) {
var glyphStream = charProcs[key]; var glyphStream = charProcs.get(key);
var operatorList = new OperatorList(); var operatorList = new OperatorList();
return evaluator.getOperatorList(glyphStream, task, fontResources, return evaluator.getOperatorList(glyphStream, task, fontResources,
operatorList).then(function () { operatorList).then(function () {

View File

@ -168,6 +168,7 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY subscribeToPageMenuitem.label "Subscribe to This Page…"> <!ENTITY subscribeToPageMenuitem.label "Subscribe to This Page…">
<!ENTITY addCurPagesCmd.label "Bookmark All Tabs…"> <!ENTITY addCurPagesCmd.label "Bookmark All Tabs…">
<!ENTITY showAllBookmarks2.label "Show All Bookmarks"> <!ENTITY showAllBookmarks2.label "Show All Bookmarks">
<!ENTITY recentBookmarks.label "Recently Bookmarked">
<!ENTITY unsortedBookmarksCmd.label "Unsorted Bookmarks"> <!ENTITY unsortedBookmarksCmd.label "Unsorted Bookmarks">
<!ENTITY bookmarksToolbarChevron.tooltip "Show more bookmarks"> <!ENTITY bookmarksToolbarChevron.tooltip "Show more bookmarks">
@ -528,6 +529,8 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY openFrameCmdInTab.accesskey "T"> <!ENTITY openFrameCmdInTab.accesskey "T">
<!ENTITY openFrameCmd.label "Open Frame in New Window"> <!ENTITY openFrameCmd.label "Open Frame in New Window">
<!ENTITY openFrameCmd.accesskey "W"> <!ENTITY openFrameCmd.accesskey "W">
<!ENTITY openLinkCmdInContainerTab.label "Open Link in New Container Tab">
<!ENTITY openLinkCmdInContainerTab.accesskey "C">
<!ENTITY showOnlyThisFrameCmd.label "Show Only This Frame"> <!ENTITY showOnlyThisFrameCmd.label "Show Only This Frame">
<!ENTITY showOnlyThisFrameCmd.accesskey "S"> <!ENTITY showOnlyThisFrameCmd.accesskey "S">
<!ENTITY reloadCmd.commandkey "r"> <!ENTITY reloadCmd.commandkey "r">

View File

@ -4,4 +4,3 @@ support-files =
head.js head.js
[browser_screenshots.js] [browser_screenshots.js]
tags = screenshots

View File

@ -5,15 +5,12 @@
"use strict"; "use strict";
add_task(function* capture() { add_task(function* capture() {
if (!shouldCapture()) { let setsEnv = env.get("MOZSCREENSHOTS_SETS");
if (!setsEnv) {
ok(true, "MOZSCREENSHOTS_SETS wasn't specified so there's nothing to capture");
return; return;
} }
let { TestRunner } = Cu.import("chrome://mozscreenshots/content/TestRunner.jsm", {});
let sets = ["TabsInTitlebar", "Tabs", "WindowSize", "Toolbars", "LightweightThemes"];
let setsEnv = env.get("MOZSCREENSHOTS_SETS");
if (setsEnv) {
sets = setsEnv.trim().split(",");
}
let sets = setsEnv.trim().split(",");
yield TestRunner.start(sets); yield TestRunner.start(sets);
}); });

View File

@ -0,0 +1,6 @@
[DEFAULT]
subsuite = screenshots
support-files =
../head.js
[browser_devtools.js]

View File

@ -0,0 +1,14 @@
/* 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/. */
"use strict";
add_task(function* capture() {
if (!shouldCapture()) {
return;
}
let sets = ["DevTools"];
yield TestRunner.start(sets);
});

View File

@ -6,6 +6,7 @@
const {AddonWatcher} = Cu.import("resource://gre/modules/AddonWatcher.jsm", {}); const {AddonWatcher} = Cu.import("resource://gre/modules/AddonWatcher.jsm", {});
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
let TestRunner;
function setup() { function setup() {
requestLongerTimeout(20); requestLongerTimeout(20);
@ -15,18 +16,25 @@ function setup() {
AddonManager.getAddonByID("mozscreenshots@mozilla.org", function(aAddon) { AddonManager.getAddonByID("mozscreenshots@mozilla.org", function(aAddon) {
isnot(aAddon, null, "The mozscreenshots extension should be installed"); isnot(aAddon, null, "The mozscreenshots extension should be installed");
AddonWatcher.ignoreAddonPermanently(aAddon.id); AddonWatcher.ignoreAddonPermanently(aAddon.id);
TestRunner = Cu.import("chrome://mozscreenshots/content/TestRunner.jsm", {}).TestRunner;
resolve(); resolve();
}); });
}); });
} }
function shouldCapture() { function shouldCapture() {
// Try pushes only capture in browser_screenshots.js with MOZSCREENSHOTS_SETS.
if (env.get("MOZSCREENSHOTS_SETS")) {
ok(true, "MOZSCREENSHOTS_SETS was specified so only capture what was " +
"requested (in browser_screenshots.js)");
return false;
}
// Automation isn't able to schedule test jobs to only run on nightlies so we handle it here // Automation isn't able to schedule test jobs to only run on nightlies so we handle it here
// (see also: bug 1116275). Try pushes and local builds should also capture. // (see also: bug 1116275).
let capture = AppConstants.MOZ_UPDATE_CHANNEL == "nightly" || let capture = AppConstants.MOZ_UPDATE_CHANNEL == "nightly" ||
(AppConstants.SOURCE_REVISION_URL.includes("/try/rev/") && AppConstants.SOURCE_REVISION_URL == "" ||
env.get("MOZSCREENSHOTS_SETS")) || AppConstants.SOURCE_REVISION_URL == "1"; // bug 1248027
AppConstants.SOURCE_REVISION_URL == "";
if (!capture) { if (!capture) {
ok(true, "Capturing is disabled for this MOZ_UPDATE_CHANNEL or REPO"); ok(true, "Capturing is disabled for this MOZ_UPDATE_CHANNEL or REPO");
} }

View File

@ -4,7 +4,14 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
BROWSER_CHROME_MANIFESTS += ['browser.ini'] BROWSER_CHROME_MANIFESTS += [
# Each test is in it's own directory so it gets run in a clean profile with
# run-by-dir.
'browser.ini',
'devtools/browser.ini',
'preferences/browser.ini',
'primaryUI/browser.ini',
]
TEST_DIRS += [ TEST_DIRS += [
'mozscreenshots/extension', 'mozscreenshots/extension',

View File

@ -11,7 +11,6 @@ const defaultSetNames = ["TabsInTitlebar", "Tabs", "WindowSize", "Toolbars", "Li
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
const HOME_PAGE = "chrome://mozscreenshots/content/lib/mozscreenshots.html"; const HOME_PAGE = "chrome://mozscreenshots/content/lib/mozscreenshots.html";
Cu.import("resource://testing-common/BrowserTestUtils.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/Task.jsm");
@ -19,6 +18,9 @@ Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "BrowserTestUtils",
"resource://testing-common/BrowserTestUtils.jsm");
Cu.import("chrome://mozscreenshots/content/Screenshot.jsm"); Cu.import("chrome://mozscreenshots/content/Screenshot.jsm");
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref. // Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
@ -42,6 +44,7 @@ this.TestRunner = {
_libDir: null, _libDir: null,
init(extensionPath) { init(extensionPath) {
log.info("init");
this._extensionPath = extensionPath; this._extensionPath = extensionPath;
}, },

View File

@ -9,7 +9,9 @@ this.EXPORTED_SYMBOLS = ["Preferences"];
const {classes: Cc, interfaces: Ci, utils: Cu} = Components; const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Timer.jsm"); Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://testing-common/TestUtils.jsm");
this.Preferences = { this.Preferences = {
@ -31,7 +33,7 @@ this.Preferences = {
["paneAdvanced", "encryptionTab"], ["paneAdvanced", "encryptionTab"],
]; ];
for (let [primary, advanced] of panes) { for (let [primary, advanced] of panes) {
let configName = primary + ("-" + advanced || ""); let configName = primary.replace(/^pane/, "prefs") + (advanced ? "-" + advanced : "");
this.configurations[configName] = {}; this.configurations[configName] = {};
this.configurations[configName].applyConfig = prefHelper.bind(null, primary, advanced); this.configurations[configName].applyConfig = prefHelper.bind(null, primary, advanced);
} }
@ -40,14 +42,27 @@ this.Preferences = {
configurations: {}, configurations: {},
}; };
function prefHelper(primary, advanced) { let prefHelper = Task.async(function*(primary, advanced) {
return new Promise((resolve) => { let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); let selectedBrowser = browserWindow.gBrowser;
if (primary == "paneAdvanced") { let readyPromise = null;
browserWindow.openAdvancedPreferences(advanced); if (selectedBrowser.currentURI.specIgnoringRef == "about:preferences") {
} else { readyPromise = new Promise((resolve) => {
browserWindow.openPreferences(primary); browserWindow.addEventListener("MozAfterPaint", function paneSwitch() {
} browserWindow.removeEventListener("MozAfterPaint", paneSwitch);
setTimeout(resolve, 50); resolve();
}); });
} });
} else {
readyPromise = TestUtils.topicObserved("advanced-pane-loaded");
}
if (primary == "paneAdvanced") {
browserWindow.openAdvancedPreferences(advanced);
} else {
browserWindow.openPreferences(primary);
}
yield readyPromise;
});

View File

@ -0,0 +1,6 @@
[DEFAULT]
subsuite = screenshots
support-files =
../head.js
[browser_preferences.js]

View File

@ -0,0 +1,14 @@
/* 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/. */
"use strict";
add_task(function* capture() {
if (!shouldCapture()) {
return;
}
let sets = ["Preferences"];
yield TestRunner.start(sets);
});

View File

@ -0,0 +1,6 @@
[DEFAULT]
subsuite = screenshots
support-files =
../head.js
[browser_primaryUI.js]

View File

@ -0,0 +1,14 @@
/* 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/. */
"use strict";
add_task(function* capture() {
if (!shouldCapture()) {
return;
}
let sets = ["TabsInTitlebar", "Tabs", "WindowSize", "Toolbars", "LightweightThemes"];
yield TestRunner.start(sets);
});

View File

@ -19,19 +19,11 @@ buildscript {
repositories { repositories {
jcenter() jcenter()
// For spoon-gradle-plugin SNAPSHOT release. This needs to go before
// the snapshots repository, otherwise we find a remote 1.0.3-SNAPSHOT
// that doesn't include nalexander's local changes.
maven {
url "file://${gradle.mozconfig.topsrcdir}/mobile/android/gradle/m2repo"
}
} }
dependencies { dependencies {
// Unit testing support was added in 1.1.0. IntelliJ 14.1.4 and Android
// Studio 1.2.1.1 appear to work fine with plugin version 1.3.0.
classpath 'com.android.tools.build:gradle:1.3.0' classpath 'com.android.tools.build:gradle:1.3.0'
classpath('com.stanfy.spoon:spoon-gradle-plugin:1.0.3-SNAPSHOT') { classpath('com.stanfy.spoon:spoon-gradle-plugin:1.0.4') {
// Without these, we get errors linting. // Without these, we get errors linting.
exclude module: 'guava' exclude module: 'guava'
} }

View File

@ -106,6 +106,7 @@ leak:nsBaseWidget::StoreWindowClipRegion
leak:libcairo.so leak:libcairo.so
leak:libdl.so leak:libdl.so
leak:libdricore.so leak:libdricore.so
leak:libdricore9.2.1.so
leak:libGL.so leak:libGL.so
leak:libglib-2.0.so leak:libglib-2.0.so
leak:libp11-kit.so leak:libp11-kit.so

View File

@ -3758,6 +3758,7 @@ MOZ_SCTP=
MOZ_ANDROID_OMX= MOZ_ANDROID_OMX=
MOZ_MEDIA_NAVIGATOR= MOZ_MEDIA_NAVIGATOR=
MOZ_OMX_PLUGIN= MOZ_OMX_PLUGIN=
MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE=
MOZ_VPX_ERROR_CONCEALMENT= MOZ_VPX_ERROR_CONCEALMENT=
MOZ_WEBSPEECH=1 MOZ_WEBSPEECH=1
MOZ_WEBSPEECH_MODELS= MOZ_WEBSPEECH_MODELS=
@ -5366,8 +5367,26 @@ if test -n "$MOZ_OMX_PLUGIN"; then
dnl Only allow building OMX plugin on Gonk (B2G) or Android dnl Only allow building OMX plugin on Gonk (B2G) or Android
AC_DEFINE(MOZ_OMX_PLUGIN) AC_DEFINE(MOZ_OMX_PLUGIN)
else else
dnl fail if we're not building on Gonk or Android dnl fail if we're not building on Gonk or Android
AC_MSG_ERROR([OMX media plugin can only be built on B2G or Android]) AC_MSG_ERROR([OMX media plugin can only be built on B2G or Android])
fi
fi
dnl ========================================================
dnl = Enable building mobile/android with Gradle
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(gradle-mobile-android-builds,
[ --enable-gradle-mobile-android-builds Enable building mobile/android with Gradle],
MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE=1,
MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE=)
if test -n "$MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE"; then
if test "$OS_TARGET" = "Android" -a x"$MOZ_WIDGET_TOOLKIT" != x"gonk"; then
dnl Only allow building mobile/android with Gradle.
AC_DEFINE(MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE)
else
dnl fail if we're not building mobile/android.
AC_MSG_ERROR([Can only build mobile/android with Gradle])
fi fi
fi fi
@ -8900,6 +8919,7 @@ AC_SUBST(MOZ_DIRECTSHOW)
AC_SUBST(MOZ_ANDROID_OMX) AC_SUBST(MOZ_ANDROID_OMX)
AC_SUBST(MOZ_APPLEMEDIA) AC_SUBST(MOZ_APPLEMEDIA)
AC_SUBST(MOZ_OMX_PLUGIN) AC_SUBST(MOZ_OMX_PLUGIN)
AC_SUBST(MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE)
AC_SUBST(MOZ_VPX_ERROR_CONCEALMENT) AC_SUBST(MOZ_VPX_ERROR_CONCEALMENT)
AC_SUBST(VPX_AS) AC_SUBST(VPX_AS)
AC_SUBST_LIST(VPX_ASFLAGS) AC_SUBST_LIST(VPX_ASFLAGS)

View File

@ -5,10 +5,13 @@
"use strict"; "use strict";
const l10n = require("gcli/l10n"); const l10n = require("gcli/l10n");
const {
getCssDocs, var MdnDocsWidget;
PAGE_LINK_URL try {
} = require("devtools/client/shared/widgets/MdnDocsWidget"); MdnDocsWidget = require("devtools/client/shared/widgets/MdnDocsWidget");
} catch (e) {
// DevTools MdnDocsWidget only available in Firefox Desktop
}
exports.items = [{ exports.items = [{
name: "mdn", name: "mdn",
@ -26,10 +29,14 @@ exports.items = [{
description: l10n.lookup("mdnCssProp") description: l10n.lookup("mdnCssProp")
}], }],
exec: function(args) { exec: function(args) {
return getCssDocs(args.property).then(result => { if (!MdnDocsWidget) {
return null;
}
return MdnDocsWidget.getCssDocs(args.property).then(result => {
return { return {
data: result, data: result,
url: PAGE_LINK_URL + args.property, url: MdnDocsWidget.PAGE_LINK_URL + args.property,
property: args.property property: args.property
}; };
}, error => { }, error => {

View File

@ -3513,22 +3513,19 @@ nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
nsCOMPtr<nsIDocShell> targetDS = do_QueryInterface(aTargetItem); nsCOMPtr<nsIDocShell> targetDS = do_QueryInterface(aTargetItem);
nsCOMPtr<nsIDocShell> accessingDS = do_QueryInterface(aAccessingItem); nsCOMPtr<nsIDocShell> accessingDS = do_QueryInterface(aAccessingItem);
if (!!targetDS != !!accessingDS) { if (!targetDS || !accessingDS) {
// We must be able to convert both or neither to nsIDocShell. // We must be able to convert both to nsIDocShell.
return false; return false;
} }
if (targetDS && accessingDS && if (targetDS->GetIsInBrowserElement() != accessingDS->GetIsInBrowserElement() ||
(targetDS->GetIsInBrowserElement() != targetDS->GetAppId() != accessingDS->GetAppId()) {
accessingDS->GetIsInBrowserElement() ||
targetDS->GetAppId() != accessingDS->GetAppId())) {
return false; return false;
} }
// A private document can't access a non-private one, and vice versa. // A private document can't access a non-private one, and vice versa.
if (aTargetItem->GetDocument()->GetLoadContext()->UsePrivateBrowsing() != if (static_cast<nsDocShell*>(targetDS.get())->UsePrivateBrowsing() !=
aAccessingItem->GetDocument()->GetLoadContext()->UsePrivateBrowsing()) static_cast<nsDocShell*>(accessingDS.get())->UsePrivateBrowsing()) {
{
return false; return false;
} }

View File

@ -6,7 +6,6 @@
document.write("<h5 id='dynamic'>document.written content</h5>"); document.write("<h5 id='dynamic'>document.written content</h5>");
document.close(); document.close();
window.history.go(-1); window.history.go(-1);
opener.setTimeout("isTestDynamic()", 2500);
} }
function start() { function start() {
@ -14,6 +13,15 @@
setTimeout(run, 0); setTimeout(run, 0);
} }
} }
window.addEventListener("pageshow",
function() {
++opener.file_document_write_1_loadCount;
if (opener.file_document_write_1_loadCount == 2) {
opener.setTimeout("isTestDynamic()", 0);
}
opener.ok(opener.file_document_write_1_loadCount <= 2);
});
</script> </script>
</head> </head>
<body onload="start();"> <body onload="start();">

View File

@ -49,6 +49,7 @@ function nextTest_() {
} }
// Needed by file_document_write_1.html // Needed by file_document_write_1.html
window.file_document_write_1_loadCount = 0;
function isTestDynamic() { function isTestDynamic() {
var dyn = testWindow.document.getElementById("dynamic"); var dyn = testWindow.document.getElementById("dynamic");
is(dyn, null, "Should have gone back to the static page!"); is(dyn, null, "Should have gone back to the static page!");

View File

@ -0,0 +1,22 @@
/* -*- 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 "mozilla/dom/AnimationEffectTiming.h"
#include "mozilla/dom/AnimatableBinding.h"
#include "mozilla/dom/AnimationEffectTimingBinding.h"
namespace mozilla {
namespace dom {
JSObject*
AnimationEffectTiming::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return AnimationEffectTimingBinding::Wrap(aCx, this, aGivenProto);
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,27 @@
/* -*- 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_AnimationEffectTiming_h
#define mozilla_dom_AnimationEffectTiming_h
#include "mozilla/dom/AnimationEffectTimingReadOnly.h"
namespace mozilla {
namespace dom {
class AnimationEffectTiming : public AnimationEffectTimingReadOnly
{
public:
explicit AnimationEffectTiming(const TimingParams& aTiming)
: AnimationEffectTimingReadOnly(aTiming) { }
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_AnimationEffectTiming_h

View File

@ -11,6 +11,7 @@
#include "nsIAtom.h" #include "nsIAtom.h"
#include "nsIContent.h" #include "nsIContent.h"
#include "nsString.h" #include "nsString.h"
#include "mozilla/Attributes.h"
#include "mozilla/ComputedTimingFunction.h" // ComputedTimingFunction #include "mozilla/ComputedTimingFunction.h" // ComputedTimingFunction
#include "mozilla/dom/Element.h" // For dom::Element #include "mozilla/dom/Element.h" // For dom::Element
@ -69,7 +70,7 @@ AnimationUtils::ParseEasing(const dom::Element* aTarget,
NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR) { NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR) {
return Nothing(); return Nothing();
} }
// Fall through MOZ_FALLTHROUGH;
case eCSSUnit_Cubic_Bezier: case eCSSUnit_Cubic_Bezier:
case eCSSUnit_Steps: { case eCSSUnit_Steps: {
nsTimingFunction timingFunction; nsTimingFunction timingFunction;

View File

@ -76,14 +76,24 @@ KeyframeEffectReadOnly::KeyframeEffectReadOnly(
Element* aTarget, Element* aTarget,
nsCSSPseudoElements::Type aPseudoType, nsCSSPseudoElements::Type aPseudoType,
const TimingParams& aTiming) const TimingParams& aTiming)
: KeyframeEffectReadOnly(aDocument, aTarget, aPseudoType,
new AnimationEffectTimingReadOnly(aTiming))
{
}
KeyframeEffectReadOnly::KeyframeEffectReadOnly(
nsIDocument* aDocument,
Element* aTarget,
nsCSSPseudoElements::Type aPseudoType,
AnimationEffectTimingReadOnly* aTiming)
: AnimationEffectReadOnly(aDocument) : AnimationEffectReadOnly(aDocument)
, mTarget(aTarget) , mTarget(aTarget)
, mTiming(*aTiming)
, mPseudoType(aPseudoType) , mPseudoType(aPseudoType)
, mInEffectOnLastAnimationTimingUpdate(false) , mInEffectOnLastAnimationTimingUpdate(false)
{ {
MOZ_ASSERT(aTiming);
MOZ_ASSERT(aTarget, "null animation target is not yet supported"); MOZ_ASSERT(aTarget, "null animation target is not yet supported");
mTiming = new AnimationEffectTimingReadOnly(aTiming);
} }
JSObject* JSObject*
@ -595,6 +605,72 @@ KeyframeEffectReadOnly::~KeyframeEffectReadOnly()
{ {
} }
template <class KeyframeEffectType>
/* static */ already_AddRefed<KeyframeEffectType>
KeyframeEffectReadOnly::ConstructKeyframeEffect(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const TimingParams& aTiming,
ErrorResult& aRv)
{
if (aTarget.IsNull()) {
// We don't support null targets yet.
aRv.Throw(NS_ERROR_DOM_ANIM_NO_TARGET_ERR);
return nullptr;
}
const ElementOrCSSPseudoElement& target = aTarget.Value();
MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(),
"Uninitialized target");
RefPtr<Element> targetElement;
nsCSSPseudoElements::Type pseudoType =
nsCSSPseudoElements::ePseudo_NotPseudoElement;
if (target.IsElement()) {
targetElement = &target.GetAsElement();
} else {
targetElement = target.GetAsCSSPseudoElement().ParentElement();
pseudoType = target.GetAsCSSPseudoElement().GetType();
}
if (!targetElement->GetComposedDoc()) {
aRv.Throw(NS_ERROR_DOM_ANIM_TARGET_NOT_IN_DOC_ERR);
return nullptr;
}
InfallibleTArray<AnimationProperty> animationProperties;
BuildAnimationPropertyList(aGlobal.Context(), targetElement, pseudoType,
aFrames, animationProperties, aRv);
if (aRv.Failed()) {
return nullptr;
}
RefPtr<KeyframeEffectType> effect =
new KeyframeEffectType(targetElement->OwnerDoc(), targetElement,
pseudoType, aTiming);
effect->mProperties = Move(animationProperties);
return effect.forget();
}
// Explicit instantiations to avoid linker errors.
template
already_AddRefed<KeyframeEffectReadOnly>
KeyframeEffectReadOnly::ConstructKeyframeEffect<>(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const TimingParams& aTiming,
ErrorResult& aRv);
template
already_AddRefed<KeyframeEffect>
KeyframeEffectReadOnly::ConstructKeyframeEffect<>(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const TimingParams& aTiming,
ErrorResult& aRv);
void void
KeyframeEffectReadOnly::ResetIsRunningOnCompositor() KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
{ {
@ -1635,55 +1711,6 @@ KeyframeEffectReadOnly::BuildAnimationPropertyList(
} }
} }
/* static */ already_AddRefed<KeyframeEffectReadOnly>
KeyframeEffectReadOnly::Constructor(
const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const TimingParams& aTiming,
ErrorResult& aRv)
{
if (aTarget.IsNull()) {
// We don't support null targets yet.
aRv.Throw(NS_ERROR_DOM_ANIM_NO_TARGET_ERR);
return nullptr;
}
const ElementOrCSSPseudoElement& target = aTarget.Value();
MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(),
"Uninitialized target");
RefPtr<Element> targetElement;
nsCSSPseudoElements::Type pseudoType =
nsCSSPseudoElements::ePseudo_NotPseudoElement;
if (target.IsElement()) {
targetElement = &target.GetAsElement();
} else {
targetElement = target.GetAsCSSPseudoElement().ParentElement();
pseudoType = target.GetAsCSSPseudoElement().GetType();
}
if (!targetElement->GetCurrentDoc()) {
// Bug 1245748: We don't support targets that are not in a document yet.
aRv.Throw(NS_ERROR_DOM_ANIM_TARGET_NOT_IN_DOC_ERR);
return nullptr;
}
InfallibleTArray<AnimationProperty> animationProperties;
BuildAnimationPropertyList(aGlobal.Context(), targetElement, pseudoType,
aFrames, animationProperties, aRv);
if (aRv.Failed()) {
return nullptr;
}
RefPtr<KeyframeEffectReadOnly> effect =
new KeyframeEffectReadOnly(targetElement->OwnerDoc(), targetElement,
pseudoType, aTiming);
effect->mProperties = Move(animationProperties);
return effect.forget();
}
void void
KeyframeEffectReadOnly::GetTarget( KeyframeEffectReadOnly::GetTarget(
Nullable<OwningElementOrCSSPseudoElement>& aRv) const Nullable<OwningElementOrCSSPseudoElement>& aRv) const
@ -2091,5 +2118,27 @@ KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(const nsIFrame*
return false; return false;
} }
//---------------------------------------------------------------------
//
// KeyframeEffect
//
//---------------------------------------------------------------------
KeyframeEffect::KeyframeEffect(nsIDocument* aDocument,
Element* aTarget,
nsCSSPseudoElements::Type aPseudoType,
const TimingParams& aTiming)
: KeyframeEffectReadOnly(aDocument, aTarget, aPseudoType,
new AnimationEffectTiming(aTiming))
{
}
JSObject*
KeyframeEffect::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return KeyframeEffectBinding::Wrap(aCx, this, aGivenProto);
}
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla

View File

@ -20,6 +20,7 @@
#include "mozilla/StyleAnimationValue.h" #include "mozilla/StyleAnimationValue.h"
#include "mozilla/TimeStamp.h" #include "mozilla/TimeStamp.h"
#include "mozilla/dom/AnimationEffectReadOnly.h" #include "mozilla/dom/AnimationEffectReadOnly.h"
#include "mozilla/dom/AnimationEffectTiming.h"
#include "mozilla/dom/AnimationEffectTimingReadOnly.h" // TimingParams #include "mozilla/dom/AnimationEffectTimingReadOnly.h" // TimingParams
#include "mozilla/dom/Element.h" #include "mozilla/dom/Element.h"
#include "mozilla/dom/KeyframeBinding.h" #include "mozilla/dom/KeyframeBinding.h"
@ -195,20 +196,11 @@ public:
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
ErrorResult& aRv) ErrorResult& aRv)
{ {
return Constructor(aGlobal, aTarget, aFrames, return ConstructKeyframeEffect<KeyframeEffectReadOnly>(
TimingParams::FromOptionsUnion(aOptions, aTarget), aGlobal, aTarget, aFrames,
aRv); TimingParams::FromOptionsUnion(aOptions, aTarget), aRv);
} }
// More generalized version for Animatable.animate.
// Not exposed to content.
static already_AddRefed<KeyframeEffectReadOnly>
Constructor(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const TimingParams& aTiming,
ErrorResult& aRv);
void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const; void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const;
void GetFrames(JSContext*& aCx, void GetFrames(JSContext*& aCx,
nsTArray<JSObject*>& aResult, nsTArray<JSObject*>& aResult,
@ -328,7 +320,21 @@ public:
inline AnimationCollection* GetCollection() const; inline AnimationCollection* GetCollection() const;
protected: protected:
KeyframeEffectReadOnly(nsIDocument* aDocument,
Element* aTarget,
nsCSSPseudoElements::Type aPseudoType,
AnimationEffectTimingReadOnly* aTiming);
virtual ~KeyframeEffectReadOnly(); virtual ~KeyframeEffectReadOnly();
template<typename KeyframeEffectType>
static already_AddRefed<KeyframeEffectType>
ConstructKeyframeEffect(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const TimingParams& aTiming,
ErrorResult& aRv);
void ResetIsRunningOnCompositor(); void ResetIsRunningOnCompositor();
// This effect is registered with its target element so long as: // This effect is registered with its target element so long as:
@ -384,6 +390,43 @@ private:
static const TimeDuration OverflowRegionRefreshInterval(); static const TimeDuration OverflowRegionRefreshInterval();
}; };
class KeyframeEffect : public KeyframeEffectReadOnly
{
public:
KeyframeEffect(nsIDocument* aDocument,
Element* aTarget,
nsCSSPseudoElements::Type aPseudoType,
const TimingParams& aTiming);
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
static already_AddRefed<KeyframeEffect>
Constructor(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
ErrorResult& aRv)
{
return ConstructKeyframeEffect<KeyframeEffect>(
aGlobal, aTarget, aFrames,
TimingParams::FromOptionsUnion(aOptions, aTarget), aRv);
}
// More generalized version for Animatable.animate.
// Not exposed to content.
static already_AddRefed<KeyframeEffect>
inline Constructor(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const TimingParams& aTiming,
ErrorResult& aRv)
{
return ConstructKeyframeEffect<KeyframeEffect>(aGlobal, aTarget, aFrames,
aTiming, aRv);
}
};
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla

View File

@ -10,6 +10,7 @@ MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
EXPORTS.mozilla.dom += [ EXPORTS.mozilla.dom += [
'Animation.h', 'Animation.h',
'AnimationEffectReadOnly.h', 'AnimationEffectReadOnly.h',
'AnimationEffectTiming.h',
'AnimationEffectTimingReadOnly.h', 'AnimationEffectTimingReadOnly.h',
'AnimationTimeline.h', 'AnimationTimeline.h',
'CSSPseudoElement.h', 'CSSPseudoElement.h',
@ -31,6 +32,7 @@ EXPORTS.mozilla += [
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [
'Animation.cpp', 'Animation.cpp',
'AnimationEffectReadOnly.cpp', 'AnimationEffectReadOnly.cpp',
'AnimationEffectTiming.cpp',
'AnimationEffectTimingReadOnly.cpp', 'AnimationEffectTimingReadOnly.cpp',
'AnimationTimeline.cpp', 'AnimationTimeline.cpp',
'AnimationUtils.cpp', 'AnimationUtils.cpp',

View File

@ -1004,7 +1004,8 @@ ParentRunnable::Run()
// Metadata is now open. // Metadata is now open.
if (!SendOnOpenMetadataForRead(mMetadata)) { if (!SendOnOpenMetadataForRead(mMetadata)) {
Unused << Send__delete__(this, JS::AsmJSCache_InternalError); Fail();
return NS_OK;
} }
return NS_OK; return NS_OK;
@ -1038,7 +1039,8 @@ ParentRunnable::Run()
FileDescriptor::PlatformHandleType handle = FileDescriptor::PlatformHandleType handle =
FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFileDesc)); FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFileDesc));
if (!SendOnOpenCacheFile(mFileSize, FileDescriptor(handle))) { if (!SendOnOpenCacheFile(mFileSize, FileDescriptor(handle))) {
Unused << Send__delete__(this, JS::AsmJSCache_InternalError); Fail();
return NS_OK;
} }
return NS_OK; return NS_OK;

View File

@ -1547,7 +1547,7 @@ bool
Console::ProcessArguments(JSContext* aCx, Console::ProcessArguments(JSContext* aCx,
const Sequence<JS::Value>& aData, const Sequence<JS::Value>& aData,
Sequence<JS::Value>& aSequence, Sequence<JS::Value>& aSequence,
Sequence<JS::Value>& aStyles) const Sequence<nsString>& aStyles) const
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
@ -1693,13 +1693,18 @@ Console::ProcessArguments(JSContext* aCx,
int32_t diff = aSequence.Length() - aStyles.Length(); int32_t diff = aSequence.Length() - aStyles.Length();
if (diff > 0) { if (diff > 0) {
for (int32_t i = 0; i < diff; i++) { for (int32_t i = 0; i < diff; i++) {
if (!aStyles.AppendElement(JS::NullValue(), fallible)) { if (!aStyles.AppendElement(NullString(), fallible)) {
return false; return false;
} }
} }
} }
if (!aStyles.AppendElement(JS::StringValue(jsString), fallible)) { nsAutoJSString string;
if (!string.init(aCx, jsString)) {
return false;
}
if (!aStyles.AppendElement(string, fallible)) {
return false; return false;
} }
} }

View File

@ -169,7 +169,7 @@ private:
bool bool
ProcessArguments(JSContext* aCx, const Sequence<JS::Value>& aData, ProcessArguments(JSContext* aCx, const Sequence<JS::Value>& aData,
Sequence<JS::Value>& aSequence, Sequence<JS::Value>& aSequence,
Sequence<JS::Value>& aStyles) const; Sequence<nsString>& aStyles) const;
void void
MakeFormatString(nsCString& aFormat, int32_t aInteger, int32_t aMantissa, MakeFormatString(nsCString& aFormat, int32_t aInteger, int32_t aMantissa,

View File

@ -918,7 +918,7 @@ already_AddRefed<DOMRect>
Element::GetBoundingClientRect() Element::GetBoundingClientRect()
{ {
RefPtr<DOMRect> rect = new DOMRect(this); RefPtr<DOMRect> rect = new DOMRect(this);
nsIFrame* frame = GetPrimaryFrame(Flush_Layout); nsIFrame* frame = GetPrimaryFrame(Flush_Layout);
if (!frame) { if (!frame) {
// display:none, perhaps? Return the empty rect // display:none, perhaps? Return the empty rect
@ -1462,7 +1462,7 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
if (xulElem) { if (xulElem) {
xulElem->SetXULBindingParent(aBindingParent); xulElem->SetXULBindingParent(aBindingParent);
} }
else else
#endif #endif
{ {
if (aBindingParent) { if (aBindingParent) {
@ -1522,7 +1522,7 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
// XXXbz ordering issues here? Probably not, since ChangeDocumentFor is // XXXbz ordering issues here? Probably not, since ChangeDocumentFor is
// just pretty broken anyway.... Need to get it working. // just pretty broken anyway.... Need to get it working.
// XXXbz XBL doesn't handle this (asserts), and we don't really want // XXXbz XBL doesn't handle this (asserts), and we don't really want
// to be doing this during parsing anyway... sort this out. // to be doing this during parsing anyway... sort this out.
// aDocument->BindingManager()->ChangeDocumentFor(this, nullptr, // aDocument->BindingManager()->ChangeDocumentFor(this, nullptr,
// aDocument); // aDocument);
@ -1850,7 +1850,7 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
} }
} }
// This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree, // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree,
// because it has to happen after unsetting the parent pointer, but before // because it has to happen after unsetting the parent pointer, but before
// recursively unbinding the kids. // recursively unbinding the kids.
if (IsHTMLElement()) { if (IsHTMLElement()) {
@ -2542,7 +2542,7 @@ Element::GetAttrInfo(int32_t aNamespaceID, nsIAtom* aName) const
return nsAttrInfo(nullptr, nullptr); return nsAttrInfo(nullptr, nullptr);
} }
bool bool
Element::GetAttr(int32_t aNameSpaceID, nsIAtom* aName, Element::GetAttr(int32_t aNameSpaceID, nsIAtom* aName,
@ -2563,7 +2563,7 @@ Element::FindAttrValueIn(int32_t aNameSpaceID,
NS_ASSERTION(aName, "Must have attr name"); NS_ASSERTION(aName, "Must have attr name");
NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown, "Must have namespace"); NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown, "Must have namespace");
NS_ASSERTION(aValues, "Null value array"); NS_ASSERTION(aValues, "Null value array");
const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName, aNameSpaceID); const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName, aNameSpaceID);
if (val) { if (val) {
for (int32_t i = 0; aValues[i]; ++i) { for (int32_t i = 0; aValues[i]; ++i) {
@ -2767,7 +2767,7 @@ Element::List(FILE* out, int32_t aIndent,
nsIContent* child = GetFirstChild(); nsIContent* child = GetFirstChild();
if (child) { if (child) {
fputs("\n", out); fputs("\n", out);
for (; child; child = child->GetNextSibling()) { for (; child; child = child->GetNextSibling()) {
child->List(out, aIndent + 1); child->List(out, aIndent + 1);
} }
@ -2776,7 +2776,7 @@ Element::List(FILE* out, int32_t aIndent,
} }
fputs(">\n", out); fputs(">\n", out);
Element* nonConstThis = const_cast<Element*>(this); Element* nonConstThis = const_cast<Element*>(this);
// XXX sXBL/XBL2 issue! Owner or current document? // XXX sXBL/XBL2 issue! Owner or current document?
@ -3341,9 +3341,8 @@ Element::Animate(JSContext* aContext,
Nullable<ElementOrCSSPseudoElement> target; Nullable<ElementOrCSSPseudoElement> target;
target.SetValue().SetAsElement() = this; target.SetValue().SetAsElement() = this;
// Bug 1211783: Use KeyframeEffect here (instead of KeyframeEffectReadOnly) RefPtr<KeyframeEffect> effect =
RefPtr<KeyframeEffectReadOnly> effect = KeyframeEffect::Constructor(global, target, frames,
KeyframeEffectReadOnly::Constructor(global, target, frames,
TimingParams::FromOptionsUnion(aOptions, target), aError); TimingParams::FromOptionsUnion(aOptions, target), aError);
if (aError.Failed()) { if (aError.Failed()) {
return nullptr; return nullptr;
@ -3525,7 +3524,7 @@ Element::InsertAdjacentHTML(const nsAString& aPosition, const nsAString& aText,
// Needed when insertAdjacentHTML is used in combination with contenteditable // Needed when insertAdjacentHTML is used in combination with contenteditable
mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true); mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
nsAutoScriptLoaderDisabler sld(doc); nsAutoScriptLoaderDisabler sld(doc);
// Batch possible DOMSubtreeModified events. // Batch possible DOMSubtreeModified events.
mozAutoSubtreeModified subtree(doc, nullptr); mozAutoSubtreeModified subtree(doc, nullptr);

View File

@ -2660,6 +2660,9 @@ nsDocument::ApplySettingsFromCSP(bool aSpeculative)
rv = csp->GetUpgradeInsecureRequests(&mUpgradeInsecureRequests); rv = csp->GetUpgradeInsecureRequests(&mUpgradeInsecureRequests);
NS_ENSURE_SUCCESS_VOID(rv); NS_ENSURE_SUCCESS_VOID(rv);
} }
if (!mUpgradeInsecurePreloads) {
mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
}
} }
return; return;
} }

View File

@ -151,6 +151,34 @@ GetNextRangeCommonAncestor(nsINode* aNode)
return aNode; return aNode;
} }
/**
* A Comparator suitable for mozilla::BinarySearchIf for searching a collection
* of nsRange* for an overlap of (mNode, mStartOffset) .. (mNode, mEndOffset).
*/
struct IsItemInRangeComparator
{
nsINode* mNode;
uint32_t mStartOffset;
uint32_t mEndOffset;
int operator()(const nsRange* const aRange) const
{
int32_t cmp = nsContentUtils::ComparePoints(mNode, mEndOffset,
aRange->GetStartParent(),
aRange->StartOffset());
if (cmp == 1) {
cmp = nsContentUtils::ComparePoints(mNode, mStartOffset,
aRange->GetEndParent(),
aRange->EndOffset());
if (cmp == -1) {
return 0;
}
return 1;
}
return -1;
}
};
/* static */ bool /* static */ bool
nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
uint32_t aEndOffset) uint32_t aEndOffset)
@ -160,24 +188,45 @@ nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
nsINode* n = GetNextRangeCommonAncestor(aNode); nsINode* n = GetNextRangeCommonAncestor(aNode);
NS_ASSERTION(n || !aNode->IsSelectionDescendant(), NS_ASSERTION(n || !aNode->IsSelectionDescendant(),
"orphan selection descendant"); "orphan selection descendant");
// Collect the potential ranges and their selection objects.
RangeHashTable ancestorSelectionRanges;
nsTHashtable<nsPtrHashKey<Selection>> ancestorSelections;
uint32_t maxRangeCount = 0;
for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) { for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) {
RangeHashTable* ranges = RangeHashTable* ranges =
static_cast<RangeHashTable*>(n->GetProperty(nsGkAtoms::range)); static_cast<RangeHashTable*>(n->GetProperty(nsGkAtoms::range));
for (auto iter = ranges->ConstIter(); !iter.Done(); iter.Next()) { for (auto iter = ranges->ConstIter(); !iter.Done(); iter.Next()) {
nsRange* range = iter.Get()->GetKey(); nsRange* range = iter.Get()->GetKey();
if (range->IsInSelection() && !range->Collapsed()) { if (range->IsInSelection() && !range->Collapsed()) {
int32_t cmp = nsContentUtils::ComparePoints(aNode, aEndOffset, ancestorSelectionRanges.PutEntry(range);
range->GetStartParent(), Selection* selection = range->mSelection;
range->StartOffset()); ancestorSelections.PutEntry(selection);
if (cmp == 1) { maxRangeCount = std::max(maxRangeCount, selection->RangeCount());
cmp = nsContentUtils::ComparePoints(aNode, aStartOffset, }
range->GetEndParent(), }
range->EndOffset()); }
if (cmp == -1) {
return true; if (!ancestorSelectionRanges.IsEmpty()) {
} nsTArray<const nsRange*> sortedRanges(maxRangeCount);
for (auto iter = ancestorSelections.ConstIter(); !iter.Done(); iter.Next()) {
Selection* selection = iter.Get()->GetKey();
// Sort the found ranges for |selection| in document order
// (Selection::GetRangeAt returns its ranges ordered).
for (uint32_t i = 0, len = selection->RangeCount(); i < len; ++i) {
nsRange* range = selection->GetRangeAt(i);
if (ancestorSelectionRanges.Contains(range)) {
sortedRanges.AppendElement(range);
} }
} }
MOZ_ASSERT(!sortedRanges.IsEmpty());
// Binary search the now sorted ranges.
IsItemInRangeComparator comparator = { aNode, aStartOffset, aEndOffset };
size_t unused;
if (mozilla::BinarySearchIf(sortedRanges, 0, sortedRanges.Length(), comparator, &unused)) {
return true;
}
sortedRanges.ClearAndRetainStorage();
} }
} }
return false; return false;
@ -3105,6 +3154,12 @@ nsRange::Constructor(const GlobalObject& aGlobal,
return window->GetDoc()->CreateRange(aRv); return window->GetDoc()->CreateRange(aRv);
} }
static bool ExcludeIfNextToNonSelectable(nsIContent* aContent)
{
return aContent->IsNodeOfType(nsINode::eTEXT) &&
aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE);
}
void void
nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges) nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges)
{ {
@ -3123,6 +3178,10 @@ nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges)
bool added = false; bool added = false;
bool seenSelectable = false; bool seenSelectable = false;
// |firstNonSelectableContent| is the first node in a consecutive sequence
// of non-IsSelectable nodes. When we find a selectable node after such
// a sequence we'll end the last nsRange, create a new one and restart
// the outer loop.
nsIContent* firstNonSelectableContent = nullptr; nsIContent* firstNonSelectableContent = nullptr;
while (true) { while (true) {
ErrorResult err; ErrorResult err;
@ -3132,12 +3191,19 @@ nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges)
nsIContent* content = nsIContent* content =
node && node->IsContent() ? node->AsContent() : nullptr; node && node->IsContent() ? node->AsContent() : nullptr;
if (content) { if (content) {
nsIFrame* frame = content->GetPrimaryFrame(); if (firstNonSelectableContent && ExcludeIfNextToNonSelectable(content)) {
for (nsIContent* p = content; !frame && (p = p->GetParent()); ) { // Ignorable whitespace next to a sequence of non-selectable nodes
frame = p->GetPrimaryFrame(); // counts as non-selectable (bug 1216001).
selectable = false;
} }
if (frame) { if (selectable) {
frame->IsSelectable(&selectable, nullptr); nsIFrame* frame = content->GetPrimaryFrame();
for (nsIContent* p = content; !frame && (p = p->GetParent()); ) {
frame = p->GetPrimaryFrame();
}
if (frame) {
frame->IsSelectable(&selectable, nullptr);
}
} }
} }

View File

@ -252,6 +252,14 @@ public:
bool *outNodeBefore, bool *outNodeBefore,
bool *outNodeAfter); bool *outNodeAfter);
/**
* Return true if any part of (aNode, aStartOffset) .. (aNode, aEndOffset)
* overlaps any nsRange in aNode's GetNextRangeCommonAncestor ranges (i.e.
* where aNode is a descendant of a range's common ancestor node).
* If a nsRange starts in (aNode, aEndOffset) or if it ends in
* (aNode, aStartOffset) then it is non-overlapping and the result is false
* for that nsRange. Collapsed ranges always counts as non-overlapping.
*/
static bool IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, static bool IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
uint32_t aEndOffset); uint32_t aEndOffset);
@ -298,6 +306,14 @@ protected:
*/ */
nsINode* GetRegisteredCommonAncestor(); nsINode* GetRegisteredCommonAncestor();
// Helper to IsNodeSelected.
static bool IsNodeInSortedRanges(nsINode* aNode,
uint32_t aStartOffset,
uint32_t aEndOffset,
const nsTArray<const nsRange*>& aRanges,
size_t aRangeStart,
size_t aRangeEnd);
struct MOZ_STACK_CLASS AutoInvalidateSelection struct MOZ_STACK_CLASS AutoInvalidateSelection
{ {
explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange) explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange)

View File

@ -133,9 +133,15 @@ nsScreen::GetRect(nsRect& aRect)
} }
context->GetRect(aRect); context->GetRect(aRect);
LayoutDevicePoint screenTopLeftDev =
LayoutDevicePixel::FromAppUnits(aRect.TopLeft(),
context->AppUnitsPerDevPixel());
DesktopPoint screenTopLeftDesk =
screenTopLeftDev / context->GetDesktopToDeviceScale();
aRect.x = NSToIntRound(screenTopLeftDesk.x);
aRect.y = NSToIntRound(screenTopLeftDesk.y);
aRect.x = nsPresContext::AppUnitsToIntCSSPixels(aRect.x);
aRect.y = nsPresContext::AppUnitsToIntCSSPixels(aRect.y);
aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height); aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height);
aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width); aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width);
@ -156,10 +162,21 @@ nsScreen::GetAvailRect(nsRect& aRect)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
nsRect r;
context->GetRect(r);
LayoutDevicePoint screenTopLeftDev =
LayoutDevicePixel::FromAppUnits(r.TopLeft(),
context->AppUnitsPerDevPixel());
DesktopPoint screenTopLeftDesk =
screenTopLeftDev / context->GetDesktopToDeviceScale();
context->GetClientRect(aRect); context->GetClientRect(aRect);
aRect.x = nsPresContext::AppUnitsToIntCSSPixels(aRect.x); aRect.x = NSToIntRound(screenTopLeftDesk.x) +
aRect.y = nsPresContext::AppUnitsToIntCSSPixels(aRect.y); nsPresContext::AppUnitsToIntCSSPixels(aRect.x - r.x);
aRect.y = NSToIntRound(screenTopLeftDesk.y) +
nsPresContext::AppUnitsToIntCSSPixels(aRect.y - r.y);
aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height); aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height);
aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width); aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width);

View File

@ -12,7 +12,7 @@
src: url("Ahem.ttf"); src: url("Ahem.ttf");
} }
body { font-family: Ahem; font-size: 20px; } body { font-family: Ahem; font-size: 20px; }
s { -moz-user-select: none; } s, .non-selectable { -moz-user-select: none; }
n { display: none; } n { display: none; }
a { position:absolute; bottom: 0; right:0; } a { position:absolute; bottom: 0; right:0; }
.text { -moz-user-select: text; } .text { -moz-user-select: text; }
@ -34,6 +34,16 @@ a { position:absolute; bottom: 0; right:0; }
<div id="testB"><n><s>aaaa</s>aaa</n>bbbbbbbbccccccc</div> <div id="testB"><n><s>aaaa</s>aaa</n>bbbbbbbbccccccc</div>
<div id="testC">aaaaaaabbbbbbbb<n>cc<s>c</s>cccc</n></div> <div id="testC">aaaaaaabbbbbbbb<n>cc<s>c</s>cccc</n></div>
<div id="testE">aaa<s id="testEc1">aaaa<a class="text">bbbb</a>dd<a>cccc</a>ddddddd</s>eeee</div> <div id="testE">aaa<s id="testEc1">aaaa<a class="text">bbbb</a>dd<a>cccc</a>ddddddd</s>eeee</div>
<div id="testF">aaaa
<div class="non-selectable">x</div>
<div class="non-selectable">x</div>
<div class="non-selectable">x</div>
bbbb</div>
<div id="testG" style="white-space:pre">aaaa
<div class="non-selectable">x</div>
<div class="non-selectable">x</div>
<div class="non-selectable">x</div>
bbbb</div>
<iframe id="testD" src="data:text/html,<body>aaaa<span style='-moz-user-select:none'>bbbb</span>cccc"></iframe> <iframe id="testD" src="data:text/html,<body>aaaa<span style='-moz-user-select:none'>bbbb</span>cccc"></iframe>
@ -100,9 +110,11 @@ function test()
is(NL(r.toString()), text, e.id + ": range["+index+"].toString()") is(NL(r.toString()), text, e.id + ": range["+index+"].toString()")
} }
function node(e, index) function node(e, arg)
{ {
return index == -1 ? e : e.childNodes[index]; if (typeof arg == "number")
return arg == -1 ? e : e.childNodes[arg];
return arg;
} }
function checkRangeCount(n, e) function checkRangeCount(n, e)
@ -258,6 +270,22 @@ function test()
checkRanges([[0,1,-1,1]], e); checkRanges([[0,1,-1,1]], e);
doneTest(e); doneTest(e);
clear();
e = document.getElementById('testF');
synthesizeMouse(e, 1, 1, {});
synthesizeMouse(e, 400, 100, { shiftKey: true });
checkText("aaaa bbbb", e);
checkRanges([[0,0,-1,1],[6,0,6,5]], e);
doneTest(e);
clear();
e = document.getElementById('testG');
synthesizeMouse(e, 1, 1, {});
synthesizeMouse(e, 400, 180, { shiftKey: true });
checkText("aaaa bbbb", e); // XXX this doesn't seem right - bug 1247799
checkRanges([[0,0,-1,1],[2,0,-1,3],[4,0,-1,5],[6,0,6,5]], e);
doneTest(e);
// ====================================================== // ======================================================
// ==================== Script tests ==================== // ==================== Script tests ====================
// ====================================================== // ======================================================

View File

@ -31,8 +31,9 @@ ENUM_ENTRY_VARIABLE_NAME = 'strings'
INSTANCE_RESERVED_SLOTS = 1 INSTANCE_RESERVED_SLOTS = 1
def memberReservedSlot(member): def memberReservedSlot(member, descriptor):
return "(DOM_INSTANCE_RESERVED_SLOTS + %d)" % member.slotIndex return ("(DOM_INSTANCE_RESERVED_SLOTS + %d)" %
member.slotIndices[descriptor.interface.identifier.name])
def toStringBool(arg): def toStringBool(arg):
@ -2149,7 +2150,7 @@ def clearableCachedAttrs(descriptor):
m.isAttr() and m.isAttr() and
# Constants should never need clearing! # Constants should never need clearing!
m.dependsOn != "Nothing" and m.dependsOn != "Nothing" and
m.slotIndex is not None) m.slotIndices is not None)
def MakeClearCachedValueNativeName(member): def MakeClearCachedValueNativeName(member):
@ -3805,8 +3806,6 @@ class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod):
} }
// Getter handled setting our reserved slots // Getter handled setting our reserved slots
""", """,
slot=memberReservedSlot(m),
interface=self.descriptor.interface.identifier.name,
member=m.identifier.name) member=m.identifier.name)
body += "\nreturn true;\n" body += "\nreturn true;\n"
@ -3828,7 +3827,7 @@ class CGClearCachedValueMethod(CGAbstractMethod):
CGAbstractMethod.__init__(self, descriptor, name, returnType, args) CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
def definition_body(self): def definition_body(self):
slotIndex = memberReservedSlot(self.member) slotIndex = memberReservedSlot(self.member, self.descriptor)
if self.member.getExtendedAttribute("StoreInSlot"): if self.member.getExtendedAttribute("StoreInSlot"):
# We have to root things and save the old value in case # We have to root things and save the old value in case
# regetting fails, so we can restore it. # regetting fails, so we can restore it.
@ -7371,7 +7370,7 @@ class CGPerSignatureCall(CGThing):
"NewObject implies that we need to keep the object alive with a strong reference."); "NewObject implies that we need to keep the object alive with a strong reference.");
""") """)
setSlot = self.idlNode.isAttr() and self.idlNode.slotIndex is not None setSlot = self.idlNode.isAttr() and self.idlNode.slotIndices is not None
if setSlot: if setSlot:
# For attributes in slots, we want to do some # For attributes in slots, we want to do some
# post-processing once we've wrapped them. # post-processing once we've wrapped them.
@ -7418,7 +7417,7 @@ class CGPerSignatureCall(CGThing):
"args.rval().isObject()") "args.rval().isObject()")
postSteps += freezeValue.define() postSteps += freezeValue.define()
postSteps += ("js::SetReservedSlot(reflector, %s, args.rval());\n" % postSteps += ("js::SetReservedSlot(reflector, %s, args.rval());\n" %
memberReservedSlot(self.idlNode)) memberReservedSlot(self.idlNode, self.descriptor))
# For the case of Cached attributes, go ahead and preserve our # For the case of Cached attributes, go ahead and preserve our
# wrapper if needed. We need to do this because otherwise the # wrapper if needed. We need to do this because otherwise the
# wrapper could get garbage-collected and the cached value would # wrapper could get garbage-collected and the cached value would
@ -8003,7 +8002,7 @@ class CGSetterCall(CGPerSignatureCall):
def wrap_return_value(self): def wrap_return_value(self):
attr = self.idlNode attr = self.idlNode
if self.descriptor.wrapperCache and attr.slotIndex is not None: if self.descriptor.wrapperCache and attr.slotIndices is not None:
if attr.getExtendedAttribute("StoreInSlot"): if attr.getExtendedAttribute("StoreInSlot"):
args = "cx, self" args = "cx, self"
else: else:
@ -8550,7 +8549,7 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
return getMaplikeOrSetlikeSizeGetterBody(self.descriptor, self.attr) return getMaplikeOrSetlikeSizeGetterBody(self.descriptor, self.attr)
nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
self.attr) self.attr)
if self.attr.slotIndex is not None: if self.attr.slotIndices is not None:
if self.descriptor.hasXPConnectImpls: if self.descriptor.hasXPConnectImpls:
raise TypeError("Interface '%s' has XPConnect impls, so we " raise TypeError("Interface '%s' has XPConnect impls, so we "
"can't use our slot for property '%s'!" % "can't use our slot for property '%s'!" %
@ -8576,7 +8575,7 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
} }
""", """,
slot=memberReservedSlot(self.attr), slot=memberReservedSlot(self.attr, self.descriptor),
maybeWrap=getMaybeWrapValueFuncForType(self.attr.type)) maybeWrap=getMaybeWrapValueFuncForType(self.attr.type))
else: else:
prefix = "" prefix = ""
@ -8827,10 +8826,13 @@ class CGMemberJITInfo(CGThing):
slotIndex=slotIndex) slotIndex=slotIndex)
return initializer.rstrip() return initializer.rstrip()
slotAssert = dedent( slotAssert = fill(
""" """
static_assert(%s <= JSJitInfo::maxSlotIndex, "We won't fit"); static_assert(${slotIndex} <= JSJitInfo::maxSlotIndex, "We won't fit");
""" % slotIndex) static_assert(${slotIndex} < ${classReservedSlots}, "There is no slot for us");
""",
slotIndex=slotIndex,
classReservedSlots=INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots)
if args is not None: if args is not None:
argTypes = "%s_argTypes" % infoName argTypes = "%s_argTypes" % infoName
args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args] args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
@ -8878,10 +8880,10 @@ class CGMemberJITInfo(CGThing):
getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor) getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot") isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot")
if self.member.slotIndex is not None: if self.member.slotIndices is not None:
assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached") assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached")
isLazilyCachedInSlot = not isAlwaysInSlot isLazilyCachedInSlot = not isAlwaysInSlot
slotIndex = memberReservedSlot(self.member) slotIndex = memberReservedSlot(self.member, self.descriptor)
# We'll statically assert that this is not too big in # We'll statically assert that this is not too big in
# CGUpdateMemberSlotsMethod, in the case when # CGUpdateMemberSlotsMethod, in the case when
# isAlwaysInSlot is true. # isAlwaysInSlot is true.
@ -15328,7 +15330,7 @@ def getMaplikeOrSetlikeBackingObject(descriptor, maplikeOrSetlike, helperImpl=No
PreserveWrapper<${selfType}>(self); PreserveWrapper<${selfType}>(self);
} }
""", """,
slot=memberReservedSlot(maplikeOrSetlike), slot=memberReservedSlot(maplikeOrSetlike, descriptor),
func_prefix=func_prefix, func_prefix=func_prefix,
errorReturn=getMaplikeOrSetlikeErrorReturn(helperImpl), errorReturn=getMaplikeOrSetlikeErrorReturn(helperImpl),
selfType=descriptor.nativeType) selfType=descriptor.nativeType)

View File

@ -978,7 +978,9 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
(member.getExtendedAttribute("StoreInSlot") or (member.getExtendedAttribute("StoreInSlot") or
member.getExtendedAttribute("Cached"))) or member.getExtendedAttribute("Cached"))) or
member.isMaplikeOrSetlike()): member.isMaplikeOrSetlike()):
member.slotIndex = self.totalMembersInSlots if member.slotIndices is None:
member.slotIndices = dict()
member.slotIndices[self.identifier.name] = self.totalMembersInSlots
self.totalMembersInSlots += 1 self.totalMembersInSlots += 1
if member.getExtendedAttribute("StoreInSlot"): if member.getExtendedAttribute("StoreInSlot"):
self._ownMembersInSlots += 1 self._ownMembersInSlots += 1
@ -3599,7 +3601,7 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
IDLMaplikeOrSetlikeOrIterableBase.__init__(self, location, identifier, maplikeOrSetlikeType, IDLMaplikeOrSetlikeOrIterableBase.__init__(self, location, identifier, maplikeOrSetlikeType,
keyType, valueType, IDLInterfaceMember.Tags.MaplikeOrSetlike) keyType, valueType, IDLInterfaceMember.Tags.MaplikeOrSetlike)
self.readonly = readonly self.readonly = readonly
self.slotIndex = None self.slotIndices = None
# When generating JSAPI access code, we need to know the backing object # When generating JSAPI access code, we need to know the backing object
# type prefix to create the correct function. Generate here for reuse. # type prefix to create the correct function. Generate here for reuse.
@ -3793,7 +3795,7 @@ class IDLAttribute(IDLInterfaceMember):
self.stringifier = stringifier self.stringifier = stringifier
self.enforceRange = False self.enforceRange = False
self.clamp = False self.clamp = False
self.slotIndex = None self.slotIndices = None
assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike) assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike)
self.maplikeOrSetlike = maplikeOrSetlike self.maplikeOrSetlike = maplikeOrSetlike
self.dependsOn = "Everything" self.dependsOn = "Everything"

View File

@ -1570,9 +1570,7 @@ CanvasRenderingContext2D::ReturnTarget()
{ {
if (mTarget && mBufferProvider) { if (mTarget && mBufferProvider) {
CurrentState().transform = mTarget->GetTransform(); CurrentState().transform = mTarget->GetTransform();
DrawTarget* oldDT = mTarget; mBufferProvider->ReturnAndUseDT(mTarget.forget());
mTarget = nullptr;
mBufferProvider->ReturnAndUseDT(oldDT);
} }
} }
@ -4916,6 +4914,9 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& aWindow, double aX,
nsCOMPtr<nsIPresShell> shell = presContext->PresShell(); nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
Unused << shell->RenderDocument(r, renderDocFlags, backgroundColor, thebes); Unused << shell->RenderDocument(r, renderDocFlags, backgroundColor, thebes);
// If this canvas was contained in the drawn window, the pre-transaction callback
// may have returned its DT. If so, we must reacquire it here.
EnsureTarget();
if (drawDT) { if (drawDT) {
RefPtr<SourceSurface> snapshot = drawDT->Snapshot(); RefPtr<SourceSurface> snapshot = drawDT->Snapshot();
RefPtr<DataSourceSurface> data = snapshot->GetDataSurface(); RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();

View File

@ -20,28 +20,33 @@ GetFBInfoForBlit(const WebGLFramebuffer* fb, const char* const fbInfo,
const webgl::FormatInfo** const out_depthFormat, const webgl::FormatInfo** const out_depthFormat,
const webgl::FormatInfo** const out_stencilFormat) const webgl::FormatInfo** const out_stencilFormat)
{ {
*out_samples = 1; // TODO *out_samples = 0;
*out_colorFormat = nullptr; *out_colorFormat = nullptr;
*out_depthFormat = nullptr; *out_depthFormat = nullptr;
*out_stencilFormat = nullptr; *out_stencilFormat = nullptr;
if (fb->ColorAttachment(0).IsDefined()) { if (fb->ColorAttachment(0).IsDefined()) {
const auto& attachment = fb->ColorAttachment(0); const auto& attachment = fb->ColorAttachment(0);
*out_samples = attachment.Samples();
*out_colorFormat = attachment.Format()->format; *out_colorFormat = attachment.Format()->format;
} }
if (fb->DepthStencilAttachment().IsDefined()) { if (fb->DepthStencilAttachment().IsDefined()) {
const auto& attachment = fb->DepthStencilAttachment(); const auto& attachment = fb->DepthStencilAttachment();
*out_samples = attachment.Samples();
*out_depthFormat = attachment.Format()->format; *out_depthFormat = attachment.Format()->format;
*out_stencilFormat = *out_depthFormat; *out_stencilFormat = *out_depthFormat;
} else { } else {
if (fb->DepthAttachment().IsDefined()) { if (fb->DepthAttachment().IsDefined()) {
const auto& attachment = fb->DepthAttachment(); const auto& attachment = fb->DepthAttachment();
*out_samples = attachment.Samples();
*out_depthFormat = attachment.Format()->format; *out_depthFormat = attachment.Format()->format;
} }
if (fb->StencilAttachment().IsDefined()) { if (fb->StencilAttachment().IsDefined()) {
const auto& attachment = fb->StencilAttachment(); const auto& attachment = fb->StencilAttachment();
*out_samples = attachment.Samples();
*out_stencilFormat = attachment.Format()->format; *out_stencilFormat = attachment.Format()->format;
} }
} }
@ -133,7 +138,7 @@ WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY
return; return;
} }
} else { } else {
srcSamples = 1; // Always 1. srcSamples = 0; // Always 0.
GetBackbufferFormats(mOptions, &srcColorFormat, &srcDepthFormat, GetBackbufferFormats(mOptions, &srcColorFormat, &srcDepthFormat,
&srcStencilFormat); &srcStencilFormat);
@ -221,13 +226,13 @@ WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY
return; return;
} }
if (dstSamples != 1) { if (dstSamples != 0) {
ErrorInvalidOperation("blitFramebuffer: DRAW_FRAMEBUFFER may not have" ErrorInvalidOperation("blitFramebuffer: DRAW_FRAMEBUFFER may not have"
" multiple samples."); " multiple samples.");
return; return;
} }
if (srcSamples != 1) { if (srcSamples != 0) {
if (mask & LOCAL_GL_COLOR_BUFFER_BIT && if (mask & LOCAL_GL_COLOR_BUFFER_BIT &&
dstColorFormat != srcColorFormat) dstColorFormat != srcColorFormat)
{ {

View File

@ -15,43 +15,45 @@ void
WebGL2Context::GetInternalformatParameter(JSContext* cx, GLenum target, WebGL2Context::GetInternalformatParameter(JSContext* cx, GLenum target,
GLenum internalformat, GLenum pname, GLenum internalformat, GLenum pname,
JS::MutableHandleValue retval, JS::MutableHandleValue retval,
ErrorResult& rv) ErrorResult& out_rv)
{ {
if (IsContextLost()) const char funcName[] = "getInternalfomratParameter";
return; if (IsContextLost())
return;
if (target != LOCAL_GL_RENDERBUFFER) { if (target != LOCAL_GL_RENDERBUFFER) {
return ErrorInvalidEnumInfo("getInternalfomratParameter: target must be " ErrorInvalidEnum("%s: `target` must be RENDERBUFFER, was: 0x%04x.", funcName,
"RENDERBUFFER. Was:", target); target);
} return;
}
// GL_INVALID_ENUM is generated if internalformat is not color-, // GL_INVALID_ENUM is generated if internalformat is not color-, depth-, or
// depth-, or stencil-renderable. // stencil-renderable.
// TODO: When format table queries lands. // TODO: When format table queries lands.
if (pname != LOCAL_GL_SAMPLES) { if (pname != LOCAL_GL_SAMPLES) {
return ErrorInvalidEnumInfo("getInternalformatParameter: pname must be SAMPLES. " ErrorInvalidEnumInfo("%s: `pname` must be SAMPLES, was 0x%04x.", funcName, pname);
"Was:", pname); return;
} }
GLint* samples = nullptr; GLint* samples = nullptr;
GLint sampleCount = 0; GLint sampleCount = 0;
gl->fGetInternalformativ(LOCAL_GL_RENDERBUFFER, internalformat, gl->fGetInternalformativ(LOCAL_GL_RENDERBUFFER, internalformat,
LOCAL_GL_NUM_SAMPLE_COUNTS, 1, &sampleCount); LOCAL_GL_NUM_SAMPLE_COUNTS, 1, &sampleCount);
if (sampleCount > 0) { if (sampleCount > 0) {
samples = new GLint[sampleCount]; samples = new GLint[sampleCount];
gl->fGetInternalformativ(LOCAL_GL_RENDERBUFFER, internalformat, LOCAL_GL_SAMPLES, gl->fGetInternalformativ(LOCAL_GL_RENDERBUFFER, internalformat, LOCAL_GL_SAMPLES,
sampleCount, samples); sampleCount, samples);
} }
JSObject* obj = dom::Int32Array::Create(cx, this, sampleCount, samples); JSObject* obj = dom::Int32Array::Create(cx, this, sampleCount, samples);
if (!obj) { if (!obj) {
rv = NS_ERROR_OUT_OF_MEMORY; out_rv = NS_ERROR_OUT_OF_MEMORY;
} }
delete[] samples; delete[] samples;
retval.setObjectOrNull(obj); retval.setObjectOrNull(obj);
} }
void void
@ -59,14 +61,11 @@ WebGL2Context::RenderbufferStorageMultisample(GLenum target, GLsizei samples,
GLenum internalFormat, GLenum internalFormat,
GLsizei width, GLsizei height) GLsizei width, GLsizei height)
{ {
const char funcName[] = "renderbufferStorageMultisample"; const char funcName[] = "renderbufferStorageMultisample";
if (IsContextLost()) if (IsContextLost())
return; return;
//RenderbufferStorage_base(funcName, target, samples, internalFormat, width, height); RenderbufferStorage_base(funcName, target, samples, internalFormat, width, height);
ErrorInvalidOperation("%s: Multisampling is still under development, and is currently"
" disabled.", funcName);
} }
} // namespace mozilla } // namespace mozilla

View File

@ -106,7 +106,6 @@ WebGLContext::WebGLContext()
, mMaxFetchedVertices(0) , mMaxFetchedVertices(0)
, mMaxFetchedInstances(0) , mMaxFetchedInstances(0)
, mBypassShaderValidation(false) , mBypassShaderValidation(false)
, mGLMaxSamples(1)
, mNeedsFakeNoAlpha(false) , mNeedsFakeNoAlpha(false)
, mNeedsFakeNoDepth(false) , mNeedsFakeNoDepth(false)
, mNeedsFakeNoStencil(false) , mNeedsFakeNoStencil(false)

View File

@ -1114,7 +1114,6 @@ protected:
int32_t mGLMaxVertexUniformVectors; int32_t mGLMaxVertexUniformVectors;
uint32_t mGLMaxTransformFeedbackSeparateAttribs; uint32_t mGLMaxTransformFeedbackSeparateAttribs;
GLuint mGLMaxUniformBufferBindings; GLuint mGLMaxUniformBufferBindings;
GLsizei mGLMaxSamples;
// What is supported: // What is supported:
uint32_t mGLMaxColorAttachments; uint32_t mGLMaxColorAttachments;

View File

@ -173,17 +173,13 @@ WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer* wrb)
if (wrb && wrb->IsDeleted()) if (wrb && wrb->IsDeleted())
return; return;
MakeContextCurrent(); // Usually, we would now call into glBindRenderbuffer. However, since we have to
// potentially emulate packed-depth-stencil, there's not a specific renderbuffer that
// we know we should bind here.
// Instead, we do all renderbuffer binding lazily.
// Sometimes we emulate renderbuffers (depth-stencil emu), so there's not
// always a 1-1 mapping from `wrb` to GL name. Just have `wrb` handle it.
if (wrb) { if (wrb) {
wrb->BindRenderbuffer(); wrb->mHasBeenBound = true;
#ifdef ANDROID
wrb->mIsRB = true;
#endif
} else {
gl->fBindRenderbuffer(target, 0);
} }
mBoundRenderbuffer = wrb; mBoundRenderbuffer = wrb;
@ -1068,16 +1064,7 @@ WebGLContext::IsRenderbuffer(WebGLRenderbuffer* rb)
if (rb->IsDeleted()) if (rb->IsDeleted())
return false; return false;
#ifdef ANDROID return rb->mHasBeenBound;
if (gl->WorkAroundDriverBugs() &&
gl->Renderer() == GLRenderer::AndroidEmulator)
{
return rb->mIsRB;
}
#endif
MakeContextCurrent();
return gl->fIsRenderbuffer(rb->PrimaryGLName());
} }
bool bool
@ -1799,66 +1786,41 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
void void
WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target, WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target,
GLsizei samples, GLsizei samples, GLenum internalFormat,
GLenum internalFormat, GLsizei width, GLsizei width, GLsizei height)
GLsizei height)
{ {
if (IsContextLost()) if (IsContextLost())
return; return;
if (!mBoundRenderbuffer) {
ErrorInvalidOperation("%s: Called on renderbuffer 0.", funcName);
return;
}
if (target != LOCAL_GL_RENDERBUFFER) { if (target != LOCAL_GL_RENDERBUFFER) {
ErrorInvalidEnumInfo("`target`", funcName, target); ErrorInvalidEnumInfo("`target`", funcName, target);
return; return;
} }
if (samples < 0 || samples > mGLMaxSamples) { if (!mBoundRenderbuffer) {
ErrorInvalidValue("%s: `samples` is out of the valid range.", funcName); ErrorInvalidOperation("%s: Called on renderbuffer 0.", funcName);
return;
}
if (samples < 0) {
ErrorInvalidValue("%s: `samples` must be >= 0.", funcName);
return; return;
} }
if (width < 0 || height < 0) { if (width < 0 || height < 0) {
ErrorInvalidValue("%s: Width and height must be >= 0.", funcName); ErrorInvalidValue("%s: `width` and `height` must be >= 0.", funcName);
return; return;
} }
if (uint32_t(width) > mImplMaxRenderbufferSize || mBoundRenderbuffer->RenderbufferStorage(funcName, uint32_t(samples), internalFormat,
uint32_t(height) > mImplMaxRenderbufferSize) uint32_t(width), uint32_t(height));
{
ErrorInvalidValue("%s: Width or height exceeds maximum renderbuffer"
" size.", funcName);
return;
}
const auto usage = mFormatUsage->GetRBUsage(internalFormat);
if (!usage) {
ErrorInvalidEnumInfo("`internalFormat`", funcName, internalFormat);
return;
}
// Validation complete.
MakeContextCurrent();
GetAndFlushUnderlyingGLErrors();
mBoundRenderbuffer->RenderbufferStorage(samples, usage, width, height);
GLenum error = GetAndFlushUnderlyingGLErrors();
if (error) {
GenerateWarning("%s generated error %s", funcName,
ErrorName(error));
return;
}
} }
void void
WebGLContext::RenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height) WebGLContext::RenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height)
{ {
RenderbufferStorage_base("renderbufferStorage", target, 0, RenderbufferStorage_base("renderbufferStorage", target, 0, internalFormat, width,
internalFormat, width, height); height);
} }
void void
@ -2323,6 +2285,8 @@ WebGLContext::CreateRenderbuffer()
{ {
if (IsContextLost()) if (IsContextLost())
return nullptr; return nullptr;
MakeContextCurrent();
RefPtr<WebGLRenderbuffer> globj = new WebGLRenderbuffer(this); RefPtr<WebGLRenderbuffer> globj = new WebGLRenderbuffer(this);
return globj.forget(); return globj.forget();
} }

View File

@ -727,6 +727,8 @@ WebGLContext::AssertCachedBindings()
MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors()); MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors());
#endif #endif
// We do not check the renderbuffer binding, because we never rely on it matching.
} }
void void

View File

@ -786,8 +786,6 @@ WebGLContext::InitAndValidateGL()
mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS; mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS;
mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS;
mGLMaxSamples = 1;
} else { } else {
gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&mImplMaxTextureSize); gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&mImplMaxTextureSize);
gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, (GLint*)&mImplMaxCubeMapTextureSize); gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, (GLint*)&mImplMaxCubeMapTextureSize);
@ -800,9 +798,6 @@ WebGLContext::InitAndValidateGL()
gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits); gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits);
gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits); gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits);
if (!gl->GetPotentialInteger(LOCAL_GL_MAX_SAMPLES, (GLint*)&mGLMaxSamples))
mGLMaxSamples = 1;
} }
// If we don't support a target, its max size is 0. We should only floor-to-POT if the // If we don't support a target, its max size is 0. We should only floor-to-POT if the

View File

@ -416,8 +416,6 @@ BytesPerPixel(const PackingInfo& packing)
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// FormatUsageAuthority // FormatUsageAuthority
bool bool
FormatUsageInfo::IsUnpackValid(const PackingInfo& key, FormatUsageInfo::IsUnpackValid(const PackingInfo& key,
const DriverUnpackInfo** const out_value) const const DriverUnpackInfo** const out_value) const
@ -430,6 +428,29 @@ FormatUsageInfo::IsUnpackValid(const PackingInfo& key,
return true; return true;
} }
void
FormatUsageInfo::ResolveMaxSamples(gl::GLContext* gl)
{
MOZ_ASSERT(!this->maxSamplesKnown);
MOZ_ASSERT(this->maxSamples == 0);
MOZ_ASSERT(gl->IsCurrent());
this->maxSamplesKnown = true;
const GLenum internalFormat = this->format->sizedFormat;
if (!internalFormat)
return;
if (!gl->IsSupported(gl::GLFeature::internalformat_query))
return; // Leave it at 0.
GLint maxSamplesGL = 0;
gl->fGetInternalformativ(LOCAL_GL_RENDERBUFFER, internalFormat, LOCAL_GL_SAMPLES, 1,
&maxSamplesGL);
this->maxSamples = maxSamplesGL;
}
//////////////////////////////////////// ////////////////////////////////////////
static void static void

View File

@ -248,10 +248,15 @@ struct FormatUsageInfo
const FormatInfo* const format; const FormatInfo* const format;
bool isRenderable; bool isRenderable;
bool isFilterable; bool isFilterable;
std::map<PackingInfo, DriverUnpackInfo> validUnpacks; std::map<PackingInfo, DriverUnpackInfo> validUnpacks;
const DriverUnpackInfo* idealUnpack; const DriverUnpackInfo* idealUnpack;
const GLint* textureSwizzleRGBA; const GLint* textureSwizzleRGBA;
bool maxSamplesKnown;
uint32_t maxSamples;
static const GLint kLuminanceSwizzleRGBA[4]; static const GLint kLuminanceSwizzleRGBA[4];
static const GLint kAlphaSwizzleRGBA[4]; static const GLint kAlphaSwizzleRGBA[4];
static const GLint kLumAlphaSwizzleRGBA[4]; static const GLint kLumAlphaSwizzleRGBA[4];
@ -262,10 +267,14 @@ struct FormatUsageInfo
, isFilterable(false) , isFilterable(false)
, idealUnpack(nullptr) , idealUnpack(nullptr)
, textureSwizzleRGBA(nullptr) , textureSwizzleRGBA(nullptr)
, maxSamplesKnown(false)
, maxSamples(0)
{ } { }
bool IsUnpackValid(const PackingInfo& key, bool IsUnpackValid(const PackingInfo& key,
const DriverUnpackInfo** const out_value) const; const DriverUnpackInfo** const out_value) const;
void ResolveMaxSamples(gl::GLContext* gl);
}; };
class FormatUsageAuthority class FormatUsageAuthority

View File

@ -69,6 +69,17 @@ WebGLFBAttachPoint::Format() const
return nullptr; return nullptr;
} }
uint32_t
WebGLFBAttachPoint::Samples() const
{
MOZ_ASSERT(IsDefined());
if (mRenderbufferPtr)
return mRenderbufferPtr->Samples();
return 0;
}
bool bool
WebGLFBAttachPoint::HasAlpha() const WebGLFBAttachPoint::HasAlpha() const
{ {
@ -394,11 +405,11 @@ WebGLFBAttachPoint::FinalizeAttachment(gl::GLContext* gl, GLenum attachment) con
} }
break; break;
} }
return ; return;
} }
if (Renderbuffer()) { if (Renderbuffer()) {
Renderbuffer()->FramebufferRenderbuffer(attachment); Renderbuffer()->DoFramebufferRenderbuffer(attachment);
return; return;
} }
@ -790,28 +801,6 @@ WebGLFramebuffer::HasIncompleteAttachments(nsCString* const out_info) const
return hasIncomplete; return hasIncomplete;
} }
static bool
MatchOrReplaceSize(const WebGLFBAttachPoint& cur, uint32_t* const out_width,
uint32_t* const out_height)
{
if (!cur.HasImage())
return true;
uint32_t width;
uint32_t height;
cur.Size(&width, &height);
if (!*out_width) {
MOZ_ASSERT(!*out_height);
*out_width = width;
*out_height = height;
return true;
}
return (width == *out_width &&
height == *out_height);
}
bool bool
WebGLFramebuffer::AllImageRectsMatch() const WebGLFramebuffer::AllImageRectsMatch() const
{ {
@ -819,20 +808,84 @@ WebGLFramebuffer::AllImageRectsMatch() const
DebugOnly<nsCString> fbStatusInfo; DebugOnly<nsCString> fbStatusInfo;
MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo)); MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo));
bool needsInit = true;
uint32_t width = 0; uint32_t width = 0;
uint32_t height = 0; uint32_t height = 0;
bool imageRectsMatch = true;
imageRectsMatch &= MatchOrReplaceSize(mColorAttachment0, &width, &height); const auto fnInitializeOrMatch = [&needsInit, &width,
imageRectsMatch &= MatchOrReplaceSize(mDepthAttachment, &width, &height); &height](const WebGLFBAttachPoint& attach)
imageRectsMatch &= MatchOrReplaceSize(mStencilAttachment, &width, &height); {
imageRectsMatch &= MatchOrReplaceSize(mDepthStencilAttachment, &width, &height); if (!attach.HasImage())
return true;
uint32_t curWidth;
uint32_t curHeight;
attach.Size(&curWidth, &curHeight);
if (needsInit) {
needsInit = false;
width = curWidth;
height = curHeight;
return true;
}
return (curWidth == width &&
curHeight == height);
};
bool matches = true;
matches &= fnInitializeOrMatch(mColorAttachment0 );
matches &= fnInitializeOrMatch(mDepthAttachment );
matches &= fnInitializeOrMatch(mStencilAttachment );
matches &= fnInitializeOrMatch(mDepthStencilAttachment);
for (const auto& cur : mMoreColorAttachments) { for (const auto& cur : mMoreColorAttachments) {
imageRectsMatch &= MatchOrReplaceSize(cur, &width, &height); matches &= fnInitializeOrMatch(cur);
} }
return imageRectsMatch; return matches;
}
bool
WebGLFramebuffer::AllImageSamplesMatch() const
{
MOZ_ASSERT(HasDefinedAttachments());
DebugOnly<nsCString> fbStatusInfo;
MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo));
bool needsInit = true;
uint32_t samples = 0;
const auto fnInitializeOrMatch = [&needsInit,
&samples](const WebGLFBAttachPoint& attach)
{
if (!attach.HasImage())
return true;
const uint32_t curSamples = attach.Samples();
if (needsInit) {
needsInit = false;
samples = curSamples;
return true;
}
return (curSamples == samples);
};
bool matches = true;
matches &= fnInitializeOrMatch(mColorAttachment0 );
matches &= fnInitializeOrMatch(mDepthAttachment );
matches &= fnInitializeOrMatch(mStencilAttachment );
matches &= fnInitializeOrMatch(mDepthStencilAttachment);
for (const auto& cur : mMoreColorAttachments) {
matches &= fnInitializeOrMatch(cur);
}
return matches;
} }
FBStatus FBStatus
@ -850,7 +903,11 @@ WebGLFramebuffer::PrecheckFramebufferStatus(nsCString* const out_info) const
if (!AllImageRectsMatch()) if (!AllImageRectsMatch())
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // Inconsistent sizes return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // Inconsistent sizes
if (!AllImageSamplesMatch())
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; // Inconsistent samples
if (!mContext->IsWebGL2()) { if (!mContext->IsWebGL2()) {
// INCOMPLETE_DIMENSIONS doesn't exist in GLES3.
const auto depthOrStencilCount = int(mDepthAttachment.IsDefined()) + const auto depthOrStencilCount = int(mDepthAttachment.IsDefined()) +
int(mStencilAttachment.IsDefined()) + int(mStencilAttachment.IsDefined()) +
int(mDepthStencilAttachment.IsDefined()); int(mDepthStencilAttachment.IsDefined());

View File

@ -49,7 +49,6 @@ private:
, mAttachmentPoint(0) , mAttachmentPoint(0)
{ } { }
public: public:
WebGLFBAttachPoint(WebGLFramebuffer* fb, GLenum attachmentPoint); WebGLFBAttachPoint(WebGLFramebuffer* fb, GLenum attachmentPoint);
~WebGLFBAttachPoint(); ~WebGLFBAttachPoint();
@ -60,6 +59,7 @@ public:
bool IsDeleteRequested() const; bool IsDeleteRequested() const;
const webgl::FormatUsageInfo* Format() const; const webgl::FormatUsageInfo* Format() const;
uint32_t Samples() const;
bool HasAlpha() const; bool HasAlpha() const;
bool IsReadableFloat() const; bool IsReadableFloat() const;
@ -98,7 +98,6 @@ public:
void SetImageDataStatus(WebGLImageDataStatus x); void SetImageDataStatus(WebGLImageDataStatus x);
void Size(uint32_t* const out_width, uint32_t* const out_height) const; void Size(uint32_t* const out_width, uint32_t* const out_height) const;
//const WebGLRectangleObject& RectangleObject() const;
bool HasImage() const; bool HasImage() const;
bool IsComplete(WebGLContext* webgl, nsCString* const out_info) const; bool IsComplete(WebGLContext* webgl, nsCString* const out_info) const;
@ -229,6 +228,7 @@ public:
bool HasDefinedAttachments() const; bool HasDefinedAttachments() const;
bool HasIncompleteAttachments(nsCString* const out_info) const; bool HasIncompleteAttachments(nsCString* const out_info) const;
bool AllImageRectsMatch() const; bool AllImageRectsMatch() const;
bool AllImageSamplesMatch() const;
FBStatus PrecheckFramebufferStatus(nsCString* const out_info) const; FBStatus PrecheckFramebufferStatus(nsCString* const out_info) const;
FBStatus CheckFramebufferStatus(nsCString* const out_info) const; FBStatus CheckFramebufferStatus(nsCString* const out_info) const;

View File

@ -15,7 +15,7 @@
namespace mozilla { namespace mozilla {
static GLenum static GLenum
DepthStencilDepthFormat(gl::GLContext* gl) DepthFormatForDepthStencilEmu(gl::GLContext* gl)
{ {
// We might not be able to get 24-bit, so let's pretend! // We might not be able to get 24-bit, so let's pretend!
if (gl->IsGLES() && !gl->IsExtensionSupported(gl::GLContext::OES_depth24)) if (gl->IsGLES() && !gl->IsExtensionSupported(gl::GLContext::OES_depth24))
@ -24,46 +24,38 @@ DepthStencilDepthFormat(gl::GLContext* gl)
return LOCAL_GL_DEPTH_COMPONENT24; return LOCAL_GL_DEPTH_COMPONENT24;
} }
static bool
NeedsDepthStencilEmu(gl::GLContext* gl, GLenum internalFormat)
{
MOZ_ASSERT(internalFormat != LOCAL_GL_DEPTH_STENCIL);
if (internalFormat != LOCAL_GL_DEPTH24_STENCIL8)
return false;
if (gl->IsSupported(gl::GLFeature::packed_depth_stencil))
return false;
return true;
}
JSObject* JSObject*
WebGLRenderbuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) WebGLRenderbuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
{ {
return dom::WebGLRenderbufferBinding::Wrap(cx, this, givenProto); return dom::WebGLRenderbufferBinding::Wrap(cx, this, givenProto);
} }
static GLuint
DoCreateRenderbuffer(gl::GLContext* gl)
{
MOZ_ASSERT(gl->IsCurrent());
GLuint ret = 0;
gl->fGenRenderbuffers(1, &ret);
return ret;
}
static bool
EmulatePackedDepthStencil(gl::GLContext* gl)
{
return !gl->IsSupported(gl::GLFeature::packed_depth_stencil);
}
WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext* webgl) WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext* webgl)
: WebGLContextBoundObject(webgl) : WebGLContextBoundObject(webgl)
, mPrimaryRB(0) , mPrimaryRB( DoCreateRenderbuffer(webgl->gl) )
, mEmulatePackedDepthStencil( EmulatePackedDepthStencil(webgl->gl) )
, mSecondaryRB(0) , mSecondaryRB(0)
, mFormat(nullptr) , mFormat(nullptr)
, mSamples(0)
, mImageDataStatus(WebGLImageDataStatus::NoImageData) , mImageDataStatus(WebGLImageDataStatus::NoImageData)
, mSamples(1) , mHasBeenBound(false)
, mIsUsingSecondary(false)
#ifdef ANDROID
, mIsRB(false)
#endif
{ {
mContext->MakeContextCurrent();
mContext->gl->fGenRenderbuffers(1, &mPrimaryRB);
if (!mContext->gl->IsSupported(gl::GLFeature::packed_depth_stencil)) {
mContext->gl->fGenRenderbuffers(1, &mSecondaryRB);
}
mContext->mRenderbuffers.insertBack(this); mContext->mRenderbuffers.insertBack(this);
} }
@ -77,9 +69,6 @@ WebGLRenderbuffer::Delete()
mContext->gl->fDeleteRenderbuffers(1, &mSecondaryRB); mContext->gl->fDeleteRenderbuffers(1, &mSecondaryRB);
LinkedListElement<WebGLRenderbuffer>::removeFrom(mContext->mRenderbuffers); LinkedListElement<WebGLRenderbuffer>::removeFrom(mContext->mRenderbuffers);
#ifdef ANDROID
mIsRB = false;
#endif
} }
int64_t int64_t
@ -89,156 +78,173 @@ WebGLRenderbuffer::MemoryUsage() const
if (!mFormat) if (!mFormat)
return 0; return 0;
auto bytesPerPixel = mFormat->format->estimatedBytesPerPixel; const auto bytesPerPixel = mFormat->format->estimatedBytesPerPixel;
uint64_t pixels = uint64_t(mWidth) * uint64_t(mHeight); const int64_t pixels = int64_t(mWidth) * int64_t(mHeight);
uint64_t totalSize = pixels * bytesPerPixel; const int64_t totalSize = pixels * bytesPerPixel;
return totalSize;
// If we have the same bytesPerPixel whether or not we have a secondary RB.
if (mSecondaryRB && !mIsUsingSecondary) {
totalSize += 2; // 1x1xRGBA4
}
return int64_t(totalSize);
} }
void static GLenum
WebGLRenderbuffer::BindRenderbuffer() const DoRenderbufferStorageMaybeMultisample(gl::GLContext* gl, GLsizei samples,
{ GLenum internalFormat, GLsizei width,
/* Do this explicitly here, since the meaning changes for depth-stencil emu. GLsizei height)
* Under normal circumstances, there's only one RB: `mPrimaryRB`.
* `mSecondaryRB` is used when we have to pretend that the renderbuffer is
* DEPTH_STENCIL, when it's actually one DEPTH buffer `mPrimaryRB` and one
* STENCIL buffer `mSecondaryRB`.
*
* In the DEPTH_STENCIL emulation case, we're actually juggling two RBs, but
* we can only bind one of them at a time. We choose to unconditionally bind
* the depth RB. When we need to ask about the stencil buffer (say, how many
* stencil bits we have), we temporarily bind the stencil RB, so that it
* looks like we're just asking the question of a combined DEPTH_STENCIL
* buffer.
*/
mContext->gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mPrimaryRB);
}
static void
RenderbufferStorageMaybeMultisample(gl::GLContext* gl, GLsizei samples,
GLenum internalFormat, GLsizei width,
GLsizei height)
{ {
MOZ_ASSERT_IF(samples >= 1, gl->IsSupported(gl::GLFeature::framebuffer_multisample)); MOZ_ASSERT_IF(samples >= 1, gl->IsSupported(gl::GLFeature::framebuffer_multisample));
MOZ_ASSERT(samples >= 0);
MOZ_ASSERT(samples <= gl->MaxSamples());
// certain OpenGL ES renderbuffer formats may not exist on desktop OpenGL
GLenum internalFormatForGL = internalFormat;
// Certain OpenGL ES renderbuffer formats may not exist on desktop OpenGL.
switch (internalFormat) { switch (internalFormat) {
case LOCAL_GL_RGBA4: case LOCAL_GL_RGBA4:
case LOCAL_GL_RGB5_A1: case LOCAL_GL_RGB5_A1:
// 16-bit RGBA formats are not supported on desktop GL // 16-bit RGBA formats are not supported on desktop GL.
if (!gl->IsGLES()) if (!gl->IsGLES())
internalFormatForGL = LOCAL_GL_RGBA8; internalFormat = LOCAL_GL_RGBA8;
break; break;
case LOCAL_GL_RGB565: case LOCAL_GL_RGB565:
// the RGB565 format is not supported on desktop GL // RGB565 is not supported on desktop GL.
if (!gl->IsGLES()) if (!gl->IsGLES())
internalFormatForGL = LOCAL_GL_RGB8; internalFormat = LOCAL_GL_RGB8;
break; break;
case LOCAL_GL_DEPTH_COMPONENT16: case LOCAL_GL_DEPTH_COMPONENT16:
if (!gl->IsGLES() || gl->IsExtensionSupported(gl::GLContext::OES_depth24)) if (!gl->IsGLES() || gl->IsExtensionSupported(gl::GLContext::OES_depth24))
internalFormatForGL = LOCAL_GL_DEPTH_COMPONENT24; internalFormat = LOCAL_GL_DEPTH_COMPONENT24;
else if (gl->IsSupported(gl::GLFeature::packed_depth_stencil)) else if (gl->IsSupported(gl::GLFeature::packed_depth_stencil))
internalFormatForGL = LOCAL_GL_DEPTH24_STENCIL8; internalFormat = LOCAL_GL_DEPTH24_STENCIL8;
break; break;
case LOCAL_GL_DEPTH_STENCIL: case LOCAL_GL_DEPTH_STENCIL:
// We emulate this in WebGLRenderbuffer if we don't have the requisite extension. MOZ_CRASH("GL_DEPTH_STENCIL is not valid here.");
internalFormatForGL = LOCAL_GL_DEPTH24_STENCIL8;
break; break;
default: default:
break; break;
} }
gl::GLContext::LocalErrorScope errorScope(*gl);
if (samples > 0) { if (samples > 0) {
gl->fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER, samples, gl->fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER, samples,
internalFormatForGL, width, height); internalFormat, width, height);
} else { } else {
gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, internalFormatForGL, width, gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, internalFormat, width, height);
height);
} }
return errorScope.GetError();
} }
void GLenum
WebGLRenderbuffer::RenderbufferStorage(GLsizei samples, WebGLRenderbuffer::DoRenderbufferStorage(uint32_t samples,
const webgl::FormatUsageInfo* format, const webgl::FormatUsageInfo* format,
GLsizei width, GLsizei height) uint32_t width, uint32_t height)
{ {
MOZ_ASSERT(mContext->mBoundRenderbuffer == this); MOZ_ASSERT(mContext->mBoundRenderbuffer == this);
gl::GLContext* gl = mContext->gl; gl::GLContext* gl = mContext->gl;
MOZ_ASSERT(samples >= 0 && samples <= 256); // Sanity check. MOZ_ASSERT(samples <= 256); // Sanity check.
GLenum primaryFormat = format->format->sizedFormat; GLenum primaryFormat = format->format->sizedFormat;
GLenum secondaryFormat = 0; GLenum secondaryFormat = 0;
if (NeedsDepthStencilEmu(mContext->gl, primaryFormat)) { if (mEmulatePackedDepthStencil && primaryFormat == LOCAL_GL_DEPTH24_STENCIL8) {
primaryFormat = DepthStencilDepthFormat(gl); primaryFormat = DepthFormatForDepthStencilEmu(gl);
secondaryFormat = LOCAL_GL_STENCIL_INDEX8; secondaryFormat = LOCAL_GL_STENCIL_INDEX8;
} }
RenderbufferStorageMaybeMultisample(gl, samples, primaryFormat, width, gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mPrimaryRB);
height); GLenum error = DoRenderbufferStorageMaybeMultisample(gl, samples, primaryFormat,
width, height);
if (error)
return error;
if (mSecondaryRB) { if (secondaryFormat) {
// We can't leave the secondary RB unspecified either, since we should if (!mSecondaryRB) {
// handle the case where we attach a non-depth-stencil RB to a gl->fGenRenderbuffers(1, &mSecondaryRB);
// depth-stencil attachment point, or attach this depth-stencil RB to a
// non-depth-stencil attachment point.
gl::ScopedBindRenderbuffer autoRB(gl, mSecondaryRB);
if (secondaryFormat) {
RenderbufferStorageMaybeMultisample(gl, samples, secondaryFormat, width,
height);
} else {
RenderbufferStorageMaybeMultisample(gl, samples, LOCAL_GL_RGBA4, 1, 1);
} }
gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mSecondaryRB);
error = DoRenderbufferStorageMaybeMultisample(gl, samples, secondaryFormat,
width, height);
if (error)
return error;
} else if (mSecondaryRB) {
gl->fDeleteRenderbuffers(1, &mSecondaryRB);
mSecondaryRB = 0;
}
return 0;
}
void
WebGLRenderbuffer::RenderbufferStorage(const char* funcName, uint32_t samples,
GLenum internalFormat, uint32_t width,
uint32_t height)
{
const auto usage = mContext->mFormatUsage->GetRBUsage(internalFormat);
if (!usage) {
mContext->ErrorInvalidEnum("%s: Invalid `internalFormat`: 0x%04x.", funcName,
internalFormat);
return;
}
if (width > mContext->mImplMaxRenderbufferSize ||
height > mContext->mImplMaxRenderbufferSize)
{
mContext->ErrorInvalidValue("%s: Width or height exceeds maximum renderbuffer"
" size.",
funcName);
return;
}
mContext->MakeContextCurrent();
if (!usage->maxSamplesKnown) {
const_cast<webgl::FormatUsageInfo*>(usage)->ResolveMaxSamples(mContext->gl);
}
MOZ_ASSERT(usage->maxSamplesKnown);
if (samples > usage->maxSamples) {
mContext->ErrorInvalidValue("%s: `samples` is out of the valid range.", funcName);
return;
}
// Validation complete.
const GLenum error = DoRenderbufferStorage(samples, usage, width, height);
if (error) {
const char* errorName = mContext->ErrorName(error);
mContext->GenerateWarning("%s generated error %s", funcName, errorName);
return;
} }
mSamples = samples; mSamples = samples;
mFormat = format; mFormat = usage;
mWidth = width; mWidth = width;
mHeight = height; mHeight = height;
mImageDataStatus = WebGLImageDataStatus::UninitializedImageData; mImageDataStatus = WebGLImageDataStatus::UninitializedImageData;
mIsUsingSecondary = bool(secondaryFormat);
InvalidateStatusOfAttachedFBs(); InvalidateStatusOfAttachedFBs();
} }
void void
WebGLRenderbuffer::FramebufferRenderbuffer(GLenum attachment) const WebGLRenderbuffer::DoFramebufferRenderbuffer(GLenum attachment) const
{ {
gl::GLContext* gl = mContext->gl; gl::GLContext* gl = mContext->gl;
if (attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachment, if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
const GLuint stencilRB = (mSecondaryRB ? mSecondaryRB : mPrimaryRB);
gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
LOCAL_GL_DEPTH_ATTACHMENT,
LOCAL_GL_RENDERBUFFER, mPrimaryRB); LOCAL_GL_RENDERBUFFER, mPrimaryRB);
gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
LOCAL_GL_STENCIL_ATTACHMENT,
LOCAL_GL_RENDERBUFFER, stencilRB);
return; return;
} }
GLuint stencilRB = mPrimaryRB; gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachment,
if (mIsUsingSecondary) {
MOZ_ASSERT(mSecondaryRB);
stencilRB = mSecondaryRB;
}
gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
LOCAL_GL_DEPTH_ATTACHMENT,
LOCAL_GL_RENDERBUFFER, mPrimaryRB); LOCAL_GL_RENDERBUFFER, mPrimaryRB);
gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
LOCAL_GL_STENCIL_ATTACHMENT,
LOCAL_GL_RENDERBUFFER, stencilRB);
} }
GLint GLint
@ -266,6 +272,7 @@ WebGLRenderbuffer::GetRenderbufferParameter(RBTarget target,
case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE: case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE:
case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE: case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE:
{ {
gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mPrimaryRB);
GLint i = 0; GLint i = 0;
gl->fGetRenderbufferParameteriv(target.get(), pname.get(), &i); gl->fGetRenderbufferParameteriv(target.get(), pname.get(), &i);
return i; return i;
@ -285,8 +292,7 @@ WebGLRenderbuffer::GetRenderbufferParameter(RBTarget target,
} }
} }
MOZ_ASSERT(false, MOZ_ASSERT(false, "This function should only be called with valid `pname`.");
"This function should only be called with valid `pname`.");
return 0; return 0;
} }

View File

@ -26,7 +26,26 @@ class WebGLRenderbuffer final
, public WebGLContextBoundObject , public WebGLContextBoundObject
, public WebGLFramebufferAttachable , public WebGLFramebufferAttachable
{ {
friend class WebGLContext;
friend class WebGLFramebuffer;
friend class WebGLFBAttachPoint;
public: public:
const GLuint mPrimaryRB;
protected:
const bool mEmulatePackedDepthStencil;
GLuint mSecondaryRB;
const webgl::FormatUsageInfo* mFormat;
GLsizei mSamples;
WebGLImageDataStatus mImageDataStatus;
bool mHasBeenBound;
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLRenderbuffer)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLRenderbuffer)
explicit WebGLRenderbuffer(WebGLContext* webgl); explicit WebGLRenderbuffer(WebGLContext* webgl);
void Delete(); void Delete();
@ -46,8 +65,6 @@ public:
GLsizei Samples() const { return mSamples; } GLsizei Samples() const { return mSamples; }
GLuint PrimaryGLName() const { return mPrimaryRB; }
const webgl::FormatUsageInfo* Format() const { return mFormat; } const webgl::FormatUsageInfo* Format() const { return mFormat; }
int64_t MemoryUsage() const; int64_t MemoryUsage() const;
@ -56,41 +73,21 @@ public:
return mContext; return mContext;
} }
void BindRenderbuffer() const; void RenderbufferStorage(const char* funcName, uint32_t samples,
void RenderbufferStorage(GLsizei samples, const webgl::FormatUsageInfo* format, GLenum internalFormat, uint32_t width, uint32_t height);
GLsizei width, GLsizei height);
void FramebufferRenderbuffer(GLenum attachment) const;
// Only handles a subset of `pname`s. // Only handles a subset of `pname`s.
GLint GetRenderbufferParameter(RBTarget target, RBParam pname) const; GLint GetRenderbufferParameter(RBTarget target, RBParam pname) const;
virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override; virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLRenderbuffer)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLRenderbuffer)
protected: protected:
~WebGLRenderbuffer() { ~WebGLRenderbuffer() {
DeleteOnce(); DeleteOnce();
} }
GLuint mPrimaryRB; void DoFramebufferRenderbuffer(GLenum attachment) const;
GLuint mSecondaryRB; GLenum DoRenderbufferStorage(uint32_t samples, const webgl::FormatUsageInfo* format,
const webgl::FormatUsageInfo* mFormat; uint32_t width, uint32_t height);
WebGLImageDataStatus mImageDataStatus;
GLsizei mSamples;
bool mIsUsingSecondary;
#ifdef ANDROID
// Bug 1140459: Some drivers (including our test slaves!) don't
// give reasonable answers for IsRenderbuffer, maybe others.
// This shows up on Android 2.3 emulator.
//
// So we track the `is a Renderbuffer` state ourselves.
bool mIsRB;
#endif
friend class WebGLContext;
friend class WebGLFramebuffer;
friend class WebGLFBAttachPoint;
}; };
} // namespace mozilla } // namespace mozilla

View File

@ -0,0 +1,15 @@
<html>
<head>
<script>
function boom()
{
var canvas = document.getElementById("canvas");
var ctx = SpecialPowers.wrap(canvas.getContext("2d"));
ctx.drawWindow(window, 0, 0, canvas.width, canvas.height, "red");
}
</script>
</head>
<body onload="boom();">
<canvas id="canvas" width="10" height="10"></canvas>
</body>
</html>

View File

@ -29,4 +29,5 @@ skip-if(azureCairo) load 1229983-1.html
load 1229932-1.html load 1229932-1.html
load 1233613.html load 1233613.html
load 1244850-1.html load 1244850-1.html
load 1246775-1.html
load texImage2D.html load texImage2D.html

View File

@ -2170,11 +2170,9 @@ private:
mJwk.mExt.Construct(mExtractable); mJwk.mExt.Construct(mExtractable);
if (!mKeyUsages.IsEmpty()) { mJwk.mKey_ops.Construct();
mJwk.mKey_ops.Construct(); if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages, fallible)) {
if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages, fallible)) { return NS_ERROR_OUT_OF_MEMORY;
return NS_ERROR_OUT_OF_MEMORY;
}
} }
return NS_OK; return NS_OK;

View File

@ -250,6 +250,58 @@ TestArray.addTest(
); );
} }
); );
// --------
TestArray.addTest(
"Check JWK parameters on generated ECDSA key pair",
function() {
crypto.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, ['sign', 'verify'])
.then(pair => Promise.all([
crypto.subtle.exportKey('jwk', pair.privateKey),
crypto.subtle.exportKey('jwk', pair.publicKey)
]))
.then(
complete(this, function(x) {
var priv = x[0];
var pub = x[1];
var pubIsSubsetOfPriv = Object.keys(pub)
.filter(k => k !== 'key_ops') // key_ops is the only complex attr
.reduce((all, k) => all && pub[k] === priv[k], true);
// Can't use hasBaseJwkFields() because EC keys don't get "alg":
// "alg" matches curve to hash, but WebCrypto keys are more flexible.
return hasFields(pub, ['kty', 'crv', 'key_ops', 'ext']) &&
pub.kty === 'EC' &&
pub.crv === 'P-256' &&
pub.ext &&
typeof(pub.x) === 'string' &&
typeof(pub.y) === 'string' &&
shallowArrayEquals(pub.key_ops, ['verify']) &&
pubIsSubsetOfPriv &&
shallowArrayEquals(priv.key_ops, ['sign']) &&
typeof(priv.d) === 'string';
}),
error(this));
}
);
// --------
TestArray.addTest(
"Check key_ops parameter on an unusable RSA public key",
function() {
var parameters = {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 1024,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256'
};
// The public key generated here will have no usages and will therefore
// have an empty key_ops list.
crypto.subtle.generateKey(parameters, true, ['sign'])
.then(pair => crypto.subtle.exportKey('jwk', pair.publicKey))
.then(complete(this, x => x.key_ops.length === 0),
error(this));
}
);
/*]]>*/</script> /*]]>*/</script>
</head> </head>

View File

@ -194,7 +194,6 @@ TouchEvent::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
} }
prefValue = sIsTouchDeviceSupportPresent; prefValue = sIsTouchDeviceSupportPresent;
#else #else
NS_WARNING("dom.w3c_touch_events.enabled=2 not implemented!");
prefValue = false; prefValue = false;
#endif #endif
} else { } else {

View File

@ -487,20 +487,6 @@ MediaSource::NotifyEvicted(double aStart, double aEnd)
mSourceBuffers->Evict(aStart, aEnd); mSourceBuffers->Evict(aStart, aEnd);
} }
#if defined(DEBUG)
void
MediaSource::Dump(const char* aPath)
{
char buf[255];
PR_snprintf(buf, sizeof(buf), "%s/mediasource-%p", aPath, this);
PR_MkDir(buf, 0700);
if (mSourceBuffers) {
mSourceBuffers->Dump(buf);
}
}
#endif
void void
MediaSource::GetMozDebugReaderData(nsAString& aString) MediaSource::GetMozDebugReaderData(nsAString& aString)
{ {

View File

@ -100,12 +100,6 @@ public:
// that were evicted are provided. // that were evicted are provided.
void NotifyEvicted(double aStart, double aEnd); void NotifyEvicted(double aStart, double aEnd);
#if defined(DEBUG)
// Dump the contents of each SourceBuffer to a series of files under aPath.
// aPath must exist. Debug only, invoke from your favourite debugger.
void Dump(const char* aPath);
#endif
// Returns a string describing the state of the MediaSource internal // Returns a string describing the state of the MediaSource internal
// buffered data. Used for debugging purposes. // buffered data. Used for debugging purposes.
void GetMozDebugReaderData(nsAString& aString); void GetMozDebugReaderData(nsAString& aString);

View File

@ -256,6 +256,12 @@ MediaDecoderOwner::NextFrameStatus
MediaSourceDecoder::NextFrameBufferedStatus() MediaSourceDecoder::NextFrameBufferedStatus()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
if (!mMediaSource ||
mMediaSource->ReadyState() == dom::MediaSourceReadyState::Closed) {
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
}
// Next frame hasn't been decoded yet. // Next frame hasn't been decoded yet.
// Use the buffered range to consider if we have the next frame available. // Use the buffered range to consider if we have the next frame available.
TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition()); TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition());

View File

@ -22,7 +22,7 @@ using media::TimeIntervals;
MediaSourceDemuxer::MediaSourceDemuxer() MediaSourceDemuxer::MediaSourceDemuxer()
: mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK), : mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
/* aSupportsTailDispatch = */ true)) /* aSupportsTailDispatch = */ false))
, mMonitor("MediaSourceDemuxer") , mMonitor("MediaSourceDemuxer")
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());

View File

@ -39,27 +39,6 @@ using media::TimeUnit;
namespace dom { namespace dom {
class BufferAppendRunnable : public nsRunnable {
public:
BufferAppendRunnable(SourceBuffer* aSourceBuffer,
uint32_t aUpdateID)
: mSourceBuffer(aSourceBuffer)
, mUpdateID(aUpdateID)
{
}
NS_IMETHOD Run() override final {
mSourceBuffer->BufferAppend(mUpdateID);
return NS_OK;
}
private:
RefPtr<SourceBuffer> mSourceBuffer;
uint32_t mUpdateID;
};
void void
SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv) SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
{ {
@ -226,10 +205,13 @@ void
SourceBuffer::AbortBufferAppend() SourceBuffer::AbortBufferAppend()
{ {
if (mUpdating) { if (mUpdating) {
mPendingAppend.DisconnectIfExists(); if (mPendingAppend.Exists()) {
// TODO: Abort stream append loop algorithms. mPendingAppend.Disconnect();
// cancel any pending buffer append. mContentManager->AbortAppendData();
mContentManager->AbortAppendData(); // Some data may have been added by the Segment Parser Loop.
// Check if we need to update the duration.
CheckEndTime();
}
AbortUpdating(); AbortUpdating();
} }
} }
@ -309,7 +291,6 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
, mMediaSource(aMediaSource) , mMediaSource(aMediaSource)
, mUpdating(false) , mUpdating(false)
, mActive(false) , mActive(false)
, mUpdateID(0)
, mType(aType) , mType(aType)
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
@ -381,7 +362,6 @@ SourceBuffer::StartUpdating()
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mUpdating); MOZ_ASSERT(!mUpdating);
mUpdating = true; mUpdating = true;
mUpdateID++;
QueueAsyncSimpleEvent("updatestart"); QueueAsyncSimpleEvent("updatestart");
} }
@ -390,12 +370,8 @@ SourceBuffer::StopUpdating()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
if (!mUpdating) { if (!mUpdating) {
// The buffer append algorithm has been interrupted by abort(). // The buffer append or range removal algorithm has been interrupted by
// // abort().
// If the sequence appendBuffer(), abort(), appendBuffer() occurs before
// the first StopUpdating() runnable runs, then a second StopUpdating()
// runnable will be scheduled, but still only one (the first) will queue
// events.
return; return;
} }
mUpdating = false; mUpdating = false;
@ -407,7 +383,6 @@ void
SourceBuffer::AbortUpdating() SourceBuffer::AbortUpdating()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mUpdating);
mUpdating = false; mUpdating = false;
QueueAsyncSimpleEvent("abort"); QueueAsyncSimpleEvent("abort");
QueueAsyncSimpleEvent("updateend"); QueueAsyncSimpleEvent("updateend");
@ -438,23 +413,13 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
StartUpdating(); StartUpdating();
nsCOMPtr<nsIRunnable> task = new BufferAppendRunnable(this, mUpdateID); BufferAppend();
NS_DispatchToMainThread(task);
} }
void void
SourceBuffer::BufferAppend(uint32_t aUpdateID) SourceBuffer::BufferAppend()
{ {
if (!mUpdating || aUpdateID != mUpdateID) { MOZ_ASSERT(mUpdating);
// The buffer append algorithm has been interrupted by abort().
//
// If the sequence appendBuffer(), abort(), appendBuffer() occurs before
// the first StopUpdating() runnable runs, then a second StopUpdating()
// runnable will be scheduled, but still only one (the first) will queue
// events.
return;
}
MOZ_ASSERT(mMediaSource); MOZ_ASSERT(mMediaSource);
MOZ_ASSERT(!mPendingAppend.Exists()); MOZ_ASSERT(!mPendingAppend.Exists());
@ -467,11 +432,8 @@ SourceBuffer::BufferAppend(uint32_t aUpdateID)
void void
SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks) SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks)
{ {
MOZ_ASSERT(mUpdating);
mPendingAppend.Complete(); mPendingAppend.Complete();
if (!mUpdating) {
// The buffer append algorithm has been interrupted by abort().
return;
}
if (aHasActiveTracks) { if (aHasActiveTracks) {
if (!mActive) { if (!mActive) {
@ -494,7 +456,9 @@ SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks)
void void
SourceBuffer::AppendDataErrored(nsresult aError) SourceBuffer::AppendDataErrored(nsresult aError)
{ {
MOZ_ASSERT(mUpdating);
mPendingAppend.Complete(); mPendingAppend.Complete();
switch (aError) { switch (aError) {
case NS_ERROR_ABORT: case NS_ERROR_ABORT:
// Nothing further to do as the trackbuffer has been shutdown. // Nothing further to do as the trackbuffer has been shutdown.
@ -510,10 +474,7 @@ void
SourceBuffer::AppendError(bool aDecoderError) SourceBuffer::AppendError(bool aDecoderError)
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
if (!mUpdating) {
// The buffer append algorithm has been interrupted by abort().
return;
}
mContentManager->ResetParserState(); mContentManager->ResetParserState();
mUpdating = false; mUpdating = false;
@ -625,16 +586,6 @@ SourceBuffer::Evict(double aStart, double aEnd)
mContentManager->EvictBefore(TimeUnit::FromSeconds(evictTime)); mContentManager->EvictBefore(TimeUnit::FromSeconds(evictTime));
} }
#if defined(DEBUG)
void
SourceBuffer::Dump(const char* aPath)
{
if (mContentManager) {
mContentManager->Dump(aPath);
}
}
#endif
NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer) NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer)

View File

@ -213,10 +213,6 @@ public:
return mActive; return mActive;
} }
#if defined(DEBUG)
void Dump(const char* aPath);
#endif
private: private:
~SourceBuffer(); ~SourceBuffer();
@ -238,7 +234,7 @@ private:
// Shared implementation of AppendBuffer overloads. // Shared implementation of AppendBuffer overloads.
void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv); void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
void BufferAppend(uint32_t aAppendID); void BufferAppend();
// Implement the "Append Error Algorithm". // Implement the "Append Error Algorithm".
// Will call endOfStream() with "decode" error if aDecodeError is true. // Will call endOfStream() with "decode" error if aDecodeError is true.
@ -266,11 +262,6 @@ private:
mozilla::Atomic<bool> mActive; mozilla::Atomic<bool> mActive;
// Each time mUpdating is set to true, mUpdateID will be incremented.
// This allows for a queued AppendData task to identify if it was earlier
// aborted and another AppendData queued.
uint32_t mUpdateID;
MozPromiseRequestHolder<SourceBufferContentManager::AppendPromise> mPendingAppend; MozPromiseRequestHolder<SourceBufferContentManager::AppendPromise> mPendingAppend;
const nsCString mType; const nsCString mType;

View File

@ -108,10 +108,6 @@ public:
virtual void RestartGroupStartTimestamp() {} virtual void RestartGroupStartTimestamp() {}
virtual media::TimeUnit GroupEndTimestamp() = 0; virtual media::TimeUnit GroupEndTimestamp() = 0;
#if defined(DEBUG)
virtual void Dump(const char* aPath) { }
#endif
protected: protected:
virtual ~SourceBufferContentManager() { } virtual ~SourceBufferContentManager() { }
}; };

View File

@ -176,16 +176,6 @@ SourceBufferList::QueueAsyncSimpleEvent(const char* aName)
NS_DispatchToMainThread(event); NS_DispatchToMainThread(event);
} }
#if defined(DEBUG)
void
SourceBufferList::Dump(const char* aPath)
{
for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
mSourceBuffers[i]->Dump(aPath);
}
}
#endif
SourceBufferList::SourceBufferList(MediaSource* aMediaSource) SourceBufferList::SourceBufferList(MediaSource* aMediaSource)
: DOMEventTargetHelper(aMediaSource->GetParentObject()) : DOMEventTargetHelper(aMediaSource->GetParentObject())
, mMediaSource(aMediaSource) , mMediaSource(aMediaSource)

View File

@ -84,10 +84,6 @@ public:
// No event is fired and no action is performed on the sourcebuffers. // No event is fired and no action is performed on the sourcebuffers.
void ClearSimple(); void ClearSimple();
#if defined(DEBUG)
void Dump(const char* aPath);
#endif
private: private:
~SourceBufferList(); ~SourceBufferList();

View File

@ -96,24 +96,17 @@ TrackBuffersManager::TrackBuffersManager(dom::SourceBufferAttributes* aAttribute
, mType(aType) , mType(aType)
, mParser(ContainerParser::CreateForMIMEType(aType)) , mParser(ContainerParser::CreateForMIMEType(aType))
, mProcessedInput(0) , mProcessedInput(0)
, mAppendRunning(false)
, mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue()) , mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
, mSourceBufferAttributes(aAttributes) , mSourceBufferAttributes(aAttributes)
, mParentDecoder(new nsMainThreadPtrHolder<MediaSourceDecoder>(aParentDecoder, false /* strict */)) , mParentDecoder(new nsMainThreadPtrHolder<MediaSourceDecoder>(aParentDecoder, false /* strict */))
, mMediaSourceDuration(mTaskQueue, Maybe<double>(), "TrackBuffersManager::mMediaSourceDuration (Mirror)")
, mAbort(false)
, mEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold", , mEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold",
100 * (1 << 20))) 100 * (1 << 20)))
, mEvictionOccurred(false) , mEvictionOccurred(false)
, mMonitor("TrackBuffersManager") , mMonitor("TrackBuffersManager")
, mAppendRunning(false)
, mSegmentParserLoopRunning(false)
{ {
MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread"); MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread");
RefPtr<TrackBuffersManager> self = this;
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableFunction([self] () {
self->mMediaSourceDuration.Connect(self->mParentDecoder->CanonicalExplicitDuration());
});
GetTaskQueue()->Dispatch(task.forget());
} }
TrackBuffersManager::~TrackBuffersManager() TrackBuffersManager::~TrackBuffersManager()
@ -142,7 +135,6 @@ TrackBuffersManager::AppendIncomingBuffer(IncomingBuffer aData)
{ {
MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(OnTaskQueue());
mIncomingBuffers.AppendElement(aData); mIncomingBuffers.AppendElement(aData);
mAbort = false;
} }
RefPtr<TrackBuffersManager::AppendPromise> RefPtr<TrackBuffersManager::AppendPromise>
@ -151,49 +143,42 @@ TrackBuffersManager::BufferAppend()
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MSE_DEBUG(""); MSE_DEBUG("");
mAppendRunning = true;
return InvokeAsync(GetTaskQueue(), this, return InvokeAsync(GetTaskQueue(), this,
__func__, &TrackBuffersManager::InitSegmentParserLoop); __func__, &TrackBuffersManager::InitSegmentParserLoop);
} }
// Abort any pending AppendData. // The MSE spec requires that we abort the current SegmentParserLoop
// We don't really care about really aborting our inner loop as by spec the // which is then followed by a call to ResetParserState.
// process is happening asynchronously, as such where and when we would abort is // However due to our asynchronous design this causes inherent difficulities.
// non-deterministic. The SourceBuffer also makes sure BufferAppend // As the spec behaviour is non deterministic anyway, we instead wait until the
// isn't called should the appendBuffer be immediately aborted. // current AppendData has completed its run.
// We do however want to ensure that no new task will be dispatched on our task
// queue and only let the current one finish its job. For this we set mAbort
// to true.
void void
TrackBuffersManager::AbortAppendData() TrackBuffersManager::AbortAppendData()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MSE_DEBUG(""); MSE_DEBUG("");
mAbort = true; MonitorAutoLock mon(mMonitor);
while (mAppendRunning) {
mon.Wait();
}
} }
void void
TrackBuffersManager::ResetParserState() TrackBuffersManager::ResetParserState()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mAppendRunning, "AbortAppendData must have been called"); MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running, abort must have been called");
MSE_DEBUG(""); MSE_DEBUG("");
// 1. If the append state equals PARSING_MEDIA_SEGMENT and the input buffer contains some complete coded frames, then run the coded frame processing algorithm until all of these complete coded frames have been processed. // 1. If the append state equals PARSING_MEDIA_SEGMENT and the input buffer contains some complete coded frames, then run the coded frame processing algorithm until all of these complete coded frames have been processed.
if (mAppendState == AppendState::PARSING_MEDIA_SEGMENT) { // SourceBuffer.abort() has ensured that all complete coded frames have been
nsCOMPtr<nsIRunnable> task = // processed. As such, we don't need to check for the value of mAppendState.
NS_NewRunnableMethod(this, &TrackBuffersManager::FinishCodedFrameProcessing); nsCOMPtr<nsIRunnable> task =
GetTaskQueue()->Dispatch(task.forget()); NS_NewRunnableMethod(this, &TrackBuffersManager::CompleteResetParserState);
} else { GetTaskQueue()->Dispatch(task.forget());
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableMethod(this, &TrackBuffersManager::CompleteResetParserState);
GetTaskQueue()->Dispatch(task.forget());
}
// Our ResetParserState is really asynchronous, the current task has been
// interrupted and will complete shortly (or has already completed).
// We must however present to the main thread a stable, reset state.
// So we run the following operation now in the main thread.
// 7. Set append state to WAITING_FOR_SEGMENT. // 7. Set append state to WAITING_FOR_SEGMENT.
SetAppendState(AppendState::WAITING_FOR_SEGMENT); SetAppendState(AppendState::WAITING_FOR_SEGMENT);
} }
@ -202,6 +187,7 @@ RefPtr<TrackBuffersManager::RangeRemovalPromise>
TrackBuffersManager::RangeRemoval(TimeUnit aStart, TimeUnit aEnd) TrackBuffersManager::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running");
MSE_DEBUG("From %.2f to %.2f", aStart.ToSeconds(), aEnd.ToSeconds()); MSE_DEBUG("From %.2f to %.2f", aStart.ToSeconds(), aEnd.ToSeconds());
mEnded = false; mEnded = false;
@ -309,54 +295,13 @@ TrackBuffersManager::Detach()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MSE_DEBUG(""); MSE_DEBUG("");
// Abort pending operations if any.
AbortAppendData();
RefPtr<TrackBuffersManager> self = this;
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableFunction([self] () {
// Clear our sourcebuffer
self->CodedFrameRemoval(TimeInterval(TimeUnit::FromSeconds(0),
TimeUnit::FromInfinity()));
self->mProcessingPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
self->mAppendPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
self->mMediaSourceDuration.DisconnectIfConnected();
});
GetTaskQueue()->Dispatch(task.forget());
}
#if defined(DEBUG)
void
TrackBuffersManager::Dump(const char* aPath)
{
}
#endif
void
TrackBuffersManager::FinishCodedFrameProcessing()
{
MOZ_ASSERT(OnTaskQueue());
if (mProcessingRequest.Exists()) {
NS_WARNING("Processing request pending");
mProcessingRequest.Disconnect();
}
// The spec requires us to complete parsing synchronously any outstanding
// frames in the current media segment. This can't be implemented in a way
// that makes sense.
// As such we simply completely ignore the result of any pending input buffer.
// TODO: Link to W3C bug.
CompleteResetParserState();
} }
void void
TrackBuffersManager::CompleteResetParserState() TrackBuffersManager::CompleteResetParserState()
{ {
MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(!mAppendRunning); MOZ_RELEASE_ASSERT(!mSegmentParserLoopRunning);
MSE_DEBUG(""); MSE_DEBUG("");
for (auto& track : GetTracksList()) { for (auto& track : GetTracksList()) {
@ -492,19 +437,11 @@ bool
TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval) TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval)
{ {
MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(!mAppendRunning, "Logic error: Append in progress"); MOZ_ASSERT(!mSegmentParserLoopRunning, "Logic error: Append in progress");
MSE_DEBUG("From %.2fs to %.2f", MSE_DEBUG("From %.2fs to %.2f",
aInterval.mStart.ToSeconds(), aInterval.mEnd.ToSeconds()); aInterval.mStart.ToSeconds(), aInterval.mEnd.ToSeconds());
if (mMediaSourceDuration.Ref().isNothing() ||
IsNaN(mMediaSourceDuration.Ref().ref())) {
MSE_DEBUG("Nothing to remove, aborting");
return false;
}
TimeUnit duration{TimeUnit::FromSeconds(mMediaSourceDuration.Ref().ref())};
#if DEBUG #if DEBUG
MSE_DEBUG("duration:%.2f", duration.ToSeconds());
if (HasVideo()) { if (HasVideo()) {
MSE_DEBUG("before video ranges=%s", MSE_DEBUG("before video ranges=%s",
DumpTimeRanges(mVideoTracks.mBufferedRanges).get()); DumpTimeRanges(mVideoTracks.mBufferedRanges).get());
@ -527,7 +464,14 @@ TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval)
MSE_DEBUGV("Processing %s track", track->mInfo->mMimeType.get()); MSE_DEBUGV("Processing %s track", track->mInfo->mMimeType.get());
// 1. Let remove end timestamp be the current value of duration // 1. Let remove end timestamp be the current value of duration
// See bug: https://www.w3.org/Bugs/Public/show_bug.cgi?id=28727 // See bug: https://www.w3.org/Bugs/Public/show_bug.cgi?id=28727
TimeUnit removeEndTimestamp = std::max(duration, track->mBufferedRanges.GetEnd()); // At worse we will remove all frames until the end, unless a key frame is
// found between the current interval's end and the trackbuffer's end.
TimeUnit removeEndTimestamp = track->mBufferedRanges.GetEnd();
if (start > removeEndTimestamp) {
// Nothing to remove.
continue;
}
// 2. If this track buffer has a random access point timestamp that is greater than or equal to end, // 2. If this track buffer has a random access point timestamp that is greater than or equal to end,
// then update remove end timestamp to that random access point timestamp. // then update remove end timestamp to that random access point timestamp.
@ -595,8 +539,9 @@ RefPtr<TrackBuffersManager::AppendPromise>
TrackBuffersManager::InitSegmentParserLoop() TrackBuffersManager::InitSegmentParserLoop()
{ {
MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(OnTaskQueue());
MOZ_RELEASE_ASSERT(mAppendPromise.IsEmpty());
MSE_DEBUG("");
MOZ_ASSERT(mAppendPromise.IsEmpty() && !mAppendRunning);
RefPtr<AppendPromise> p = mAppendPromise.Ensure(__func__); RefPtr<AppendPromise> p = mAppendPromise.Ensure(__func__);
AppendIncomingBuffers(); AppendIncomingBuffers();
@ -630,6 +575,9 @@ void
TrackBuffersManager::SegmentParserLoop() TrackBuffersManager::SegmentParserLoop()
{ {
MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(OnTaskQueue());
mSegmentParserLoopRunning = true;
while (true) { while (true) {
// 1. If the input buffer is empty, then jump to the need more data step below. // 1. If the input buffer is empty, then jump to the need more data step below.
if (!mInputBuffer || mInputBuffer->IsEmpty()) { if (!mInputBuffer || mInputBuffer->IsEmpty()) {
@ -733,7 +681,7 @@ TrackBuffersManager::SegmentParserLoop()
->Then(GetTaskQueue(), __func__, ->Then(GetTaskQueue(), __func__,
[self] (bool aNeedMoreData) { [self] (bool aNeedMoreData) {
self->mProcessingRequest.Complete(); self->mProcessingRequest.Complete();
if (aNeedMoreData || self->mAbort) { if (aNeedMoreData) {
self->NeedMoreData(); self->NeedMoreData();
} else { } else {
self->ScheduleSegmentParserLoop(); self->ScheduleSegmentParserLoop();
@ -752,10 +700,14 @@ void
TrackBuffersManager::NeedMoreData() TrackBuffersManager::NeedMoreData()
{ {
MSE_DEBUG(""); MSE_DEBUG("");
if (!mAbort) { RestoreCachedVariables();
RestoreCachedVariables();
}
mAppendRunning = false; mAppendRunning = false;
mSegmentParserLoopRunning = false;
{
// Wake-up any pending Abort()
MonitorAutoLock mon(mMonitor);
mon.NotifyAll();
}
mAppendPromise.ResolveIfExists(mActiveTrack, __func__); mAppendPromise.ResolveIfExists(mActiveTrack, __func__);
} }
@ -764,6 +716,12 @@ TrackBuffersManager::RejectAppend(nsresult aRejectValue, const char* aName)
{ {
MSE_DEBUG("rv=%d", aRejectValue); MSE_DEBUG("rv=%d", aRejectValue);
mAppendRunning = false; mAppendRunning = false;
mSegmentParserLoopRunning = false;
{
// Wake-up any pending Abort()
MonitorAutoLock mon(mMonitor);
mon.NotifyAll();
}
mAppendPromise.RejectIfExists(aRejectValue, aName); mAppendPromise.RejectIfExists(aRejectValue, aName);
} }
@ -840,12 +798,7 @@ void
TrackBuffersManager::OnDemuxerResetDone(nsresult) TrackBuffersManager::OnDemuxerResetDone(nsresult)
{ {
MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(OnTaskQueue());
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
mDemuxerInitRequest.Complete(); mDemuxerInitRequest.Complete();
if (mAbort) {
RejectAppend(NS_ERROR_ABORT, __func__);
return;
}
// mInputDemuxer shouldn't have been destroyed while a demuxer init/reset // mInputDemuxer shouldn't have been destroyed while a demuxer init/reset
// request was being processed. See bug 1239983. // request was being processed. See bug 1239983.
MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer); MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer);
@ -917,13 +870,8 @@ void
TrackBuffersManager::OnDemuxerInitDone(nsresult) TrackBuffersManager::OnDemuxerInitDone(nsresult)
{ {
MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(OnTaskQueue());
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
mDemuxerInitRequest.Complete(); mDemuxerInitRequest.Complete();
if (mAbort) {
RejectAppend(NS_ERROR_ABORT, __func__);
return;
}
// mInputDemuxer shouldn't have been destroyed while a demuxer init/reset // mInputDemuxer shouldn't have been destroyed while a demuxer init/reset
// request was being processed. See bug 1239983. // request was being processed. See bug 1239983.
MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer); MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer);
@ -1183,9 +1131,8 @@ TrackBuffersManager::OnDemuxFailed(TrackType aTrack,
DemuxerFailureReason aFailure) DemuxerFailureReason aFailure)
{ {
MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(OnTaskQueue());
MSE_DEBUG("Failed to demux %s, failure:%d mAbort:%d", MSE_DEBUG("Failed to demux %s, failure:%d",
aTrack == TrackType::kVideoTrack ? "video" : "audio", aTrack == TrackType::kVideoTrack ? "video" : "audio", aFailure);
aFailure, static_cast<bool>(mAbort));
switch (aFailure) { switch (aFailure) {
case DemuxerFailureReason::END_OF_STREAM: case DemuxerFailureReason::END_OF_STREAM:
case DemuxerFailureReason::WAITING_FOR_DATA: case DemuxerFailureReason::WAITING_FOR_DATA:
@ -1212,15 +1159,10 @@ void
TrackBuffersManager::DoDemuxVideo() TrackBuffersManager::DoDemuxVideo()
{ {
MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(OnTaskQueue());
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
if (!HasVideo()) { if (!HasVideo()) {
DoDemuxAudio(); DoDemuxAudio();
return; return;
} }
if (mAbort) {
RejectProcessing(NS_ERROR_ABORT, __func__);
return;
}
mVideoTracks.mDemuxRequest.Begin(mVideoTracks.mDemuxer->GetSamples(-1) mVideoTracks.mDemuxRequest.Begin(mVideoTracks.mDemuxer->GetSamples(-1)
->Then(GetTaskQueue(), __func__, this, ->Then(GetTaskQueue(), __func__, this,
&TrackBuffersManager::OnVideoDemuxCompleted, &TrackBuffersManager::OnVideoDemuxCompleted,
@ -1241,15 +1183,10 @@ void
TrackBuffersManager::DoDemuxAudio() TrackBuffersManager::DoDemuxAudio()
{ {
MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(OnTaskQueue());
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
if (!HasAudio()) { if (!HasAudio()) {
CompleteCodedFrameProcessing(); CompleteCodedFrameProcessing();
return; return;
} }
if (mAbort) {
RejectProcessing(NS_ERROR_ABORT, __func__);
return;
}
mAudioTracks.mDemuxRequest.Begin(mAudioTracks.mDemuxer->GetSamples(-1) mAudioTracks.mDemuxRequest.Begin(mAudioTracks.mDemuxer->GetSamples(-1)
->Then(GetTaskQueue(), __func__, this, ->Then(GetTaskQueue(), __func__, this,
&TrackBuffersManager::OnAudioDemuxCompleted, &TrackBuffersManager::OnAudioDemuxCompleted,
@ -1270,7 +1207,6 @@ void
TrackBuffersManager::CompleteCodedFrameProcessing() TrackBuffersManager::CompleteCodedFrameProcessing()
{ {
MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(OnTaskQueue());
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
// 1. For each coded frame in the media segment run the following steps: // 1. For each coded frame in the media segment run the following steps:
// Coded Frame Processing steps 1.1 to 1.21. // Coded Frame Processing steps 1.1 to 1.21.
@ -1341,22 +1277,12 @@ TrackBuffersManager::CompleteCodedFrameProcessing()
void void
TrackBuffersManager::RejectProcessing(nsresult aRejectValue, const char* aName) TrackBuffersManager::RejectProcessing(nsresult aRejectValue, const char* aName)
{ {
if (mAbort) {
// mAppendPromise will be resolved immediately upon mProcessingPromise
// completing.
mAppendRunning = false;
}
mProcessingPromise.RejectIfExists(aRejectValue, __func__); mProcessingPromise.RejectIfExists(aRejectValue, __func__);
} }
void void
TrackBuffersManager::ResolveProcessing(bool aResolveValue, const char* aName) TrackBuffersManager::ResolveProcessing(bool aResolveValue, const char* aName)
{ {
if (mAbort) {
// mAppendPromise will be resolved immediately upon mProcessingPromise
// completing.
mAppendRunning = false;
}
mProcessingPromise.ResolveIfExists(aResolveValue, __func__); mProcessingPromise.ResolveIfExists(aResolveValue, __func__);
} }

View File

@ -98,10 +98,6 @@ public:
bool& aError); bool& aError);
media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack); media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack);
#if defined(DEBUG)
void Dump(const char* aPath) override;
#endif
void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes); void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes);
private: private:
@ -123,9 +119,7 @@ private:
// media segment have been processed. // media segment have been processed.
RefPtr<CodedFrameProcessingPromise> CodedFrameProcessing(); RefPtr<CodedFrameProcessingPromise> CodedFrameProcessing();
void CompleteCodedFrameProcessing(); void CompleteCodedFrameProcessing();
// Called by ResetParserState. Complete parsing the input buffer for the // Called by ResetParserState.
// current media segment.
void FinishCodedFrameProcessing();
void CompleteResetParserState(); void CompleteResetParserState();
RefPtr<RangeRemovalPromise> RefPtr<RangeRemovalPromise>
CodedFrameRemovalWithPromise(media::TimeInterval aInterval); CodedFrameRemovalWithPromise(media::TimeInterval aInterval);
@ -310,10 +304,6 @@ private:
MozPromiseHolder<CodedFrameProcessingPromise> mProcessingPromise; MozPromiseHolder<CodedFrameProcessingPromise> mProcessingPromise;
MozPromiseHolder<AppendPromise> mAppendPromise; MozPromiseHolder<AppendPromise> mAppendPromise;
// Set to true while SegmentParserLoop is running. This is used for diagnostic
// purposes only. We can't rely on mAppendPromise to be empty as it is only
// cleared in a follow up task.
bool mAppendRunning;
// Trackbuffers definition. // Trackbuffers definition.
nsTArray<TrackData*> GetTracksList(); nsTArray<TrackData*> GetTracksList();
@ -349,11 +339,6 @@ private:
RefPtr<dom::SourceBufferAttributes> mSourceBufferAttributes; RefPtr<dom::SourceBufferAttributes> mSourceBufferAttributes;
nsMainThreadPtrHandle<MediaSourceDecoder> mParentDecoder; nsMainThreadPtrHandle<MediaSourceDecoder> mParentDecoder;
// MediaSource duration mirrored from MediaDecoder on the main thread..
Mirror<Maybe<double>> mMediaSourceDuration;
// Set to true if abort was called.
Atomic<bool> mAbort;
// Set to true if mediasource state changed to ended. // Set to true if mediasource state changed to ended.
Atomic<bool> mEnded; Atomic<bool> mEnded;
@ -363,7 +348,13 @@ private:
Atomic<bool> mEvictionOccurred; Atomic<bool> mEvictionOccurred;
// Monitor to protect following objects accessed across multipple threads. // Monitor to protect following objects accessed across multipple threads.
// mMonitor is also notified if the value of mAppendRunning becomes false.
mutable Monitor mMonitor; mutable Monitor mMonitor;
// Set to true while SegmentParserLoop is running.
Atomic<bool> mAppendRunning;
// Set to true while SegmentParserLoop is running.
// This is for diagnostic only. Only accessed on the task queue.
bool mSegmentParserLoopRunning;
// Stable audio and video track time ranges. // Stable audio and video track time ranges.
media::TimeIntervals mVideoBufferedRanges; media::TimeIntervals mVideoBufferedRanges;
media::TimeIntervals mAudioBufferedRanges; media::TimeIntervals mAudioBufferedRanges;

View File

@ -126,6 +126,8 @@ var interfaceNamesInGlobalScope =
{name: "Animation", release: false}, {name: "Animation", release: false},
// IMPORTANT: Do not change this list without review from a DOM peer! // IMPORTANT: Do not change this list without review from a DOM peer!
{name: "AnimationEffectReadOnly", release: false}, {name: "AnimationEffectReadOnly", release: false},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "AnimationEffectTiming", release: false},
// IMPORTANT: Do not change this list without review from a DOM peer! // IMPORTANT: Do not change this list without review from a DOM peer!
{name: "AnimationEffectTimingReadOnly", release: false}, {name: "AnimationEffectTimingReadOnly", release: false},
// IMPORTANT: Do not change this list without review from a DOM peer! // IMPORTANT: Do not change this list without review from a DOM peer!
@ -727,6 +729,8 @@ var interfaceNamesInGlobalScope =
"KeyboardEvent", "KeyboardEvent",
// IMPORTANT: Do not change this list without review from a DOM peer! // IMPORTANT: Do not change this list without review from a DOM peer!
{name: "KeyframeEffectReadOnly", release: false}, {name: "KeyframeEffectReadOnly", release: false},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "KeyframeEffect", release: false},
// IMPORTANT: Do not change this list without review from a DOM peer! // IMPORTANT: Do not change this list without review from a DOM peer!
"LocalMediaStream", "LocalMediaStream",
// IMPORTANT: Do not change this list without review from a DOM peer! // IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -0,0 +1,31 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* https://w3c.github.io/web-animations/#animationeffecttiming
*
* Copyright © 2015 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
*/
[Func="nsDocument::IsWebAnimationsEnabled"]
interface AnimationEffectTiming : AnimationEffectTimingReadOnly {
//Bug 1244633 - implement AnimationEffectTiming delay
//inherit attribute double delay;
//Bug 1244635 - implement AnimationEffectTiming endDelay
//inherit attribute double endDelay;
//Bug 1244637 - implement AnimationEffectTiming fill
//inherit attribute FillMode fill;
//Bug 1244638 - implement AnimationEffectTiming iterationStart
//inherit attribute double iterationStart;
//Bug 1244640 - implement AnimationEffectTiming iterations
//inherit attribute unrestricted double iterations;
//Bug 1244641 - implement AnimationEffectTiming duration
//inherit attribute (unrestricted double or DOMString) duration;
//Bug 1244642 - implement AnimationEffectTiming direction
//inherit attribute PlaybackDirection direction;
//Bug 1244643 - implement AnimationEffectTiming easing
//inherit attribute DOMString easing;
};

View File

@ -52,10 +52,7 @@ dictionary ConsoleEvent {
DOMString functionName = ""; DOMString functionName = "";
double timeStamp = 0; double timeStamp = 0;
sequence<any> arguments; sequence<any> arguments;
sequence<DOMString?> styles;
// This array will only hold strings or null elements.
sequence<any> styles;
boolean private = false; boolean private = false;
// stacktrace is handled via a getter in some cases so we can construct it // stacktrace is handled via a getter in some cases so we can construct it
// lazily. Note that we're not making this whole thing an interface because // lazily. Note that we're not making this whole thing an interface because

View File

@ -4,7 +4,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. * You can obtain one at http://mozilla.org/MPL/2.0/.
* *
* The origin of this IDL file is * The origin of this IDL file is
* http://w3c.github.io/web-animations/#the-keyframeeffect-interfaces * https://w3c.github.io/web-animations/#the-keyframeeffect-interfaces
* *
* Copyright © 2015 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C * Copyright © 2015 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply. * liability, trademark and document use rules apply.
@ -44,3 +44,20 @@ interface KeyframeEffectReadOnly : AnimationEffectReadOnly {
// property-value pairs on the object. // property-value pairs on the object.
[Throws] sequence<object> getFrames(); [Throws] sequence<object> getFrames();
}; };
[Constructor ((Element or CSSPseudoElement)? target,
object? frames,
optional (unrestricted double or KeyframeEffectOptions) options)]
interface KeyframeEffect : KeyframeEffectReadOnly {
// Bug 1067769 - Allow setting KeyframeEffect.target
// inherit attribute Animatable? target;
// Bug 1216843 - implement animation composition
// inherit attribute IterationCompositeOperation iterationComposite;
// Bug 1216844 - implement additive animation
// inherit attribute CompositeOperation composite;
// Bug 1244590 - implement spacing modes
// inherit attribute DOMString spacing;
// Bug 1244591 - implement setFrames
// void setFrames (object? frames);
};

View File

@ -190,7 +190,7 @@ Navigator implements NavigatorMobileId;
// nsIDOMNavigator // nsIDOMNavigator
partial interface Navigator { partial interface Navigator {
[Throws] [Throws, Constant, Cached]
readonly attribute DOMString oscpu; readonly attribute DOMString oscpu;
// WebKit/Blink support this; Trident/Presto do not. // WebKit/Blink support this; Trident/Presto do not.
readonly attribute DOMString vendor; readonly attribute DOMString vendor;
@ -200,7 +200,7 @@ partial interface Navigator {
readonly attribute DOMString productSub; readonly attribute DOMString productSub;
// WebKit/Blink/Trident/Presto support this. // WebKit/Blink/Trident/Presto support this.
readonly attribute boolean cookieEnabled; readonly attribute boolean cookieEnabled;
[Throws] [Throws, Constant, Cached]
readonly attribute DOMString buildID; readonly attribute DOMString buildID;
[Throws, CheckAnyPermissions="power", UnsafeInPrerendering] [Throws, CheckAnyPermissions="power", UnsafeInPrerendering]
readonly attribute MozPowerManager mozPower; readonly attribute MozPowerManager mozPower;

View File

@ -25,6 +25,7 @@ WEBIDL_FILES = [
'Animatable.webidl', 'Animatable.webidl',
'Animation.webidl', 'Animation.webidl',
'AnimationEffectReadOnly.webidl', 'AnimationEffectReadOnly.webidl',
'AnimationEffectTiming.webidl',
'AnimationEffectTimingReadOnly.webidl', 'AnimationEffectTimingReadOnly.webidl',
'AnimationEvent.webidl', 'AnimationEvent.webidl',
'AnimationTimeline.webidl', 'AnimationTimeline.webidl',

View File

@ -886,23 +886,10 @@ class WorkerJSRuntime : public mozilla::CycleCollectedJSRuntime
public: public:
// The heap size passed here doesn't matter, we will change it later in the // The heap size passed here doesn't matter, we will change it later in the
// call to JS_SetGCParameter inside CreateJSContextForWorker. // call to JS_SetGCParameter inside CreateJSContextForWorker.
WorkerJSRuntime(JSRuntime* aParentRuntime, WorkerPrivate* aWorkerPrivate) explicit WorkerJSRuntime(WorkerPrivate* aWorkerPrivate)
: CycleCollectedJSRuntime(aParentRuntime, : mWorkerPrivate(aWorkerPrivate)
WORKER_DEFAULT_RUNTIME_HEAPSIZE,
WORKER_DEFAULT_NURSERY_SIZE),
mWorkerPrivate(aWorkerPrivate)
{ {
JSRuntime* rt = Runtime(); MOZ_ASSERT(aWorkerPrivate);
MOZ_ASSERT(rt);
JS_SetRuntimePrivate(rt, new WorkerThreadRuntimePrivate(aWorkerPrivate));
js::SetPreserveWrapperCallback(rt, PreserveWrapper);
JS_InitDestroyPrincipalsCallback(rt, DestroyWorkerPrincipals);
JS_SetWrapObjectCallbacks(rt, &WrapObjectCallbacks);
if (aWorkerPrivate->IsDedicatedWorker()) {
JS_SetFutexCanWait(rt);
}
} }
~WorkerJSRuntime() ~WorkerJSRuntime()
@ -924,6 +911,31 @@ public:
mWorkerPrivate = nullptr; mWorkerPrivate = nullptr;
} }
nsresult Initialize(JSRuntime* aParentRuntime)
{
nsresult rv =
CycleCollectedJSRuntime::Initialize(aParentRuntime,
WORKER_DEFAULT_RUNTIME_HEAPSIZE,
WORKER_DEFAULT_NURSERY_SIZE);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
JSRuntime* rt = Runtime();
MOZ_ASSERT(rt);
JS_SetRuntimePrivate(rt, new WorkerThreadRuntimePrivate(mWorkerPrivate));
js::SetPreserveWrapperCallback(rt, PreserveWrapper);
JS_InitDestroyPrincipalsCallback(rt, DestroyWorkerPrincipals);
JS_SetWrapObjectCallbacks(rt, &WrapObjectCallbacks);
if (mWorkerPrivate->IsDedicatedWorker()) {
JS_SetFutexCanWait(rt);
}
return NS_OK;
}
virtual void virtual void
PrepareForForgetSkippable() override PrepareForForgetSkippable() override
{ {
@ -2695,7 +2707,12 @@ WorkerThreadPrimaryRunnable::Run()
{ {
nsCycleCollector_startup(); nsCycleCollector_startup();
WorkerJSRuntime runtime(mParentRuntime, mWorkerPrivate); WorkerJSRuntime runtime(mWorkerPrivate);
nsresult rv = runtime.Initialize(mParentRuntime);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
JSRuntime* rt = runtime.Runtime(); JSRuntime* rt = runtime.Runtime();
JSContext* cx = CreateJSContextForWorker(mWorkerPrivate, rt); JSContext* cx = CreateJSContextForWorker(mWorkerPrivate, rt);

View File

@ -2391,6 +2391,7 @@ nsWebBrowserPersist::FixRedirectedChannelEntry(nsIChannel *aNewChannel)
// matching the one specified. // matching the one specified.
nsCOMPtr<nsIURI> originalURI; nsCOMPtr<nsIURI> originalURI;
aNewChannel->GetOriginalURI(getter_AddRefs(originalURI)); aNewChannel->GetOriginalURI(getter_AddRefs(originalURI));
nsISupports* matchingKey = nullptr;
for (auto iter = mOutputMap.Iter(); !iter.Done(); iter.Next()) { for (auto iter = mOutputMap.Iter(); !iter.Done(); iter.Next()) {
nsISupports* key = iter.Key(); nsISupports* key = iter.Key();
nsCOMPtr<nsIChannel> thisChannel = do_QueryInterface(key); nsCOMPtr<nsIChannel> thisChannel = do_QueryInterface(key);
@ -2402,21 +2403,25 @@ nsWebBrowserPersist::FixRedirectedChannelEntry(nsIChannel *aNewChannel)
bool matchingURI = false; bool matchingURI = false;
thisURI->Equals(originalURI, &matchingURI); thisURI->Equals(originalURI, &matchingURI);
if (matchingURI) { if (matchingURI) {
// If a match is found, remove the data entry with the old channel matchingKey = key;
// key and re-add it with the new channel key.
nsAutoPtr<OutputData> outputData;
mOutputMap.RemoveAndForget(key, outputData);
NS_ENSURE_TRUE(outputData, NS_ERROR_FAILURE);
// Store data again with new channel unless told to ignore redirects
if (!(mPersistFlags & PERSIST_FLAGS_IGNORE_REDIRECTED_DATA)) {
nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(aNewChannel);
mOutputMap.Put(keyPtr, outputData.forget());
}
break; break;
} }
} }
if (matchingKey) {
// If a match was found, remove the data entry with the old channel
// key and re-add it with the new channel key.
nsAutoPtr<OutputData> outputData;
mOutputMap.RemoveAndForget(matchingKey, outputData);
NS_ENSURE_TRUE(outputData, NS_ERROR_FAILURE);
// Store data again with new channel unless told to ignore redirects.
if (!(mPersistFlags & PERSIST_FLAGS_IGNORE_REDIRECTED_DATA)) {
nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(aNewChannel);
mOutputMap.Put(keyPtr, outputData.forget());
}
}
return NS_OK; return NS_OK;
} }

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