Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-12-15 14:55:45 +01:00
commit 31876c7e82
365 changed files with 3465 additions and 3720 deletions

View File

@ -858,7 +858,7 @@ public:
bool IsDefunct() const { return mStateFlags & eIsDefunct; }
/**
* Return true if the accessible is no longer in the document.
* Return false if the accessible is no longer in the document.
*/
bool IsInDocument() const { return !(mStateFlags & eIsNotInDocument); }

View File

@ -1983,7 +1983,8 @@ DocAccessible::ValidateARIAOwned()
nsTArray<RefPtr<Accessible> >* children = it.UserData();
// Owner is about to die, put children back if applicable.
if (!owner->IsInDocument()) {
if (!mAccessibleCache.GetWeak(reinterpret_cast<void*>(owner)) ||
!owner->IsInDocument()) {
PutChildrenBack(children, 0);
it.Remove();
continue;

View File

@ -1336,7 +1336,7 @@ BrowserGlue.prototype = {
if (AppConstants.E10S_TESTING_ONLY) {
E10SUINotification.checkStatus();
}
if (AppConstants.XP_WIN) {
if (AppConstants.platform == "win") {
// Handles prompting to inform about incompatibilites when accessibility
// and e10s are active together.
E10SAccessibilityCheck.init();
@ -3087,6 +3087,26 @@ var E10SUINotification = {
CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1",
PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt",
get forcedOn() {
try {
return Services.prefs.getBoolPref("browser.tabs.remote.force-enable");
} catch (e) {}
return false;
},
get a11yRecentlyRan() {
try {
if (Services.prefs.getBoolPref("accessibility.loadedInLastSession")) {
return true;
}
} catch (e) {}
try {
Services.prefs.getBoolPref("accessibility.lastLoadDate");
return true;
} catch (e) {}
return false;
},
checkStatus: function() {
let updateChannel = UpdateUtils.UpdateChannel;
let channelAuthorized = updateChannel == "nightly" || updateChannel == "aurora";
@ -3095,6 +3115,9 @@ var E10SUINotification = {
}
if (Services.appinfo.browserTabsRemoteAutostart) {
if (this.forcedOn) {
return;
}
let notice = 0;
try {
notice = Services.prefs.getIntPref("browser.displayedE10SNotice");
@ -3129,6 +3152,11 @@ var E10SUINotification = {
return;
}
// If accessibility recently ran, don't prompt about trying out e10s
if (this.a11yRecentlyRan) {
return;
}
let e10sPromptShownCount = 0;
try {
e10sPromptShownCount = Services.prefs.getIntPref(this.CURRENT_PROMPT_PREF);

View File

@ -767,7 +767,7 @@ e10s.postActivationInfobar.message = You're now helping to test multi-process in
e10s.postActivationInfobar.learnMore.label = Learn More
e10s.postActivationInfobar.learnMore.accesskey = L
e10s.accessibilityNotice.mainMessage2 = Accessibility support is partially disabled due to compatibility issues with new %S features.
e10s.accessibilityNotice.acceptButton.label = Ok
e10s.accessibilityNotice.acceptButton.label = OK
e10s.accessibilityNotice.acceptButton.accesskey = O
e10s.accessibilityNotice.enableAndRestart.label = Enable (Requires Restart)
e10s.accessibilityNotice.enableAndRestart.accesskey = E

View File

@ -5,7 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 1048576; // 1 MB in bytes
const SOURCE_URL_DEFAULT_MAX_LENGTH = 64; // chars
const STACK_FRAMES_SOURCE_URL_MAX_LENGTH = 15; // chars
const STACK_FRAMES_SOURCE_URL_TRIM_SECTION = "center";
@ -47,6 +46,13 @@ var constants = require('./content/constants');
* Object defining the debugger view components.
*/
var DebuggerView = {
/**
* This is attached so tests can change it without needing to load an
* actual large file in automation
*/
LARGE_FILE_SIZE: 1048576, // 1 MB in bytes
/**
* Initializes the debugger view.
*
@ -408,12 +414,6 @@ var DebuggerView = {
* The source text content.
*/
_setEditorMode: function(aUrl, aContentType = "", aTextContent = "") {
// Avoid setting the editor mode for very large files.
// Is this still necessary? See bug 929225.
if (aTextContent.length >= SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE) {
return void this.editor.setMode(Editor.modes.text);
}
// Use JS mode for files with .js and .jsm extensions.
if (SourceUtils.isJavaScript(aUrl, aContentType)) {
return void this.editor.setMode(Editor.modes.js);

View File

@ -446,6 +446,7 @@ skip-if = true # non-named eval sources turned off for now, bug 1124106
skip-if = e10s && debug
[browser_dbg_sources-labels.js]
skip-if = e10s && debug
[browser_dbg_sources-large.js]
[browser_dbg_sources-sorting.js]
skip-if = e10s && debug
[browser_dbg_sources-bookmarklet.js]

View File

@ -0,0 +1,75 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that large files are treated differently in the debugger:
* 1) No parsing to determine current symbol is attempted when
* starting a search
*/
const TAB_URL = EXAMPLE_URL + "doc_function-search.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
const gTab = aTab;
const gDebuggee = aDebuggee;
const gPanel = aPanel;
const gDebugger = gPanel.panelWin;
const gEditor = gDebugger.DebuggerView.editor;
const gSources = gDebugger.DebuggerView.Sources;
const Filtering = gDebugger.DebuggerView.Filtering;
// Setting max size so that code_function-search-01.js will be
// considered a large file on first load
gDebugger.DebuggerView.LARGE_FILE_SIZE = 1;
function testLargeFile() {
ok(gEditor.getText().length > gDebugger.DebuggerView.LARGE_FILE_SIZE,
"First source is considered a large file.");
is(gEditor.getMode().name, "javascript",
"Editor is syntax highlighting.");
ok(gEditor.getText().includes("First source!"),
"Editor text contents appears to be correct.");
// Press ctrl+f with the cursor in a token
gEditor.focus();
gEditor.setCursor({ line: 3, ch: 10});
synthesizeKeyFromKeyTag(gDebugger.document.getElementById("tokenSearchKey"));
is(Filtering._searchbox.value, "#",
"Search box is NOT prefilled with current token");
}
function testSmallFile() {
ok(gEditor.getText().length < gDebugger.DebuggerView.LARGE_FILE_SIZE,
"Second source is considered a small file.");
is(gEditor.getMode().name, "javascript",
"Editor is syntax highlighting.");
ok(gEditor.getText().includes("First source!"),
"Editor text contents appears to be correct.");
// Press ctrl+f with the cursor in a token
gEditor.focus();
gEditor.setCursor({ line: 3, ch: 10});
synthesizeKeyFromKeyTag(gDebugger.document.getElementById("tokenSearchKey"));
is(Filtering._searchbox.value, "#test",
"Search box is prefilled with current token");
}
Task.spawn(function*() {
yield waitForSourceShown(gPanel, "-01.js");
yield testLargeFile();
info("Making it appear as a small file and then reselecting 01.js");
gDebugger.DebuggerView.LARGE_FILE_SIZE = 1000;
gSources.selectedIndex = 1;
yield waitForSourceShown(gPanel, "-02.js");
gSources.selectedIndex = 0;
yield waitForSourceShown(gPanel, "-01.js");
yield testSmallFile();
closeDebuggerAndFinish(gPanel);
});
});
}

View File

@ -443,9 +443,11 @@ FilterView.prototype = {
this._searchbox.value = aOperator + this.DebuggerView.editor.getSelection();
return;
}
if (SEARCH_AUTOFILL.indexOf(aOperator) != -1) {
let content = this.DebuggerView.editor.getText();
if (content.length < this.DebuggerView.LARGE_FILE_SIZE &&
SEARCH_AUTOFILL.indexOf(aOperator) != -1) {
let cursor = this.DebuggerView.editor.getCursor();
let content = this.DebuggerView.editor.getText();
let location = this.DebuggerView.Sources.selectedItem.attachment.source.url;
let source = this.Parser.get(content, location);
let identifier = source.getIdentifierAt({ line: cursor.line+1, column: cursor.ch });

View File

@ -24,7 +24,7 @@
<!-- LOCALIZATION NOTE (netmonitorUI.toolbar.status2): This is the label displayed
- in the network table toolbar, above the "status" column. -->
<!ENTITY netmonitorUI.toolbar.status2 "✓">
<!ENTITY netmonitorUI.toolbar.status3 "Status">
<!-- LOCALIZATION NOTE (netmonitorUI.toolbar.method): This is the label displayed
- in the network table toolbar, above the "method" column. -->

View File

@ -39,8 +39,7 @@
#details-pane-toggle,
#details-pane[pane-collapsed],
.requests-menu-waterfall,
.requests-menu-footer-label,
.requests-menu-status-code {
.requests-menu-footer-label {
display: none;
}
}

View File

@ -107,7 +107,7 @@
<button id="requests-menu-status-button"
class="requests-menu-header-button requests-menu-status"
data-key="status"
label="&netmonitorUI.toolbar.status2;"
label="&netmonitorUI.toolbar.status3;"
flex="1">
</button>
</hbox>

View File

@ -9,7 +9,7 @@ function test() {
initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... ");
let { document, L10N, NetMonitorView } = aMonitor.panelWin;
let { document, L10N, NetMonitorView, NetMonitorController } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
// Disable transferred size column support for this test.
@ -31,6 +31,11 @@ function test() {
"No 2d context should be created when the frontend is opened.");
waitForNetworkEvents(aMonitor, 1).then(() => {
// Make sure the DOMContentLoaded and load markers don't interfere with
// this test by removing them and redrawing the waterfall (bug 1224088).
NetMonitorController.NetworkEventsHandler.clearMarkers();
RequestsMenu._flushWaterfallViews(true);
ok(!document.querySelector("#requests-menu-waterfall-label"),
"The timeline label should be hidden after the first request.");
ok(document.querySelectorAll(".requests-menu-timings-division").length >= 3,

View File

@ -235,7 +235,8 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
cell.className = "plain call-tree-cell";
cell.setAttribute("type", type);
cell.setAttribute("crop", "end");
cell.setAttribute("value", value);
// Add a tabulation to the cell text in case it's is selected and copied.
cell.textContent = value + "\t";
return cell;
},
@ -261,7 +262,7 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
nameNode.className = "plain call-tree-name";
nameNode.setAttribute("flex", "1");
nameNode.setAttribute("crop", "end");
nameNode.setAttribute("value", frameName);
nameNode.textContent = frameName;
cell.appendChild(nameNode);
}
@ -276,6 +277,17 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
arrowNode.setAttribute("invisible", "");
}
// Add a line break to the last description of the row in case it's selected
// and copied.
let lastDescription = cell.querySelector('description:last-of-type');
lastDescription.textContent = lastDescription.textContent + "\n";
// Add spaces as frameLevel indicators in case the row is selected and
// copied. These spaces won't be displayed in the cell content.
let firstDescription = cell.querySelector('description:first-of-type');
let levelIndicator = frameLevel > 0 ? " ".repeat(frameLevel) : "";
firstDescription.textContent = levelIndicator + firstDescription.textContent;
return cell;
},
@ -285,7 +297,7 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
urlNode.className = "plain call-tree-url";
urlNode.setAttribute("flex", "1");
urlNode.setAttribute("crop", "end");
urlNode.setAttribute("value", frameInfo.fileName);
urlNode.textContent = frameInfo.fileName;
urlNode.setAttribute("tooltiptext", URL_LABEL_TOOLTIP + " → " + frameInfo.url);
urlNode.addEventListener("mousedown", this._onUrlClick);
cell.appendChild(urlNode);
@ -294,21 +306,21 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
if (frameInfo.line) {
let lineNode = doc.createElement("description");
lineNode.className = "plain call-tree-line";
lineNode.setAttribute("value", ":" + frameInfo.line);
lineNode.textContent = ":" + frameInfo.line;
cell.appendChild(lineNode);
}
if (frameInfo.column) {
let columnNode = doc.createElement("description");
columnNode.className = "plain call-tree-column";
columnNode.setAttribute("value", ":" + frameInfo.column);
columnNode.textContent = ":" + frameInfo.column;
cell.appendChild(columnNode);
}
if (frameInfo.host) {
let hostNode = doc.createElement("description");
hostNode.className = "plain call-tree-host";
hostNode.setAttribute("value", frameInfo.host);
hostNode.textContent = frameInfo.host;
cell.appendChild(hostNode);
}
@ -320,7 +332,7 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
let categoryNode = doc.createElement("description");
categoryNode.className = "plain call-tree-category";
categoryNode.style.color = frameInfo.categoryData.color;
categoryNode.setAttribute("value", frameInfo.categoryData.label);
categoryNode.textContent = frameInfo.categoryData.label;
cell.appendChild(categoryNode);
}
},

View File

@ -7,7 +7,7 @@
*/
function* spawnTest() {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, $, DetailsView, JsCallTreeView } = panel.panelWin;
let { EVENTS, $, $$, DetailsView, JsCallTreeView } = panel.panelWin;
// Enable platform data to show the categories.
Services.prefs.setBoolPref(PLATFORM_DATA_PREF, true);
@ -22,17 +22,26 @@ function* spawnTest() {
is($(".call-tree-cells-container").hasAttribute("categories-hidden"), false,
"The call tree cells container should show the categories now.");
ok($(".call-tree-category[value=Gecko]"),
"A category node with the label `Gecko` is displayed in the tree.");
ok(geckoCategoryPresent($$),
"A category node with the text `Gecko` is displayed in the tree.");
// Disable platform data to show the categories.
Services.prefs.setBoolPref(PLATFORM_DATA_PREF, false);
is($(".call-tree-cells-container").getAttribute("categories-hidden"), "",
"The call tree cells container should hide the categories now.");
ok(!$(".call-tree-category[value=Gecko]"),
"A category node with the label `Gecko` doesn't exist in the tree anymore.");
ok(!geckoCategoryPresent($$),
"A category node with the text `Gecko` doesn't exist in the tree anymore.");
yield teardown(panel);
finish();
}
function geckoCategoryPresent($$) {
for (let elem of $$('.call-tree-category')) {
if (elem.textContent.trim() == 'Gecko') {
return true
}
}
return false
}

View File

@ -31,27 +31,27 @@ function test() {
is(container.childNodes[0].childNodes[0].getAttribute("type"), "duration",
"The root node in the tree has a duration cell.");
is(container.childNodes[0].childNodes[0].getAttribute("value"), "20 ms",
is(container.childNodes[0].childNodes[0].textContent.trim(), "20 ms",
"The root node in the tree has the correct duration cell value.");
is(container.childNodes[0].childNodes[1].getAttribute("type"), "percentage",
"The root node in the tree has a percentage cell.");
is(container.childNodes[0].childNodes[1].getAttribute("value"), "100%",
is(container.childNodes[0].childNodes[1].textContent.trim(), "100%",
"The root node in the tree has the correct percentage cell value.");
is(container.childNodes[0].childNodes[2].getAttribute("type"), "self-duration",
"The root node in the tree has a self-duration cell.");
is(container.childNodes[0].childNodes[2].getAttribute("value"), "0 ms",
is(container.childNodes[0].childNodes[2].textContent.trim(), "0 ms",
"The root node in the tree has the correct self-duration cell value.");
is(container.childNodes[0].childNodes[3].getAttribute("type"), "self-percentage",
"The root node in the tree has a self-percentage cell.");
is(container.childNodes[0].childNodes[3].getAttribute("value"), "0%",
is(container.childNodes[0].childNodes[3].textContent.trim(), "0%",
"The root node in the tree has the correct self-percentage cell value.");
is(container.childNodes[0].childNodes[4].getAttribute("type"), "samples",
"The root node in the tree has an samples cell.");
is(container.childNodes[0].childNodes[4].getAttribute("value"), "0",
is(container.childNodes[0].childNodes[4].textContent.trim(), "0",
"The root node in the tree has the correct samples cell value.");
is(container.childNodes[0].childNodes[5].getAttribute("type"), "function",

View File

@ -33,13 +33,13 @@ function test() {
is(container.childNodes[0].className, "call-tree-item",
"The root node in the tree has the correct class name.");
is($$dur(0).getAttribute("value"), "20 ms",
is($$dur(0).textContent.trim(), "20 ms",
"The root's duration cell displays the correct value.");
is($$perc(0).getAttribute("value"), "100%",
is($$perc(0).textContent.trim(), "100%",
"The root's percentage cell displays the correct value.");
is($$sampl(0).getAttribute("value"), "0",
is($$sampl(0).textContent.trim(), "0",
"The root's samples cell displays the correct value.");
is($$fun(".call-tree-name")[0].getAttribute("value"), "(root)",
is($$fun(".call-tree-name")[0].textContent.trim(), "(root)",
"The root's function cell displays the correct name.");
is($$fun(".call-tree-url")[0], null,
"The root's function cell displays no url.");
@ -59,23 +59,23 @@ function test() {
is(container.childNodes[1].className, "call-tree-item",
"The .A node in the tree has the correct class name.");
is($$dur(1).getAttribute("value"), "20 ms",
is($$dur(1).textContent.trim(), "20 ms",
"The .A node's duration cell displays the correct value.");
is($$perc(1).getAttribute("value"), "100%",
is($$perc(1).textContent.trim(), "100%",
"The .A node's percentage cell displays the correct value.");
is($$sampl(1).getAttribute("value"), "0",
is($$sampl(1).textContent.trim(), "0",
"The .A node's samples cell displays the correct value.");
is($fun(".call-tree-name", $$(".call-tree-item")[1]).getAttribute("value"), "A",
is($fun(".call-tree-name", $$(".call-tree-item")[1]).textContent.trim(), "A",
"The .A node's function cell displays the correct name.");
is($fun(".call-tree-url", $$(".call-tree-item")[1]).getAttribute("value"), "baz",
is($fun(".call-tree-url", $$(".call-tree-item")[1]).textContent.trim(), "baz",
"The .A node's function cell displays the correct url.");
ok($fun(".call-tree-url", $$(".call-tree-item")[1]).getAttribute("tooltiptext").includes("http://foo/bar/baz"),
"The .A node's function cell displays the correct url tooltiptext.");
is($fun(".call-tree-line", $$(".call-tree-item")[1]).getAttribute("value"), ":12",
is($fun(".call-tree-line", $$(".call-tree-item")[1]).textContent.trim(), ":12",
"The .A node's function cell displays the correct line.");
is($fun(".call-tree-host", $$(".call-tree-item")[1]).getAttribute("value"), "foo",
is($fun(".call-tree-host", $$(".call-tree-item")[1]).textContent.trim(), "foo",
"The .A node's function cell displays the correct host.");
is($fun(".call-tree-category", $$(".call-tree-item")[1]).getAttribute("value"), "Gecko",
is($fun(".call-tree-category", $$(".call-tree-item")[1]).textContent.trim(), "Gecko",
"The .A node's function cell displays the correct category.");
let A = treeRoot.getChild();
@ -88,42 +88,42 @@ function test() {
is(container.childNodes[3].className, "call-tree-item",
"The .E node in the tree has the correct class name.");
is($$dur(2).getAttribute("value"), "15 ms",
is($$dur(2).textContent.trim(), "15 ms",
"The .A.B node's duration cell displays the correct value.");
is($$perc(2).getAttribute("value"), "75%",
is($$perc(2).textContent.trim(), "75%",
"The .A.B node's percentage cell displays the correct value.");
is($$sampl(2).getAttribute("value"), "0",
is($$sampl(2).textContent.trim(), "0",
"The .A.B node's samples cell displays the correct value.");
is($fun(".call-tree-name", $$(".call-tree-item")[2]).getAttribute("value"), "B",
is($fun(".call-tree-name", $$(".call-tree-item")[2]).textContent.trim(), "B",
"The .A.B node's function cell displays the correct name.");
is($fun(".call-tree-url", $$(".call-tree-item")[2]).getAttribute("value"), "baz",
is($fun(".call-tree-url", $$(".call-tree-item")[2]).textContent.trim(), "baz",
"The .A.B node's function cell displays the correct url.");
ok($fun(".call-tree-url", $$(".call-tree-item")[2]).getAttribute("tooltiptext").includes("http://foo/bar/baz"),
"The .A.B node's function cell displays the correct url tooltiptext.");
is($fun(".call-tree-line", $$(".call-tree-item")[2]).getAttribute("value"), ":34",
is($fun(".call-tree-line", $$(".call-tree-item")[2]).textContent.trim(), ":34",
"The .A.B node's function cell displays the correct line.");
is($fun(".call-tree-host", $$(".call-tree-item")[2]).getAttribute("value"), "foo",
is($fun(".call-tree-host", $$(".call-tree-item")[2]).textContent.trim(), "foo",
"The .A.B node's function cell displays the correct host.");
is($fun(".call-tree-category", $$(".call-tree-item")[2]).getAttribute("value"), "Styles",
is($fun(".call-tree-category", $$(".call-tree-item")[2]).textContent.trim(), "Styles",
"The .A.B node's function cell displays the correct category.");
is($$dur(3).getAttribute("value"), "5 ms",
is($$dur(3).textContent.trim(), "5 ms",
"The .A.E node's duration cell displays the correct value.");
is($$perc(3).getAttribute("value"), "25%",
is($$perc(3).textContent.trim(), "25%",
"The .A.E node's percentage cell displays the correct value.");
is($$sampl(3).getAttribute("value"), "0",
is($$sampl(3).textContent.trim(), "0",
"The .A.E node's samples cell displays the correct value.");
is($fun(".call-tree-name", $$(".call-tree-item")[3]).getAttribute("value"), "E",
is($fun(".call-tree-name", $$(".call-tree-item")[3]).textContent.trim(), "E",
"The .A.E node's function cell displays the correct name.");
is($fun(".call-tree-url", $$(".call-tree-item")[3]).getAttribute("value"), "baz",
is($fun(".call-tree-url", $$(".call-tree-item")[3]).textContent.trim(), "baz",
"The .A.E node's function cell displays the correct url.");
ok($fun(".call-tree-url", $$(".call-tree-item")[3]).getAttribute("tooltiptext").includes("http://foo/bar/baz"),
"The .A.E node's function cell displays the correct url tooltiptext.");
is($fun(".call-tree-line", $$(".call-tree-item")[3]).getAttribute("value"), ":90",
is($fun(".call-tree-line", $$(".call-tree-item")[3]).textContent.trim(), ":90",
"The .A.E node's function cell displays the correct line.");
is($fun(".call-tree-host", $$(".call-tree-item")[3]).getAttribute("value"), "foo",
is($fun(".call-tree-host", $$(".call-tree-item")[3]).textContent.trim(), "foo",
"The .A.E node's function cell displays the correct host.");
is($fun(".call-tree-category", $$(".call-tree-item")[3]).getAttribute("value"), "GC",
is($fun(".call-tree-category", $$(".call-tree-item")[3]).textContent.trim(), "GC",
"The .A.E node's function cell displays the correct category.");
finish();

View File

@ -42,34 +42,34 @@ function test() {
is($$fun(6).style.MozMarginStart, "48px",
"The .A.E.F node's function cell has the correct indentation.");
is($$name(0).getAttribute("value"), "(root)",
is($$name(0).textContent.trim(), "(root)",
"The root node's function cell displays the correct name.");
is($$name(1).getAttribute("value"), "A",
is($$name(1).textContent.trim(), "A",
"The .A node's function cell displays the correct name.");
is($$name(2).getAttribute("value"), "B",
is($$name(2).textContent.trim(), "B",
"The .A.B node's function cell displays the correct name.");
is($$name(3).getAttribute("value"), "D",
is($$name(3).textContent.trim(), "D",
"The .A.B.D node's function cell displays the correct name.");
is($$name(4).getAttribute("value"), "C",
is($$name(4).textContent.trim(), "C",
"The .A.B.C node's function cell displays the correct name.");
is($$name(5).getAttribute("value"), "E",
is($$name(5).textContent.trim(), "E",
"The .A.E node's function cell displays the correct name.");
is($$name(6).getAttribute("value"), "F",
is($$name(6).textContent.trim(), "F",
"The .A.E.F node's function cell displays the correct name.");
is($$duration(0).getAttribute("value"), "20 ms",
is($$duration(0).textContent.trim(), "20 ms",
"The root node's function cell displays the correct duration.");
is($$duration(1).getAttribute("value"), "20 ms",
is($$duration(1).textContent.trim(), "20 ms",
"The .A node's function cell displays the correct duration.");
is($$duration(2).getAttribute("value"), "15 ms",
is($$duration(2).textContent.trim(), "15 ms",
"The .A.B node's function cell displays the correct duration.");
is($$duration(3).getAttribute("value"), "10 ms",
is($$duration(3).textContent.trim(), "10 ms",
"The .A.B.D node's function cell displays the correct duration.");
is($$duration(4).getAttribute("value"), "5 ms",
is($$duration(4).textContent.trim(), "5 ms",
"The .A.B.C node's function cell displays the correct duration.");
is($$duration(5).getAttribute("value"), "5 ms",
is($$duration(5).textContent.trim(), "5 ms",
"The .A.E node's function cell displays the correct duration.");
is($$duration(6).getAttribute("value"), "5 ms",
is($$duration(6).textContent.trim(), "5 ms",
"The .A.E.F node's function cell displays the correct duration.");
finish();
@ -108,4 +108,3 @@ var gThread = synthesizeProfileForTest([{
{ category: 256, location: "F (http://foo/bar/baz:99)" }
]
}]);

View File

@ -43,21 +43,21 @@ function test() {
"Generalized JS node has correct category");
is(JS.target.getAttribute("tooltiptext"), "JIT",
"Generalized JS node has correct category");
is(JS.target.querySelector(".call-tree-name").getAttribute("value"), "JIT",
is(JS.target.querySelector(".call-tree-name").textContent.trim(), "JIT",
"Generalized JS node has correct display value as just the category name.");
is(JS2.target.getAttribute("category"), "js",
"Generalized second JS node has correct category");
is(JS2.target.getAttribute("tooltiptext"), "JIT",
"Generalized second JS node has correct category");
is(JS2.target.querySelector(".call-tree-name").getAttribute("value"), "JIT",
is(JS2.target.querySelector(".call-tree-name").textContent.trim(), "JIT",
"Generalized second JS node has correct display value as just the category name.");
is(GC.target.getAttribute("category"), "gc",
"Generalized GC node has correct category");
is(GC.target.getAttribute("tooltiptext"), "GC",
"Generalized GC node has correct category");
is(GC.target.querySelector(".call-tree-name").getAttribute("value"), "GC",
is(GC.target.querySelector(".call-tree-name").textContent.trim(), "GC",
"Generalized GC node has correct display value as just the category name.");
finish();

View File

@ -63,9 +63,9 @@ function test() {
let [total, self, name] = def;
name = name.trim();
is($$name(i).getAttribute("value"), name, `${name} has correct name.`);
is($$percentage(i).getAttribute("value"), `${total}%`, `${name} has correct total percent.`);
is($$selfpercentage(i).getAttribute("value"), `${self}%`, `${name} has correct self percent.`);
is($$name(i).textContent.trim(), name, `${name} has correct name.`);
is($$percentage(i).textContent.trim(), `${total}%`, `${name} has correct total percent.`);
is($$selfpercentage(i).textContent.trim(), `${self}%`, `${name} has correct self percent.`);
});
finish();

View File

@ -31,7 +31,7 @@ function* spawnTest() {
let rows = $$("#js-calltree-view .call-tree-item");
is(rows.length, 4, "4 call tree rows exist");
for (let row of rows) {
let name = $(".call-tree-name", row).value;
let name = $(".call-tree-name", row).textContent.trim();
switch (name) {
case "A":
ok($(".opt-icon", row), "found an opt icon on a leaf node with opt data");

View File

@ -29,6 +29,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
var { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
var { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
var { LoadContextInfo } = Cu.import("resource://gre/modules/LoadContextInfo.jsm", {});
var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
var promise = require("promise");
@ -186,20 +187,17 @@ AppCacheUtils.prototype = {
.createInstance(Ci.nsIScriptableInputStream);
let deferred = promise.defer();
let buffer = "";
let channel = Services.io.newChannel2(uri,
null,
null,
null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_OTHER);
var channel = NetUtil.newChannel({
uri: uri,
loadUsingSystemPrincipal: true,
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
});
// Avoid the cache:
channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
channel.asyncOpen({
channel.asyncOpen2({
onStartRequest: function (request, context) {
// This empty method is needed in order for onDataAvailable to be
// called.
@ -245,7 +243,7 @@ AppCacheUtils.prototype = {
});
}
}
}, null);
});
return deferred.promise;
},

View File

@ -522,7 +522,6 @@ AbstractTreeItem.prototype = {
* Handler for the "click" event on the element displaying this tree item.
*/
_onClick: function(e) {
e.preventDefault();
e.stopPropagation();
this.focus();
},

View File

@ -116,11 +116,7 @@
/* Network requests table: specific column dimensions */
.requests-menu-status {
max-width: 4em;
width: 4vw;
}
.requests-menu-status,
.requests-menu-method-box,
.requests-menu-method {
max-width: 6em;
@ -183,10 +179,6 @@
width: 8vw;
}
.requests-menu-transferred {
width: 8vw;
}
/* Network requests table: status codes */
.requests-menu-status-code {
@ -795,11 +787,12 @@
}
.requests-menu-status {
width: 4vw;
max-width: none;
width: 12vw;
}
#requests-menu-status-button {
min-width: 26px;
.requests-menu-status-code {
width: auto;
}
.requests-menu-method,
@ -809,20 +802,20 @@
}
.requests-menu-icon-and-file {
width: 30vw;
width: 22vw;
}
.requests-menu-security-and-domain {
width: 28vw;
width: 18vw;
}
.requests-menu-type,
.requests-menu-transferred {
width: 12vw;
.requests-menu-type {
width: 10vw;
}
.requests-menu-transferred,
.requests-menu-size {
width: 16vw;
width: 12vw;
}
}

View File

@ -269,6 +269,16 @@
background-color: var(--theme-tab-toolbar-background);
}
.call-tree-item .call-tree-cell,
.call-tree-item .call-tree-cell[type=function] description {
-moz-user-select: text;
}
.call-tree-item .call-tree-cell::-moz-selection,
.call-tree-item .call-tree-cell[type=function] description::-moz-selection {
background-color: var(--theme-highlight-orange);
}
.call-tree-item:last-child {
border-bottom: 1px solid var(--cell-border-color);
}

View File

@ -145,7 +145,7 @@ var SimulatorEditor = {
// Select an available option, or set the "custom" option.
updateSelector(selector, value) {
selector.value = value;
if (selector[selector.selectedIndex].value !== value) {
if (selector.selectedIndex == -1) {
selector.value = "custom";
selector.classList.add("custom");
selector[selector.selectedIndex].textContent = value;

View File

@ -28,6 +28,7 @@
#include "mozilla/StartupTimeline.h"
#include "mozilla/Telemetry.h"
#include "mozilla/unused.h"
#include "Navigator.h"
#include "URIUtils.h"
#include "nsIContent.h"
@ -3128,6 +3129,38 @@ nsDocShell::NameEquals(const char16_t* aName, bool* aResult)
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent)
{
aCustomUserAgent = mCustomUserAgent;
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent)
{
mCustomUserAgent = aCustomUserAgent;
RefPtr<nsGlobalWindow> win = mScriptGlobal ?
mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
if (win) {
ErrorResult ignored;
Navigator* navigator = win->GetNavigator(ignored);
ignored.SuppressException();
if (navigator) {
navigator->ClearUserAgentCache();
}
}
uint32_t childCount = mChildList.Length();
for (uint32_t i = 0; i < childCount; ++i) {
nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
if (childShell) {
childShell->SetCustomUserAgent(aCustomUserAgent);
}
}
return NS_OK;
}
/* virtual */ int32_t
nsDocShell::ItemType()
{
@ -3255,6 +3288,7 @@ nsDocShell::SetDocLoaderParent(nsDocLoader* aParent)
// If parent is another docshell, we inherit all their flags for
// allowing plugins, scripting etc.
bool value;
nsString customUserAgent;
nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
if (parentAsDocShell) {
if (mAllowPlugins && NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) {
@ -3284,6 +3318,10 @@ nsDocShell::SetDocLoaderParent(nsDocLoader* aParent)
if (parentAsDocShell->GetIsPrerendered()) {
SetIsPrerendered(true);
}
if (NS_SUCCEEDED(parentAsDocShell->GetCustomUserAgent(customUserAgent)) &&
!customUserAgent.IsEmpty()) {
SetCustomUserAgent(customUserAgent);
}
if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
value = false;
}

View File

@ -793,6 +793,7 @@ protected:
nsIntRect mBounds;
nsString mName;
nsString mTitle;
nsString mCustomUserAgent;
/**
* Content-Type Hint of the most-recently initiated load. Used for

View File

@ -43,7 +43,7 @@ interface nsITabParent;
typedef unsigned long nsLoadFlags;
[scriptable, builtinclass, uuid(63adb599-6dc9-4746-972e-c22e9018020b)]
[scriptable, builtinclass, uuid(bc3524bd-023c-4fc8-ace1-472bc999fb12)]
interface nsIDocShell : nsIDocShellTreeItem
{
/**
@ -237,6 +237,11 @@ interface nsIDocShell : nsIDocShellTreeItem
*/
attribute nsIDOMEventTarget chromeEventHandler;
/**
* This allows chrome to set a custom User agent on a specific docshell
*/
attribute DOMString customUserAgent;
/**
* Whether to allow plugin execution
*/
@ -250,7 +255,7 @@ interface nsIDocShell : nsIDocShellTreeItem
/**
* Attribute stating if refresh based redirects can be allowed
*/
attribute boolean allowMetaRedirects;
attribute boolean allowMetaRedirects;
/**
* Attribute stating if it should allow subframes (framesets/iframes) or not

View File

@ -84,6 +84,7 @@ skip-if = e10s # Bug 1220927 - Test tries to do addSHistoryListener on content.
[browser_multiple_pushState.js]
[browser_onbeforeunload_navigation.js]
[browser_search_notification.js]
[browser_ua_emulation.js]
[browser_timelineMarkers-01.js]
[browser_timelineMarkers-02.js]
[browser_timelineMarkers-03.js]

View File

@ -0,0 +1,52 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the docShell UA emulation works
add_task(function*() {
yield openUrl("data:text/html;charset=utf-8,<iframe id='test-iframe'></iframe>");
let docshell = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
is(docshell.customUserAgent, "", "There should initially be no customUserAgent");
docshell.customUserAgent = "foo";
is(content.navigator.userAgent, "foo", "The user agent should be changed to foo");
let frameWin = content.document.querySelector("#test-iframe").contentWindow;
is(frameWin.navigator.userAgent, "foo", "The UA should be passed on to frames.");
let newFrame = content.document.createElement("iframe");
content.document.body.appendChild(newFrame);
let newFrameWin = newFrame.contentWindow;
is(newFrameWin.navigator.userAgent, "foo", "Newly created frames should use the new UA");
newFrameWin.location.reload();
yield waitForEvent(newFrameWin, "load");
is(newFrameWin.navigator.userAgent, "foo", "New UA should persist across reloads");
gBrowser.removeCurrentTab();
});
function waitForEvent(target, event) {
return new Promise(function(resolve) {
target.addEventListener(event, resolve);
});
}
function openUrl(url) {
return new Promise(function(resolve, reject) {
window.focus();
let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
let linkedBrowser = tab.linkedBrowser;
linkedBrowser.addEventListener("load", function onload() {
linkedBrowser.removeEventListener("load", onload, true);
resolve(tab);
}, true);
});
}

View File

@ -6,11 +6,7 @@
#include "AudioChannelAgent.h"
#include "AudioChannelService.h"
#include "mozilla/Preferences.h"
#include "nsIAppsService.h"
#include "nsIDocument.h"
#include "nsIDOMWindow.h"
#include "nsIPrincipal.h"
#include "nsPIDOMWindow.h"
#include "nsXULAppAPI.h"
@ -81,81 +77,6 @@ AudioChannelAgent::InitWithWeakCallback(nsIDOMWindow* aWindow,
/* useWeakRef = */ true);
}
nsresult
AudioChannelAgent::FindCorrectWindow(nsIDOMWindow* aWindow)
{
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
MOZ_ASSERT(window->IsInnerWindow());
mWindow = window->GetScriptableTop();
if (NS_WARN_IF(!mWindow)) {
return NS_OK;
}
mWindow = mWindow->GetOuterWindow();
if (NS_WARN_IF(!mWindow)) {
return NS_ERROR_FAILURE;
}
// From here we do an hack for nested iframes.
// The system app doesn't have access to the nested iframe objects so it
// cannot control the volume of the agents running in nested apps. What we do
// here is to assign those Agents to the top scriptable window of the parent
// iframe (what is controlled by the system app).
// For doing this we go recursively back into the chain of windows until we
// find apps that are not the system one.
window = mWindow->GetParent();
if (!window || window == mWindow) {
return NS_OK;
}
window = window->GetCurrentInnerWindow();
if (!window) {
return NS_OK;
}
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
if (!doc) {
return NS_OK;
}
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
uint32_t appId;
nsresult rv = principal->GetAppId(&appId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (appId == nsIScriptSecurityManager::NO_APP_ID ||
appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
return NS_OK;
}
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
if (NS_WARN_IF(!appsService)) {
return NS_ERROR_FAILURE;
}
nsAdoptingString systemAppManifest =
mozilla::Preferences::GetString("b2g.system_manifest_url");
if (!systemAppManifest) {
return NS_OK;
}
uint32_t systemAppId;
rv = appsService->GetAppLocalIdByManifestURL(systemAppManifest, &systemAppId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (systemAppId == appId) {
return NS_OK;
}
return FindCorrectWindow(window);
}
nsresult
AudioChannelAgent::InitInternal(nsIDOMWindow* aWindow, int32_t aChannelType,
nsIAudioChannelAgentCallback *aCallback,
@ -187,9 +108,18 @@ AudioChannelAgent::InitInternal(nsIDOMWindow* aWindow, int32_t aChannelType,
MOZ_ASSERT(pInnerWindow->IsInnerWindow());
mInnerWindowID = pInnerWindow->WindowID();
nsresult rv = FindCorrectWindow(aWindow);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
nsCOMPtr<nsPIDOMWindow> topWindow = pInnerWindow->GetScriptableTop();
if (NS_WARN_IF(!topWindow)) {
return NS_OK;
}
mWindow = do_QueryInterface(topWindow);
if (mWindow) {
mWindow = mWindow->GetOuterWindow();
}
if (NS_WARN_IF(!mWindow)) {
return NS_ERROR_FAILURE;
}
mAudioChannelType = aChannelType;

View File

@ -56,8 +56,6 @@ private:
void Shutdown();
nsresult FindCorrectWindow(nsIDOMWindow* aWindow);
nsCOMPtr<nsPIDOMWindow> mWindow;
nsCOMPtr<nsIAudioChannelAgentCallback> mCallback;

View File

@ -14,7 +14,6 @@
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/TabParent.h"
#include "nsContentUtils.h"
#include "nsIScriptSecurityManager.h"
@ -220,7 +219,6 @@ AudioChannelService::Shutdown()
gAudioChannelService->mWindows.Clear();
gAudioChannelService->mPlayingChildren.Clear();
gAudioChannelService->mTabParents.Clear();
#ifdef MOZ_WIDGET_GONK
gAudioChannelService->mSpeakerManager.Clear();
#endif
@ -343,21 +341,6 @@ AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent,
MaybeSendStatusUpdate();
}
void
AudioChannelService::RegisterTabParent(TabParent* aTabParent)
{
MOZ_ASSERT(aTabParent);
MOZ_ASSERT(!mTabParents.Contains(aTabParent));
mTabParents.AppendElement(aTabParent);
}
void
AudioChannelService::UnregisterTabParent(TabParent* aTabParent)
{
MOZ_ASSERT(aTabParent);
mTabParents.RemoveElement(aTabParent);
}
void
AudioChannelService::GetState(nsPIDOMWindow* aWindow, uint32_t aAudioChannel,
float* aVolume, bool* aMuted)
@ -577,32 +560,6 @@ AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic,
return NS_OK;
}
void
AudioChannelService::RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel,
nsPIDOMWindow* aWindow)
{
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aWindow->IsOuterWindow());
nsCOMPtr<nsPIDOMWindow> topWindow = aWindow->GetScriptableTop();
if (!topWindow) {
return;
}
AudioChannelWindow* winData = GetWindowData(topWindow->WindowID());
if (!winData) {
return;
}
for (uint32_t i = 0; i < mTabParents.Length(); ++i) {
mTabParents[i]->AudioChannelChangeNotification(aWindow, aAudioChannel,
winData->mChannels[(uint32_t)aAudioChannel].mVolume,
winData->mChannels[(uint32_t)aAudioChannel].mMuted);
}
RefreshAgentsVolume(aWindow);
}
void
AudioChannelService::RefreshAgentsVolume(nsPIDOMWindow* aWindow)
{
@ -794,7 +751,7 @@ AudioChannelService::SetAudioChannelVolume(nsPIDOMWindow* aWindow,
AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
winData->mChannels[(uint32_t)aAudioChannel].mVolume = aVolume;
RefreshAgentsVolumeAndPropagate(aAudioChannel, aWindow);
RefreshAgentsVolume(aWindow);
}
NS_IMETHODIMP
@ -857,7 +814,7 @@ AudioChannelService::SetAudioChannelMuted(nsPIDOMWindow* aWindow,
AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
winData->mChannels[(uint32_t)aAudioChannel].mMuted = aMuted;
RefreshAgentsVolumeAndPropagate(aAudioChannel, aWindow);
RefreshAgentsVolume(aWindow);
}
NS_IMETHODIMP

View File

@ -23,13 +23,10 @@ struct PRLogModuleInfo;
namespace mozilla {
namespace dom {
#ifdef MOZ_WIDGET_GONK
class SpeakerManagerService;
#endif
class TabParent;
#define NUMBER_OF_AUDIO_CHANNELS (uint32_t)AudioChannel::EndGuard_
class AudioChannelService final : public nsIAudioChannelService
@ -66,12 +63,6 @@ public:
void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent,
uint32_t aNotifyPlayback);
/**
* For nested iframes.
*/
void RegisterTabParent(TabParent* aTabParent);
void UnregisterTabParent(TabParent* aTabParent);
/**
* Return the state to indicate this audioChannel for his window should keep
* playing/muted.
@ -117,9 +108,6 @@ public:
void RefreshAgentsVolume(nsPIDOMWindow* aWindow);
void RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel,
nsPIDOMWindow* aWindow);
// This method needs to know the inner window that wants to capture audio. We
// group agents per top outer window, but we can have multiple innerWindow per
// top outerWindow (subiframes, etc.) and we have to identify all the agents
@ -235,9 +223,6 @@ private:
nsTArray<SpeakerManagerService*> mSpeakerManager;
#endif
// Raw pointers because TabParents must unregister themselves.
nsTArray<TabParent*> mTabParents;
nsCOMPtr<nsIRunnable> mRunnable;
uint64_t mDefChannelChildID;

View File

@ -332,8 +332,7 @@ public:
virtual void SetLazyData(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate,
BlobDirState aDirState) = 0;
int64_t aLastModifiedDate) = 0;
virtual bool IsMemoryFile() const = 0;
@ -498,8 +497,7 @@ public:
virtual void
SetLazyData(const nsAString& aName, const nsAString& aContentType,
uint64_t aLength, int64_t aLastModifiedDate,
BlobDirState aDirState) override
uint64_t aLength, int64_t aLastModifiedDate) override
{
mName = aName;
mContentType = aContentType;

View File

@ -372,11 +372,22 @@ Navigator::GetUserAgent(nsAString& aUserAgent)
nsCOMPtr<nsIURI> codebaseURI;
nsCOMPtr<nsPIDOMWindow> window;
if (mWindow && mWindow->GetDocShell()) {
if (mWindow) {
window = mWindow;
nsIDocument* doc = mWindow->GetExtantDoc();
if (doc) {
doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
nsIDocShell* docshell = window->GetDocShell();
nsString customUserAgent;
if (docshell) {
docshell->GetCustomUserAgent(customUserAgent);
if (!customUserAgent.IsEmpty()) {
aUserAgent = customUserAgent;
return NS_OK;
}
nsIDocument* doc = mWindow->GetExtantDoc();
if (doc) {
doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
}
}
}
@ -2741,6 +2752,12 @@ Navigator::AppName(nsAString& aAppName, bool aUsePrefOverriddenValue)
aAppName.AssignLiteral("Netscape");
}
void
Navigator::ClearUserAgentCache()
{
NavigatorBinding::ClearCachedUserAgentValue(this);
}
nsresult
Navigator::GetUserAgent(nsPIDOMWindow* aWindow, nsIURI* aURI,
bool aIsCallerChrome,

View File

@ -182,6 +182,10 @@ public:
bool aIsCallerChrome,
nsAString& aUserAgent);
// Clears the user agent cache by calling:
// NavigatorBinding::ClearCachedUserAgentValue(this);
void ClearUserAgentCache();
already_AddRefed<Promise> GetDataStores(const nsAString& aName,
const nsAString& aOwner,
ErrorResult& aRv);

View File

@ -1435,6 +1435,7 @@ nsIDocument::nsIDocument()
mPostedFlushUserFontSet(false),
mPartID(0),
mDidFireDOMContentLoaded(true),
mHasScrollLinkedEffect(false),
mUserHasInteracted(false)
{
SetInDocument();
@ -1555,6 +1556,8 @@ nsDocument::~nsDocument()
mixedContentLevel = MIXED_DISPLAY_CONTENT;
}
Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
Accumulate(Telemetry::SCROLL_LINKED_EFFECT_FOUND, mHasScrollLinkedEffect);
}
}
@ -2678,6 +2681,16 @@ nsDocument::InitCSP(nsIChannel* aChannel)
nsAutoCString tCspHeaderValue, tCspROHeaderValue;
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
if (!httpChannel) {
// check baseChannel for CSP when loading a multipart channel
nsCOMPtr<nsIMultiPartChannel> multipart = do_QueryInterface(aChannel);
if (multipart) {
nsCOMPtr<nsIChannel> baseChannel;
multipart->GetBaseChannel(getter_AddRefs(baseChannel));
httpChannel = do_QueryInterface(baseChannel);
}
}
if (httpChannel) {
httpChannel->GetResponseHeader(
NS_LITERAL_CSTRING("content-security-policy"),
@ -13276,3 +13289,17 @@ nsIDocument::Fonts()
}
return mFontFaceSet;
}
void
nsIDocument::ReportHasScrollLinkedEffect()
{
if (mHasScrollLinkedEffect) {
// We already did this once for this document, don't do it again.
return;
}
mHasScrollLinkedEffect = true;
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("Async Pan/Zoom"),
this, nsContentUtils::eLAYOUT_PROPERTIES,
"ScrollLinkedEffectFound");
}

View File

@ -155,8 +155,8 @@ typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> NodeFilterHolder;
} // namespace mozilla
#define NS_IDOCUMENT_IID \
{ 0x13011a82, 0x46cd, 0x4c33, \
{ 0x9d, 0x4e, 0x31, 0x41, 0xbb, 0x3f, 0x18, 0xe9 } }
{ 0xce1f7627, 0x7109, 0x4977, \
{ 0xba, 0x77, 0x49, 0x0f, 0xfd, 0xe0, 0x7a, 0xaa } }
// Enum for requesting a particular type of document when creating a doc
enum DocumentFlavor {
@ -2652,6 +2652,8 @@ public:
return mUserHasInteracted;
}
void ReportHasScrollLinkedEffect();
protected:
bool GetUseCounter(mozilla::UseCounter aUseCounter)
{
@ -3052,6 +3054,8 @@ protected:
uint32_t mBlockDOMContentLoaded;
bool mDidFireDOMContentLoaded:1;
bool mHasScrollLinkedEffect:1;
// Our live MediaQueryLists
PRCList mDOMMediaQueryLists;

View File

@ -5,7 +5,6 @@
#include "nsISupports.idl"
interface mozIApplication;
interface nsFrameLoader;
interface nsIDocShell;
interface nsIURI;
@ -215,7 +214,7 @@ class nsFrameLoader;
native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>);
[scriptable, uuid(adc1b3ba-8deb-4943-8045-e6de0044f2ce)]
[scriptable, uuid(c4abebcf-55f3-47d4-af15-151311971255)]
interface nsIFrameLoaderOwner : nsISupports
{
/**
@ -224,12 +223,6 @@ interface nsIFrameLoaderOwner : nsISupports
readonly attribute nsIFrameLoader frameLoader;
[noscript, notxpcom] alreadyAddRefed_nsFrameLoader GetFrameLoader();
/**
* The principal of parent mozIApplication in case of nested mozbrowser
* iframes.
*/
readonly attribute mozIApplication parentApplication;
/**
* Puts the FrameLoaderOwner in prerendering mode.
*/

View File

@ -1215,17 +1215,6 @@ nsObjectLoadingContent::GetFrameLoader()
return loader.forget();
}
NS_IMETHODIMP
nsObjectLoadingContent::GetParentApplication(mozIApplication** aApplication)
{
if (!aApplication) {
return NS_ERROR_FAILURE;
}
*aApplication = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsObjectLoadingContent::SetIsPrerendered()
{

View File

@ -770,7 +770,7 @@ CouldBeDOMBinding(nsWrapperCache* aCache)
}
inline bool
TryToOuterize(JSContext* cx, JS::MutableHandle<JS::Value> rval)
TryToOuterize(JS::MutableHandle<JS::Value> rval)
{
if (js::IsWindow(&rval.toObject())) {
JSObject* obj = js::ToWindowProxyIfWindow(&rval.toObject());
@ -812,7 +812,7 @@ MaybeWrapObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
// We're same-compartment, but even then we might need to wrap
// objects specially. Check for that.
if (IsDOMObject(obj)) {
return TryToOuterize(cx, rval);
return TryToOuterize(rval);
}
// It's not a WebIDL object. But it might be an XPConnect one, in which case
@ -1005,13 +1005,13 @@ DoGetOrCreateDOMReflector(JSContext* cx, T* value,
bool sameCompartment =
js::GetObjectCompartment(obj) == js::GetContextCompartment(cx);
if (sameCompartment && couldBeDOMBinding) {
return TypeNeedsOuterization<T>::value ? TryToOuterize(cx, rval) : true;
return TypeNeedsOuterization<T>::value ? TryToOuterize(rval) : true;
}
if (wrapBehavior == eDontWrapIntoContextCompartment) {
if (TypeNeedsOuterization<T>::value) {
JSAutoCompartment ac(cx, obj);
return TryToOuterize(cx, rval);
return TryToOuterize(rval);
}
return true;

View File

@ -7,8 +7,6 @@
#include "mozilla/Services.h"
#include "mozilla/dom/BrowserElementAudioChannelBinding.h"
#include "mozilla/dom/DOMRequest.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/ToJSValue.h"
@ -25,6 +23,16 @@
#include "nsPIDOMWindow.h"
#include "nsServiceManagerUtils.h"
namespace {
void
AssertIsInMainProcess()
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
}
} // anonymous namespace
namespace mozilla {
namespace dom {
@ -81,6 +89,7 @@ BrowserElementAudioChannel::BrowserElementAudioChannel(
, mState(eStateUnknown)
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
@ -98,6 +107,7 @@ BrowserElementAudioChannel::BrowserElementAudioChannel(
BrowserElementAudioChannel::~BrowserElementAudioChannel()
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
@ -163,6 +173,8 @@ AudioChannel
BrowserElementAudioChannel::Name() const
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
return mAudioChannel;
}
@ -349,6 +361,7 @@ already_AddRefed<dom::DOMRequest>
BrowserElementAudioChannel::GetVolume(ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
if (!mFrameWindow) {
nsCOMPtr<nsIDOMDOMRequest> request;
@ -374,6 +387,7 @@ already_AddRefed<dom::DOMRequest>
BrowserElementAudioChannel::SetVolume(float aVolume, ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
if (!mFrameWindow) {
nsCOMPtr<nsIDOMDOMRequest> request;
@ -406,6 +420,7 @@ already_AddRefed<dom::DOMRequest>
BrowserElementAudioChannel::GetMuted(ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
if (!mFrameWindow) {
nsCOMPtr<nsIDOMDOMRequest> request;
@ -431,6 +446,7 @@ already_AddRefed<dom::DOMRequest>
BrowserElementAudioChannel::SetMuted(bool aMuted, ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
if (!mFrameWindow) {
nsCOMPtr<nsIDOMDOMRequest> request;
@ -463,6 +479,7 @@ already_AddRefed<dom::DOMRequest>
BrowserElementAudioChannel::IsActive(ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
if (mState != eStateUnknown) {
RefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
@ -576,29 +593,8 @@ BrowserElementAudioChannel::Observe(nsISupports* aSubject, const char* aTopic,
}
nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
// This can be a nested iframe.
if (!wrapper) {
nsCOMPtr<nsITabParent> iTabParent = do_QueryInterface(aSubject);
if (!iTabParent) {
return NS_ERROR_FAILURE;
}
RefPtr<TabParent> tabParent = TabParent::GetFrom(iTabParent);
if (!tabParent) {
return NS_ERROR_FAILURE;
}
Element* element = tabParent->GetOwnerElement();
if (!element) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsPIDOMWindow> window = element->OwnerDoc()->GetWindow();
if (window == mFrameWindow) {
ProcessStateChanged(aData);
}
return NS_OK;
if (NS_WARN_IF(!wrapper)) {
return NS_ERROR_FAILURE;
}
uint64_t windowID;

View File

@ -597,6 +597,7 @@ BrowserElementChild.prototype = {
let handlers = {
'icon': this._iconChangedHandler.bind(this),
'apple-touch-icon': this._iconChangedHandler.bind(this),
'apple-touch-icon-precomposed': this._iconChangedHandler.bind(this),
'search': this._openSearchHandler,
'manifest': this._manifestChangedHandler
};

View File

@ -192,10 +192,7 @@ function runTests() {
}
addEventListener('testready', function() {
SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
function() {
SimpleTest.executeSoon(runTests);
});
addEventListener('load', function() {
SimpleTest.executeSoon(runTests);
});

View File

@ -1,79 +0,0 @@
/* Any copyright is dedicated to the public domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Bug 1113086 - tests for AudioChannel API into BrowserElement
"use strict";
SimpleTest.waitForExplicitFinish();
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
function runTests() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', 'true');
iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
var listener = function(e) {
var message = e.detail.message;
if (/^OK/.exec(message)) {
ok(true, "Message from app: " + message);
} else if (/^KO/.exec(message)) {
ok(false, "Message from app: " + message);
} else if (/DONE/.exec(message)) {
ok(true, "Messaging from app complete");
iframe.removeEventListener('mozbrowsershowmodalprompt', listener);
}
}
function audio_loadend() {
ok("mute" in iframe, "iframe.mute exists");
ok("unmute" in iframe, "iframe.unmute exists");
ok("getMuted" in iframe, "iframe.getMuted exists");
ok("getVolume" in iframe, "iframe.getVolume exists");
ok("setVolume" in iframe, "iframe.setVolume exists");
ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist");
var channels = iframe.allowedAudioChannels;
is(channels.length, 1, "1 audio channel by default");
var ac = channels[0];
ok(ac instanceof BrowserElementAudioChannel, "Correct class");
ok("getVolume" in ac, "ac.getVolume exists");
ok("setVolume" in ac, "ac.setVolume exists");
ok("getMuted" in ac, "ac.getMuted exists");
ok("setMuted" in ac, "ac.setMuted exists");
ok("isActive" in ac, "ac.isActive exists");
info("Setting the volume...");
ac.setVolume(0.5);
ac.onactivestatechanged = function() {
ok(true, "activestatechanged event received.");
ac.onactivestatechanged = null;
SimpleTest.finish();
}
}
iframe.addEventListener('mozbrowserloadend', audio_loadend);
iframe.addEventListener('mozbrowsershowmodalprompt', listener, false);
document.body.appendChild(iframe);
var context = { 'url': 'http://example.org',
'appId': SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID,
'isInBrowserElement': true };
SpecialPowers.pushPermissions([
{'type': 'browser', 'allow': 1, 'context': context},
{'type': 'embed-apps', 'allow': 1, 'context': context}
], function() {
iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_AudioChannel_nested.html';
});
}
addEventListener('testready', function() {
SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
function() {
SimpleTest.executeSoon(runTests);
});
});

View File

@ -68,9 +68,4 @@ function runTest() {
iframe.src = browserElementTestHelpers.emptyPage1;
}
addEventListener('testready', function() {
SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
function() {
SimpleTest.executeSoon(runTest);
});
});
addEventListener('testready', runTest);

View File

@ -102,6 +102,12 @@ function runTest() {
is(e.detail.href, 'http://example.com/testapple1.png');
is(e.detail.rel, 'apple-touch-icon');
is(e.detail.sizes, '100x100');
iframe1.src = createHtml(createLink('testapple2', '100x100', 'apple-touch-icon-precomposed'));
} else if (numIconChanges == 9) {
is(e.detail.href, 'http://example.com/testapple2.png');
is(e.detail.rel, 'apple-touch-icon-precomposed');
is(e.detail.sizes, '100x100');
SimpleTest.finish();
} else {
ok(false, 'Too many iconchange events.');

View File

@ -1,63 +0,0 @@
<html>
<head>
<script type="text/javascript">
function ok(a, msg) {
alert((!!a ? "OK" : "KO") + " " + msg);
}
function is(a, b, msg) {
ok(a === b, msg);
}
function finish(a, b, msg) {
alert("DONE");
}
addEventListener('load', function(e) {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', 'true');
// set 'remote' to true here will make the the iframe remote in _inproc_
// test and in-process in _oop_ test.
iframe.setAttribute('remote', 'true');
iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
iframe.addEventListener('mozbrowserloadend', function(e) {
ok("mute" in iframe, "iframe.mute exists");
ok("unmute" in iframe, "iframe.unmute exists");
ok("getMuted" in iframe, "iframe.getMuted exists");
ok("getVolume" in iframe, "iframe.getVolume exists");
ok("setVolume" in iframe, "iframe.setVolume exists");
ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist");
var channels = iframe.allowedAudioChannels;
is(channels.length, 1, "1 audio channel by default");
var ac = channels[0];
ok(ac instanceof BrowserElementAudioChannel, "Correct class");
ok("getVolume" in ac, "ac.getVolume exists");
ok("setVolume" in ac, "ac.setVolume exists");
ok("getMuted" in ac, "ac.getMuted exists");
ok("setMuted" in ac, "ac.setMuted exists");
ok("isActive" in ac, "ac.isActive exists");
ac.onactivestatechanged = function() {
ok("activestatechanged event received.");
ac.getVolume().onsuccess = function(e) {
ok(e.target.result, 1, "Default volume is 1");
};
finish();
}
});
document.body.appendChild(iframe);
iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_audio.html';
});
</script>
</head>
<body>
</body>
</html>

View File

@ -121,7 +121,6 @@ disabled = bug 924771
disabled = bug 924771
[test_browserElement_oop_GetContentDimensions.html]
[test_browserElement_oop_AudioChannel.html]
[test_browserElement_oop_AudioChannel_nested.html]
[test_browserElement_oop_SetNFCFocus.html]
[test_browserElement_oop_getWebManifest.html]
[test_browserElement_oop_OpenWindowEmpty.html]

View File

@ -83,13 +83,11 @@ support-files =
browserElement_XFrameOptionsSameOrigin.js
browserElement_GetContentDimensions.js
browserElement_AudioChannel.js
browserElement_AudioChannel_nested.js
file_browserElement_AlertInFrame.html
file_browserElement_AlertInFrame_Inner.html
file_browserElement_AllowEmbedAppsInNestedOOIframe.html
file_browserElement_AppFramePermission.html
file_browserElement_AppWindowNamespace.html
file_browserElement_AudioChannel_nested.html
file_browserElement_Viewmode.html
file_browserElement_ThemeColor.html
file_browserElement_BrowserWindowNamespace.html
@ -252,7 +250,6 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
disabled = bug 774100
[test_browserElement_inproc_GetContentDimensions.html]
[test_browserElement_inproc_AudioChannel.html]
[test_browserElement_inproc_AudioChannel_nested.html]
[test_browserElement_inproc_SetNFCFocus.html]
[test_browserElement_inproc_getStructuredData.html]
[test_browserElement_inproc_OpenWindowEmpty.html]

View File

@ -1,13 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test of browser element audioChannel in nested mozbrowser iframes.</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_AudioChannel_nested.js">
</script>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test of browser element audioChannel.</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_AudioChannel_nested.js">
</script>
</body>
</html>

View File

@ -14,19 +14,12 @@
namespace mozilla {
static bool
GetFBInfoForBlit(const WebGLFramebuffer* fb, WebGLContext* webgl,
const char* const fbInfo, GLsizei* const out_samples,
GetFBInfoForBlit(const WebGLFramebuffer* fb, const char* const fbInfo,
GLsizei* const out_samples,
const webgl::FormatInfo** const out_colorFormat,
const webgl::FormatInfo** const out_depthFormat,
const webgl::FormatInfo** const out_stencilFormat)
{
auto status = fb->PrecheckFramebufferStatus();
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
webgl->ErrorInvalidOperation("blitFramebuffer: %s is not"
" framebuffer-complete.", fbInfo);
return false;
}
*out_samples = 1; // TODO
*out_colorFormat = nullptr;
*out_depthFormat = nullptr;
@ -85,6 +78,9 @@ WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter)
{
if (IsContextLost())
return;
const GLbitfield validBits = LOCAL_GL_COLOR_BUFFER_BIT |
LOCAL_GL_DEPTH_BUFFER_BIT |
LOCAL_GL_STENCIL_BUFFER_BIT;
@ -122,15 +118,20 @@ WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY
return;
}
if (!mBoundReadFramebuffer->ValidateAndInitAttachments("blitFramebuffer's READ_FRAMEBUFFER") ||
!mBoundDrawFramebuffer->ValidateAndInitAttachments("blitFramebuffer's DRAW_FRAMEBUFFER"))
{
return;
}
GLsizei srcSamples;
const webgl::FormatInfo* srcColorFormat = nullptr;
const webgl::FormatInfo* srcDepthFormat = nullptr;
const webgl::FormatInfo* srcStencilFormat = nullptr;
if (mBoundReadFramebuffer) {
if (!GetFBInfoForBlit(mBoundReadFramebuffer, this, "READ_FRAMEBUFFER",
&srcSamples, &srcColorFormat, &srcDepthFormat,
&srcStencilFormat))
if (!GetFBInfoForBlit(mBoundReadFramebuffer, "READ_FRAMEBUFFER", &srcSamples,
&srcColorFormat, &srcDepthFormat, &srcStencilFormat))
{
return;
}
@ -147,9 +148,8 @@ WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY
const webgl::FormatInfo* dstStencilFormat = nullptr;
if (mBoundDrawFramebuffer) {
if (!GetFBInfoForBlit(mBoundDrawFramebuffer, this, "DRAW_FRAMEBUFFER",
&dstSamples, &dstColorFormat, &dstDepthFormat,
&dstStencilFormat))
if (!GetFBInfoForBlit(mBoundDrawFramebuffer, "DRAW_FRAMEBUFFER", &dstSamples,
&dstColorFormat, &dstDepthFormat, &dstStencilFormat))
{
return;
}

View File

@ -104,14 +104,18 @@ WebGL2Context::VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w)
mVertexAttribType[index] = LOCAL_GL_INT;
if (index || gl->IsGLES()) {
MakeContextCurrent();
MakeContextCurrent();
if (index) {
gl->fVertexAttribI4i(index, x, y, z, w);
} else {
mVertexAttrib0Vector[0] = BitwiseCast<GLfloat>(x);
mVertexAttrib0Vector[1] = BitwiseCast<GLfloat>(y);
mVertexAttrib0Vector[2] = BitwiseCast<GLfloat>(z);
mVertexAttrib0Vector[3] = BitwiseCast<GLfloat>(w);
if (gl->IsGLES()) {
gl->fVertexAttribI4i(index, x, y, z, w);
}
}
}
@ -126,14 +130,18 @@ WebGL2Context::VertexAttribI4iv(GLuint index, size_t length, const GLint* v)
mVertexAttribType[index] = LOCAL_GL_INT;
if (index || gl->IsGLES()) {
MakeContextCurrent();
MakeContextCurrent();
if (index) {
gl->fVertexAttribI4iv(index, v);
} else {
mVertexAttrib0Vector[0] = BitwiseCast<GLfloat>(v[0]);
mVertexAttrib0Vector[1] = BitwiseCast<GLfloat>(v[1]);
mVertexAttrib0Vector[2] = BitwiseCast<GLfloat>(v[2]);
mVertexAttrib0Vector[3] = BitwiseCast<GLfloat>(v[3]);
if (gl->IsGLES()) {
gl->fVertexAttribI4iv(index, v);
}
}
}
@ -154,14 +162,18 @@ WebGL2Context::VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLui
mVertexAttribType[index] = LOCAL_GL_UNSIGNED_INT;
if (index || gl->IsGLES()) {
MakeContextCurrent();
MakeContextCurrent();
if (index) {
gl->fVertexAttribI4ui(index, x, y, z, w);
} else {
mVertexAttrib0Vector[0] = BitwiseCast<GLfloat>(x);
mVertexAttrib0Vector[1] = BitwiseCast<GLfloat>(y);
mVertexAttrib0Vector[2] = BitwiseCast<GLfloat>(z);
mVertexAttrib0Vector[3] = BitwiseCast<GLfloat>(w);
if (gl->IsGLES()) {
gl->fVertexAttribI4ui(index, x, y, z, w);
}
}
}
@ -179,14 +191,18 @@ WebGL2Context::VertexAttribI4uiv(GLuint index, size_t length, const GLuint* v)
mVertexAttribType[index] = LOCAL_GL_UNSIGNED_INT;
if (index || gl->IsGLES()) {
MakeContextCurrent();
MakeContextCurrent();
if (index) {
gl->fVertexAttribI4uiv(index, v);
} else {
mVertexAttrib0Vector[0] = BitwiseCast<GLfloat>(v[0]);
mVertexAttrib0Vector[1] = BitwiseCast<GLfloat>(v[1]);
mVertexAttrib0Vector[2] = BitwiseCast<GLfloat>(v[2]);
mVertexAttrib0Vector[3] = BitwiseCast<GLfloat>(v[3]);
if (gl->IsGLES()) {
gl->fVertexAttribI4uiv(index, v);
}
}
}

View File

@ -1414,12 +1414,23 @@ WebGLContext::PresentScreenBuffer()
}
void
WebGLContext::DummyFramebufferOperation(const char* funcName)
WebGLContext::DummyReadFramebufferOperation(const char* funcName)
{
FBStatus status = CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
if (!mBoundReadFramebuffer)
return; // Infallible.
nsCString fbStatusInfo;
const auto status = mBoundReadFramebuffer->CheckFramebufferStatus(&fbStatusInfo);
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
ErrorInvalidFramebufferOperation("%s: incomplete framebuffer",
funcName);
nsCString errorText("Incomplete framebuffer");
if (fbStatusInfo.Length()) {
errorText += ": ";
errorText += fbStatusInfo;
}
ErrorInvalidFramebufferOperation("%s: %s.", funcName, errorText.BeginReading());
}
}
@ -1884,9 +1895,70 @@ Intersect(uint32_t srcSize, int32_t dstStartInSrc, uint32_t dstSize,
*out_intSize = std::max<int32_t>(0, intEndInSrc - *out_intStartInSrc);
}
static bool
ZeroTexImageWithClear(WebGLContext* webgl, GLContext* gl, TexImageTarget target,
GLuint tex, uint32_t level, const webgl::FormatUsageInfo* usage,
uint32_t width, uint32_t height)
{
MOZ_ASSERT(gl->IsCurrent());
ScopedFramebuffer scopedFB(gl);
ScopedBindFramebuffer scopedBindFB(gl, scopedFB.FB());
const auto format = usage->format;
GLenum attachPoint = 0;
GLbitfield clearBits = 0;
if (format->isColorFormat) {
attachPoint = LOCAL_GL_COLOR_ATTACHMENT0;
clearBits = LOCAL_GL_COLOR_BUFFER_BIT;
}
if (format->hasDepth) {
attachPoint = LOCAL_GL_DEPTH_ATTACHMENT;
clearBits |= LOCAL_GL_DEPTH_BUFFER_BIT;
}
if (format->hasStencil) {
attachPoint = (format->hasDepth ? LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
: LOCAL_GL_STENCIL_ATTACHMENT);
clearBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
}
MOZ_RELEASE_ASSERT(attachPoint && clearBits);
{
gl::GLContext::LocalErrorScope errorScope(*gl);
gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachPoint, target.get(), tex,
level);
if (errorScope.GetError()) {
MOZ_ASSERT(false);
return false;
}
}
auto status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
return false;
{
gl::GLContext::LocalErrorScope errorScope(*gl);
const bool fakeNoAlpha = false;
webgl->ForceClearFramebufferWithDefaultValues(clearBits, fakeNoAlpha);
if (errorScope.GetError()) {
MOZ_ASSERT(false);
return false;
}
}
return true;
}
bool
ZeroTextureData(WebGLContext* webgl, const char* funcName, bool respecifyTexture,
TexImageTarget target, uint32_t level,
GLuint tex, TexImageTarget target, uint32_t level,
const webgl::FormatUsageInfo* usage, uint32_t xOffset, uint32_t yOffset,
uint32_t zOffset, uint32_t width, uint32_t height, uint32_t depth)
{
@ -1906,9 +1978,6 @@ ZeroTextureData(WebGLContext* webgl, const char* funcName, bool respecifyTexture
gl::GLContext* gl = webgl->GL();
gl->MakeCurrent();
ScopedUnpackReset scopedReset(webgl);
gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well.
auto compression = usage->format->compression;
if (compression) {
MOZ_RELEASE_ASSERT(!xOffset && !yOffset && !zOffset);
@ -1938,6 +2007,10 @@ ZeroTextureData(WebGLContext* webgl, const char* funcName, bool respecifyTexture
if (!zeros)
return false;
ScopedUnpackReset scopedReset(webgl);
gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it
// well.
GLenum error = DoCompressedTexSubImage(gl, target.get(), level, xOffset, yOffset,
zOffset, width, height, depth, sizedFormat,
byteCount, zeros.get());
@ -1950,6 +2023,27 @@ ZeroTextureData(WebGLContext* webgl, const char* funcName, bool respecifyTexture
const auto driverUnpackInfo = usage->idealUnpack;
MOZ_RELEASE_ASSERT(driverUnpackInfo);
if (usage->isRenderable && depth == 1 &&
!xOffset && !yOffset && !zOffset)
{
// While we would like to skip the extra complexity of trying to zero with an FB
// clear, ANGLE_depth_texture requires this.
do {
if (respecifyTexture) {
const auto error = DoTexImage(gl, target, level, driverUnpackInfo, width,
height, depth, nullptr);
if (error)
break;
}
if (ZeroTexImageWithClear(webgl, gl, target, tex, level, usage, width,
height))
{
return true;
}
} while (false);
}
const webgl::PackingInfo packing = driverUnpackInfo->ToPacking();
const auto bytesPerPixel = webgl::BytesPerPixel(packing);
@ -1968,6 +2062,9 @@ ZeroTextureData(WebGLContext* webgl, const char* funcName, bool respecifyTexture
if (!zeros)
return false;
ScopedUnpackReset scopedReset(webgl);
gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well.
GLenum error;
if (respecifyTexture) {
MOZ_RELEASE_ASSERT(!xOffset && !yOffset && !zOffset);

View File

@ -303,7 +303,7 @@ public:
// Returns hex formatted version of glenum if glenum is unknown.
static void EnumName(GLenum glenum, nsACString* out_name);
void DummyFramebufferOperation(const char* funcName);
void DummyReadFramebufferOperation(const char* funcName);
WebGLTexture* ActiveBoundTextureForTarget(const TexTarget texTarget) const {
switch (texTarget.get()) {
@ -1756,7 +1756,7 @@ Intersect(uint32_t srcSize, int32_t dstStartInSrc, uint32_t dstSize,
bool
ZeroTextureData(WebGLContext* webgl, const char* funcName, bool respecifyTexture,
TexImageTarget target, uint32_t level,
GLuint tex, TexImageTarget target, uint32_t level,
const webgl::FormatUsageInfo* usage, uint32_t xOffset, uint32_t yOffset,
uint32_t zOffset, uint32_t width, uint32_t height, uint32_t depth);

View File

@ -50,7 +50,7 @@ ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl,
//typedef nsTArray<WebGLRefPtr<WebGLTexture>> TexturesT;
typedef decltype(WebGLContext::mBound2DTextures) TexturesT;
const auto fnResolveAll = [this, funcName, out_error](const TexturesT& textures)
const auto fnResolveAll = [this, funcName](const TexturesT& textures)
{
const auto len = textures.Length();
for (uint32_t texUnit = 0; texUnit < len; ++texUnit) {
@ -59,7 +59,8 @@ ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl,
continue;
FakeBlackType fakeBlack;
*out_error |= !tex->ResolveForDraw(funcName, texUnit, &fakeBlack);
if (!tex->ResolveForDraw(funcName, texUnit, &fakeBlack))
return false;
if (fakeBlack == FakeBlackType::None)
continue;
@ -67,14 +68,16 @@ ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl,
mWebGL->BindFakeBlack(texUnit, tex->Target(), fakeBlack);
mRebindRequests.push_back({texUnit, tex});
}
return true;
};
*out_error = false;
fnResolveAll(mWebGL->mBound2DTextures);
fnResolveAll(mWebGL->mBoundCubeMapTextures);
fnResolveAll(mWebGL->mBound3DTextures);
fnResolveAll(mWebGL->mBound2DArrayTextures);
*out_error |= !fnResolveAll(mWebGL->mBound2DTextures);
*out_error |= !fnResolveAll(mWebGL->mBoundCubeMapTextures);
*out_error |= !fnResolveAll(mWebGL->mBound3DTextures);
*out_error |= !fnResolveAll(mWebGL->mBound2DArrayTextures);
if (*out_error) {
mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.", funcName);
@ -220,10 +223,8 @@ WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcount,
MakeContextCurrent();
if (mBoundDrawFramebuffer) {
if (!mBoundDrawFramebuffer->CheckAndInitializeAttachments()) {
ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(info))
return false;
}
} else {
ClearBackbufferIfNeeded();
}
@ -411,10 +412,8 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type,
MakeContextCurrent();
if (mBoundDrawFramebuffer) {
if (!mBoundDrawFramebuffer->CheckAndInitializeAttachments()) {
ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(info))
return false;
}
} else {
ClearBackbufferIfNeeded();
}

View File

@ -15,6 +15,8 @@ namespace mozilla {
void
WebGLContext::Clear(GLbitfield mask)
{
const char funcName[] = "clear";
if (IsContextLost())
return;
@ -22,7 +24,7 @@ WebGLContext::Clear(GLbitfield mask)
uint32_t m = mask & (LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT);
if (mask != m)
return ErrorInvalidValue("clear: invalid mask bits");
return ErrorInvalidValue("%s: invalid mask bits", funcName);
if (mask == 0) {
GenerateWarning("Calling gl.clear(0) has no effect.");
@ -31,8 +33,8 @@ WebGLContext::Clear(GLbitfield mask)
}
if (mBoundDrawFramebuffer) {
if (!mBoundDrawFramebuffer->CheckAndInitializeAttachments())
return ErrorInvalidFramebufferOperation("clear: incomplete framebuffer");
if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName))
return;
gl->fClear(mask);
return;

View File

@ -279,7 +279,8 @@ WebGLContext::CheckFramebufferStatus(GLenum target)
if (!fb)
return LOCAL_GL_FRAMEBUFFER_COMPLETE;
return fb->CheckFramebufferStatus().get();
nsCString fbErrorInfo;
return fb->CheckFramebufferStatus(&fbErrorInfo).get();
}
already_AddRefed<WebGLProgram>
@ -1572,7 +1573,7 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
if (!rwWidth || !rwHeight) {
// There aren't any, so we're 'done'.
DummyFramebufferOperation("readPixels");
DummyReadFramebufferOperation("readPixels");
return;
}

View File

@ -294,7 +294,8 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
}
case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: {
if (mBoundReadFramebuffer) {
FBStatus status = mBoundReadFramebuffer->CheckFramebufferStatus();
nsCString fbStatusInfoIgnored;
const auto status = mBoundReadFramebuffer->CheckFramebufferStatus(&fbStatusInfoIgnored);
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
ErrorInvalidOperation("getParameter: Read framebuffer must be"
" complete before querying"
@ -313,7 +314,8 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
}
case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT: {
if (mBoundReadFramebuffer) {
FBStatus status = mBoundReadFramebuffer->CheckFramebufferStatus();
nsCString fbStatusInfoIgnored;
const auto status = mBoundReadFramebuffer->CheckFramebufferStatus(&fbStatusInfoIgnored);
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
ErrorInvalidOperation("getParameter: Read framebuffer must be"
" complete before querying"

View File

@ -7,6 +7,7 @@
#include "GLContext.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#include "nsPrintfCString.h"
#include "WebGLContext.h"
#include "WebGLContextUtils.h"
#include "WebGLExtensions.h"
@ -218,48 +219,114 @@ WebGLFBAttachPoint::OnBackingStoreRespecified() const
mFB->InvalidateFramebufferStatus();
}
void
WebGLFBAttachPoint::AttachmentName(nsCString* out) const
{
switch (mAttachmentPoint) {
case LOCAL_GL_DEPTH_ATTACHMENT:
out->AssignLiteral("DEPTH_ATTACHMENT");
return;
case LOCAL_GL_STENCIL_ATTACHMENT:
out->AssignLiteral("STENCIL_ATTACHMENT");
return;
case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
out->AssignLiteral("DEPTH_STENCIL_ATTACHMENT");
return;
default:
MOZ_ASSERT(mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0);
out->AssignLiteral("COLOR_ATTACHMENT");
const uint32_t n = mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0;
out->AppendInt(n);
return;
}
}
bool
WebGLFBAttachPoint::IsComplete(WebGLContext* webgl) const
WebGLFBAttachPoint::IsComplete(WebGLContext* webgl, nsCString* const out_info) const
{
MOZ_ASSERT(IsDefined());
if (!HasImage())
if (!HasImage()) {
AttachmentName(out_info);
out_info->AppendLiteral("'s image is not defined");
return false;
}
uint32_t width;
uint32_t height;
Size(&width, &height);
if (!width || !height)
if (!width || !height) {
AttachmentName(out_info);
out_info->AppendLiteral(" has no width or height");
return false;
auto formatUsage = Format();
if (!formatUsage->isRenderable)
return false;
auto format = formatUsage->format;
if (webgl->IsWebGL2()) {
if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT)
return format->hasDepth;
if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT)
return format->hasStencil;
if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
MOZ_CRASH("No DEPTH_STENCIL_ATTACHMENT in WebGL 2.");
} else {
if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT)
return format->hasDepth && !format->hasStencil;
if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT)
return !format->hasDepth && format->hasStencil;
if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
return format->hasDepth && format->hasStencil;
}
MOZ_ASSERT(mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0);
return format->isColorFormat;
const auto formatUsage = Format();
if (!formatUsage->isRenderable) {
nsAutoCString attachName;
AttachmentName(&attachName);
*out_info = nsPrintfCString("%s has an effective format of %s, which is not"
" renderable",
attachName.BeginReading(), formatUsage->format->name);
return false;
}
const auto format = formatUsage->format;
bool hasRequiredBits;
switch (mAttachmentPoint) {
case LOCAL_GL_DEPTH_ATTACHMENT:
hasRequiredBits = format->hasDepth;
break;
case LOCAL_GL_STENCIL_ATTACHMENT:
hasRequiredBits = format->hasStencil;
break;
case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
MOZ_ASSERT(!webgl->IsWebGL2());
hasRequiredBits = (format->hasDepth && format->hasStencil);
break;
default:
MOZ_ASSERT(mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0);
hasRequiredBits = format->isColorFormat;
break;
}
if (!hasRequiredBits) {
AttachmentName(out_info);
out_info->AppendLiteral("'s format is missing required color/depth/stencil bits");
return false;
}
if (!webgl->IsWebGL2()) {
bool hasSurplusPlanes = false;
switch (mAttachmentPoint) {
case LOCAL_GL_DEPTH_ATTACHMENT:
hasSurplusPlanes = format->hasStencil;
break;
case LOCAL_GL_STENCIL_ATTACHMENT:
hasSurplusPlanes = format->hasDepth;
break;
}
if (hasSurplusPlanes) {
AttachmentName(out_info);
out_info->AppendLiteral("'s format has depth or stencil bits when it"
" shouldn't");
return false;
}
}
return true;
}
void
@ -696,13 +763,13 @@ WebGLFramebuffer::HasDefinedAttachments() const
}
bool
WebGLFramebuffer::HasIncompleteAttachments() const
WebGLFramebuffer::HasIncompleteAttachments(nsCString* const out_info) const
{
const auto fnIsIncomplete = [this](const WebGLFBAttachPoint& cur) {
const auto fnIsIncomplete = [this, out_info](const WebGLFBAttachPoint& cur) {
if (!cur.IsDefined())
return false; // Not defined, so can't count as incomplete.
return !cur.IsComplete(this->mContext);
return !cur.IsComplete(this->mContext, out_info);
};
bool hasIncomplete = false;
@ -745,7 +812,8 @@ bool
WebGLFramebuffer::AllImageRectsMatch() const
{
MOZ_ASSERT(HasDefinedAttachments());
MOZ_ASSERT(!HasIncompleteAttachments());
DebugOnly<nsCString> fbStatusInfo;
MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo));
uint32_t width = 0;
uint32_t height = 0;
@ -764,7 +832,7 @@ WebGLFramebuffer::AllImageRectsMatch() const
}
FBStatus
WebGLFramebuffer::PrecheckFramebufferStatus() const
WebGLFramebuffer::PrecheckFramebufferStatus(nsCString* const out_info) const
{
MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
mContext->mBoundReadFramebuffer == this);
@ -772,7 +840,7 @@ WebGLFramebuffer::PrecheckFramebufferStatus() const
if (!HasDefinedAttachments())
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // No attachments
if (HasIncompleteAttachments())
if (HasIncompleteAttachments(out_info))
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
if (!mContext->IsWebGL2()) {
@ -791,12 +859,12 @@ WebGLFramebuffer::PrecheckFramebufferStatus() const
}
FBStatus
WebGLFramebuffer::CheckFramebufferStatus() const
WebGLFramebuffer::CheckFramebufferStatus(nsCString* const out_info) const
{
if (mIsKnownFBComplete)
return LOCAL_GL_FRAMEBUFFER_COMPLETE;
FBStatus ret = PrecheckFramebufferStatus();
FBStatus ret = PrecheckFramebufferStatus(out_info);
if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE)
return ret;
@ -809,46 +877,35 @@ WebGLFramebuffer::CheckFramebufferStatus() const
// TODO: This should not be unconditionally GL_FRAMEBUFFER.
ret = mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
if (ret == LOCAL_GL_FRAMEBUFFER_COMPLETE)
if (ret == LOCAL_GL_FRAMEBUFFER_COMPLETE) {
mIsKnownFBComplete = true;
} else {
out_info->AssignLiteral("Bad status according to the driver");
}
return ret;
}
bool
WebGLFramebuffer::HasCompletePlanes(GLbitfield mask)
{
if (CheckFramebufferStatus() != LOCAL_GL_FRAMEBUFFER_COMPLETE)
return false;
MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
mContext->mBoundReadFramebuffer == this);
bool hasPlanes = true;
if (mask & LOCAL_GL_COLOR_BUFFER_BIT)
hasPlanes &= mColorAttachment0.IsDefined();
if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) {
hasPlanes &= mDepthAttachment.IsDefined() ||
mDepthStencilAttachment.IsDefined();
}
if (mask & LOCAL_GL_STENCIL_BUFFER_BIT) {
hasPlanes &= mStencilAttachment.IsDefined() ||
mDepthStencilAttachment.IsDefined();
}
return hasPlanes;
}
bool
WebGLFramebuffer::CheckAndInitializeAttachments()
WebGLFramebuffer::ValidateAndInitAttachments(const char* funcName)
{
MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
mContext->mBoundReadFramebuffer == this);
if (CheckFramebufferStatus() != LOCAL_GL_FRAMEBUFFER_COMPLETE)
nsCString fbStatusInfo;
const auto fbStatus = CheckFramebufferStatus(&fbStatusInfo);
if (fbStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
nsCString errorText = nsPrintfCString("Incomplete framebuffer: Status 0x%04x",
fbStatus.get());
if (fbStatusInfo.Length()) {
errorText += ": ";
errorText += fbStatusInfo;
}
mContext->ErrorInvalidFramebufferOperation("%s: %s.", funcName,
errorText.BeginReading());
return false;
}
// Cool! We've checked out ok. Just need to initialize.
@ -1019,11 +1076,8 @@ WebGLFramebuffer::ValidateForRead(const char* funcName,
const webgl::FormatUsageInfo** const out_format,
uint32_t* const out_width, uint32_t* const out_height)
{
if (!CheckAndInitializeAttachments()) {
mContext->ErrorInvalidFramebufferOperation("%s: Incomplete framebuffer.",
funcName);
if (!ValidateAndInitAttachments(funcName))
return false;
}
if (mReadBufferMode == LOCAL_GL_NONE) {
mContext->ErrorInvalidOperation("%s: Read buffer mode must not be"

View File

@ -92,6 +92,7 @@ public:
GLint MipLevel() const {
return mTexImageLevel;
}
void AttachmentName(nsCString* out) const;
bool HasUninitializedImageData() const;
void SetImageDataStatus(WebGLImageDataStatus x);
@ -100,7 +101,7 @@ public:
//const WebGLRectangleObject& RectangleObject() const;
bool HasImage() const;
bool IsComplete(WebGLContext* webgl) const;
bool IsComplete(WebGLContext* webgl, nsCString* const out_info) const;
void FinalizeAttachment(gl::GLContext* gl, GLenum attachmentLoc) const;
@ -226,10 +227,10 @@ public:
GLint layer);
bool HasDefinedAttachments() const;
bool HasIncompleteAttachments() const;
bool HasIncompleteAttachments(nsCString* const out_info) const;
bool AllImageRectsMatch() const;
FBStatus PrecheckFramebufferStatus() const;
FBStatus CheckFramebufferStatus() const;
FBStatus PrecheckFramebufferStatus(nsCString* const out_info) const;
FBStatus CheckFramebufferStatus(nsCString* const out_info) const;
const webgl::FormatUsageInfo*
GetFormatForAttachment(const WebGLFBAttachPoint& attachment) const;
@ -271,10 +272,7 @@ public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLFramebuffer)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLFramebuffer)
// mask mirrors glClear.
bool HasCompletePlanes(GLbitfield mask);
bool CheckAndInitializeAttachments();
bool ValidateAndInitAttachments(const char* funcName);
void InvalidateFramebufferStatus() const {
mIsKnownFBComplete = false;

View File

@ -40,6 +40,9 @@ WebGLQuery::Delete()
bool
WebGLQuery::IsActive() const
{
if (!HasEverBeenActive())
return false;
WebGLRefPtr<WebGLQuery>& targetSlot = mContext->GetQuerySlotByTarget(mType);
return targetSlot.get() == this;

View File

@ -24,7 +24,7 @@ public:
bool IsActive() const;
bool HasEverBeenActive() {
bool HasEverBeenActive() const {
return mType != 0;
}

View File

@ -164,9 +164,11 @@ WebGLTexture::MemoryUsage() const
if (IsDeleted())
return 0;
size_t result = 0;
MOZ_CRASH("todo");
return result;
size_t accum = 0;
for (const auto& cur : mImageInfoArr) {
accum += cur.MemoryUsage();
}
return accum;
}
void
@ -588,8 +590,8 @@ WebGLTexture::InitializeImageData(const char* funcName, TexImageTarget target,
const auto& height = imageInfo.mHeight;
const auto& depth = imageInfo.mDepth;
if (!ZeroTextureData(mContext, funcName, respecifyTexture, target, level, usage, 0, 0,
0, width, height, depth))
if (!ZeroTextureData(mContext, funcName, respecifyTexture, mGLName, target, level,
usage, 0, 0, 0, width, height, depth))
{
return false;
}

View File

@ -1778,7 +1778,7 @@ WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internal
const bool respecifyTexture = true;
const uint8_t zOffset = 0;
if (!ZeroTextureData(mContext, funcName, respecifyTexture, target, level,
if (!ZeroTextureData(mContext, funcName, respecifyTexture, mGLName, target, level,
dstUsage, 0, 0, zOffset, width, height, depth))
{
mContext->ErrorOutOfMemory("%s: Failed to zero texture data.", funcName);
@ -1788,7 +1788,7 @@ WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internal
if (!rwWidth || !rwHeight) {
// There aren't any, so we're 'done'.
mContext->DummyFramebufferOperation(funcName);
mContext->DummyReadFramebufferOperation(funcName);
return;
}
@ -1879,7 +1879,7 @@ WebGLTexture::CopyTexSubImage(const char* funcName, TexImageTarget target, GLint
if (!rwWidth || !rwHeight) {
// There aren't any, so we're 'done'.
mContext->DummyFramebufferOperation(funcName);
mContext->DummyReadFramebufferOperation(funcName);
return;
}

View File

@ -1148,9 +1148,11 @@ HTMLSelectElement::SetValue(const nsAString& aValue)
option->GetValue(optionVal);
if (optionVal.Equals(aValue)) {
SetSelectedIndexInternal(int32_t(i), true);
break;
return NS_OK;
}
}
// No matching option was found.
SetSelectedIndexInternal(-1, true);
return NS_OK;
}

View File

@ -563,18 +563,12 @@ nsBrowserElement::GetAllowedAudioChannels(
return;
}
nsCOMPtr<mozIApplication> parentApp;
aRv = GetParentApplication(getter_AddRefs(parentApp));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
("nsBrowserElement, GetAllowedAudioChannels, this = %p\n", this));
GenerateAllowedAudioChannels(window, frameLoader, mBrowserElementAPI,
manifestURL, parentApp,
mBrowserElementAudioChannels, aRv);
manifestURL, mBrowserElementAudioChannels,
aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
@ -589,7 +583,6 @@ nsBrowserElement::GenerateAllowedAudioChannels(
nsIFrameLoader* aFrameLoader,
nsIBrowserElementAPI* aAPI,
const nsAString& aManifestURL,
mozIApplication* aParentApp,
nsTArray<RefPtr<BrowserElementAudioChannel>>& aAudioChannels,
ErrorResult& aRv)
{
@ -632,19 +625,6 @@ nsBrowserElement::GenerateAllowedAudioChannels(
permissionName.AssignASCII("audio-channel-");
permissionName.AppendASCII(audioChannelTable[i].tag);
// In case of nested iframes we want to check if the parent has the
// permission to use this AudioChannel.
if (aParentApp) {
aRv = aParentApp->HasPermission(permissionName.get(), &allowed);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
if (!allowed) {
continue;
}
}
aRv = app->HasPermission(permissionName.get(), &allowed);
if (NS_WARN_IF(aRv.Failed())) {
return;

View File

@ -125,14 +125,11 @@ public:
nsIFrameLoader* aFrameLoader,
nsIBrowserElementAPI* aAPI,
const nsAString& aManifestURL,
mozIApplication* aParentApp,
nsTArray<RefPtr<dom::BrowserElementAudioChannel>>& aAudioChannels,
ErrorResult& aRv);
protected:
NS_IMETHOD_(already_AddRefed<nsFrameLoader>) GetFrameLoader() = 0;
NS_IMETHOD GetParentApplication(mozIApplication** aApplication) = 0;
void InitBrowserElementAPI();
nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
nsTArray<RefPtr<dom::BrowserElementAudioChannel>> mBrowserElementAudioChannels;

View File

@ -191,35 +191,6 @@ nsGenericHTMLFrameElement::GetFrameLoader()
return loader.forget();
}
NS_IMETHODIMP
nsGenericHTMLFrameElement::GetParentApplication(mozIApplication** aApplication)
{
if (!aApplication) {
return NS_ERROR_FAILURE;
}
*aApplication = nullptr;
uint32_t appId;
nsIPrincipal *principal = NodePrincipal();
nsresult rv = principal->GetAppId(&appId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
if (NS_WARN_IF(!appsService)) {
return NS_ERROR_FAILURE;
}
rv = appsService->GetAppByLocalId(appId, aApplication);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
nsGenericHTMLFrameElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
{

View File

@ -598,4 +598,5 @@ skip-if = buildapp == 'b2g' # bug 1129014
[test_viewport_resize.html]
[test_extapp.html]
[test_image_clone_load.html]
[test_bug1203668.html]
[test_bug1166138.html]

View File

@ -0,0 +1,62 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1203668
-->
<head>
<title>Test for Bug 1203668</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1203668">Mozilla Bug 1203668</a>
<p id="display"></p>
<div id="content">
<select class="select" multiple>
<option value="foo" selected>foo</option>
<option value="bar" selected>bar</option>
</select>
<select class="select" multiple>
<option value="foo">foo</option>
<option value="bar" selected>bar</option>
</select>
<select class="select" multiple>
<option value="foo">foo</option>
<option value="bar">bar</option>
</select>
<select class="select" size=1>
<option value="foo">foo</option>
<option value="bar" selected>bar</option>
</select>
<select class="select" size=1>
<option value="foo">foo</option>
<option value="bar">bar</option>
</select>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 1203668 **/
SimpleTest.waitForExplicitFinish();
function runTest()
{
var selects = document.querySelectorAll('.select');
for (i=0; i < selects.length; i++) {
var select = selects[i];
select.value = "bogus"
is(select.selectedIndex, -1, "no option is selected");
is(select.children[0].selected, false, "first option is not selected");
is(select.children[1].selected, false, "second option is not selected");
}
SimpleTest.finish();
}
SimpleTest.waitForFocus(runTest);
</script>
</pre>
</body>
</html>

View File

@ -157,8 +157,7 @@ private:
SetLazyData(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate,
BlobDirState aDirState) override
int64_t aLastModifiedDate) override
{
MOZ_CRASH("This should never be called!");
}

View File

@ -5,7 +5,7 @@
#include "domstubs.idl"
[builtinclass, scriptable, uuid(8e49f7b0-1f98-4939-bf91-e9c39cd56434)]
[scriptable, uuid(7615408c-1fb3-4128-8dd5-a3e2f3fa8842)]
interface nsITabParent : nsISupports
{
void injectTouchEvent(in AString aType,

View File

@ -2039,8 +2039,7 @@ public:
SetLazyData(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate,
BlobDirState aDirState) override;
int64_t aLastModifiedDate) override;
virtual bool
IsMemoryFile() const override;
@ -2782,8 +2781,7 @@ BlobParent::
RemoteBlobImpl::SetLazyData(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate,
BlobDirState aDirState)
int64_t aLastModifiedDate)
{
MOZ_CRASH("This should never be called!");
}
@ -3487,8 +3485,7 @@ BlobChild::SetMysteryBlobInfo(const nsString& aName,
MOZ_ASSERT(mRemoteBlobImpl);
MOZ_ASSERT(aLastModifiedDate != INT64_MAX);
mBlobImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate,
aDirState);
mBlobImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate);
FileBlobConstructorParams params(aName,
aContentType,
@ -3509,8 +3506,7 @@ BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
nsString voidString;
voidString.SetIsVoid(true);
mBlobImpl->SetLazyData(voidString, aContentType, aLength, INT64_MAX,
BlobDirState::eUnknownIfDir);
mBlobImpl->SetLazyData(voidString, aContentType, aLength, INT64_MAX);
NormalBlobConstructorParams params(aContentType,
aLength,
@ -4336,8 +4332,7 @@ BlobParent::RecvResolveMystery(const ResolveMysteryParams& aParams)
mBlobImpl->SetLazyData(voidString,
params.contentType(),
params.length(),
INT64_MAX,
BlobDirState::eUnknownIfDir);
INT64_MAX);
return true;
}
@ -4362,8 +4357,7 @@ BlobParent::RecvResolveMystery(const ResolveMysteryParams& aParams)
mBlobImpl->SetLazyData(params.name(),
params.contentType(),
params.length(),
params.modDate(),
BlobDirState(params.dirState()));
params.modDate());
return true;
}

View File

@ -760,13 +760,6 @@ child:
*/
HandleAccessKey(uint32_t[] charCodes, bool isTrusted, int32_t modifierMask);
/**
* Propagate a refresh to the child process
*/
AudioChannelChangeNotification(uint32_t aAudioChannel,
float aVolume,
bool aMuted);
/*
* FIXME: write protocol!

View File

@ -2254,27 +2254,6 @@ TabChild::RecvHandleAccessKey(nsTArray<uint32_t>&& aCharCodes,
return true;
}
bool
TabChild::RecvAudioChannelChangeNotification(const uint32_t& aAudioChannel,
const float& aVolume,
const bool& aMuted)
{
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(WebNavigation());
if (window) {
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
MOZ_ASSERT(service);
service->SetAudioChannelVolume(window,
static_cast<AudioChannel>(aAudioChannel),
aVolume);
service->SetAudioChannelMuted(window,
static_cast<AudioChannel>(aAudioChannel),
aMuted);
}
return true;
}
bool
TabChild::RecvDestroy()
{

View File

@ -504,10 +504,6 @@ public:
const bool& aIsTrusted,
const int32_t& aModifierMask) override;
virtual bool RecvAudioChannelChangeNotification(const uint32_t& aAudioChannel,
const float& aVolume,
const bool& aMuted) override;
/**
* Native widget remoting protocol for use with windowed plugins with e10s.
*/

View File

@ -184,7 +184,7 @@ private:
// Our TabParent may have been destroyed already. If so, don't send any
// fds over, just go back to the IO thread and close them.
if (!tabParent->IsDestroyed()) {
Unused << tabParent->SendCacheFileDescriptor(mPath, fd);
mozilla::Unused << tabParent->SendCacheFileDescriptor(mPath, fd);
}
if (!mFD) {
@ -232,7 +232,7 @@ private:
// Intentionally leak the runnable (but not the fd) rather
// than crash when trying to release a main thread object
// off the main thread.
Unused << mTabParent.forget();
mozilla::Unused << mTabParent.forget();
CloseFile();
}
}
@ -387,11 +387,6 @@ TabParent::AddWindowListeners()
mPresShellWithRefreshListener = shell;
shell->AddPostRefreshObserver(this);
}
RefPtr<AudioChannelService> acs = AudioChannelService::GetOrCreate();
if (acs) {
acs->RegisterTabParent(this);
}
}
}
@ -410,11 +405,6 @@ TabParent::RemoveWindowListeners()
mPresShellWithRefreshListener->RemovePostRefreshObserver(this);
mPresShellWithRefreshListener = nullptr;
}
RefPtr<AudioChannelService> acs = AudioChannelService::GetOrCreate();
if (acs) {
acs->UnregisterTabParent(this);
}
}
void
@ -2646,6 +2636,7 @@ TabParent::RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel,
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
nsAutoCString topic;
topic.Assign("audiochannel-activity-");
topic.Append(AudioChannelService::GetAudioChannelTable()[aAudioChannel].tag);
@ -3406,33 +3397,6 @@ TabParent::GetShowInfo()
mDPI, mDefaultScale.scale);
}
void
TabParent::AudioChannelChangeNotification(nsPIDOMWindow* aWindow,
AudioChannel aAudioChannel,
float aVolume,
bool aMuted)
{
if (!mFrameElement || !mFrameElement->OwnerDoc()) {
return;
}
nsCOMPtr<nsPIDOMWindow> window = mFrameElement->OwnerDoc()->GetWindow();
while (window) {
if (window == aWindow) {
Unused << SendAudioChannelChangeNotification(static_cast<uint32_t>(aAudioChannel),
aVolume, aMuted);
break;
}
nsCOMPtr<nsPIDOMWindow> win = window->GetScriptableParent();
if (window == win) {
break;
}
window = win;
}
}
NS_IMETHODIMP
FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo)
{

View File

@ -9,7 +9,6 @@
#include "js/TypeDecls.h"
#include "mozilla/ContentCache.h"
#include "mozilla/dom/AudioChannelBinding.h"
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/dom/PBrowserParent.h"
#include "mozilla/dom/PContent.h"
@ -456,11 +455,6 @@ public:
void OnStartSignedPackageRequest(nsIChannel* aChannel,
const nsACString& aPackageId);
void AudioChannelChangeNotification(nsPIDOMWindow* aWindow,
AudioChannel aAudioChannel,
float aVolume,
bool aMuted);
protected:
bool ReceiveMessage(const nsString& aMessage,
bool aSync,

View File

@ -9,3 +9,4 @@ ImageMapPolyWrongNumberOfCoords=The "coords" attribute of the <area shape="poly"
ImageMapPolyOddNumberOfCoords=The "coords" attribute of the <area shape="poly"> tag is missing the last "y" coordinate (the correct format is "x1,y1,x2,y2 …").
TablePartRelPosWarning=Relative positioning of table rows and row groups is now supported. This site may need to be updated because it may depend on this feature having no effect.
ScrollLinkedEffectFound=This site appears to use a scroll-linked positioning effect. This may not work well with asynchronous panning; see https://developers.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for further details and to join the discussion on related tools and features!

View File

@ -2174,7 +2174,6 @@ MediaDecoderStateMachine::SeekCompleted()
RefPtr<ShutdownPromise>
MediaDecoderStateMachine::BeginShutdown()
{
mStreamSink->BeginShutdown();
return InvokeAsync(OwnerThread(), this, __func__,
&MediaDecoderStateMachine::Shutdown);
}

View File

@ -1,573 +0,0 @@
/* -*- 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/GMPVideoDecoderTrialCreator.h"
#include "mozilla/Preferences.h"
#include "prsystem.h"
#include "GMPVideoHost.h"
#include "mozilla/EMEUtils.h"
#include "nsServiceManagerUtils.h"
#include "GMPService.h"
#include "VideoUtils.h"
#include "nsPrintfCString.h"
#include "nsXULAppAPI.h"
namespace mozilla {
namespace dom {
static already_AddRefed<nsIThread>
GetGMPThread()
{
nsCOMPtr<mozIGeckoMediaPluginService> gmps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (!gmps) {
return nullptr;
}
nsCOMPtr<nsIThread> gmpThread;
nsresult rv = gmps->GetThread(getter_AddRefs(gmpThread));
if (NS_FAILED(rv)) {
return nullptr;
}
return gmpThread.forget();
}
#ifdef DEBUG
static bool
OnGMPThread()
{
nsCOMPtr<nsIThread> currentThread;
NS_GetCurrentThread(getter_AddRefs(currentThread));
nsCOMPtr<nsIThread> gmpThread(GetGMPThread());
return !!gmpThread && !!currentThread && gmpThread == currentThread;
}
#endif
static const char*
TrialCreatePrefName(const nsAString& aKeySystem)
{
if (aKeySystem.EqualsLiteral("com.adobe.primetime")) {
return "media.gmp-eme-adobe.trial-create";
}
if (aKeySystem.EqualsLiteral("org.w3.clearkey")) {
return "media.gmp-eme-clearkey.trial-create";
}
return nullptr;
}
/* static */
GMPVideoDecoderTrialCreator::TrialCreateState
GMPVideoDecoderTrialCreator::GetCreateTrialState(const nsAString& aKeySystem)
{
if (Preferences::GetBool("media.gmp.always-trial-create", false)) {
return Pending;
}
const char* pref = TrialCreatePrefName(aKeySystem);
if (!pref) {
return Pending;
}
switch (Preferences::GetInt(pref, (int)Pending)) {
case 0: return Pending;
case 1: return Succeeded;
case 2: return Failed;
default: return Pending;
}
}
/* static */ void
GMPVideoDecoderTrialCreator::UpdateTrialCreateState(const nsAString& aKeySystem,
uint32_t aState)
{
UpdateTrialCreateState(aKeySystem, (TrialCreateState)aState);
}
/* static */ void
GMPVideoDecoderTrialCreator::UpdateTrialCreateState(const nsAString& aKeySystem,
TrialCreateState aState)
{
MOZ_ASSERT(NS_IsMainThread());
if (XRE_GetProcessType() == GeckoProcessType_Content) {
// Pref has to be set from the chrome process. Dispatch to chrome via
// GMPService.
nsCOMPtr<mozIGeckoMediaPluginService> service =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
NS_ENSURE_TRUE_VOID(service);
service->UpdateTrialCreateState(aKeySystem, (uint32_t)aState);
return;
}
const char* pref = TrialCreatePrefName(aKeySystem);
if (pref) {
Preferences::SetInt(pref, (int)aState);
}
}
void
GMPVideoDecoderTrialCreator::TrialCreateGMPVideoDecoderFailed(const nsAString& aKeySystem,
const nsACString& aReason)
{
MOZ_ASSERT(NS_IsMainThread());
EME_LOG("GMPVideoDecoderTrialCreator::TrialCreateGMPVideoDecoderFailed(%s)",
NS_ConvertUTF16toUTF8(aKeySystem).get());
TrialCreateData* data = mTestCreate.Get(aKeySystem);
if (!data) {
return;
}
data->mStatus = Failed;
UpdateTrialCreateState(aKeySystem, Failed);
for (RefPtr<AbstractPromiseLike>& promise: data->mPending) {
promise->Reject(NS_ERROR_DOM_NOT_SUPPORTED_ERR, aReason);
}
data->mPending.Clear();
data->mTest = nullptr;
}
void
GMPVideoDecoderTrialCreator::TrialCreateGMPVideoDecoderSucceeded(const nsAString& aKeySystem)
{
MOZ_ASSERT(NS_IsMainThread());
EME_LOG("GMPVideoDecoderTrialCreator::TrialCreateGMPVideoDecoderSucceeded(%s)",
NS_ConvertUTF16toUTF8(aKeySystem).get());
TrialCreateData* data = mTestCreate.Get(aKeySystem);
if (!data) {
return;
}
data->mStatus = Succeeded;
UpdateTrialCreateState(aKeySystem, Succeeded);
for (RefPtr<AbstractPromiseLike>& promise : data->mPending) {
promise->Resolve();
}
data->mPending.Clear();
data->mTest = nullptr;
}
nsresult
TestGMPVideoDecoder::Start()
{
MOZ_ASSERT(NS_IsMainThread());
mGMPService = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (!mGMPService) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIThread> thread(GetGMPThread());
if (!thread) {
return NS_ERROR_FAILURE;
}
RefPtr<nsIRunnable> task(NS_NewRunnableMethod(this, &TestGMPVideoDecoder::CreateGMPVideoDecoder));
return thread->Dispatch(task, NS_DISPATCH_NORMAL);
}
struct ExpectedPlaneDecodePlane {
GMPPlaneType mPlane;
size_t mLength;
uint8_t mValue;
int32_t mSize; // width & height
};
static const ExpectedPlaneDecodePlane sExpectedPlanes[3] = {
{
kGMPYPlane,
112 * 112, // 12544
0x4c,
112
},
{ // U
kGMPUPlane,
56 * 56, // 3136
0x55,
56,
},
{ // V
kGMPVPlane,
56 * 56, // 3136
0xff,
56,
}
};
static bool TestDecodedFrame(GMPVideoi420Frame* aDecodedFrame)
{
MOZ_ASSERT(OnGMPThread());
if (aDecodedFrame->Width() != 112 || aDecodedFrame->Height() != 112) {
EME_LOG("TestDecodedFrame() - Invalid decoded frame dimensions");
return false;
}
for (const ExpectedPlaneDecodePlane& plane : sExpectedPlanes) {
int32_t stride = aDecodedFrame->Stride(plane.mPlane);
if (stride < plane.mSize) {
EME_LOG("TestDecodedFrame() - Insufficient decoded frame stride");
return false;
}
int32_t length = plane.mSize * plane.mSize;
if (aDecodedFrame->AllocatedSize(plane.mPlane) < length) {
EME_LOG("TestDecodedFrame() - Insufficient decoded frame allocated size");
return false;
}
const uint8_t* data = aDecodedFrame->Buffer(plane.mPlane);
for (int32_t row = 0; row < plane.mSize; row++) {
for (int32_t i = 0; i < plane.mSize; i++) {
size_t off = (stride * row) + i;
if (data[off] != plane.mValue) {
EME_LOG("TestDecodedFrame() - Invalid decoded frame contents");
return false;
}
}
}
}
return true;
}
void
TestGMPVideoDecoder::Decoded(GMPVideoi420Frame* aDecodedFrame)
{
MOZ_ASSERT(OnGMPThread());
if (!mReceivedDecoded) {
mReceivedDecoded = true;
} else {
EME_LOG("Received multiple decoded frames");
ReportFailure(NS_LITERAL_CSTRING("TestGMPVideoDecoder received multiple decoded frames"));
return;
}
GMPUniquePtr<GMPVideoi420Frame> decodedFrame(aDecodedFrame);
if (!TestDecodedFrame(aDecodedFrame)) {
EME_LOG("decoded frame failed verification");
ReportFailure(NS_LITERAL_CSTRING("TestGMPVideoDecoder decoded frame failed verification"));
}
}
void
TestGMPVideoDecoder::DrainComplete()
{
EME_LOG("TestGMPVideoDecoder::DrainComplete()");
MOZ_ASSERT(OnGMPThread());
ReportSuccess();
}
void
TestGMPVideoDecoder::Error(GMPErr aErr)
{
EME_LOG("TestGMPVideoDecoder::ReceivedDecodedFrame()");
MOZ_ASSERT(OnGMPThread());
ReportFailure(nsPrintfCString("TestGMPVideoDecoder error %d", aErr));
}
void
TestGMPVideoDecoder::Terminated()
{
EME_LOG("TestGMPVideoDecoder::Terminated()");
MOZ_ASSERT(OnGMPThread());
ReportFailure(NS_LITERAL_CSTRING("TestGMPVideoDecoder GMP terminated"));
}
void
TestGMPVideoDecoder::ReportFailure(const nsACString& aReason)
{
MOZ_ASSERT(OnGMPThread());
if (mGMP) {
mGMP->Close();
mGMP = nullptr;
}
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArgs<nsString, nsCString>(mInstance,
&GMPVideoDecoderTrialCreator::TrialCreateGMPVideoDecoderFailed,
mKeySystem,
aReason);
NS_DispatchToMainThread(task, NS_DISPATCH_NORMAL);
}
void
TestGMPVideoDecoder::ReportSuccess()
{
MOZ_ASSERT(OnGMPThread());
if (mGMP) {
mGMP->Close();
mGMP = nullptr;
}
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArg<nsString>(mInstance,
&GMPVideoDecoderTrialCreator::TrialCreateGMPVideoDecoderSucceeded,
mKeySystem);
NS_DispatchToMainThread(task, NS_DISPATCH_NORMAL);
}
// A solid red, 112x112 frame. Display size 100x100 pixels.
// Generated with ImageMagick/ffmpeg
// $ convert -size 100x100 xc:rgb\(255, 0, 0\) red.png
// $ ffmpeg -f image2 -i red.png -r 24 -b:v 200k -c:v libx264 -profile:v baseline -level 1 -v:r 24 red.mp4 -y
static const uint8_t sTestH264Frame[] = {
0x00, 0x00, 0x02, 0x81, 0x06, 0x05, 0xff, 0xff, 0x7d, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9,
0x48, 0xb7, 0x96, 0x2c, 0xd8, 0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20,
0x2d, 0x20, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x31, 0x33, 0x35, 0x20, 0x72, 0x32, 0x33, 0x34,
0x35, 0x20, 0x66, 0x30, 0x63, 0x31, 0x63, 0x35, 0x33, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32,
0x36, 0x34, 0x2f, 0x4d, 0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63,
0x6f, 0x64, 0x65, 0x63, 0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74,
0x20, 0x32, 0x30, 0x30, 0x33, 0x2d, 0x32, 0x30, 0x31, 0x33, 0x20, 0x2d, 0x20, 0x68, 0x74,
0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c,
0x61, 0x6e, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d,
0x6c, 0x20, 0x2d, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x20, 0x63, 0x61,
0x62, 0x61, 0x63, 0x3d, 0x30, 0x20, 0x72, 0x65, 0x66, 0x3d, 0x33, 0x20, 0x64, 0x65, 0x62,
0x6c, 0x6f, 0x63, 0x6b, 0x3d, 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c,
0x79, 0x73, 0x65, 0x3d, 0x30, 0x78, 0x31, 0x3a, 0x30, 0x78, 0x31, 0x31, 0x31, 0x20, 0x6d,
0x65, 0x3d, 0x68, 0x65, 0x78, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x65, 0x3d, 0x37, 0x20, 0x70,
0x73, 0x79, 0x3d, 0x31, 0x20, 0x70, 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, 0x30,
0x30, 0x3a, 0x30, 0x2e, 0x30, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x72, 0x65,
0x66, 0x3d, 0x31, 0x20, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x31, 0x36,
0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x6d, 0x65, 0x3d, 0x31, 0x20, 0x74, 0x72,
0x65, 0x6c, 0x6c, 0x69, 0x73, 0x3d, 0x31, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d,
0x30, 0x20, 0x63, 0x71, 0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, 0x6f, 0x6e,
0x65, 0x3d, 0x32, 0x31, 0x2c, 0x31, 0x31, 0x20, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x73,
0x6b, 0x69, 0x70, 0x3d, 0x31, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70,
0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65,
0x61, 0x64, 0x73, 0x3d, 0x31, 0x32, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61,
0x64, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x31, 0x20, 0x73, 0x6c, 0x69,
0x63, 0x65, 0x64, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x30, 0x20, 0x6e,
0x72, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x3d, 0x30, 0x20, 0x62, 0x6c,
0x75, 0x72, 0x61, 0x79, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x3d, 0x30, 0x20, 0x63,
0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x72,
0x61, 0x3d, 0x30, 0x20, 0x62, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, 0x30, 0x20, 0x77,
0x65, 0x69, 0x67, 0x68, 0x74, 0x70, 0x3d, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74,
0x3d, 0x32, 0x35, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e,
0x3d, 0x32, 0x34, 0x20, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x63, 0x75, 0x74, 0x3d, 0x34, 0x30,
0x20, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x3d,
0x30, 0x20, 0x72, 0x63, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61, 0x64, 0x3d,
0x34, 0x30, 0x20, 0x72, 0x63, 0x3d, 0x61, 0x62, 0x72, 0x20, 0x6d, 0x62, 0x74, 0x72, 0x65,
0x65, 0x3d, 0x31, 0x20, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x3d, 0x32, 0x30, 0x30,
0x20, 0x72, 0x61, 0x74, 0x65, 0x74, 0x6f, 0x6c, 0x3d, 0x31, 0x2e, 0x30, 0x20, 0x71, 0x63,
0x6f, 0x6d, 0x70, 0x3d, 0x30, 0x2e, 0x36, 0x30, 0x20, 0x71, 0x70, 0x6d, 0x69, 0x6e, 0x3d,
0x30, 0x20, 0x71, 0x70, 0x6d, 0x61, 0x78, 0x3d, 0x36, 0x39, 0x20, 0x71, 0x70, 0x73, 0x74,
0x65, 0x70, 0x3d, 0x34, 0x20, 0x69, 0x70, 0x5f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x3d, 0x31,
0x2e, 0x34, 0x30, 0x20, 0x61, 0x71, 0x3d, 0x31, 0x3a, 0x31, 0x2e, 0x30, 0x30, 0x00, 0x80,
0x00, 0x00, 0x00, 0x39, 0x65, 0x88, 0x84, 0x0c, 0xf1, 0x18, 0xa0, 0x00, 0x23, 0xbf, 0x1c,
0x00, 0x04, 0x3c, 0x63, 0x80, 0x00, 0x98, 0x44, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x75,
0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75,
0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75,
0xe0
};
static GMPUniquePtr<GMPVideoEncodedFrame>
CreateFrame(GMPVideoHost* aHost)
{
MOZ_ASSERT(OnGMPThread());
GMPVideoFrame* ftmp = nullptr;
GMPErr err = aHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
if (GMP_FAILED(err)) {
return nullptr;
}
GMPUniquePtr<GMPVideoEncodedFrame> frame(static_cast<GMPVideoEncodedFrame*>(ftmp));
err = frame->CreateEmptyFrame(MOZ_ARRAY_LENGTH(sTestH264Frame));
if (GMP_FAILED(err)) {
return nullptr;
}
memcpy(frame->Buffer(), sTestH264Frame, MOZ_ARRAY_LENGTH(sTestH264Frame));
frame->SetBufferType(GMP_BufferLength32);
frame->SetEncodedWidth(100);
frame->SetEncodedHeight(100);
frame->SetTimeStamp(0);
frame->SetCompleteFrame(true);
frame->SetDuration(41666);
frame->SetFrameType(kGMPKeyFrame);
return frame;
}
static const uint8_t sTestH264CodecSpecific[] = {
0x01, 0x42, 0xc0, 0x0a, 0xff, 0xe1, 0x00, 0x18, 0x67, 0x42, 0xc0, 0x0a, 0xd9, 0x07, 0x3f,
0x9e, 0x79, 0xb2, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x60, 0x1e, 0x24,
0x4c, 0x90, 0x01, 0x00, 0x04, 0x68, 0xcb, 0x8c, 0xb2
};
void
TestGMPVideoDecoder::Callback::Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
{
MOZ_ASSERT(OnGMPThread());
if (!aHost || !aGMP) {
mInstance->ReportFailure(NS_LITERAL_CSTRING("TestGMPVideoDecoder null host or GMP on Get"));
return;
}
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArgs<GMPVideoDecoderProxy*, GMPVideoHost*>(mInstance,
&TestGMPVideoDecoder::ActorCreated,
aGMP, aHost);
NS_DispatchToMainThread(task, NS_DISPATCH_NORMAL);
}
void
TestGMPVideoDecoder::ActorCreated(GMPVideoDecoderProxy* aGMP,
GMPVideoHost* aHost)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aHost && aGMP);
// Add crash handler.
RefPtr<gmp::GeckoMediaPluginService> service =
gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
service->AddPluginCrashedEventTarget(aGMP->GetPluginId(), mWindow);
nsCOMPtr<nsIThread> thread(GetGMPThread());
if (!thread) {
mInstance->TrialCreateGMPVideoDecoderFailed(mKeySystem,
NS_LITERAL_CSTRING("Failed to get GMP thread in TestGMPVideoDecoder::ActorCreated"));
return;
}
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArgs<GMPVideoDecoderProxy*, GMPVideoHost*>(this,
&TestGMPVideoDecoder::InitGMPDone,
aGMP, aHost);
thread->Dispatch(task, NS_DISPATCH_NORMAL);
}
void
TestGMPVideoDecoder::InitGMPDone(GMPVideoDecoderProxy* aGMP,
GMPVideoHost* aHost)
{
MOZ_ASSERT(OnGMPThread());
MOZ_ASSERT(aHost && aGMP);
mGMP = aGMP;
mHost = aHost;
GMPVideoCodec codec;
memset(&codec, 0, sizeof(codec));
codec.mGMPApiVersion = kGMPVersion33;
codec.mCodecType = kGMPVideoCodecH264;
codec.mWidth = 100;
codec.mHeight = 100;
nsTArray<uint8_t> codecSpecific;
codecSpecific.AppendElement(0); // mPacketizationMode.
codecSpecific.AppendElements(sTestH264CodecSpecific,
MOZ_ARRAY_LENGTH(sTestH264CodecSpecific));
nsresult rv = mGMP->InitDecode(codec,
codecSpecific,
this,
PR_GetNumberOfProcessors());
if (NS_FAILED(rv)) {
EME_LOG("InitGMPDone() - InitDecode() failed!");
ReportFailure(NS_LITERAL_CSTRING("TestGMPVideoDecoder InitDecode() returned failure"));
return;
}
GMPUniquePtr<GMPVideoEncodedFrame> frame = CreateFrame(aHost);
if (!frame) {
EME_LOG("InitGMPDone() - Decode() failed to create frame!");
ReportFailure(NS_LITERAL_CSTRING("TestGMPVideoDecoder Decode() failed to create frame"));
return;
}
nsTArray<uint8_t> info; // No codec specific per-frame info to pass.
rv = mGMP->Decode(Move(frame), false, info, 0);
if (NS_FAILED(rv)) {
EME_LOG("InitGMPDone() - Decode() failed to send Decode message!");
ReportFailure(NS_LITERAL_CSTRING("TestGMPVideoDecoder Decode() returned failure"));
return;
}
rv = mGMP->Drain();
if (NS_FAILED(rv)) {
EME_LOG("InitGMPDone() - Drain() failed to send Drain message!");
ReportFailure(NS_LITERAL_CSTRING("TestGMPVideoDecoder Drain() returned failure"));
return;
}
}
void
TestGMPVideoDecoder::CreateGMPVideoDecoder()
{
MOZ_ASSERT(OnGMPThread());
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
UniquePtr<GetGMPVideoDecoderCallback> callback(new Callback(this));
if (NS_FAILED(mGMPService->GetGMPVideoDecoder(&tags,
NS_LITERAL_CSTRING("fakeNodeId1234567890fakeNodeId12"),
Move(callback)))) {
ReportFailure(NS_LITERAL_CSTRING("TestGMPVideoDecoder GMPService GetGMPVideoDecoder returned failure"));
}
}
void
GMPVideoDecoderTrialCreator::MaybeAwaitTrialCreate(const nsAString& aKeySystem,
AbstractPromiseLike* aPromisey,
nsPIDOMWindow* aParent)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mTestCreate.Contains(aKeySystem)) {
mTestCreate.Put(aKeySystem, new TrialCreateData(aKeySystem));
}
TrialCreateData* data = mTestCreate.Get(aKeySystem);
MOZ_ASSERT(data);
switch (data->mStatus) {
case TrialCreateState::Succeeded: {
EME_LOG("GMPVideoDecoderTrialCreator::MaybeAwaitTrialCreate(%s) already succeeded",
NS_ConvertUTF16toUTF8(aKeySystem).get());
aPromisey->Resolve();
break;
}
case TrialCreateState::Failed: {
// Something is broken about this configuration. Report as unsupported.
EME_LOG("GMPVideoDecoderTrialCreator::MaybeAwaitTrialCreate(%s) already failed",
NS_ConvertUTF16toUTF8(aKeySystem).get());
aPromisey->Reject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
NS_LITERAL_CSTRING("navigator.requestMediaKeySystemAccess trial CDM creation failed"));
break;
}
case TrialCreateState::Pending: {
EME_LOG("GMPVideoDecoderTrialCreator::MaybeAwaitTrialCreate(%s) pending",
NS_ConvertUTF16toUTF8(aKeySystem).get());
// Add request to the list of pending items waiting.
data->mPending.AppendElement(aPromisey);
if (!data->mTest) {
// Not already waiting for CDM to be created. Create and Init
// a CDM, to test whether it will work.
data->mTest = new TestGMPVideoDecoder(this, aKeySystem, aParent);
if (NS_FAILED(data->mTest->Start())) {
TrialCreateGMPVideoDecoderFailed(aKeySystem,
NS_LITERAL_CSTRING("TestGMPVideoDecoder::Start() failed"));
return;
}
// Promise will call InitMediaKeysPromiseHandler when Init()
// succeeds/fails.
}
break;
}
}
return;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,179 +0,0 @@
/* -*- 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_GMPVideoDecoderTrialCreator_h
#define mozilla_dom_GMPVideoDecoderTrialCreator_h
#include "mozilla/dom/MediaKeySystemAccess.h"
#include "nsIObserver.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupportsImpl.h"
#include "nsITimer.h"
#include "nsClassHashtable.h"
#include "mozilla/dom/MediaKeys.h"
#include "GMPVideoDecoderProxy.h"
namespace mozilla {
namespace dom {
class TestGMPVideoDecoder;
class GMPVideoDecoderTrialCreator {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPVideoDecoderTrialCreator);
void TrialCreateGMPVideoDecoderFailed(const nsAString& aKeySystem,
const nsACString& aReason);
void TrialCreateGMPVideoDecoderSucceeded(const nsAString& aKeySystem);
template<class T>
void MaybeAwaitTrialCreate(const nsAString& aKeySystem,
MediaKeySystemAccess* aAccess,
T* aPromiseLike,
nsPIDOMWindow* aParent)
{
RefPtr<PromiseLike<T>> p(new PromiseLike<T>(aPromiseLike, aAccess));
MaybeAwaitTrialCreate(aKeySystem, p, aParent);
}
static void UpdateTrialCreateState(const nsAString& aKeySystem, uint32_t aState);
private:
class AbstractPromiseLike {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractPromiseLike);
virtual void Resolve() = 0;
virtual void Reject(nsresult aResult, const nsACString& aMessage) = 0;
protected:
virtual ~AbstractPromiseLike() {}
};
template<class T>
class PromiseLike : public AbstractPromiseLike
{
public:
explicit PromiseLike(T* aPromiseLike, MediaKeySystemAccess* aAccess)
: mPromiseLike(aPromiseLike)
, mAccess(aAccess)
{
}
void Resolve() override {
MOZ_ASSERT(NS_IsMainThread());
mPromiseLike->MaybeResolve(mAccess);
}
void Reject(nsresult aResult, const nsACString& aMessage) override {
MOZ_ASSERT(NS_IsMainThread());
mPromiseLike->MaybeReject(aResult, aMessage);
}
protected:
~PromiseLike() {}
RefPtr<T> mPromiseLike;
RefPtr<MediaKeySystemAccess> mAccess;
};
void MaybeAwaitTrialCreate(const nsAString& aKeySystem,
AbstractPromiseLike* aPromisey,
nsPIDOMWindow* aParent);
~GMPVideoDecoderTrialCreator() {}
// Note: Keep this in sync with GetCreateTrialState.
enum TrialCreateState {
Pending = 0,
Succeeded = 1,
Failed = 2,
};
static TrialCreateState GetCreateTrialState(const nsAString& aKeySystem);
static void UpdateTrialCreateState(const nsAString& aKeySystem,
TrialCreateState aState);
struct TrialCreateData {
explicit TrialCreateData(const nsAString& aKeySystem)
: mKeySystem(aKeySystem)
, mStatus(GetCreateTrialState(aKeySystem))
{}
~TrialCreateData() {}
const nsString mKeySystem;
RefPtr<TestGMPVideoDecoder> mTest;
nsTArray<RefPtr<AbstractPromiseLike>> mPending;
TrialCreateState mStatus;
private:
TrialCreateData(const TrialCreateData& aOther) = delete;
TrialCreateData() = delete;
TrialCreateData& operator =(const TrialCreateData&) = delete;
};
nsClassHashtable<nsStringHashKey, TrialCreateData> mTestCreate;
};
class TestGMPVideoDecoder : public GMPVideoDecoderCallbackProxy {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestGMPVideoDecoder);
TestGMPVideoDecoder(GMPVideoDecoderTrialCreator* aInstance,
const nsAString& aKeySystem,
nsPIDOMWindow* aParent)
: mKeySystem(aKeySystem)
, mInstance(aInstance)
, mWindow(aParent)
, mGMP(nullptr)
, mHost(nullptr)
, mReceivedDecoded(false)
{}
nsresult Start();
// GMPVideoDecoderCallbackProxy
virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) override;
virtual void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) override {}
virtual void ReceivedDecodedFrame(const uint64_t aPictureId) override {}
virtual void InputDataExhausted() override {}
virtual void DrainComplete() override;
virtual void ResetComplete() override {}
virtual void Error(GMPErr aErr) override;
virtual void Terminated() override;
void ActorCreated(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost); // Main thread.
class Callback : public GetGMPVideoDecoderCallback
{
public:
explicit Callback(TestGMPVideoDecoder* aInstance)
: mInstance(aInstance)
{}
~Callback() {}
void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost) override;
private:
RefPtr<TestGMPVideoDecoder> mInstance;
};
private:
void InitGMPDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost); // GMP thread.
void CreateGMPVideoDecoder();
~TestGMPVideoDecoder() {}
void ReportFailure(const nsACString& aReason);
void ReportSuccess();
const nsString mKeySystem;
nsCOMPtr<mozIGeckoMediaPluginService> mGMPService;
RefPtr<GMPVideoDecoderTrialCreator> mInstance;
nsCOMPtr<nsPIDOMWindow> mWindow;
GMPVideoDecoderProxy* mGMP;
GMPVideoHost* mHost;
bool mReceivedDecoded;
};
} // namespace dom
} // namespace mozilla
#endif

View File

@ -33,6 +33,9 @@
#include "nsXULAppAPI.h"
#include "gmp-audio-decode.h"
#include "gmp-video-decode.h"
#ifdef XP_WIN
#include "WMFDecoderModule.h"
#endif
#if defined(XP_WIN) || defined(XP_MACOSX)
#define PRIMETIME_EME_SUPPORTED 1
@ -316,7 +319,16 @@ GMPDecryptsAndDecodesAAC(mozIGeckoMediaPluginService* aGMPS,
return HaveGMPFor(aGMPS,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
NS_LITERAL_CSTRING("aac"));
NS_LITERAL_CSTRING("aac"))
#ifdef XP_WIN
// Clearkey on Windows advertises that it can decode in its GMP info
// file, but uses Windows Media Foundation to decode. That's not present
// on Windows XP, and on some Vista, Windows N, and KN variants without
// certain services packs. So for ClearKey we must check that WMF will
// work.
&& (!aKeySystem.EqualsLiteral("org.w3.clearkey") || WMFDecoderModule::HasAAC())
#endif
;
}
static bool
@ -328,8 +340,17 @@ GMPDecryptsAndDecodesH264(mozIGeckoMediaPluginService* aGMPS,
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
return HaveGMPFor(aGMPS,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
NS_LITERAL_CSTRING("aac"));
NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
NS_LITERAL_CSTRING("h264"))
#ifdef XP_WIN
// Clearkey on Windows advertises that it can decode in its GMP info
// file, but uses Windows Media Foundation to decode. That's not present
// on Windows XP, and on some Vista, Windows N, and KN variants without
// certain services packs. So for ClearKey we must check that WMF will
// work.
&& (!aKeySystem.EqualsLiteral("org.w3.clearkey") || WMFDecoderModule::HasH264())
#endif
;
}
// If this keysystem's CDM explicitly says it doesn't support decoding,
@ -352,9 +373,12 @@ GMPDecryptsAndGeckoDecodesH264(mozIGeckoMediaPluginService* aGMPService,
NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
NS_LITERAL_CSTRING("h264"))
#ifdef XP_WIN
// Clearkey on Windows XP can't decode, but advertises that it can
// in its GMP info file.
|| (aKeySystem.EqualsLiteral("org.w3.clearkey") && !IsVistaOrLater())
// Clearkey on Windows advertises that it can decode in its GMP info
// file, but uses Windows Media Foundation to decode. That's not present
// on Windows XP, and on some Vista, Windows N, and KN variants without
// certain services packs. So don't try to use gmp-clearkey for decoding
// if we don't have a decoder here.
|| (aKeySystem.EqualsLiteral("org.w3.clearkey") && !WMFDecoderModule::HasH264())
#endif
) && MP4Decoder::CanHandleMediaType(aContentType);
}
@ -374,9 +398,12 @@ GMPDecryptsAndGeckoDecodesAAC(mozIGeckoMediaPluginService* aGMPService,
NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
NS_LITERAL_CSTRING("aac"))
#ifdef XP_WIN
// Clearkey on Windows XP can't decode, but advertises that it can
// in its GMP info file.
|| (aKeySystem.EqualsLiteral("org.w3.clearkey") && !IsVistaOrLater())
// Clearkey on Windows advertises that it can decode in its GMP info
// file, but uses Windows Media Foundation to decode. That's not present
// on Windows XP, and on some Vista, Windows N, and KN variants without
// certain services packs. So don't try to use gmp-clearkey for decoding
// if we don't have a decoder here.
|| (aKeySystem.EqualsLiteral("org.w3.clearkey") && !WMFDecoderModule::HasAAC())
#endif
) && MP4Decoder::CanHandleMediaType(aContentType);
}

View File

@ -51,7 +51,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
MediaKeySystemAccessManager::MediaKeySystemAccessManager(nsPIDOMWindow* aWindow)
: mWindow(aWindow)
, mAddedObservers(false)
, mTrialCreator(new GMPVideoDecoderTrialCreator())
{
}
@ -73,19 +72,6 @@ MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
Request(aPromise, aKeySystem, aConfigs, RequestType::Initial);
}
static bool
ShouldTrialCreateGMP(const nsAString& aKeySystem)
{
// Trial create where the CDM has a Windows Media Foundation decoder.
#ifdef XP_WIN
return Preferences::GetBool("media.gmp.trial-create.enabled", false) &&
aKeySystem.EqualsLiteral("org.w3.clearkey") &&
IsVistaOrLater();
#else
return false;
#endif
}
void
MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
const nsAString& aKeySystem,
@ -179,13 +165,6 @@ MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
MediaKeySystemAccess::IsSupported(keySystem, aConfigs)) {
RefPtr<MediaKeySystemAccess> access(
new MediaKeySystemAccess(mWindow, keySystem, NS_ConvertUTF8toUTF16(cdmVersion), config));
if (ShouldTrialCreateGMP(keySystem)) {
// Ensure we have tried creating a GMPVideoDecoder for this
// keySystem, and that we can use it to decode. This ensures that we only
// report that we support this keySystem when the CDM us usable.
mTrialCreator->MaybeAwaitTrialCreate(keySystem, access, aPromise, mWindow);
return;
}
aPromise->MaybeResolve(access);
return;
}

View File

@ -6,7 +6,6 @@
#define mozilla_dom_MediaKeySystemAccessManager_h
#include "mozilla/dom/MediaKeySystemAccess.h"
#include "mozilla/dom/GMPVideoDecoderTrialCreator.h"
#include "nsIObserver.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupportsImpl.h"
@ -76,8 +75,6 @@ private:
nsCOMPtr<nsPIDOMWindow> mWindow;
bool mAddedObservers;
RefPtr<GMPVideoDecoderTrialCreator> mTrialCreator;
};
} // namespace dom

View File

@ -5,7 +5,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.dom += [
'GMPVideoDecoderTrialCreator.h',
'MediaEncryptedEvent.h',
'MediaKeyError.h',
'MediaKeyMessageEvent.h',
@ -30,7 +29,6 @@ UNIFIED_SOURCES += [
'CDMProxy.cpp',
'DetailedPromise.cpp',
'EMEUtils.cpp',
'GMPVideoDecoderTrialCreator.cpp',
'MediaEncryptedEvent.cpp',
'MediaKeyError.cpp',
'MediaKeyMessageEvent.cpp',

View File

@ -73,8 +73,6 @@ GMPParent::GMPParent()
GMPParent::~GMPParent()
{
// Can't Close or Destroy the process here, since destruction is MainThread only
MOZ_ASSERT(NS_IsMainThread());
LOGD("GMPParent dtor");
MOZ_ASSERT(!mProcess);

View File

@ -21,7 +21,6 @@
#include "nsString.h"
#include "nsTArray.h"
#include "nsIFile.h"
#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
class nsIThread;
@ -72,7 +71,7 @@ public:
class GMPParent final : public PGMPParent
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(GMPParent)
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPParent)
GMPParent();

View File

@ -186,40 +186,6 @@ GeckoMediaPluginServiceChild::GetNodeId(const nsAString& aOrigin,
return NS_OK;
}
NS_IMETHODIMP
GeckoMediaPluginServiceChild::UpdateTrialCreateState(const nsAString& aKeySystem,
uint32_t aState)
{
if (NS_GetCurrentThread() != mGMPThread) {
mGMPThread->Dispatch(NS_NewRunnableMethodWithArgs<nsString, uint32_t>(
this, &GeckoMediaPluginServiceChild::UpdateTrialCreateState,
aKeySystem, aState), NS_DISPATCH_NORMAL);
return NS_OK;
}
class Callback : public GetServiceChildCallback
{
public:
Callback(const nsAString& aKeySystem, uint32_t aState)
: mKeySystem(aKeySystem)
, mState(aState)
{ }
virtual void Done(GMPServiceChild* aService) override
{
aService->SendUpdateGMPTrialCreateState(mKeySystem, mState);
}
private:
nsString mKeySystem;
uint32_t mState;
};
UniquePtr<GetServiceChildCallback> callback(new Callback(aKeySystem, aState));
GetServiceChild(Move(callback));
return NS_OK;
}
NS_IMETHODIMP
GeckoMediaPluginServiceChild::Observe(nsISupports* aSubject,
const char* aTopic,

View File

@ -50,8 +50,6 @@ public:
const nsAString& aGMPName,
bool aInPrivateBrowsingMode,
UniquePtr<GetNodeIdCallback>&& aCallback) override;
NS_IMETHOD UpdateTrialCreateState(const nsAString& aKeySystem,
uint32_t aState) override;
NS_DECL_NSIOBSERVER

View File

@ -9,9 +9,6 @@
#include "mozilla/Logging.h"
#include "GMPParent.h"
#include "GMPVideoDecoderParent.h"
#ifdef MOZ_EME
#include "mozilla/dom/GMPVideoDecoderTrialCreator.h"
#endif
#include "nsIObserverService.h"
#include "GeckoChildProcessHost.h"
#include "mozilla/Preferences.h"
@ -84,7 +81,8 @@ NS_IMPL_ISUPPORTS_INHERITED(GeckoMediaPluginServiceParent,
mozIGeckoMediaPluginChromeService)
static int32_t sMaxAsyncShutdownWaitMs = 0;
static bool sHaveSetTimeoutPrefCache = false;
static bool sAllowInsecureGMP = false;
static bool sHaveSetGMPServiceParentPrefCaches = false;
GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
: mShuttingDown(false)
@ -95,11 +93,13 @@ GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
, mWaitingForPluginsSyncShutdown(false)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sHaveSetTimeoutPrefCache) {
sHaveSetTimeoutPrefCache = true;
if (!sHaveSetGMPServiceParentPrefCaches) {
sHaveSetGMPServiceParentPrefCaches = true;
Preferences::AddIntVarCache(&sMaxAsyncShutdownWaitMs,
"media.gmp.async-shutdown-timeout",
GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT);
Preferences::AddBoolVarCache(&sAllowInsecureGMP,
"media.gmp.insecure.allow", false);
}
}
@ -112,7 +112,7 @@ GeckoMediaPluginServiceParent::~GeckoMediaPluginServiceParent()
int32_t
GeckoMediaPluginServiceParent::AsyncShutdownTimeoutMs()
{
MOZ_ASSERT(sHaveSetTimeoutPrefCache);
MOZ_ASSERT(sHaveSetGMPServiceParentPrefCaches);
return sMaxAsyncShutdownWaitMs;
}
@ -945,45 +945,28 @@ GeckoMediaPluginServiceParent::SelectPluginForAPI(const nsACString& aNodeId,
return nullptr;
}
class CreateGMPParentTask : public nsRunnable {
public:
NS_IMETHOD Run() {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<GMPParent>
CreateGMPParent()
{
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
if (!SandboxInfo::Get().CanSandboxMedia()) {
if (!Preferences::GetBool("media.gmp.insecure.allow")) {
NS_WARNING("Denying media plugin load due to lack of sandboxing.");
return NS_ERROR_NOT_AVAILABLE;
}
NS_WARNING("Loading media plugin despite lack of sandboxing.");
if (!SandboxInfo::Get().CanSandboxMedia()) {
if (!sAllowInsecureGMP) {
NS_WARNING("Denying media plugin load due to lack of sandboxing.");
return nullptr;
}
NS_WARNING("Loading media plugin despite lack of sandboxing.");
}
#endif
mParent = new GMPParent();
return NS_OK;
}
already_AddRefed<GMPParent> GetParent() {
return mParent.forget();
}
private:
RefPtr<GMPParent> mParent;
};
return new GMPParent();
}
GMPParent*
GeckoMediaPluginServiceParent::ClonePlugin(const GMPParent* aOriginal)
{
MOZ_ASSERT(aOriginal);
// The GMPParent inherits from IToplevelProtocol, which must be created
// on the main thread to be threadsafe. See Bug 1035653.
RefPtr<CreateGMPParentTask> task(new CreateGMPParentTask());
if (!NS_IsMainThread()) {
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
MOZ_ASSERT(mainThread);
mozilla::SyncRunnable::DispatchToThread(mainThread, task);
}
RefPtr<GMPParent> gmp = task->GetParent();
nsresult rv = gmp->CloneFrom(aOriginal);
RefPtr<GMPParent> gmp = CreateGMPParent();
nsresult rv = gmp ? gmp->CloneFrom(aOriginal) : NS_ERROR_NOT_AVAILABLE;
if (NS_FAILED(rv)) {
NS_WARNING("Can't Create GMPParent");
@ -1008,13 +991,7 @@ GeckoMediaPluginServiceParent::AddOnGMPThread(const nsAString& aDirectory)
return;
}
// The GMPParent inherits from IToplevelProtocol, which must be created
// on the main thread to be threadsafe. See Bug 1035653.
RefPtr<CreateGMPParentTask> task(new CreateGMPParentTask());
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
MOZ_ASSERT(mainThread);
mozilla::SyncRunnable::DispatchToThread(mainThread, task);
RefPtr<GMPParent> gmp = task->GetParent();
RefPtr<GMPParent> gmp = CreateGMPParent();
rv = gmp ? gmp->Init(this, directory) : NS_ERROR_NOT_AVAILABLE;
if (NS_FAILED(rv)) {
NS_WARNING("Can't Create GMPParent");
@ -1412,22 +1389,6 @@ GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
return rv;
}
NS_IMETHODIMP
GeckoMediaPluginServiceParent::UpdateTrialCreateState(const nsAString& aKeySystem,
uint32_t aState)
{
#ifdef MOZ_EME
nsString keySystem(aKeySystem);
NS_DispatchToMainThread(NS_NewRunnableFunction([keySystem, aState] {
mozilla::dom::GMPVideoDecoderTrialCreator::UpdateTrialCreateState(keySystem, aState);
}));
return NS_OK;
#else
return NS_ERROR_FAILURE;
#endif
}
static bool
ExtractHostName(const nsACString& aOrigin, nsACString& aOutData)
{
@ -1756,14 +1717,6 @@ GMPServiceParent::RecvGetGMPNodeId(const nsString& aOrigin,
return NS_SUCCEEDED(rv);
}
bool
GMPServiceParent::RecvUpdateGMPTrialCreateState(const nsString& aKeySystem,
const uint32_t& aState)
{
mService->UpdateTrialCreateState(aKeySystem, aState);
return true;
}
/* static */
bool
GMPServiceParent::RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,

View File

@ -44,8 +44,6 @@ public:
const nsAString& aGMPName,
bool aInPrivateBrowsingMode,
UniquePtr<GetNodeIdCallback>&& aCallback) override;
NS_IMETHOD UpdateTrialCreateState(const nsAString& aKeySystem,
uint32_t aState) override;
NS_DECL_MOZIGECKOMEDIAPLUGINCHROMESERVICE
NS_DECL_NSIOBSERVER
@ -225,9 +223,6 @@ public:
nsTArray<nsCString>&& aTags,
bool* aHasPlugin,
nsCString* aVersion);
virtual bool RecvUpdateGMPTrialCreateState(const nsString& aKeySystem,
const uint32_t& aState) override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
static PGMPServiceParent* Create(Transport* aTransport, ProcessId aOtherPid);

View File

@ -22,8 +22,6 @@ parent:
nsString gmpName,
bool inPrivateBrowsing)
returns (nsCString id);
async UpdateGMPTrialCreateState(nsString keySystem, uint32_t status);
};
} // namespace gmp

View File

@ -52,7 +52,7 @@ native GetGMPVideoDecoderCallback(mozilla::UniquePtr<GetGMPVideoDecoderCallback>
native GetGMPVideoEncoderCallback(mozilla::UniquePtr<GetGMPVideoEncoderCallback>&&);
native GetNodeIdCallback(mozilla::UniquePtr<GetNodeIdCallback>&&);
[scriptable, uuid(b5492915-2f0e-4973-9f91-a6fe61ac4749)]
[scriptable, uuid(44d362ae-937a-4803-bee6-f2512a0149d1)]
interface mozIGeckoMediaPluginService : nsISupports
{
@ -148,10 +148,4 @@ interface mozIGeckoMediaPluginService : nsISupports
in AString gmpName,
in bool inPrivateBrowsingMode,
in GetNodeIdCallback callback);
/**
* Stores the result of trying to create a decoder for the given keysystem.
*/
[noscript]
void updateTrialCreateState(in AString keySystem, in uint32_t status);
};

View File

@ -13,11 +13,6 @@
#include "GMPVideoEncoderProxy.h"
#include "GMPDecryptorProxy.h"
#include "GMPServiceParent.h"
#ifdef XP_WIN
#include "GMPVideoDecoderTrialCreator.h"
#include "mozilla/dom/MediaKeySystemAccess.h"
#include "mozilla/Monitor.h"
#endif
#include "nsAppDirectoryServiceDefs.h"
#include "nsIFile.h"
#include "nsISimpleEnumerator.h"
@ -1494,65 +1489,3 @@ TEST(GeckoMediaPlugins, GMPStorageLongRecordNames) {
RefPtr<GMPStorageTest> runner = new GMPStorageTest();
runner->DoTest(&GMPStorageTest::TestLongRecordNames);
}
#ifdef XP_WIN
class GMPTrialCreateTest
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPStorageTest)
void DoTest() {
EnsureNSSInitializedChromeOrContent();
mCreator = new mozilla::dom::GMPVideoDecoderTrialCreator();
mCreator->MaybeAwaitTrialCreate(NS_LITERAL_STRING("broken"), nullptr, this, nullptr);
AwaitFinished();
}
GMPTrialCreateTest()
: mMonitor("GMPTrialCreateTest")
, mFinished(false)
, mPassed(false)
{
}
void MaybeResolve(mozilla::dom::MediaKeySystemAccess* aAccess) {
mPassed = false;
SetFinished();
}
void MaybeReject(nsresult aResult, const nsACString& aUnusedMessage) {
mPassed = true;
SetFinished();
}
private:
~GMPTrialCreateTest() { }
void Dummy() {
// Intentionally left blank.
}
void SetFinished() {
mFinished = true;
NS_DispatchToMainThread(NS_NewRunnableMethod(this, &GMPTrialCreateTest::Dummy));
}
void AwaitFinished() {
while (!mFinished) {
NS_ProcessNextEvent(nullptr, true);
}
mFinished = false;
}
RefPtr<mozilla::dom::GMPVideoDecoderTrialCreator> mCreator;
Monitor mMonitor;
Atomic<bool> mFinished;
bool mPassed;
};
TEST(GeckoMediaPlugins, GMPTrialCreateFail) {
RefPtr<GMPTrialCreateTest> runner = new GMPTrialCreateTest();
runner->DoTest();
}
#endif // XP_WIN

View File

@ -314,7 +314,6 @@ DecodedStream::DecodedStream(AbstractThread* aOwnerThread,
MediaQueue<MediaData>& aAudioQueue,
MediaQueue<MediaData>& aVideoQueue)
: mOwnerThread(aOwnerThread)
, mShuttingDown(false)
, mPlaying(false)
, mSameOrigin(false)
, mAudioQueue(aAudioQueue)
@ -357,13 +356,6 @@ DecodedStream::OnEnded(TrackType aType)
return nullptr;
}
void
DecodedStream::BeginShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
mShuttingDown = true;
}
void
DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo)
{
@ -455,8 +447,7 @@ DecodedStream::CreateData(MozPromiseHolder<GenericPromise>&& aPromise)
// No need to create a source stream when there are no output streams. This
// happens when RemoveOutput() is called immediately after StartPlayback().
// Also we don't create a source stream when MDSM has begun shutdown.
if (!mOutputStreamManager.Graph() || mShuttingDown) {
if (!mOutputStreamManager.Graph()) {
// Resolve the promise to indicate the end of playback.
aPromise.Resolve(true, __func__);
return;
@ -477,16 +468,30 @@ DecodedStream::CreateData(MozPromiseHolder<GenericPromise>&& aPromise)
return NS_OK;
}
private:
virtual ~R()
{
// mData is not transferred when dispatch fails and Run() is not called.
// We need to dispatch a task to ensure DecodedStreamData is destroyed
// properly on the main thread.
if (mData) {
DecodedStreamData* data = mData.release();
RefPtr<DecodedStream> self = mThis.forget();
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
self->mOutputStreamManager.Disconnect();
delete data;
});
AbstractThread::MainThread()->Dispatch(r.forget());
}
}
RefPtr<DecodedStream> mThis;
Method mMethod;
UniquePtr<DecodedStreamData> mData;
};
// Post a message to ensure |mData| is only updated on the worker thread.
// Note this must be done before MDSM's shutdown since dispatch could fail
// when the worker thread is shut down.
// Note this could fail when MDSM begin to shut down the worker thread.
nsCOMPtr<nsIRunnable> r = new R(this, &DecodedStream::OnDataCreated, data);
mOwnerThread->Dispatch(r.forget());
mOwnerThread->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
}
bool

View File

@ -120,7 +120,6 @@ public:
bool IsPlaying() const override;
// TODO: fix these functions that don't fit into the interface of MediaSink.
void BeginShutdown();
void AddOutput(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
void RemoveOutput(MediaStream* aStream);
void SetSameOrigin(bool aSameOrigin);
@ -154,8 +153,6 @@ private:
*/
// Data about MediaStreams that are being fed by the decoder.
OutputStreamManager mOutputStreamManager;
// True if MDSM has begun shutdown.
bool mShuttingDown;
/*
* Worker thread only members.

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