mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge branch 'mozilla-central'
This commit is contained in:
commit
33db3dfa02
10
.gitignore
vendored
10
.gitignore
vendored
@ -54,6 +54,16 @@ parser/html/java/javaparser/
|
||||
.cproject
|
||||
.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/psutil/**/*.so
|
||||
python/psutil/**/*.pyd
|
||||
|
@ -821,9 +821,17 @@ Accessible::XULElmName(DocAccessible* aDocument,
|
||||
nsIContent *bindingParent = aElm->GetBindingParent();
|
||||
nsIContent* parent =
|
||||
bindingParent? bindingParent->GetParent() : aElm->GetParent();
|
||||
nsAutoString ancestorTitle;
|
||||
while (parent) {
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
@ -13,4 +13,5 @@ support-files =
|
||||
[test_list.html]
|
||||
[test_markup.html]
|
||||
[test_svg.html]
|
||||
[test_toolbaritem.xul]
|
||||
[test_tree.xul]
|
||||
|
84
accessible/tests/mochitest/name/test_toolbaritem.xul
Normal file
84
accessible/tests/mochitest/name/test_toolbaritem.xul
Normal 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>
|
@ -57,7 +57,29 @@
|
||||
<menuitem id="context-openlinkintab"
|
||||
label="&openLinkCmdInTab.label;"
|
||||
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"
|
||||
label="&openLinkCmd.label;"
|
||||
accesskey="&openLinkCmd.accesskey;"
|
||||
|
@ -451,6 +451,10 @@
|
||||
class="show-only-for-keyboard"
|
||||
command="Browser:BookmarkAllTabs"
|
||||
key="bookmarkAllTabsKb"/>
|
||||
<menuseparator/>
|
||||
<menuitem label="&recentBookmarks.label;"
|
||||
disabled="true"/>
|
||||
<vbox id="menu_recentBookmarks"/>
|
||||
<menuseparator id="bookmarksToolbarSeparator"/>
|
||||
<menu id="bookmarksToolbarFolderMenu"
|
||||
class="menu-iconic bookmark-item"
|
||||
|
@ -1305,6 +1305,9 @@ var BookmarkingUI = {
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateRecentBookmarks(document.getElementById("BMB_recentBookmarks"),
|
||||
"subviewbutton");
|
||||
|
||||
if (!this._popupNeedsUpdate)
|
||||
return;
|
||||
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.
|
||||
*/
|
||||
@ -1550,6 +1607,7 @@ var BookmarkingUI = {
|
||||
onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) {
|
||||
this._updateBookmarkPageMenuItem();
|
||||
PlacesCommandHook.updateBookmarkAllTabsCommand();
|
||||
this._updateRecentBookmarks(document.getElementById("menu_recentBookmarks"));
|
||||
},
|
||||
|
||||
_showBookmarkedNotification: function BUI_showBookmarkedNotification() {
|
||||
|
@ -8,9 +8,14 @@
|
||||
var gSafeBrowsing = {
|
||||
|
||||
setReportPhishingMenu: function() {
|
||||
// A phishing page will have a specific about:blocked content documentURI
|
||||
var uri = gBrowser.currentURI;
|
||||
var isPhishingPage = uri && uri.spec.startsWith("about:blocked?e=phishingBlocked");
|
||||
// In order to detect whether or not we're at the phishing warning
|
||||
// page, we have to check the documentURI instead of the currentURI.
|
||||
// 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.
|
||||
document.getElementById("menu_HelpPopup_reportPhishingtoolmenu")
|
||||
@ -26,6 +31,9 @@ var gSafeBrowsing = {
|
||||
if (!broadcaster)
|
||||
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")))
|
||||
broadcaster.removeAttribute("disabled");
|
||||
else
|
||||
|
@ -812,6 +812,11 @@
|
||||
command="Browser:ShowAllBookmarks"
|
||||
key="manBookmarkKb"/>
|
||||
<menuseparator/>
|
||||
<menuitem label="&recentBookmarks.label;"
|
||||
disabled="true"
|
||||
class="subviewbutton"/>
|
||||
<vbox id="BMB_recentBookmarks"/>
|
||||
<menuseparator/>
|
||||
<menu id="BMB_bookmarksToolbar"
|
||||
class="menu-iconic bookmark-item subviewbutton"
|
||||
label="&personalbarCmd.label;"
|
||||
|
@ -144,9 +144,11 @@ nsContextMenu.prototype = {
|
||||
|
||||
var shouldShow = this.onSaveableLink || isMailtoInternal || this.onPlainTextLink;
|
||||
var isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
|
||||
var showContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
|
||||
this.showItem("context-openlink", shouldShow && !isWindowPrivate);
|
||||
this.showItem("context-openlinkprivate", shouldShow);
|
||||
this.showItem("context-openlinkintab", shouldShow);
|
||||
this.showItem("context-openlinkinusercontext-menu", shouldShow && showContainers);
|
||||
this.showItem("context-openlinkincurrent", this.onPlainTextLink);
|
||||
this.showItem("context-sep-open", shouldShow);
|
||||
},
|
||||
@ -960,7 +962,7 @@ nsContextMenu.prototype = {
|
||||
},
|
||||
|
||||
// Open linked-to URL in a new tab.
|
||||
openLinkInTab: function() {
|
||||
openLinkInTab: function(event) {
|
||||
urlSecurityCheck(this.linkURL, this.principal);
|
||||
let referrerURI = gContextMenuContentData.documentURIObject;
|
||||
|
||||
@ -981,6 +983,7 @@ nsContextMenu.prototype = {
|
||||
|
||||
let params = this._openLinkInParameters({
|
||||
allowMixedContent: persistAllowMixedContentInChildTab,
|
||||
userContextId: event.target.getAttribute('usercontextid'),
|
||||
});
|
||||
openLinkIn(this.linkURL, "tab", params);
|
||||
},
|
||||
|
@ -121,7 +121,7 @@ SocialErrorListener = {
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
dwu.allowScriptsToClose();
|
||||
|
||||
content.addEventListener("DOMWindowClose", function _mozSocialDOMWindowClose(evt) {
|
||||
addEventListener("DOMWindowClose", function _mozSocialDOMWindowClose(evt) {
|
||||
sendAsyncMessage("DOMWindowClose");
|
||||
// preventDefault stops the default window.close() function being called,
|
||||
// which doesn't actually close anything but causes things to get into
|
||||
@ -135,7 +135,7 @@ SocialErrorListener = {
|
||||
break;
|
||||
case "Social:ListenForEvents":
|
||||
for (let eventName of message.data.eventNames) {
|
||||
content.addEventListener(eventName, this);
|
||||
addEventListener(eventName, this);
|
||||
}
|
||||
break;
|
||||
case "Social:SetDocumentTitle":
|
||||
|
@ -207,6 +207,16 @@
|
||||
content.setAttribute("origin", this.content.getAttribute("origin"));
|
||||
content.popupnotificationanchor.className = this.content.popupnotificationanchor.className;
|
||||
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>
|
||||
</method>
|
||||
|
||||
|
@ -1828,6 +1828,13 @@
|
||||
|
||||
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)
|
||||
t.setAttribute("usercontextid", aUserContextId);
|
||||
t.setAttribute("crop", "end");
|
||||
@ -1906,15 +1913,6 @@
|
||||
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();
|
||||
|
||||
// wire up a progress listener for the new browser object.
|
||||
@ -6073,8 +6071,7 @@
|
||||
class="tab-icon-overlay"
|
||||
role="presentation"/>
|
||||
<xul:label flex="1"
|
||||
anonid="tab-label"
|
||||
xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected,visuallyselected,attention"
|
||||
xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected,visuallyselected,attention"
|
||||
class="tab-text tab-label"
|
||||
role="presentation"/>
|
||||
<xul:image xbl:inherits="soundplaying,pinned,muted,visuallyselected"
|
||||
@ -6140,32 +6137,6 @@
|
||||
</setter>
|
||||
</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">
|
||||
<getter>
|
||||
return this.getAttribute("pinned") == "true";
|
||||
|
@ -8,4 +8,3 @@ support-files =
|
||||
skip-if = true # Bug 1245937 - Intermittent failures/timeouts.
|
||||
[browser_focus.js]
|
||||
[browser_tearoff.js]
|
||||
skip-if = true # Bug 1245805 - tearing off chat windows causes a browser crash.
|
||||
|
@ -65,7 +65,7 @@ add_chat_task(function* testTearoffChat() {
|
||||
Assert.equal(chatbox.getAttribute("label"), chatTitle, "window should have same title as chat");
|
||||
|
||||
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");
|
||||
div.setAttribute("test", "2");
|
||||
});
|
||||
|
@ -491,7 +491,6 @@ skip-if = os == "linux" # Bug 1073339 - Investigate autocomplete test unreliabil
|
||||
[browser_utilityOverlay.js]
|
||||
[browser_viewSourceInTabOnViewSource.js]
|
||||
[browser_visibleFindSelection.js]
|
||||
[browser_visibleLabel.js]
|
||||
[browser_visibleTabs.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'
|
||||
|
@ -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);
|
||||
}
|
@ -184,8 +184,6 @@ const CustomizableWidgets = [
|
||||
|
||||
let options = PlacesUtils.history.getNewQueryOptions();
|
||||
options.excludeQueries = true;
|
||||
options.includeHidden = false;
|
||||
options.resultType = options.RESULTS_AS_URI;
|
||||
options.queryType = options.QUERY_TYPE_HISTORY;
|
||||
options.sortingMode = options.SORT_BY_DATE_DESCENDING;
|
||||
options.maxResults = kMaxResults;
|
||||
|
@ -5,8 +5,6 @@ support-files = head.js
|
||||
[browser_bug400731.js]
|
||||
skip-if = e10s
|
||||
[browser_bug415846.js]
|
||||
skip-if = true
|
||||
# Disabled because it seems to now touch network resources
|
||||
# skip-if = os == "mac"
|
||||
skip-if = os == "mac" || e10s
|
||||
# Disabled on Mac because of its bizarre special-and-unique
|
||||
# snowflake of a help menu.
|
||||
|
@ -3,60 +3,84 @@ menu items.
|
||||
|
||||
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.*/
|
||||
var menu;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
const NORMAL_PAGE = "http://example.com";
|
||||
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
|
||||
gBrowser.addEventListener("DOMContentLoaded", testNormal, false);
|
||||
content.location = "http://example.com/";
|
||||
let menu = document.getElementById("menu_HelpPopup");
|
||||
ok(menu, "Help menu should exist");
|
||||
|
||||
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");
|
||||
ok(menu, "Help menu should exist!");
|
||||
/**
|
||||
* Tests that we show the "Report this page is okay" menu item at
|
||||
* 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();
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ run-if = e10s
|
||||
[browser_cleaner.js]
|
||||
[browser_cookies.js]
|
||||
[browser_crashedTabs.js]
|
||||
skip-if = true # bug 1236414
|
||||
skip-if = !e10s || !crashreporter
|
||||
[browser_unrestored_crashedTabs.js]
|
||||
skip-if = !e10s || !crashreporter
|
||||
[browser_revive_crashed_bg_tabs.js]
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
requestLongerTimeout(2);
|
||||
requestLongerTimeout(10);
|
||||
|
||||
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.";
|
||||
@ -17,9 +17,6 @@ registerCleanupFunction(() => {
|
||||
// Allow tabs to restore on demand so we can test pending states
|
||||
Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
|
||||
|
||||
// Running this test in ASAN is slow.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
function clickButton(browser, id) {
|
||||
info("Clicking " + id);
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
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
|
||||
|
@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdf = {}));
|
||||
// Use strict in our context only - users might not want it
|
||||
'use strict';
|
||||
|
||||
var pdfjsVersion = '1.4.42';
|
||||
var pdfjsBuild = '4c59712';
|
||||
var pdfjsVersion = '1.4.64';
|
||||
var pdfjsBuild = '2f145d8';
|
||||
|
||||
var pdfjsFilePath =
|
||||
typeof document !== 'undefined' && document.currentScript ?
|
||||
@ -6258,9 +6258,9 @@ PDFJS.getDocument = function getDocument(src,
|
||||
throw new Error('Loading aborted');
|
||||
}
|
||||
var messageHandler = new MessageHandler(docId, workerId, worker.port);
|
||||
messageHandler.send('Ready', null);
|
||||
var transport = new WorkerTransport(messageHandler, task, rangeTransport);
|
||||
task._transport = transport;
|
||||
messageHandler.send('Ready', null);
|
||||
});
|
||||
}).catch(task._capability.reject);
|
||||
|
||||
@ -7102,7 +7102,7 @@ var PDFWorker = (function PDFWorkerClosure() {
|
||||
|
||||
_initialize: function PDFWorker_initialize() {
|
||||
// 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.
|
||||
// Right now, the requirement is, that an Uint8Array is still an
|
||||
// Uint8Array as it arrives on the worker. (Chrome added this with v.15.)
|
||||
|
119
browser/extensions/pdfjs/content/build/pdf.worker.js
vendored
119
browser/extensions/pdfjs/content/build/pdf.worker.js
vendored
@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdfWorker = {}));
|
||||
// Use strict in our context only - users might not want it
|
||||
'use strict';
|
||||
|
||||
var pdfjsVersion = '1.4.42';
|
||||
var pdfjsBuild = '4c59712';
|
||||
var pdfjsVersion = '1.4.64';
|
||||
var pdfjsBuild = '2f145d8';
|
||||
|
||||
var pdfjsFilePath =
|
||||
typeof document !== 'undefined' && document.currentScript ?
|
||||
@ -29687,15 +29687,21 @@ var Type1Parser = (function Type1ParserClosure() {
|
||||
}
|
||||
|
||||
function decrypt(data, key, discardNumber) {
|
||||
var r = key | 0, c1 = 52845, c2 = 22719;
|
||||
var count = data.length;
|
||||
if (discardNumber >= 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);
|
||||
for (var i = 0; i < count; i++) {
|
||||
for (i = discardNumber, j = 0; j < count; i++, j++) {
|
||||
var value = data[i];
|
||||
decrypted[i] = value ^ (r >> 8);
|
||||
decrypted[j] = value ^ (r >> 8);
|
||||
r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
|
||||
}
|
||||
return Array.prototype.slice.call(decrypted, discardNumber);
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
function decryptAscii(data, key, discardNumber) {
|
||||
@ -33249,15 +33255,15 @@ var ColorSpace = (function ColorSpaceClosure() {
|
||||
case 'DeviceCmykCS':
|
||||
return this.singletons.cmyk;
|
||||
case 'CalGrayCS':
|
||||
whitePoint = IR[1].WhitePoint;
|
||||
blackPoint = IR[1].BlackPoint;
|
||||
gamma = IR[1].Gamma;
|
||||
whitePoint = IR[1];
|
||||
blackPoint = IR[2];
|
||||
gamma = IR[3];
|
||||
return new CalGrayCS(whitePoint, blackPoint, gamma);
|
||||
case 'CalRGBCS':
|
||||
whitePoint = IR[1].WhitePoint;
|
||||
blackPoint = IR[1].BlackPoint;
|
||||
gamma = IR[1].Gamma;
|
||||
var matrix = IR[1].Matrix;
|
||||
whitePoint = IR[1];
|
||||
blackPoint = IR[2];
|
||||
gamma = IR[3];
|
||||
var matrix = IR[4];
|
||||
return new CalRGBCS(whitePoint, blackPoint, gamma, matrix);
|
||||
case 'PatternCS':
|
||||
var basePatternCS = IR[1];
|
||||
@ -33276,11 +33282,11 @@ var ColorSpace = (function ColorSpaceClosure() {
|
||||
var tintFnIR = IR[3];
|
||||
|
||||
return new AlternateCS(numComps, ColorSpace.fromIR(alt),
|
||||
PDFFunction.fromIR(tintFnIR));
|
||||
PDFFunction.fromIR(tintFnIR));
|
||||
case 'LabCS':
|
||||
whitePoint = IR[1].WhitePoint;
|
||||
blackPoint = IR[1].BlackPoint;
|
||||
var range = IR[1].Range;
|
||||
whitePoint = IR[1];
|
||||
blackPoint = IR[2];
|
||||
var range = IR[3];
|
||||
return new LabCS(whitePoint, blackPoint, range);
|
||||
default:
|
||||
error('Unknown name ' + name);
|
||||
@ -33324,7 +33330,7 @@ var ColorSpace = (function ColorSpaceClosure() {
|
||||
} else if (isArray(cs)) {
|
||||
mode = xref.fetchIfRef(cs[0]).name;
|
||||
this.mode = mode;
|
||||
var numComps, params, alt;
|
||||
var numComps, params, alt, whitePoint, blackPoint, gamma;
|
||||
|
||||
switch (mode) {
|
||||
case 'DeviceGray':
|
||||
@ -33337,11 +33343,18 @@ var ColorSpace = (function ColorSpaceClosure() {
|
||||
case 'CMYK':
|
||||
return 'DeviceCmykCS';
|
||||
case 'CalGray':
|
||||
params = xref.fetchIfRef(cs[1]).getAll();
|
||||
return ['CalGrayCS', params];
|
||||
params = xref.fetchIfRef(cs[1]);
|
||||
whitePoint = params.get('WhitePoint');
|
||||
blackPoint = params.get('BlackPoint');
|
||||
gamma = params.get('Gamma');
|
||||
return ['CalGrayCS', whitePoint, blackPoint, gamma];
|
||||
case 'CalRGB':
|
||||
params = xref.fetchIfRef(cs[1]).getAll();
|
||||
return ['CalRGBCS', params];
|
||||
params = xref.fetchIfRef(cs[1]);
|
||||
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':
|
||||
var stream = xref.fetchIfRef(cs[1]);
|
||||
var dict = stream.dict;
|
||||
@ -33393,8 +33406,11 @@ var ColorSpace = (function ColorSpaceClosure() {
|
||||
var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3]));
|
||||
return ['AlternateCS', numComps, alt, tintFnIR];
|
||||
case 'Lab':
|
||||
params = xref.fetchIfRef(cs[1]).getAll();
|
||||
return ['LabCS', params];
|
||||
params = xref.fetchIfRef(cs[1]);
|
||||
whitePoint = params.get('WhitePoint');
|
||||
blackPoint = params.get('BlackPoint');
|
||||
var range = params.get('Range');
|
||||
return ['LabCS', whitePoint, blackPoint, range];
|
||||
default:
|
||||
error('unimplemented color space object "' + mode + '"');
|
||||
}
|
||||
@ -35949,17 +35965,19 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
processed[resources.objId] = true;
|
||||
}
|
||||
|
||||
var nodes = [resources];
|
||||
var nodes = [resources], xref = this.xref;
|
||||
while (nodes.length) {
|
||||
var key;
|
||||
var key, i, ii;
|
||||
var node = nodes.shift();
|
||||
// First check the current resources for blend modes.
|
||||
var graphicStates = node.get('ExtGState');
|
||||
if (isDict(graphicStates)) {
|
||||
graphicStates = graphicStates.getAll();
|
||||
for (key in graphicStates) {
|
||||
var graphicState = graphicStates[key];
|
||||
var bm = graphicState['BM'];
|
||||
var graphicStatesKeys = graphicStates.getKeys();
|
||||
for (i = 0, ii = graphicStatesKeys.length; i < ii; i++) {
|
||||
key = graphicStatesKeys[i];
|
||||
|
||||
var graphicState = graphicStates.get(key);
|
||||
var bm = graphicState.get('BM');
|
||||
if (isName(bm) && bm.name !== 'Normal') {
|
||||
return true;
|
||||
}
|
||||
@ -35970,9 +35988,20 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
if (!isDict(xObjects)) {
|
||||
continue;
|
||||
}
|
||||
xObjects = xObjects.getAll();
|
||||
for (key in xObjects) {
|
||||
var xObject = xObjects[key];
|
||||
var xObjectsKeys = xObjects.getKeys();
|
||||
for (i = 0, ii = xObjectsKeys.length; i < ii; i++) {
|
||||
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)) {
|
||||
continue;
|
||||
}
|
||||
@ -36266,11 +36295,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
xref, stateManager) {
|
||||
// This array holds the converted/processed state data.
|
||||
var gStateObj = [];
|
||||
var gStateMap = gState.map;
|
||||
var gStateKeys = gState.getKeys();
|
||||
var self = this;
|
||||
var promise = Promise.resolve();
|
||||
for (var key in gStateMap) {
|
||||
var value = gStateMap[key];
|
||||
for (var i = 0, ii = gStateKeys.length; i < ii; i++) {
|
||||
var key = gStateKeys[i];
|
||||
var value = gState.get(key);
|
||||
switch (key) {
|
||||
case 'Type':
|
||||
break;
|
||||
@ -36303,12 +36333,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
gStateObj.push([key, false]);
|
||||
break;
|
||||
}
|
||||
var dict = xref.fetchIfRef(value);
|
||||
if (isDict(dict)) {
|
||||
promise = promise.then(function () {
|
||||
if (isDict(value)) {
|
||||
promise = promise.then(function (dict) {
|
||||
return self.handleSMask(dict, resources, operatorList,
|
||||
task, stateManager);
|
||||
});
|
||||
}.bind(this, value));
|
||||
gStateObj.push([key, true]);
|
||||
} else {
|
||||
warn('Unsupported SMask type');
|
||||
@ -36340,7 +36369,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
}
|
||||
}
|
||||
return promise.then(function () {
|
||||
if (gStateObj.length >= 0) {
|
||||
if (gStateObj.length > 0) {
|
||||
operatorList.addOp(OPS.setGState, [gStateObj]);
|
||||
}
|
||||
});
|
||||
@ -37885,13 +37914,13 @@ var TranslatedFont = (function TranslatedFontClosure() {
|
||||
|
||||
var translatedFont = this.font;
|
||||
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 charProcKeys = Object.keys(charProcs);
|
||||
var charProcKeys = charProcs.getKeys();
|
||||
var charProcOperatorList = Object.create(null);
|
||||
for (var i = 0, n = charProcKeys.length; i < n; ++i) {
|
||||
loadCharProcsPromise = loadCharProcsPromise.then(function (key) {
|
||||
var glyphStream = charProcs[key];
|
||||
var glyphStream = charProcs.get(key);
|
||||
var operatorList = new OperatorList();
|
||||
return evaluator.getOperatorList(glyphStream, task, fontResources,
|
||||
operatorList).then(function () {
|
||||
|
@ -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 addCurPagesCmd.label "Bookmark All Tabs…">
|
||||
<!ENTITY showAllBookmarks2.label "Show All Bookmarks">
|
||||
<!ENTITY recentBookmarks.label "Recently Bookmarked">
|
||||
<!ENTITY unsortedBookmarksCmd.label "Unsorted 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 openFrameCmd.label "Open Frame in New Window">
|
||||
<!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.accesskey "S">
|
||||
<!ENTITY reloadCmd.commandkey "r">
|
||||
|
@ -4,4 +4,3 @@ support-files =
|
||||
head.js
|
||||
|
||||
[browser_screenshots.js]
|
||||
tags = screenshots
|
||||
|
@ -5,15 +5,12 @@
|
||||
"use strict";
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
});
|
||||
|
6
browser/tools/mozscreenshots/devtools/browser.ini
Normal file
6
browser/tools/mozscreenshots/devtools/browser.ini
Normal file
@ -0,0 +1,6 @@
|
||||
[DEFAULT]
|
||||
subsuite = screenshots
|
||||
support-files =
|
||||
../head.js
|
||||
|
||||
[browser_devtools.js]
|
14
browser/tools/mozscreenshots/devtools/browser_devtools.js
Normal file
14
browser/tools/mozscreenshots/devtools/browser_devtools.js
Normal 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);
|
||||
});
|
@ -6,6 +6,7 @@
|
||||
|
||||
const {AddonWatcher} = Cu.import("resource://gre/modules/AddonWatcher.jsm", {});
|
||||
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
let TestRunner;
|
||||
|
||||
function setup() {
|
||||
requestLongerTimeout(20);
|
||||
@ -15,18 +16,25 @@ function setup() {
|
||||
AddonManager.getAddonByID("mozscreenshots@mozilla.org", function(aAddon) {
|
||||
isnot(aAddon, null, "The mozscreenshots extension should be installed");
|
||||
AddonWatcher.ignoreAddonPermanently(aAddon.id);
|
||||
TestRunner = Cu.import("chrome://mozscreenshots/content/TestRunner.jsm", {}).TestRunner;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
// (see also: bug 1116275). Try pushes and local builds should also capture.
|
||||
// (see also: bug 1116275).
|
||||
let capture = AppConstants.MOZ_UPDATE_CHANNEL == "nightly" ||
|
||||
(AppConstants.SOURCE_REVISION_URL.includes("/try/rev/") &&
|
||||
env.get("MOZSCREENSHOTS_SETS")) ||
|
||||
AppConstants.SOURCE_REVISION_URL == "";
|
||||
AppConstants.SOURCE_REVISION_URL == "" ||
|
||||
AppConstants.SOURCE_REVISION_URL == "1"; // bug 1248027
|
||||
if (!capture) {
|
||||
ok(true, "Capturing is disabled for this MOZ_UPDATE_CHANNEL or REPO");
|
||||
}
|
||||
|
@ -4,7 +4,14 @@
|
||||
# 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/.
|
||||
|
||||
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 += [
|
||||
'mozscreenshots/extension',
|
||||
|
@ -11,7 +11,6 @@ const defaultSetNames = ["TabsInTitlebar", "Tabs", "WindowSize", "Toolbars", "Li
|
||||
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
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/Services.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/osfile.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserTestUtils",
|
||||
"resource://testing-common/BrowserTestUtils.jsm");
|
||||
|
||||
Cu.import("chrome://mozscreenshots/content/Screenshot.jsm");
|
||||
|
||||
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
|
||||
@ -42,6 +44,7 @@ this.TestRunner = {
|
||||
_libDir: null,
|
||||
|
||||
init(extensionPath) {
|
||||
log.info("init");
|
||||
this._extensionPath = extensionPath;
|
||||
},
|
||||
|
||||
|
@ -9,7 +9,9 @@ this.EXPORTED_SYMBOLS = ["Preferences"];
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
Cu.import("resource://testing-common/TestUtils.jsm");
|
||||
|
||||
this.Preferences = {
|
||||
|
||||
@ -31,7 +33,7 @@ this.Preferences = {
|
||||
["paneAdvanced", "encryptionTab"],
|
||||
];
|
||||
for (let [primary, advanced] of panes) {
|
||||
let configName = primary + ("-" + advanced || "");
|
||||
let configName = primary.replace(/^pane/, "prefs") + (advanced ? "-" + advanced : "");
|
||||
this.configurations[configName] = {};
|
||||
this.configurations[configName].applyConfig = prefHelper.bind(null, primary, advanced);
|
||||
}
|
||||
@ -40,14 +42,27 @@ this.Preferences = {
|
||||
configurations: {},
|
||||
};
|
||||
|
||||
function prefHelper(primary, advanced) {
|
||||
return new Promise((resolve) => {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (primary == "paneAdvanced") {
|
||||
browserWindow.openAdvancedPreferences(advanced);
|
||||
} else {
|
||||
browserWindow.openPreferences(primary);
|
||||
}
|
||||
setTimeout(resolve, 50);
|
||||
});
|
||||
}
|
||||
let prefHelper = Task.async(function*(primary, advanced) {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let selectedBrowser = browserWindow.gBrowser;
|
||||
let readyPromise = null;
|
||||
if (selectedBrowser.currentURI.specIgnoringRef == "about:preferences") {
|
||||
readyPromise = new Promise((resolve) => {
|
||||
browserWindow.addEventListener("MozAfterPaint", function paneSwitch() {
|
||||
browserWindow.removeEventListener("MozAfterPaint", paneSwitch);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
readyPromise = TestUtils.topicObserved("advanced-pane-loaded");
|
||||
}
|
||||
|
||||
if (primary == "paneAdvanced") {
|
||||
browserWindow.openAdvancedPreferences(advanced);
|
||||
} else {
|
||||
browserWindow.openPreferences(primary);
|
||||
}
|
||||
|
||||
yield readyPromise;
|
||||
});
|
||||
|
6
browser/tools/mozscreenshots/preferences/browser.ini
Normal file
6
browser/tools/mozscreenshots/preferences/browser.ini
Normal file
@ -0,0 +1,6 @@
|
||||
[DEFAULT]
|
||||
subsuite = screenshots
|
||||
support-files =
|
||||
../head.js
|
||||
|
||||
[browser_preferences.js]
|
@ -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);
|
||||
});
|
6
browser/tools/mozscreenshots/primaryUI/browser.ini
Normal file
6
browser/tools/mozscreenshots/primaryUI/browser.ini
Normal file
@ -0,0 +1,6 @@
|
||||
[DEFAULT]
|
||||
subsuite = screenshots
|
||||
support-files =
|
||||
../head.js
|
||||
|
||||
[browser_primaryUI.js]
|
14
browser/tools/mozscreenshots/primaryUI/browser_primaryUI.js
Normal file
14
browser/tools/mozscreenshots/primaryUI/browser_primaryUI.js
Normal 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);
|
||||
});
|
10
build.gradle
10
build.gradle
@ -19,19 +19,11 @@ buildscript {
|
||||
repositories {
|
||||
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 {
|
||||
// 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.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.
|
||||
exclude module: 'guava'
|
||||
}
|
||||
|
@ -106,6 +106,7 @@ leak:nsBaseWidget::StoreWindowClipRegion
|
||||
leak:libcairo.so
|
||||
leak:libdl.so
|
||||
leak:libdricore.so
|
||||
leak:libdricore9.2.1.so
|
||||
leak:libGL.so
|
||||
leak:libglib-2.0.so
|
||||
leak:libp11-kit.so
|
||||
|
24
configure.in
24
configure.in
@ -3758,6 +3758,7 @@ MOZ_SCTP=
|
||||
MOZ_ANDROID_OMX=
|
||||
MOZ_MEDIA_NAVIGATOR=
|
||||
MOZ_OMX_PLUGIN=
|
||||
MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE=
|
||||
MOZ_VPX_ERROR_CONCEALMENT=
|
||||
MOZ_WEBSPEECH=1
|
||||
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
|
||||
AC_DEFINE(MOZ_OMX_PLUGIN)
|
||||
else
|
||||
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])
|
||||
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])
|
||||
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
|
||||
|
||||
@ -8900,6 +8919,7 @@ AC_SUBST(MOZ_DIRECTSHOW)
|
||||
AC_SUBST(MOZ_ANDROID_OMX)
|
||||
AC_SUBST(MOZ_APPLEMEDIA)
|
||||
AC_SUBST(MOZ_OMX_PLUGIN)
|
||||
AC_SUBST(MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE)
|
||||
AC_SUBST(MOZ_VPX_ERROR_CONCEALMENT)
|
||||
AC_SUBST(VPX_AS)
|
||||
AC_SUBST_LIST(VPX_ASFLAGS)
|
||||
|
@ -5,10 +5,13 @@
|
||||
"use strict";
|
||||
|
||||
const l10n = require("gcli/l10n");
|
||||
const {
|
||||
getCssDocs,
|
||||
PAGE_LINK_URL
|
||||
} = require("devtools/client/shared/widgets/MdnDocsWidget");
|
||||
|
||||
var MdnDocsWidget;
|
||||
try {
|
||||
MdnDocsWidget = require("devtools/client/shared/widgets/MdnDocsWidget");
|
||||
} catch (e) {
|
||||
// DevTools MdnDocsWidget only available in Firefox Desktop
|
||||
}
|
||||
|
||||
exports.items = [{
|
||||
name: "mdn",
|
||||
@ -26,10 +29,14 @@ exports.items = [{
|
||||
description: l10n.lookup("mdnCssProp")
|
||||
}],
|
||||
exec: function(args) {
|
||||
return getCssDocs(args.property).then(result => {
|
||||
if (!MdnDocsWidget) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return MdnDocsWidget.getCssDocs(args.property).then(result => {
|
||||
return {
|
||||
data: result,
|
||||
url: PAGE_LINK_URL + args.property,
|
||||
url: MdnDocsWidget.PAGE_LINK_URL + args.property,
|
||||
property: args.property
|
||||
};
|
||||
}, error => {
|
||||
|
@ -3513,22 +3513,19 @@ nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
|
||||
|
||||
nsCOMPtr<nsIDocShell> targetDS = do_QueryInterface(aTargetItem);
|
||||
nsCOMPtr<nsIDocShell> accessingDS = do_QueryInterface(aAccessingItem);
|
||||
if (!!targetDS != !!accessingDS) {
|
||||
// We must be able to convert both or neither to nsIDocShell.
|
||||
if (!targetDS || !accessingDS) {
|
||||
// We must be able to convert both to nsIDocShell.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetDS && accessingDS &&
|
||||
(targetDS->GetIsInBrowserElement() !=
|
||||
accessingDS->GetIsInBrowserElement() ||
|
||||
targetDS->GetAppId() != accessingDS->GetAppId())) {
|
||||
if (targetDS->GetIsInBrowserElement() != accessingDS->GetIsInBrowserElement() ||
|
||||
targetDS->GetAppId() != accessingDS->GetAppId()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A private document can't access a non-private one, and vice versa.
|
||||
if (aTargetItem->GetDocument()->GetLoadContext()->UsePrivateBrowsing() !=
|
||||
aAccessingItem->GetDocument()->GetLoadContext()->UsePrivateBrowsing())
|
||||
{
|
||||
if (static_cast<nsDocShell*>(targetDS.get())->UsePrivateBrowsing() !=
|
||||
static_cast<nsDocShell*>(accessingDS.get())->UsePrivateBrowsing()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
document.write("<h5 id='dynamic'>document.written content</h5>");
|
||||
document.close();
|
||||
window.history.go(-1);
|
||||
opener.setTimeout("isTestDynamic()", 2500);
|
||||
}
|
||||
|
||||
function start() {
|
||||
@ -14,6 +13,15 @@
|
||||
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>
|
||||
</head>
|
||||
<body onload="start();">
|
||||
|
@ -49,6 +49,7 @@ function nextTest_() {
|
||||
}
|
||||
|
||||
// Needed by file_document_write_1.html
|
||||
window.file_document_write_1_loadCount = 0;
|
||||
function isTestDynamic() {
|
||||
var dyn = testWindow.document.getElementById("dynamic");
|
||||
is(dyn, null, "Should have gone back to the static page!");
|
||||
|
22
dom/animation/AnimationEffectTiming.cpp
Normal file
22
dom/animation/AnimationEffectTiming.cpp
Normal 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
|
27
dom/animation/AnimationEffectTiming.h
Normal file
27
dom/animation/AnimationEffectTiming.h
Normal 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
|
@ -11,6 +11,7 @@
|
||||
#include "nsIAtom.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ComputedTimingFunction.h" // ComputedTimingFunction
|
||||
#include "mozilla/dom/Element.h" // For dom::Element
|
||||
|
||||
@ -69,7 +70,7 @@ AnimationUtils::ParseEasing(const dom::Element* aTarget,
|
||||
NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR) {
|
||||
return Nothing();
|
||||
}
|
||||
// Fall through
|
||||
MOZ_FALLTHROUGH;
|
||||
case eCSSUnit_Cubic_Bezier:
|
||||
case eCSSUnit_Steps: {
|
||||
nsTimingFunction timingFunction;
|
||||
|
@ -76,14 +76,24 @@ KeyframeEffectReadOnly::KeyframeEffectReadOnly(
|
||||
Element* aTarget,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
const TimingParams& aTiming)
|
||||
: KeyframeEffectReadOnly(aDocument, aTarget, aPseudoType,
|
||||
new AnimationEffectTimingReadOnly(aTiming))
|
||||
{
|
||||
}
|
||||
|
||||
KeyframeEffectReadOnly::KeyframeEffectReadOnly(
|
||||
nsIDocument* aDocument,
|
||||
Element* aTarget,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
AnimationEffectTimingReadOnly* aTiming)
|
||||
: AnimationEffectReadOnly(aDocument)
|
||||
, mTarget(aTarget)
|
||||
, mTiming(*aTiming)
|
||||
, mPseudoType(aPseudoType)
|
||||
, mInEffectOnLastAnimationTimingUpdate(false)
|
||||
{
|
||||
MOZ_ASSERT(aTiming);
|
||||
MOZ_ASSERT(aTarget, "null animation target is not yet supported");
|
||||
|
||||
mTiming = new AnimationEffectTimingReadOnly(aTiming);
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
KeyframeEffectReadOnly::GetTarget(
|
||||
Nullable<OwningElementOrCSSPseudoElement>& aRv) const
|
||||
@ -2091,5 +2118,27 @@ KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(const nsIFrame*
|
||||
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 mozilla
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/AnimationEffectReadOnly.h"
|
||||
#include "mozilla/dom/AnimationEffectTiming.h"
|
||||
#include "mozilla/dom/AnimationEffectTimingReadOnly.h" // TimingParams
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/KeyframeBinding.h"
|
||||
@ -195,20 +196,11 @@ public:
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
return Constructor(aGlobal, aTarget, aFrames,
|
||||
TimingParams::FromOptionsUnion(aOptions, aTarget),
|
||||
aRv);
|
||||
return ConstructKeyframeEffect<KeyframeEffectReadOnly>(
|
||||
aGlobal, aTarget, aFrames,
|
||||
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 GetFrames(JSContext*& aCx,
|
||||
nsTArray<JSObject*>& aResult,
|
||||
@ -328,7 +320,21 @@ public:
|
||||
inline AnimationCollection* GetCollection() const;
|
||||
|
||||
protected:
|
||||
KeyframeEffectReadOnly(nsIDocument* aDocument,
|
||||
Element* aTarget,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
AnimationEffectTimingReadOnly* aTiming);
|
||||
|
||||
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();
|
||||
|
||||
// This effect is registered with its target element so long as:
|
||||
@ -384,6 +390,43 @@ private:
|
||||
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 mozilla
|
||||
|
||||
|
@ -10,6 +10,7 @@ MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
|
||||
EXPORTS.mozilla.dom += [
|
||||
'Animation.h',
|
||||
'AnimationEffectReadOnly.h',
|
||||
'AnimationEffectTiming.h',
|
||||
'AnimationEffectTimingReadOnly.h',
|
||||
'AnimationTimeline.h',
|
||||
'CSSPseudoElement.h',
|
||||
@ -31,6 +32,7 @@ EXPORTS.mozilla += [
|
||||
UNIFIED_SOURCES += [
|
||||
'Animation.cpp',
|
||||
'AnimationEffectReadOnly.cpp',
|
||||
'AnimationEffectTiming.cpp',
|
||||
'AnimationEffectTimingReadOnly.cpp',
|
||||
'AnimationTimeline.cpp',
|
||||
'AnimationUtils.cpp',
|
||||
|
@ -1004,7 +1004,8 @@ ParentRunnable::Run()
|
||||
|
||||
// Metadata is now open.
|
||||
if (!SendOnOpenMetadataForRead(mMetadata)) {
|
||||
Unused << Send__delete__(this, JS::AsmJSCache_InternalError);
|
||||
Fail();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -1038,7 +1039,8 @@ ParentRunnable::Run()
|
||||
FileDescriptor::PlatformHandleType handle =
|
||||
FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFileDesc));
|
||||
if (!SendOnOpenCacheFile(mFileSize, FileDescriptor(handle))) {
|
||||
Unused << Send__delete__(this, JS::AsmJSCache_InternalError);
|
||||
Fail();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -1547,7 +1547,7 @@ bool
|
||||
Console::ProcessArguments(JSContext* aCx,
|
||||
const Sequence<JS::Value>& aData,
|
||||
Sequence<JS::Value>& aSequence,
|
||||
Sequence<JS::Value>& aStyles) const
|
||||
Sequence<nsString>& aStyles) const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
@ -1693,13 +1693,18 @@ Console::ProcessArguments(JSContext* aCx,
|
||||
int32_t diff = aSequence.Length() - aStyles.Length();
|
||||
if (diff > 0) {
|
||||
for (int32_t i = 0; i < diff; i++) {
|
||||
if (!aStyles.AppendElement(JS::NullValue(), fallible)) {
|
||||
if (!aStyles.AppendElement(NullString(), fallible)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ private:
|
||||
bool
|
||||
ProcessArguments(JSContext* aCx, const Sequence<JS::Value>& aData,
|
||||
Sequence<JS::Value>& aSequence,
|
||||
Sequence<JS::Value>& aStyles) const;
|
||||
Sequence<nsString>& aStyles) const;
|
||||
|
||||
void
|
||||
MakeFormatString(nsCString& aFormat, int32_t aInteger, int32_t aMantissa,
|
||||
|
@ -918,7 +918,7 @@ already_AddRefed<DOMRect>
|
||||
Element::GetBoundingClientRect()
|
||||
{
|
||||
RefPtr<DOMRect> rect = new DOMRect(this);
|
||||
|
||||
|
||||
nsIFrame* frame = GetPrimaryFrame(Flush_Layout);
|
||||
if (!frame) {
|
||||
// display:none, perhaps? Return the empty rect
|
||||
@ -1462,7 +1462,7 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
if (xulElem) {
|
||||
xulElem->SetXULBindingParent(aBindingParent);
|
||||
}
|
||||
else
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (aBindingParent) {
|
||||
@ -1522,7 +1522,7 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
// XXXbz ordering issues here? Probably not, since ChangeDocumentFor is
|
||||
// just pretty broken anyway.... Need to get it working.
|
||||
// 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);
|
||||
|
||||
@ -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
|
||||
// recursively unbinding the kids.
|
||||
if (IsHTMLElement()) {
|
||||
@ -2542,7 +2542,7 @@ Element::GetAttrInfo(int32_t aNamespaceID, nsIAtom* aName) const
|
||||
|
||||
return nsAttrInfo(nullptr, nullptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
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(aNameSpaceID != kNameSpaceID_Unknown, "Must have namespace");
|
||||
NS_ASSERTION(aValues, "Null value array");
|
||||
|
||||
|
||||
const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName, aNameSpaceID);
|
||||
if (val) {
|
||||
for (int32_t i = 0; aValues[i]; ++i) {
|
||||
@ -2767,7 +2767,7 @@ Element::List(FILE* out, int32_t aIndent,
|
||||
nsIContent* child = GetFirstChild();
|
||||
if (child) {
|
||||
fputs("\n", out);
|
||||
|
||||
|
||||
for (; child; child = child->GetNextSibling()) {
|
||||
child->List(out, aIndent + 1);
|
||||
}
|
||||
@ -2776,7 +2776,7 @@ Element::List(FILE* out, int32_t aIndent,
|
||||
}
|
||||
|
||||
fputs(">\n", out);
|
||||
|
||||
|
||||
Element* nonConstThis = const_cast<Element*>(this);
|
||||
|
||||
// XXX sXBL/XBL2 issue! Owner or current document?
|
||||
@ -3341,9 +3341,8 @@ Element::Animate(JSContext* aContext,
|
||||
|
||||
Nullable<ElementOrCSSPseudoElement> target;
|
||||
target.SetValue().SetAsElement() = this;
|
||||
// Bug 1211783: Use KeyframeEffect here (instead of KeyframeEffectReadOnly)
|
||||
RefPtr<KeyframeEffectReadOnly> effect =
|
||||
KeyframeEffectReadOnly::Constructor(global, target, frames,
|
||||
RefPtr<KeyframeEffect> effect =
|
||||
KeyframeEffect::Constructor(global, target, frames,
|
||||
TimingParams::FromOptionsUnion(aOptions, target), aError);
|
||||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
@ -3525,7 +3524,7 @@ Element::InsertAdjacentHTML(const nsAString& aPosition, const nsAString& aText,
|
||||
// Needed when insertAdjacentHTML is used in combination with contenteditable
|
||||
mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
|
||||
nsAutoScriptLoaderDisabler sld(doc);
|
||||
|
||||
|
||||
// Batch possible DOMSubtreeModified events.
|
||||
mozAutoSubtreeModified subtree(doc, nullptr);
|
||||
|
||||
|
@ -2660,6 +2660,9 @@ nsDocument::ApplySettingsFromCSP(bool aSpeculative)
|
||||
rv = csp->GetUpgradeInsecureRequests(&mUpgradeInsecureRequests);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
}
|
||||
if (!mUpgradeInsecurePreloads) {
|
||||
mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -151,6 +151,34 @@ GetNextRangeCommonAncestor(nsINode* 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
|
||||
nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
|
||||
uint32_t aEndOffset)
|
||||
@ -160,24 +188,45 @@ nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
|
||||
nsINode* n = GetNextRangeCommonAncestor(aNode);
|
||||
NS_ASSERTION(n || !aNode->IsSelectionDescendant(),
|
||||
"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())) {
|
||||
RangeHashTable* ranges =
|
||||
static_cast<RangeHashTable*>(n->GetProperty(nsGkAtoms::range));
|
||||
for (auto iter = ranges->ConstIter(); !iter.Done(); iter.Next()) {
|
||||
nsRange* range = iter.Get()->GetKey();
|
||||
if (range->IsInSelection() && !range->Collapsed()) {
|
||||
int32_t cmp = nsContentUtils::ComparePoints(aNode, aEndOffset,
|
||||
range->GetStartParent(),
|
||||
range->StartOffset());
|
||||
if (cmp == 1) {
|
||||
cmp = nsContentUtils::ComparePoints(aNode, aStartOffset,
|
||||
range->GetEndParent(),
|
||||
range->EndOffset());
|
||||
if (cmp == -1) {
|
||||
return true;
|
||||
}
|
||||
ancestorSelectionRanges.PutEntry(range);
|
||||
Selection* selection = range->mSelection;
|
||||
ancestorSelections.PutEntry(selection);
|
||||
maxRangeCount = std::max(maxRangeCount, selection->RangeCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@ -3105,6 +3154,12 @@ nsRange::Constructor(const GlobalObject& aGlobal,
|
||||
return window->GetDoc()->CreateRange(aRv);
|
||||
}
|
||||
|
||||
static bool ExcludeIfNextToNonSelectable(nsIContent* aContent)
|
||||
{
|
||||
return aContent->IsNodeOfType(nsINode::eTEXT) &&
|
||||
aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE);
|
||||
}
|
||||
|
||||
void
|
||||
nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges)
|
||||
{
|
||||
@ -3123,6 +3178,10 @@ nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges)
|
||||
|
||||
bool added = 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;
|
||||
while (true) {
|
||||
ErrorResult err;
|
||||
@ -3132,12 +3191,19 @@ nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges)
|
||||
nsIContent* content =
|
||||
node && node->IsContent() ? node->AsContent() : nullptr;
|
||||
if (content) {
|
||||
nsIFrame* frame = content->GetPrimaryFrame();
|
||||
for (nsIContent* p = content; !frame && (p = p->GetParent()); ) {
|
||||
frame = p->GetPrimaryFrame();
|
||||
if (firstNonSelectableContent && ExcludeIfNextToNonSelectable(content)) {
|
||||
// Ignorable whitespace next to a sequence of non-selectable nodes
|
||||
// counts as non-selectable (bug 1216001).
|
||||
selectable = false;
|
||||
}
|
||||
if (frame) {
|
||||
frame->IsSelectable(&selectable, nullptr);
|
||||
if (selectable) {
|
||||
nsIFrame* frame = content->GetPrimaryFrame();
|
||||
for (nsIContent* p = content; !frame && (p = p->GetParent()); ) {
|
||||
frame = p->GetPrimaryFrame();
|
||||
}
|
||||
if (frame) {
|
||||
frame->IsSelectable(&selectable, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,6 +252,14 @@ public:
|
||||
bool *outNodeBefore,
|
||||
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,
|
||||
uint32_t aEndOffset);
|
||||
|
||||
@ -298,6 +306,14 @@ protected:
|
||||
*/
|
||||
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
|
||||
{
|
||||
explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange)
|
||||
|
@ -133,9 +133,15 @@ nsScreen::GetRect(nsRect& 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.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width);
|
||||
|
||||
@ -156,10 +162,21 @@ nsScreen::GetAvailRect(nsRect& aRect)
|
||||
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);
|
||||
|
||||
aRect.x = nsPresContext::AppUnitsToIntCSSPixels(aRect.x);
|
||||
aRect.y = nsPresContext::AppUnitsToIntCSSPixels(aRect.y);
|
||||
aRect.x = NSToIntRound(screenTopLeftDesk.x) +
|
||||
nsPresContext::AppUnitsToIntCSSPixels(aRect.x - r.x);
|
||||
aRect.y = NSToIntRound(screenTopLeftDesk.y) +
|
||||
nsPresContext::AppUnitsToIntCSSPixels(aRect.y - r.y);
|
||||
|
||||
aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height);
|
||||
aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width);
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
src: url("Ahem.ttf");
|
||||
}
|
||||
body { font-family: Ahem; font-size: 20px; }
|
||||
s { -moz-user-select: none; }
|
||||
s, .non-selectable { -moz-user-select: none; }
|
||||
n { display: none; }
|
||||
a { position:absolute; bottom: 0; right:0; }
|
||||
.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="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="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>
|
||||
|
||||
@ -100,9 +110,11 @@ function test()
|
||||
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)
|
||||
@ -258,6 +270,22 @@ function test()
|
||||
checkRanges([[0,1,-1,1]], 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 ====================
|
||||
// ======================================================
|
||||
|
@ -31,8 +31,9 @@ ENUM_ENTRY_VARIABLE_NAME = 'strings'
|
||||
INSTANCE_RESERVED_SLOTS = 1
|
||||
|
||||
|
||||
def memberReservedSlot(member):
|
||||
return "(DOM_INSTANCE_RESERVED_SLOTS + %d)" % member.slotIndex
|
||||
def memberReservedSlot(member, descriptor):
|
||||
return ("(DOM_INSTANCE_RESERVED_SLOTS + %d)" %
|
||||
member.slotIndices[descriptor.interface.identifier.name])
|
||||
|
||||
|
||||
def toStringBool(arg):
|
||||
@ -2149,7 +2150,7 @@ def clearableCachedAttrs(descriptor):
|
||||
m.isAttr() and
|
||||
# Constants should never need clearing!
|
||||
m.dependsOn != "Nothing" and
|
||||
m.slotIndex is not None)
|
||||
m.slotIndices is not None)
|
||||
|
||||
|
||||
def MakeClearCachedValueNativeName(member):
|
||||
@ -3805,8 +3806,6 @@ class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod):
|
||||
}
|
||||
// Getter handled setting our reserved slots
|
||||
""",
|
||||
slot=memberReservedSlot(m),
|
||||
interface=self.descriptor.interface.identifier.name,
|
||||
member=m.identifier.name)
|
||||
|
||||
body += "\nreturn true;\n"
|
||||
@ -3828,7 +3827,7 @@ class CGClearCachedValueMethod(CGAbstractMethod):
|
||||
CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
|
||||
|
||||
def definition_body(self):
|
||||
slotIndex = memberReservedSlot(self.member)
|
||||
slotIndex = memberReservedSlot(self.member, self.descriptor)
|
||||
if self.member.getExtendedAttribute("StoreInSlot"):
|
||||
# We have to root things and save the old value in case
|
||||
# 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.");
|
||||
""")
|
||||
|
||||
setSlot = self.idlNode.isAttr() and self.idlNode.slotIndex is not None
|
||||
setSlot = self.idlNode.isAttr() and self.idlNode.slotIndices is not None
|
||||
if setSlot:
|
||||
# For attributes in slots, we want to do some
|
||||
# post-processing once we've wrapped them.
|
||||
@ -7418,7 +7417,7 @@ class CGPerSignatureCall(CGThing):
|
||||
"args.rval().isObject()")
|
||||
postSteps += freezeValue.define()
|
||||
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
|
||||
# wrapper if needed. We need to do this because otherwise the
|
||||
# wrapper could get garbage-collected and the cached value would
|
||||
@ -8003,7 +8002,7 @@ class CGSetterCall(CGPerSignatureCall):
|
||||
|
||||
def wrap_return_value(self):
|
||||
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"):
|
||||
args = "cx, self"
|
||||
else:
|
||||
@ -8550,7 +8549,7 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
|
||||
return getMaplikeOrSetlikeSizeGetterBody(self.descriptor, self.attr)
|
||||
nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
|
||||
self.attr)
|
||||
if self.attr.slotIndex is not None:
|
||||
if self.attr.slotIndices is not None:
|
||||
if self.descriptor.hasXPConnectImpls:
|
||||
raise TypeError("Interface '%s' has XPConnect impls, so we "
|
||||
"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))
|
||||
else:
|
||||
prefix = ""
|
||||
@ -8827,10 +8826,13 @@ class CGMemberJITInfo(CGThing):
|
||||
slotIndex=slotIndex)
|
||||
return initializer.rstrip()
|
||||
|
||||
slotAssert = dedent(
|
||||
slotAssert = fill(
|
||||
"""
|
||||
static_assert(%s <= JSJitInfo::maxSlotIndex, "We won't fit");
|
||||
""" % slotIndex)
|
||||
static_assert(${slotIndex} <= JSJitInfo::maxSlotIndex, "We won't fit");
|
||||
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:
|
||||
argTypes = "%s_argTypes" % infoName
|
||||
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)
|
||||
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")
|
||||
isLazilyCachedInSlot = not isAlwaysInSlot
|
||||
slotIndex = memberReservedSlot(self.member)
|
||||
slotIndex = memberReservedSlot(self.member, self.descriptor)
|
||||
# We'll statically assert that this is not too big in
|
||||
# CGUpdateMemberSlotsMethod, in the case when
|
||||
# isAlwaysInSlot is true.
|
||||
@ -15328,7 +15330,7 @@ def getMaplikeOrSetlikeBackingObject(descriptor, maplikeOrSetlike, helperImpl=No
|
||||
PreserveWrapper<${selfType}>(self);
|
||||
}
|
||||
""",
|
||||
slot=memberReservedSlot(maplikeOrSetlike),
|
||||
slot=memberReservedSlot(maplikeOrSetlike, descriptor),
|
||||
func_prefix=func_prefix,
|
||||
errorReturn=getMaplikeOrSetlikeErrorReturn(helperImpl),
|
||||
selfType=descriptor.nativeType)
|
||||
|
@ -978,7 +978,9 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
(member.getExtendedAttribute("StoreInSlot") or
|
||||
member.getExtendedAttribute("Cached"))) or
|
||||
member.isMaplikeOrSetlike()):
|
||||
member.slotIndex = self.totalMembersInSlots
|
||||
if member.slotIndices is None:
|
||||
member.slotIndices = dict()
|
||||
member.slotIndices[self.identifier.name] = self.totalMembersInSlots
|
||||
self.totalMembersInSlots += 1
|
||||
if member.getExtendedAttribute("StoreInSlot"):
|
||||
self._ownMembersInSlots += 1
|
||||
@ -3599,7 +3601,7 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
|
||||
IDLMaplikeOrSetlikeOrIterableBase.__init__(self, location, identifier, maplikeOrSetlikeType,
|
||||
keyType, valueType, IDLInterfaceMember.Tags.MaplikeOrSetlike)
|
||||
self.readonly = readonly
|
||||
self.slotIndex = None
|
||||
self.slotIndices = None
|
||||
|
||||
# When generating JSAPI access code, we need to know the backing object
|
||||
# type prefix to create the correct function. Generate here for reuse.
|
||||
@ -3793,7 +3795,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
self.stringifier = stringifier
|
||||
self.enforceRange = False
|
||||
self.clamp = False
|
||||
self.slotIndex = None
|
||||
self.slotIndices = None
|
||||
assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike)
|
||||
self.maplikeOrSetlike = maplikeOrSetlike
|
||||
self.dependsOn = "Everything"
|
||||
|
@ -1570,9 +1570,7 @@ CanvasRenderingContext2D::ReturnTarget()
|
||||
{
|
||||
if (mTarget && mBufferProvider) {
|
||||
CurrentState().transform = mTarget->GetTransform();
|
||||
DrawTarget* oldDT = mTarget;
|
||||
mTarget = nullptr;
|
||||
mBufferProvider->ReturnAndUseDT(oldDT);
|
||||
mBufferProvider->ReturnAndUseDT(mTarget.forget());
|
||||
}
|
||||
}
|
||||
|
||||
@ -4916,6 +4914,9 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& aWindow, double aX,
|
||||
|
||||
nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
|
||||
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) {
|
||||
RefPtr<SourceSurface> snapshot = drawDT->Snapshot();
|
||||
RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
|
||||
|
@ -20,28 +20,33 @@ GetFBInfoForBlit(const WebGLFramebuffer* fb, const char* const fbInfo,
|
||||
const webgl::FormatInfo** const out_depthFormat,
|
||||
const webgl::FormatInfo** const out_stencilFormat)
|
||||
{
|
||||
*out_samples = 1; // TODO
|
||||
*out_samples = 0;
|
||||
*out_colorFormat = nullptr;
|
||||
*out_depthFormat = nullptr;
|
||||
*out_stencilFormat = nullptr;
|
||||
|
||||
if (fb->ColorAttachment(0).IsDefined()) {
|
||||
const auto& attachment = fb->ColorAttachment(0);
|
||||
*out_samples = attachment.Samples();
|
||||
*out_colorFormat = attachment.Format()->format;
|
||||
}
|
||||
|
||||
if (fb->DepthStencilAttachment().IsDefined()) {
|
||||
const auto& attachment = fb->DepthStencilAttachment();
|
||||
*out_samples = attachment.Samples();
|
||||
|
||||
*out_depthFormat = attachment.Format()->format;
|
||||
*out_stencilFormat = *out_depthFormat;
|
||||
} else {
|
||||
if (fb->DepthAttachment().IsDefined()) {
|
||||
const auto& attachment = fb->DepthAttachment();
|
||||
*out_samples = attachment.Samples();
|
||||
*out_depthFormat = attachment.Format()->format;
|
||||
}
|
||||
|
||||
if (fb->StencilAttachment().IsDefined()) {
|
||||
const auto& attachment = fb->StencilAttachment();
|
||||
*out_samples = attachment.Samples();
|
||||
*out_stencilFormat = attachment.Format()->format;
|
||||
}
|
||||
}
|
||||
@ -133,7 +138,7 @@ WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
srcSamples = 1; // Always 1.
|
||||
srcSamples = 0; // Always 0.
|
||||
|
||||
GetBackbufferFormats(mOptions, &srcColorFormat, &srcDepthFormat,
|
||||
&srcStencilFormat);
|
||||
@ -221,13 +226,13 @@ WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY
|
||||
return;
|
||||
}
|
||||
|
||||
if (dstSamples != 1) {
|
||||
if (dstSamples != 0) {
|
||||
ErrorInvalidOperation("blitFramebuffer: DRAW_FRAMEBUFFER may not have"
|
||||
" multiple samples.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (srcSamples != 1) {
|
||||
if (srcSamples != 0) {
|
||||
if (mask & LOCAL_GL_COLOR_BUFFER_BIT &&
|
||||
dstColorFormat != srcColorFormat)
|
||||
{
|
||||
|
@ -15,43 +15,45 @@ void
|
||||
WebGL2Context::GetInternalformatParameter(JSContext* cx, GLenum target,
|
||||
GLenum internalformat, GLenum pname,
|
||||
JS::MutableHandleValue retval,
|
||||
ErrorResult& rv)
|
||||
ErrorResult& out_rv)
|
||||
{
|
||||
if (IsContextLost())
|
||||
return;
|
||||
const char funcName[] = "getInternalfomratParameter";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (target != LOCAL_GL_RENDERBUFFER) {
|
||||
return ErrorInvalidEnumInfo("getInternalfomratParameter: target must be "
|
||||
"RENDERBUFFER. Was:", target);
|
||||
}
|
||||
if (target != LOCAL_GL_RENDERBUFFER) {
|
||||
ErrorInvalidEnum("%s: `target` must be RENDERBUFFER, was: 0x%04x.", funcName,
|
||||
target);
|
||||
return;
|
||||
}
|
||||
|
||||
// GL_INVALID_ENUM is generated if internalformat is not color-,
|
||||
// depth-, or stencil-renderable.
|
||||
// TODO: When format table queries lands.
|
||||
// GL_INVALID_ENUM is generated if internalformat is not color-, depth-, or
|
||||
// stencil-renderable.
|
||||
// TODO: When format table queries lands.
|
||||
|
||||
if (pname != LOCAL_GL_SAMPLES) {
|
||||
return ErrorInvalidEnumInfo("getInternalformatParameter: pname must be SAMPLES. "
|
||||
"Was:", pname);
|
||||
}
|
||||
if (pname != LOCAL_GL_SAMPLES) {
|
||||
ErrorInvalidEnumInfo("%s: `pname` must be SAMPLES, was 0x%04x.", funcName, pname);
|
||||
return;
|
||||
}
|
||||
|
||||
GLint* samples = nullptr;
|
||||
GLint sampleCount = 0;
|
||||
gl->fGetInternalformativ(LOCAL_GL_RENDERBUFFER, internalformat,
|
||||
LOCAL_GL_NUM_SAMPLE_COUNTS, 1, &sampleCount);
|
||||
if (sampleCount > 0) {
|
||||
samples = new GLint[sampleCount];
|
||||
gl->fGetInternalformativ(LOCAL_GL_RENDERBUFFER, internalformat, LOCAL_GL_SAMPLES,
|
||||
sampleCount, samples);
|
||||
}
|
||||
GLint* samples = nullptr;
|
||||
GLint sampleCount = 0;
|
||||
gl->fGetInternalformativ(LOCAL_GL_RENDERBUFFER, internalformat,
|
||||
LOCAL_GL_NUM_SAMPLE_COUNTS, 1, &sampleCount);
|
||||
if (sampleCount > 0) {
|
||||
samples = new GLint[sampleCount];
|
||||
gl->fGetInternalformativ(LOCAL_GL_RENDERBUFFER, internalformat, LOCAL_GL_SAMPLES,
|
||||
sampleCount, samples);
|
||||
}
|
||||
|
||||
JSObject* obj = dom::Int32Array::Create(cx, this, sampleCount, samples);
|
||||
if (!obj) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
JSObject* obj = dom::Int32Array::Create(cx, this, sampleCount, samples);
|
||||
if (!obj) {
|
||||
out_rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
delete[] samples;
|
||||
delete[] samples;
|
||||
|
||||
retval.setObjectOrNull(obj);
|
||||
retval.setObjectOrNull(obj);
|
||||
}
|
||||
|
||||
void
|
||||
@ -59,14 +61,11 @@ WebGL2Context::RenderbufferStorageMultisample(GLenum target, GLsizei samples,
|
||||
GLenum internalFormat,
|
||||
GLsizei width, GLsizei height)
|
||||
{
|
||||
const char funcName[] = "renderbufferStorageMultisample";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
const char funcName[] = "renderbufferStorageMultisample";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
//RenderbufferStorage_base(funcName, target, samples, internalFormat, width, height);
|
||||
|
||||
ErrorInvalidOperation("%s: Multisampling is still under development, and is currently"
|
||||
" disabled.", funcName);
|
||||
RenderbufferStorage_base(funcName, target, samples, internalFormat, width, height);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -106,7 +106,6 @@ WebGLContext::WebGLContext()
|
||||
, mMaxFetchedVertices(0)
|
||||
, mMaxFetchedInstances(0)
|
||||
, mBypassShaderValidation(false)
|
||||
, mGLMaxSamples(1)
|
||||
, mNeedsFakeNoAlpha(false)
|
||||
, mNeedsFakeNoDepth(false)
|
||||
, mNeedsFakeNoStencil(false)
|
||||
|
@ -1114,7 +1114,6 @@ protected:
|
||||
int32_t mGLMaxVertexUniformVectors;
|
||||
uint32_t mGLMaxTransformFeedbackSeparateAttribs;
|
||||
GLuint mGLMaxUniformBufferBindings;
|
||||
GLsizei mGLMaxSamples;
|
||||
|
||||
// What is supported:
|
||||
uint32_t mGLMaxColorAttachments;
|
||||
|
@ -173,17 +173,13 @@ WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer* wrb)
|
||||
if (wrb && wrb->IsDeleted())
|
||||
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) {
|
||||
wrb->BindRenderbuffer();
|
||||
#ifdef ANDROID
|
||||
wrb->mIsRB = true;
|
||||
#endif
|
||||
} else {
|
||||
gl->fBindRenderbuffer(target, 0);
|
||||
wrb->mHasBeenBound = true;
|
||||
}
|
||||
|
||||
mBoundRenderbuffer = wrb;
|
||||
@ -1068,16 +1064,7 @@ WebGLContext::IsRenderbuffer(WebGLRenderbuffer* rb)
|
||||
if (rb->IsDeleted())
|
||||
return false;
|
||||
|
||||
#ifdef ANDROID
|
||||
if (gl->WorkAroundDriverBugs() &&
|
||||
gl->Renderer() == GLRenderer::AndroidEmulator)
|
||||
{
|
||||
return rb->mIsRB;
|
||||
}
|
||||
#endif
|
||||
|
||||
MakeContextCurrent();
|
||||
return gl->fIsRenderbuffer(rb->PrimaryGLName());
|
||||
return rb->mHasBeenBound;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1799,66 +1786,41 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
|
||||
|
||||
void
|
||||
WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target,
|
||||
GLsizei samples,
|
||||
GLenum internalFormat, GLsizei width,
|
||||
GLsizei height)
|
||||
GLsizei samples, GLenum internalFormat,
|
||||
GLsizei width, GLsizei height)
|
||||
{
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!mBoundRenderbuffer) {
|
||||
ErrorInvalidOperation("%s: Called on renderbuffer 0.", funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (target != LOCAL_GL_RENDERBUFFER) {
|
||||
ErrorInvalidEnumInfo("`target`", funcName, target);
|
||||
return;
|
||||
}
|
||||
|
||||
if (samples < 0 || samples > mGLMaxSamples) {
|
||||
ErrorInvalidValue("%s: `samples` is out of the valid range.", funcName);
|
||||
if (!mBoundRenderbuffer) {
|
||||
ErrorInvalidOperation("%s: Called on renderbuffer 0.", funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (samples < 0) {
|
||||
ErrorInvalidValue("%s: `samples` must be >= 0.", funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (width < 0 || height < 0) {
|
||||
ErrorInvalidValue("%s: Width and height must be >= 0.", funcName);
|
||||
ErrorInvalidValue("%s: `width` and `height` must be >= 0.", funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (uint32_t(width) > mImplMaxRenderbufferSize ||
|
||||
uint32_t(height) > mImplMaxRenderbufferSize)
|
||||
{
|
||||
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;
|
||||
}
|
||||
mBoundRenderbuffer->RenderbufferStorage(funcName, uint32_t(samples), internalFormat,
|
||||
uint32_t(width), uint32_t(height));
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::RenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height)
|
||||
{
|
||||
RenderbufferStorage_base("renderbufferStorage", target, 0,
|
||||
internalFormat, width, height);
|
||||
RenderbufferStorage_base("renderbufferStorage", target, 0, internalFormat, width,
|
||||
height);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2323,6 +2285,8 @@ WebGLContext::CreateRenderbuffer()
|
||||
{
|
||||
if (IsContextLost())
|
||||
return nullptr;
|
||||
|
||||
MakeContextCurrent();
|
||||
RefPtr<WebGLRenderbuffer> globj = new WebGLRenderbuffer(this);
|
||||
return globj.forget();
|
||||
}
|
||||
|
@ -727,6 +727,8 @@ WebGLContext::AssertCachedBindings()
|
||||
|
||||
MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors());
|
||||
#endif
|
||||
|
||||
// We do not check the renderbuffer binding, because we never rely on it matching.
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -786,8 +786,6 @@ WebGLContext::InitAndValidateGL()
|
||||
|
||||
mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS;
|
||||
mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS;
|
||||
|
||||
mGLMaxSamples = 1;
|
||||
} else {
|
||||
gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&mImplMaxTextureSize);
|
||||
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_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
|
||||
|
@ -416,8 +416,6 @@ BytesPerPixel(const PackingInfo& packing)
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FormatUsageAuthority
|
||||
|
||||
|
||||
|
||||
bool
|
||||
FormatUsageInfo::IsUnpackValid(const PackingInfo& key,
|
||||
const DriverUnpackInfo** const out_value) const
|
||||
@ -430,6 +428,29 @@ FormatUsageInfo::IsUnpackValid(const PackingInfo& key,
|
||||
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
|
||||
|
@ -248,10 +248,15 @@ struct FormatUsageInfo
|
||||
const FormatInfo* const format;
|
||||
bool isRenderable;
|
||||
bool isFilterable;
|
||||
|
||||
std::map<PackingInfo, DriverUnpackInfo> validUnpacks;
|
||||
const DriverUnpackInfo* idealUnpack;
|
||||
|
||||
const GLint* textureSwizzleRGBA;
|
||||
|
||||
bool maxSamplesKnown;
|
||||
uint32_t maxSamples;
|
||||
|
||||
static const GLint kLuminanceSwizzleRGBA[4];
|
||||
static const GLint kAlphaSwizzleRGBA[4];
|
||||
static const GLint kLumAlphaSwizzleRGBA[4];
|
||||
@ -262,10 +267,14 @@ struct FormatUsageInfo
|
||||
, isFilterable(false)
|
||||
, idealUnpack(nullptr)
|
||||
, textureSwizzleRGBA(nullptr)
|
||||
, maxSamplesKnown(false)
|
||||
, maxSamples(0)
|
||||
{ }
|
||||
|
||||
bool IsUnpackValid(const PackingInfo& key,
|
||||
const DriverUnpackInfo** const out_value) const;
|
||||
|
||||
void ResolveMaxSamples(gl::GLContext* gl);
|
||||
};
|
||||
|
||||
class FormatUsageAuthority
|
||||
|
@ -69,6 +69,17 @@ WebGLFBAttachPoint::Format() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
WebGLFBAttachPoint::Samples() const
|
||||
{
|
||||
MOZ_ASSERT(IsDefined());
|
||||
|
||||
if (mRenderbufferPtr)
|
||||
return mRenderbufferPtr->Samples();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLFBAttachPoint::HasAlpha() const
|
||||
{
|
||||
@ -394,11 +405,11 @@ WebGLFBAttachPoint::FinalizeAttachment(gl::GLContext* gl, GLenum attachment) con
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Renderbuffer()) {
|
||||
Renderbuffer()->FramebufferRenderbuffer(attachment);
|
||||
Renderbuffer()->DoFramebufferRenderbuffer(attachment);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -790,28 +801,6 @@ WebGLFramebuffer::HasIncompleteAttachments(nsCString* const out_info) const
|
||||
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
|
||||
WebGLFramebuffer::AllImageRectsMatch() const
|
||||
{
|
||||
@ -819,20 +808,84 @@ WebGLFramebuffer::AllImageRectsMatch() const
|
||||
DebugOnly<nsCString> fbStatusInfo;
|
||||
MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo));
|
||||
|
||||
bool needsInit = true;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
bool imageRectsMatch = true;
|
||||
|
||||
imageRectsMatch &= MatchOrReplaceSize(mColorAttachment0, &width, &height);
|
||||
imageRectsMatch &= MatchOrReplaceSize(mDepthAttachment, &width, &height);
|
||||
imageRectsMatch &= MatchOrReplaceSize(mStencilAttachment, &width, &height);
|
||||
imageRectsMatch &= MatchOrReplaceSize(mDepthStencilAttachment, &width, &height);
|
||||
const auto fnInitializeOrMatch = [&needsInit, &width,
|
||||
&height](const WebGLFBAttachPoint& attach)
|
||||
{
|
||||
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) {
|
||||
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
|
||||
@ -850,7 +903,11 @@ WebGLFramebuffer::PrecheckFramebufferStatus(nsCString* const out_info) const
|
||||
if (!AllImageRectsMatch())
|
||||
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // Inconsistent sizes
|
||||
|
||||
if (!AllImageSamplesMatch())
|
||||
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; // Inconsistent samples
|
||||
|
||||
if (!mContext->IsWebGL2()) {
|
||||
// INCOMPLETE_DIMENSIONS doesn't exist in GLES3.
|
||||
const auto depthOrStencilCount = int(mDepthAttachment.IsDefined()) +
|
||||
int(mStencilAttachment.IsDefined()) +
|
||||
int(mDepthStencilAttachment.IsDefined());
|
||||
|
@ -49,7 +49,6 @@ private:
|
||||
, mAttachmentPoint(0)
|
||||
{ }
|
||||
|
||||
|
||||
public:
|
||||
WebGLFBAttachPoint(WebGLFramebuffer* fb, GLenum attachmentPoint);
|
||||
~WebGLFBAttachPoint();
|
||||
@ -60,6 +59,7 @@ public:
|
||||
bool IsDeleteRequested() const;
|
||||
|
||||
const webgl::FormatUsageInfo* Format() const;
|
||||
uint32_t Samples() const;
|
||||
|
||||
bool HasAlpha() const;
|
||||
bool IsReadableFloat() const;
|
||||
@ -98,7 +98,6 @@ public:
|
||||
void SetImageDataStatus(WebGLImageDataStatus x);
|
||||
|
||||
void Size(uint32_t* const out_width, uint32_t* const out_height) const;
|
||||
//const WebGLRectangleObject& RectangleObject() const;
|
||||
|
||||
bool HasImage() const;
|
||||
bool IsComplete(WebGLContext* webgl, nsCString* const out_info) const;
|
||||
@ -229,6 +228,7 @@ public:
|
||||
bool HasDefinedAttachments() const;
|
||||
bool HasIncompleteAttachments(nsCString* const out_info) const;
|
||||
bool AllImageRectsMatch() const;
|
||||
bool AllImageSamplesMatch() const;
|
||||
FBStatus PrecheckFramebufferStatus(nsCString* const out_info) const;
|
||||
FBStatus CheckFramebufferStatus(nsCString* const out_info) const;
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
namespace mozilla {
|
||||
|
||||
static GLenum
|
||||
DepthStencilDepthFormat(gl::GLContext* gl)
|
||||
DepthFormatForDepthStencilEmu(gl::GLContext* gl)
|
||||
{
|
||||
// We might not be able to get 24-bit, so let's pretend!
|
||||
if (gl->IsGLES() && !gl->IsExtensionSupported(gl::GLContext::OES_depth24))
|
||||
@ -24,46 +24,38 @@ DepthStencilDepthFormat(gl::GLContext* gl)
|
||||
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*
|
||||
WebGLRenderbuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> 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)
|
||||
: WebGLContextBoundObject(webgl)
|
||||
, mPrimaryRB(0)
|
||||
, mPrimaryRB( DoCreateRenderbuffer(webgl->gl) )
|
||||
, mEmulatePackedDepthStencil( EmulatePackedDepthStencil(webgl->gl) )
|
||||
, mSecondaryRB(0)
|
||||
, mFormat(nullptr)
|
||||
, mSamples(0)
|
||||
, mImageDataStatus(WebGLImageDataStatus::NoImageData)
|
||||
, mSamples(1)
|
||||
, mIsUsingSecondary(false)
|
||||
#ifdef ANDROID
|
||||
, mIsRB(false)
|
||||
#endif
|
||||
, mHasBeenBound(false)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@ -77,9 +69,6 @@ WebGLRenderbuffer::Delete()
|
||||
mContext->gl->fDeleteRenderbuffers(1, &mSecondaryRB);
|
||||
|
||||
LinkedListElement<WebGLRenderbuffer>::removeFrom(mContext->mRenderbuffers);
|
||||
#ifdef ANDROID
|
||||
mIsRB = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
int64_t
|
||||
@ -89,156 +78,173 @@ WebGLRenderbuffer::MemoryUsage() const
|
||||
if (!mFormat)
|
||||
return 0;
|
||||
|
||||
auto bytesPerPixel = mFormat->format->estimatedBytesPerPixel;
|
||||
uint64_t pixels = uint64_t(mWidth) * uint64_t(mHeight);
|
||||
const auto bytesPerPixel = mFormat->format->estimatedBytesPerPixel;
|
||||
const int64_t pixels = int64_t(mWidth) * int64_t(mHeight);
|
||||
|
||||
uint64_t totalSize = pixels * bytesPerPixel;
|
||||
|
||||
// If we have the same bytesPerPixel whether or not we have a secondary RB.
|
||||
if (mSecondaryRB && !mIsUsingSecondary) {
|
||||
totalSize += 2; // 1x1xRGBA4
|
||||
}
|
||||
|
||||
return int64_t(totalSize);
|
||||
const int64_t totalSize = pixels * bytesPerPixel;
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLRenderbuffer::BindRenderbuffer() const
|
||||
{
|
||||
/* Do this explicitly here, since the meaning changes for depth-stencil emu.
|
||||
* 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)
|
||||
static GLenum
|
||||
DoRenderbufferStorageMaybeMultisample(gl::GLContext* gl, GLsizei samples,
|
||||
GLenum internalFormat, GLsizei width,
|
||||
GLsizei height)
|
||||
{
|
||||
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) {
|
||||
case LOCAL_GL_RGBA4:
|
||||
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())
|
||||
internalFormatForGL = LOCAL_GL_RGBA8;
|
||||
internalFormat = LOCAL_GL_RGBA8;
|
||||
break;
|
||||
|
||||
case LOCAL_GL_RGB565:
|
||||
// the RGB565 format is not supported on desktop GL
|
||||
// RGB565 is not supported on desktop GL.
|
||||
if (!gl->IsGLES())
|
||||
internalFormatForGL = LOCAL_GL_RGB8;
|
||||
internalFormat = LOCAL_GL_RGB8;
|
||||
break;
|
||||
|
||||
case LOCAL_GL_DEPTH_COMPONENT16:
|
||||
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))
|
||||
internalFormatForGL = LOCAL_GL_DEPTH24_STENCIL8;
|
||||
internalFormat = LOCAL_GL_DEPTH24_STENCIL8;
|
||||
break;
|
||||
|
||||
case LOCAL_GL_DEPTH_STENCIL:
|
||||
// We emulate this in WebGLRenderbuffer if we don't have the requisite extension.
|
||||
internalFormatForGL = LOCAL_GL_DEPTH24_STENCIL8;
|
||||
MOZ_CRASH("GL_DEPTH_STENCIL is not valid here.");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
gl::GLContext::LocalErrorScope errorScope(*gl);
|
||||
|
||||
if (samples > 0) {
|
||||
gl->fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER, samples,
|
||||
internalFormatForGL, width, height);
|
||||
internalFormat, width, height);
|
||||
} else {
|
||||
gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, internalFormatForGL, width,
|
||||
height);
|
||||
gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, internalFormat, width, height);
|
||||
}
|
||||
|
||||
return errorScope.GetError();
|
||||
}
|
||||
|
||||
void
|
||||
WebGLRenderbuffer::RenderbufferStorage(GLsizei samples,
|
||||
const webgl::FormatUsageInfo* format,
|
||||
GLsizei width, GLsizei height)
|
||||
GLenum
|
||||
WebGLRenderbuffer::DoRenderbufferStorage(uint32_t samples,
|
||||
const webgl::FormatUsageInfo* format,
|
||||
uint32_t width, uint32_t height)
|
||||
{
|
||||
MOZ_ASSERT(mContext->mBoundRenderbuffer == this);
|
||||
|
||||
|
||||
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 secondaryFormat = 0;
|
||||
|
||||
if (NeedsDepthStencilEmu(mContext->gl, primaryFormat)) {
|
||||
primaryFormat = DepthStencilDepthFormat(gl);
|
||||
if (mEmulatePackedDepthStencil && primaryFormat == LOCAL_GL_DEPTH24_STENCIL8) {
|
||||
primaryFormat = DepthFormatForDepthStencilEmu(gl);
|
||||
secondaryFormat = LOCAL_GL_STENCIL_INDEX8;
|
||||
}
|
||||
|
||||
RenderbufferStorageMaybeMultisample(gl, samples, primaryFormat, width,
|
||||
height);
|
||||
gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mPrimaryRB);
|
||||
GLenum error = DoRenderbufferStorageMaybeMultisample(gl, samples, primaryFormat,
|
||||
width, height);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (mSecondaryRB) {
|
||||
// We can't leave the secondary RB unspecified either, since we should
|
||||
// handle the case where we attach a non-depth-stencil RB to a
|
||||
// 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);
|
||||
if (secondaryFormat) {
|
||||
if (!mSecondaryRB) {
|
||||
gl->fGenRenderbuffers(1, &mSecondaryRB);
|
||||
}
|
||||
|
||||
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;
|
||||
mFormat = format;
|
||||
mFormat = usage;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mImageDataStatus = WebGLImageDataStatus::UninitializedImageData;
|
||||
mIsUsingSecondary = bool(secondaryFormat);
|
||||
|
||||
InvalidateStatusOfAttachedFBs();
|
||||
}
|
||||
|
||||
void
|
||||
WebGLRenderbuffer::FramebufferRenderbuffer(GLenum attachment) const
|
||||
WebGLRenderbuffer::DoFramebufferRenderbuffer(GLenum attachment) const
|
||||
{
|
||||
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);
|
||||
gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
|
||||
LOCAL_GL_STENCIL_ATTACHMENT,
|
||||
LOCAL_GL_RENDERBUFFER, stencilRB);
|
||||
return;
|
||||
}
|
||||
|
||||
GLuint stencilRB = mPrimaryRB;
|
||||
if (mIsUsingSecondary) {
|
||||
MOZ_ASSERT(mSecondaryRB);
|
||||
stencilRB = mSecondaryRB;
|
||||
}
|
||||
gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
|
||||
LOCAL_GL_DEPTH_ATTACHMENT,
|
||||
gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachment,
|
||||
LOCAL_GL_RENDERBUFFER, mPrimaryRB);
|
||||
gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
|
||||
LOCAL_GL_STENCIL_ATTACHMENT,
|
||||
LOCAL_GL_RENDERBUFFER, stencilRB);
|
||||
}
|
||||
|
||||
GLint
|
||||
@ -266,6 +272,7 @@ WebGLRenderbuffer::GetRenderbufferParameter(RBTarget target,
|
||||
case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE:
|
||||
case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE:
|
||||
{
|
||||
gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mPrimaryRB);
|
||||
GLint i = 0;
|
||||
gl->fGetRenderbufferParameteriv(target.get(), pname.get(), &i);
|
||||
return i;
|
||||
@ -285,8 +292,7 @@ WebGLRenderbuffer::GetRenderbufferParameter(RBTarget target,
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(false,
|
||||
"This function should only be called with valid `pname`.");
|
||||
MOZ_ASSERT(false, "This function should only be called with valid `pname`.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,26 @@ class WebGLRenderbuffer final
|
||||
, public WebGLContextBoundObject
|
||||
, public WebGLFramebufferAttachable
|
||||
{
|
||||
friend class WebGLContext;
|
||||
friend class WebGLFramebuffer;
|
||||
friend class WebGLFBAttachPoint;
|
||||
|
||||
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);
|
||||
|
||||
void Delete();
|
||||
@ -46,8 +65,6 @@ public:
|
||||
|
||||
GLsizei Samples() const { return mSamples; }
|
||||
|
||||
GLuint PrimaryGLName() const { return mPrimaryRB; }
|
||||
|
||||
const webgl::FormatUsageInfo* Format() const { return mFormat; }
|
||||
|
||||
int64_t MemoryUsage() const;
|
||||
@ -56,41 +73,21 @@ public:
|
||||
return mContext;
|
||||
}
|
||||
|
||||
void BindRenderbuffer() const;
|
||||
void RenderbufferStorage(GLsizei samples, const webgl::FormatUsageInfo* format,
|
||||
GLsizei width, GLsizei height);
|
||||
void FramebufferRenderbuffer(GLenum attachment) const;
|
||||
void RenderbufferStorage(const char* funcName, uint32_t samples,
|
||||
GLenum internalFormat, uint32_t width, uint32_t height);
|
||||
// Only handles a subset of `pname`s.
|
||||
GLint GetRenderbufferParameter(RBTarget target, RBParam pname) const;
|
||||
|
||||
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:
|
||||
~WebGLRenderbuffer() {
|
||||
DeleteOnce();
|
||||
}
|
||||
|
||||
GLuint mPrimaryRB;
|
||||
GLuint mSecondaryRB;
|
||||
const webgl::FormatUsageInfo* mFormat;
|
||||
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;
|
||||
void DoFramebufferRenderbuffer(GLenum attachment) const;
|
||||
GLenum DoRenderbufferStorage(uint32_t samples, const webgl::FormatUsageInfo* format,
|
||||
uint32_t width, uint32_t height);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
15
dom/canvas/crashtests/1246775-1.html
Normal file
15
dom/canvas/crashtests/1246775-1.html
Normal 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>
|
@ -29,4 +29,5 @@ skip-if(azureCairo) load 1229983-1.html
|
||||
load 1229932-1.html
|
||||
load 1233613.html
|
||||
load 1244850-1.html
|
||||
load 1246775-1.html
|
||||
load texImage2D.html
|
||||
|
@ -2170,11 +2170,9 @@ private:
|
||||
|
||||
mJwk.mExt.Construct(mExtractable);
|
||||
|
||||
if (!mKeyUsages.IsEmpty()) {
|
||||
mJwk.mKey_ops.Construct();
|
||||
if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages, fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
mJwk.mKey_ops.Construct();
|
||||
if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages, fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -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>
|
||||
</head>
|
||||
|
||||
|
@ -194,7 +194,6 @@ TouchEvent::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
|
||||
}
|
||||
prefValue = sIsTouchDeviceSupportPresent;
|
||||
#else
|
||||
NS_WARNING("dom.w3c_touch_events.enabled=2 not implemented!");
|
||||
prefValue = false;
|
||||
#endif
|
||||
} else {
|
||||
|
@ -487,20 +487,6 @@ MediaSource::NotifyEvicted(double aStart, double 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
|
||||
MediaSource::GetMozDebugReaderData(nsAString& aString)
|
||||
{
|
||||
|
@ -100,12 +100,6 @@ public:
|
||||
// that were evicted are provided.
|
||||
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
|
||||
// buffered data. Used for debugging purposes.
|
||||
void GetMozDebugReaderData(nsAString& aString);
|
||||
|
@ -256,6 +256,12 @@ MediaDecoderOwner::NextFrameStatus
|
||||
MediaSourceDecoder::NextFrameBufferedStatus()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mMediaSource ||
|
||||
mMediaSource->ReadyState() == dom::MediaSourceReadyState::Closed) {
|
||||
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
|
||||
}
|
||||
|
||||
// Next frame hasn't been decoded yet.
|
||||
// Use the buffered range to consider if we have the next frame available.
|
||||
TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition());
|
||||
|
@ -22,7 +22,7 @@ using media::TimeIntervals;
|
||||
|
||||
MediaSourceDemuxer::MediaSourceDemuxer()
|
||||
: mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
|
||||
/* aSupportsTailDispatch = */ true))
|
||||
/* aSupportsTailDispatch = */ false))
|
||||
, mMonitor("MediaSourceDemuxer")
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
@ -39,27 +39,6 @@ using media::TimeUnit;
|
||||
|
||||
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
|
||||
SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
|
||||
{
|
||||
@ -226,10 +205,13 @@ void
|
||||
SourceBuffer::AbortBufferAppend()
|
||||
{
|
||||
if (mUpdating) {
|
||||
mPendingAppend.DisconnectIfExists();
|
||||
// TODO: Abort stream append loop algorithms.
|
||||
// cancel any pending buffer append.
|
||||
mContentManager->AbortAppendData();
|
||||
if (mPendingAppend.Exists()) {
|
||||
mPendingAppend.Disconnect();
|
||||
mContentManager->AbortAppendData();
|
||||
// Some data may have been added by the Segment Parser Loop.
|
||||
// Check if we need to update the duration.
|
||||
CheckEndTime();
|
||||
}
|
||||
AbortUpdating();
|
||||
}
|
||||
}
|
||||
@ -309,7 +291,6 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
|
||||
, mMediaSource(aMediaSource)
|
||||
, mUpdating(false)
|
||||
, mActive(false)
|
||||
, mUpdateID(0)
|
||||
, mType(aType)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
@ -381,7 +362,6 @@ SourceBuffer::StartUpdating()
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mUpdating);
|
||||
mUpdating = true;
|
||||
mUpdateID++;
|
||||
QueueAsyncSimpleEvent("updatestart");
|
||||
}
|
||||
|
||||
@ -390,12 +370,8 @@ SourceBuffer::StopUpdating()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!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.
|
||||
// The buffer append or range removal algorithm has been interrupted by
|
||||
// abort().
|
||||
return;
|
||||
}
|
||||
mUpdating = false;
|
||||
@ -407,7 +383,6 @@ void
|
||||
SourceBuffer::AbortUpdating()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mUpdating);
|
||||
mUpdating = false;
|
||||
QueueAsyncSimpleEvent("abort");
|
||||
QueueAsyncSimpleEvent("updateend");
|
||||
@ -438,23 +413,13 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||
|
||||
StartUpdating();
|
||||
|
||||
nsCOMPtr<nsIRunnable> task = new BufferAppendRunnable(this, mUpdateID);
|
||||
NS_DispatchToMainThread(task);
|
||||
BufferAppend();
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::BufferAppend(uint32_t aUpdateID)
|
||||
SourceBuffer::BufferAppend()
|
||||
{
|
||||
if (!mUpdating || aUpdateID != mUpdateID) {
|
||||
// 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(mUpdating);
|
||||
MOZ_ASSERT(mMediaSource);
|
||||
MOZ_ASSERT(!mPendingAppend.Exists());
|
||||
|
||||
@ -467,11 +432,8 @@ SourceBuffer::BufferAppend(uint32_t aUpdateID)
|
||||
void
|
||||
SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks)
|
||||
{
|
||||
MOZ_ASSERT(mUpdating);
|
||||
mPendingAppend.Complete();
|
||||
if (!mUpdating) {
|
||||
// The buffer append algorithm has been interrupted by abort().
|
||||
return;
|
||||
}
|
||||
|
||||
if (aHasActiveTracks) {
|
||||
if (!mActive) {
|
||||
@ -494,7 +456,9 @@ SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks)
|
||||
void
|
||||
SourceBuffer::AppendDataErrored(nsresult aError)
|
||||
{
|
||||
MOZ_ASSERT(mUpdating);
|
||||
mPendingAppend.Complete();
|
||||
|
||||
switch (aError) {
|
||||
case NS_ERROR_ABORT:
|
||||
// Nothing further to do as the trackbuffer has been shutdown.
|
||||
@ -510,10 +474,7 @@ void
|
||||
SourceBuffer::AppendError(bool aDecoderError)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mUpdating) {
|
||||
// The buffer append algorithm has been interrupted by abort().
|
||||
return;
|
||||
}
|
||||
|
||||
mContentManager->ResetParserState();
|
||||
|
||||
mUpdating = false;
|
||||
@ -625,16 +586,6 @@ SourceBuffer::Evict(double aStart, double aEnd)
|
||||
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_UNLINK_BEGIN(SourceBuffer)
|
||||
|
@ -213,10 +213,6 @@ public:
|
||||
return mActive;
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void Dump(const char* aPath);
|
||||
#endif
|
||||
|
||||
private:
|
||||
~SourceBuffer();
|
||||
|
||||
@ -238,7 +234,7 @@ private:
|
||||
|
||||
// Shared implementation of AppendBuffer overloads.
|
||||
void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
|
||||
void BufferAppend(uint32_t aAppendID);
|
||||
void BufferAppend();
|
||||
|
||||
// Implement the "Append Error Algorithm".
|
||||
// Will call endOfStream() with "decode" error if aDecodeError is true.
|
||||
@ -266,11 +262,6 @@ private:
|
||||
|
||||
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;
|
||||
const nsCString mType;
|
||||
|
||||
|
@ -108,10 +108,6 @@ public:
|
||||
virtual void RestartGroupStartTimestamp() {}
|
||||
virtual media::TimeUnit GroupEndTimestamp() = 0;
|
||||
|
||||
#if defined(DEBUG)
|
||||
virtual void Dump(const char* aPath) { }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual ~SourceBufferContentManager() { }
|
||||
};
|
||||
|
@ -176,16 +176,6 @@ SourceBufferList::QueueAsyncSimpleEvent(const char* aName)
|
||||
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)
|
||||
: DOMEventTargetHelper(aMediaSource->GetParentObject())
|
||||
, mMediaSource(aMediaSource)
|
||||
|
@ -84,10 +84,6 @@ public:
|
||||
// No event is fired and no action is performed on the sourcebuffers.
|
||||
void ClearSimple();
|
||||
|
||||
#if defined(DEBUG)
|
||||
void Dump(const char* aPath);
|
||||
#endif
|
||||
|
||||
private:
|
||||
~SourceBufferList();
|
||||
|
||||
|
@ -96,24 +96,17 @@ TrackBuffersManager::TrackBuffersManager(dom::SourceBufferAttributes* aAttribute
|
||||
, mType(aType)
|
||||
, mParser(ContainerParser::CreateForMIMEType(aType))
|
||||
, mProcessedInput(0)
|
||||
, mAppendRunning(false)
|
||||
, mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
|
||||
, mSourceBufferAttributes(aAttributes)
|
||||
, mParentDecoder(new nsMainThreadPtrHolder<MediaSourceDecoder>(aParentDecoder, false /* strict */))
|
||||
, mMediaSourceDuration(mTaskQueue, Maybe<double>(), "TrackBuffersManager::mMediaSourceDuration (Mirror)")
|
||||
, mAbort(false)
|
||||
, mEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold",
|
||||
100 * (1 << 20)))
|
||||
, mEvictionOccurred(false)
|
||||
, mMonitor("TrackBuffersManager")
|
||||
, mAppendRunning(false)
|
||||
, mSegmentParserLoopRunning(false)
|
||||
{
|
||||
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()
|
||||
@ -142,7 +135,6 @@ TrackBuffersManager::AppendIncomingBuffer(IncomingBuffer aData)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
mIncomingBuffers.AppendElement(aData);
|
||||
mAbort = false;
|
||||
}
|
||||
|
||||
RefPtr<TrackBuffersManager::AppendPromise>
|
||||
@ -151,49 +143,42 @@ TrackBuffersManager::BufferAppend()
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("");
|
||||
|
||||
mAppendRunning = true;
|
||||
return InvokeAsync(GetTaskQueue(), this,
|
||||
__func__, &TrackBuffersManager::InitSegmentParserLoop);
|
||||
}
|
||||
|
||||
// Abort any pending AppendData.
|
||||
// We don't really care about really aborting our inner loop as by spec the
|
||||
// process is happening asynchronously, as such where and when we would abort is
|
||||
// non-deterministic. The SourceBuffer also makes sure BufferAppend
|
||||
// isn't called should the appendBuffer be immediately aborted.
|
||||
// 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.
|
||||
// The MSE spec requires that we abort the current SegmentParserLoop
|
||||
// which is then followed by a call to ResetParserState.
|
||||
// However due to our asynchronous design this causes inherent difficulities.
|
||||
// As the spec behaviour is non deterministic anyway, we instead wait until the
|
||||
// current AppendData has completed its run.
|
||||
void
|
||||
TrackBuffersManager::AbortAppendData()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("");
|
||||
|
||||
mAbort = true;
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
while (mAppendRunning) {
|
||||
mon.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffersManager::ResetParserState()
|
||||
{
|
||||
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("");
|
||||
|
||||
// 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) {
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethod(this, &TrackBuffersManager::FinishCodedFrameProcessing);
|
||||
GetTaskQueue()->Dispatch(task.forget());
|
||||
} else {
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethod(this, &TrackBuffersManager::CompleteResetParserState);
|
||||
GetTaskQueue()->Dispatch(task.forget());
|
||||
}
|
||||
// SourceBuffer.abort() has ensured that all complete coded frames have been
|
||||
// processed. As such, we don't need to check for the value of mAppendState.
|
||||
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.
|
||||
SetAppendState(AppendState::WAITING_FOR_SEGMENT);
|
||||
}
|
||||
@ -202,6 +187,7 @@ RefPtr<TrackBuffersManager::RangeRemovalPromise>
|
||||
TrackBuffersManager::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running");
|
||||
MSE_DEBUG("From %.2f to %.2f", aStart.ToSeconds(), aEnd.ToSeconds());
|
||||
|
||||
mEnded = false;
|
||||
@ -309,54 +295,13 @@ TrackBuffersManager::Detach()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
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
|
||||
TrackBuffersManager::CompleteResetParserState()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(!mAppendRunning);
|
||||
MOZ_RELEASE_ASSERT(!mSegmentParserLoopRunning);
|
||||
MSE_DEBUG("");
|
||||
|
||||
for (auto& track : GetTracksList()) {
|
||||
@ -492,19 +437,11 @@ bool
|
||||
TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval)
|
||||
{
|
||||
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",
|
||||
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
|
||||
MSE_DEBUG("duration:%.2f", duration.ToSeconds());
|
||||
if (HasVideo()) {
|
||||
MSE_DEBUG("before video ranges=%s",
|
||||
DumpTimeRanges(mVideoTracks.mBufferedRanges).get());
|
||||
@ -527,7 +464,14 @@ TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval)
|
||||
MSE_DEBUGV("Processing %s track", track->mInfo->mMimeType.get());
|
||||
// 1. Let remove end timestamp be the current value of duration
|
||||
// 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,
|
||||
// then update remove end timestamp to that random access point timestamp.
|
||||
@ -595,8 +539,9 @@ RefPtr<TrackBuffersManager::AppendPromise>
|
||||
TrackBuffersManager::InitSegmentParserLoop()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_RELEASE_ASSERT(mAppendPromise.IsEmpty());
|
||||
MSE_DEBUG("");
|
||||
|
||||
MOZ_ASSERT(mAppendPromise.IsEmpty() && !mAppendRunning);
|
||||
RefPtr<AppendPromise> p = mAppendPromise.Ensure(__func__);
|
||||
|
||||
AppendIncomingBuffers();
|
||||
@ -630,6 +575,9 @@ void
|
||||
TrackBuffersManager::SegmentParserLoop()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
mSegmentParserLoopRunning = true;
|
||||
|
||||
while (true) {
|
||||
// 1. If the input buffer is empty, then jump to the need more data step below.
|
||||
if (!mInputBuffer || mInputBuffer->IsEmpty()) {
|
||||
@ -733,7 +681,7 @@ TrackBuffersManager::SegmentParserLoop()
|
||||
->Then(GetTaskQueue(), __func__,
|
||||
[self] (bool aNeedMoreData) {
|
||||
self->mProcessingRequest.Complete();
|
||||
if (aNeedMoreData || self->mAbort) {
|
||||
if (aNeedMoreData) {
|
||||
self->NeedMoreData();
|
||||
} else {
|
||||
self->ScheduleSegmentParserLoop();
|
||||
@ -752,10 +700,14 @@ void
|
||||
TrackBuffersManager::NeedMoreData()
|
||||
{
|
||||
MSE_DEBUG("");
|
||||
if (!mAbort) {
|
||||
RestoreCachedVariables();
|
||||
}
|
||||
RestoreCachedVariables();
|
||||
mAppendRunning = false;
|
||||
mSegmentParserLoopRunning = false;
|
||||
{
|
||||
// Wake-up any pending Abort()
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mon.NotifyAll();
|
||||
}
|
||||
mAppendPromise.ResolveIfExists(mActiveTrack, __func__);
|
||||
}
|
||||
|
||||
@ -764,6 +716,12 @@ TrackBuffersManager::RejectAppend(nsresult aRejectValue, const char* aName)
|
||||
{
|
||||
MSE_DEBUG("rv=%d", aRejectValue);
|
||||
mAppendRunning = false;
|
||||
mSegmentParserLoopRunning = false;
|
||||
{
|
||||
// Wake-up any pending Abort()
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mon.NotifyAll();
|
||||
}
|
||||
mAppendPromise.RejectIfExists(aRejectValue, aName);
|
||||
}
|
||||
|
||||
@ -840,12 +798,7 @@ void
|
||||
TrackBuffersManager::OnDemuxerResetDone(nsresult)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
|
||||
mDemuxerInitRequest.Complete();
|
||||
if (mAbort) {
|
||||
RejectAppend(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
// mInputDemuxer shouldn't have been destroyed while a demuxer init/reset
|
||||
// request was being processed. See bug 1239983.
|
||||
MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer);
|
||||
@ -917,13 +870,8 @@ void
|
||||
TrackBuffersManager::OnDemuxerInitDone(nsresult)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
|
||||
mDemuxerInitRequest.Complete();
|
||||
|
||||
if (mAbort) {
|
||||
RejectAppend(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
// mInputDemuxer shouldn't have been destroyed while a demuxer init/reset
|
||||
// request was being processed. See bug 1239983.
|
||||
MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer);
|
||||
@ -1183,9 +1131,8 @@ TrackBuffersManager::OnDemuxFailed(TrackType aTrack,
|
||||
DemuxerFailureReason aFailure)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("Failed to demux %s, failure:%d mAbort:%d",
|
||||
aTrack == TrackType::kVideoTrack ? "video" : "audio",
|
||||
aFailure, static_cast<bool>(mAbort));
|
||||
MSE_DEBUG("Failed to demux %s, failure:%d",
|
||||
aTrack == TrackType::kVideoTrack ? "video" : "audio", aFailure);
|
||||
switch (aFailure) {
|
||||
case DemuxerFailureReason::END_OF_STREAM:
|
||||
case DemuxerFailureReason::WAITING_FOR_DATA:
|
||||
@ -1212,15 +1159,10 @@ void
|
||||
TrackBuffersManager::DoDemuxVideo()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
|
||||
if (!HasVideo()) {
|
||||
DoDemuxAudio();
|
||||
return;
|
||||
}
|
||||
if (mAbort) {
|
||||
RejectProcessing(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
mVideoTracks.mDemuxRequest.Begin(mVideoTracks.mDemuxer->GetSamples(-1)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&TrackBuffersManager::OnVideoDemuxCompleted,
|
||||
@ -1241,15 +1183,10 @@ void
|
||||
TrackBuffersManager::DoDemuxAudio()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
|
||||
if (!HasAudio()) {
|
||||
CompleteCodedFrameProcessing();
|
||||
return;
|
||||
}
|
||||
if (mAbort) {
|
||||
RejectProcessing(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
mAudioTracks.mDemuxRequest.Begin(mAudioTracks.mDemuxer->GetSamples(-1)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&TrackBuffersManager::OnAudioDemuxCompleted,
|
||||
@ -1270,7 +1207,6 @@ void
|
||||
TrackBuffersManager::CompleteCodedFrameProcessing()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
|
||||
|
||||
// 1. For each coded frame in the media segment run the following steps:
|
||||
// Coded Frame Processing steps 1.1 to 1.21.
|
||||
@ -1341,22 +1277,12 @@ TrackBuffersManager::CompleteCodedFrameProcessing()
|
||||
void
|
||||
TrackBuffersManager::RejectProcessing(nsresult aRejectValue, const char* aName)
|
||||
{
|
||||
if (mAbort) {
|
||||
// mAppendPromise will be resolved immediately upon mProcessingPromise
|
||||
// completing.
|
||||
mAppendRunning = false;
|
||||
}
|
||||
mProcessingPromise.RejectIfExists(aRejectValue, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffersManager::ResolveProcessing(bool aResolveValue, const char* aName)
|
||||
{
|
||||
if (mAbort) {
|
||||
// mAppendPromise will be resolved immediately upon mProcessingPromise
|
||||
// completing.
|
||||
mAppendRunning = false;
|
||||
}
|
||||
mProcessingPromise.ResolveIfExists(aResolveValue, __func__);
|
||||
}
|
||||
|
||||
|
@ -98,10 +98,6 @@ public:
|
||||
bool& aError);
|
||||
media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack);
|
||||
|
||||
#if defined(DEBUG)
|
||||
void Dump(const char* aPath) override;
|
||||
#endif
|
||||
|
||||
void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes);
|
||||
|
||||
private:
|
||||
@ -123,9 +119,7 @@ private:
|
||||
// media segment have been processed.
|
||||
RefPtr<CodedFrameProcessingPromise> CodedFrameProcessing();
|
||||
void CompleteCodedFrameProcessing();
|
||||
// Called by ResetParserState. Complete parsing the input buffer for the
|
||||
// current media segment.
|
||||
void FinishCodedFrameProcessing();
|
||||
// Called by ResetParserState.
|
||||
void CompleteResetParserState();
|
||||
RefPtr<RangeRemovalPromise>
|
||||
CodedFrameRemovalWithPromise(media::TimeInterval aInterval);
|
||||
@ -310,10 +304,6 @@ private:
|
||||
MozPromiseHolder<CodedFrameProcessingPromise> mProcessingPromise;
|
||||
|
||||
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.
|
||||
nsTArray<TrackData*> GetTracksList();
|
||||
@ -349,11 +339,6 @@ private:
|
||||
RefPtr<dom::SourceBufferAttributes> mSourceBufferAttributes;
|
||||
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.
|
||||
Atomic<bool> mEnded;
|
||||
|
||||
@ -363,7 +348,13 @@ private:
|
||||
Atomic<bool> mEvictionOccurred;
|
||||
|
||||
// Monitor to protect following objects accessed across multipple threads.
|
||||
// mMonitor is also notified if the value of mAppendRunning becomes false.
|
||||
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.
|
||||
media::TimeIntervals mVideoBufferedRanges;
|
||||
media::TimeIntervals mAudioBufferedRanges;
|
||||
|
@ -126,6 +126,8 @@ var interfaceNamesInGlobalScope =
|
||||
{name: "Animation", release: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{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!
|
||||
{name: "AnimationEffectTimingReadOnly", release: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
@ -727,6 +729,8 @@ var interfaceNamesInGlobalScope =
|
||||
"KeyboardEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{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!
|
||||
"LocalMediaStream",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
31
dom/webidl/AnimationEffectTiming.webidl
Normal file
31
dom/webidl/AnimationEffectTiming.webidl
Normal 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;
|
||||
};
|
@ -52,10 +52,7 @@ dictionary ConsoleEvent {
|
||||
DOMString functionName = "";
|
||||
double timeStamp = 0;
|
||||
sequence<any> arguments;
|
||||
|
||||
// This array will only hold strings or null elements.
|
||||
sequence<any> styles;
|
||||
|
||||
sequence<DOMString?> styles;
|
||||
boolean private = false;
|
||||
// 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
|
||||
|
@ -4,7 +4,7 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* 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
|
||||
* liability, trademark and document use rules apply.
|
||||
@ -44,3 +44,20 @@ interface KeyframeEffectReadOnly : AnimationEffectReadOnly {
|
||||
// property-value pairs on the object.
|
||||
[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);
|
||||
};
|
||||
|
@ -190,7 +190,7 @@ Navigator implements NavigatorMobileId;
|
||||
|
||||
// nsIDOMNavigator
|
||||
partial interface Navigator {
|
||||
[Throws]
|
||||
[Throws, Constant, Cached]
|
||||
readonly attribute DOMString oscpu;
|
||||
// WebKit/Blink support this; Trident/Presto do not.
|
||||
readonly attribute DOMString vendor;
|
||||
@ -200,7 +200,7 @@ partial interface Navigator {
|
||||
readonly attribute DOMString productSub;
|
||||
// WebKit/Blink/Trident/Presto support this.
|
||||
readonly attribute boolean cookieEnabled;
|
||||
[Throws]
|
||||
[Throws, Constant, Cached]
|
||||
readonly attribute DOMString buildID;
|
||||
[Throws, CheckAnyPermissions="power", UnsafeInPrerendering]
|
||||
readonly attribute MozPowerManager mozPower;
|
||||
|
@ -25,6 +25,7 @@ WEBIDL_FILES = [
|
||||
'Animatable.webidl',
|
||||
'Animation.webidl',
|
||||
'AnimationEffectReadOnly.webidl',
|
||||
'AnimationEffectTiming.webidl',
|
||||
'AnimationEffectTimingReadOnly.webidl',
|
||||
'AnimationEvent.webidl',
|
||||
'AnimationTimeline.webidl',
|
||||
|
@ -886,23 +886,10 @@ class WorkerJSRuntime : public mozilla::CycleCollectedJSRuntime
|
||||
public:
|
||||
// The heap size passed here doesn't matter, we will change it later in the
|
||||
// call to JS_SetGCParameter inside CreateJSContextForWorker.
|
||||
WorkerJSRuntime(JSRuntime* aParentRuntime, WorkerPrivate* aWorkerPrivate)
|
||||
: CycleCollectedJSRuntime(aParentRuntime,
|
||||
WORKER_DEFAULT_RUNTIME_HEAPSIZE,
|
||||
WORKER_DEFAULT_NURSERY_SIZE),
|
||||
mWorkerPrivate(aWorkerPrivate)
|
||||
explicit WorkerJSRuntime(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
{
|
||||
JSRuntime* rt = Runtime();
|
||||
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);
|
||||
}
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
}
|
||||
|
||||
~WorkerJSRuntime()
|
||||
@ -924,6 +911,31 @@ public:
|
||||
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
|
||||
PrepareForForgetSkippable() override
|
||||
{
|
||||
@ -2695,7 +2707,12 @@ WorkerThreadPrimaryRunnable::Run()
|
||||
{
|
||||
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();
|
||||
|
||||
JSContext* cx = CreateJSContextForWorker(mWorkerPrivate, rt);
|
||||
|
@ -2391,6 +2391,7 @@ nsWebBrowserPersist::FixRedirectedChannelEntry(nsIChannel *aNewChannel)
|
||||
// matching the one specified.
|
||||
nsCOMPtr<nsIURI> originalURI;
|
||||
aNewChannel->GetOriginalURI(getter_AddRefs(originalURI));
|
||||
nsISupports* matchingKey = nullptr;
|
||||
for (auto iter = mOutputMap.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsISupports* key = iter.Key();
|
||||
nsCOMPtr<nsIChannel> thisChannel = do_QueryInterface(key);
|
||||
@ -2402,21 +2403,25 @@ nsWebBrowserPersist::FixRedirectedChannelEntry(nsIChannel *aNewChannel)
|
||||
bool matchingURI = false;
|
||||
thisURI->Equals(originalURI, &matchingURI);
|
||||
if (matchingURI) {
|
||||
// If a match is found, remove the data entry with the old channel
|
||||
// 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());
|
||||
}
|
||||
|
||||
matchingKey = key;
|
||||
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;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user