mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-28 05:10:49 +00:00
Merge autoland to central, a=merge
This commit is contained in:
commit
b0767bc340
@ -4,4 +4,12 @@ module.exports = {
|
||||
"extends": [
|
||||
"../toolkit/.eslintrc.js"
|
||||
],
|
||||
|
||||
"rules": {
|
||||
"no-unused-vars": ["error", {
|
||||
"vars": "local",
|
||||
"varsIgnorePattern": "^Cc|Ci|Cu|Cr|EXPORTED_SYMBOLS",
|
||||
"args": "none",
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
@ -1,67 +1,69 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var testData = [
|
||||
/* baseURI, field name, expected */
|
||||
[ 'http://example.com/', 'q', 'http://example.com/?q=%s' ],
|
||||
[ 'http://example.com/new-path-here/', 'q', 'http://example.com/new-path-here/?q=%s' ],
|
||||
[ '', 'q', 'http://example.org/browser/browser/base/content/test/general/dummy_page.html?q=%s' ],
|
||||
// Tests for proper behaviour when called on a form whose action contains a question mark.
|
||||
[ 'http://example.com/search?oe=utf-8', 'q', 'http://example.com/search?oe=utf-8&q=%s' ],
|
||||
{ desc: "No path",
|
||||
action: "http://example.com/",
|
||||
param: "q",
|
||||
},
|
||||
{ desc: "With path",
|
||||
action: "http://example.com/new-path-here/",
|
||||
param: "q",
|
||||
},
|
||||
{ desc: "No action",
|
||||
action: "",
|
||||
param: "q",
|
||||
},
|
||||
{ desc: "With Query String",
|
||||
action: "http://example.com/search?oe=utf-8",
|
||||
param: "q",
|
||||
},
|
||||
];
|
||||
|
||||
add_task(function*() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "http://example.org/browser/browser/base/content/test/general/dummy_page.html",
|
||||
}, function* (browser) {
|
||||
yield ContentTask.spawn(browser, null, function* () {
|
||||
let doc = content.document;
|
||||
let base = doc.createElement("base");
|
||||
doc.head.appendChild(base);
|
||||
});
|
||||
const TEST_URL = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
|
||||
var mm = browser.messageManager;
|
||||
let count = 0;
|
||||
for (let method of ["GET", "POST"]) {
|
||||
for (let {desc, action, param } of testData) {
|
||||
info(`Running ${method} keyword test '${desc}'`);
|
||||
let id = `keyword-form-${count++}`;
|
||||
let contextMenu = document.getElementById("contentAreaContextMenu");
|
||||
let contextMenuPromise =
|
||||
BrowserTestUtils.waitForEvent(contextMenu, "popupshown")
|
||||
.then(() => gContextMenuContentData.popupNode);
|
||||
|
||||
for (let [baseURI, fieldName, expected] of testData) {
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(document.getElementById("contentAreaContextMenu"),
|
||||
"popupshown");
|
||||
|
||||
yield ContentTask.spawn(browser, { baseURI, fieldName }, function* (args) {
|
||||
yield ContentTask.spawn(tab.linkedBrowser,
|
||||
{ action, param, method, id }, function* (args) {
|
||||
let doc = content.document;
|
||||
|
||||
let base = doc.querySelector('head > base');
|
||||
base.href = args.baseURI;
|
||||
|
||||
let form = doc.createElement("form");
|
||||
form.id = "keyword-form";
|
||||
form.id = args.id;
|
||||
form.method = args.method;
|
||||
form.action = args.action;
|
||||
let element = doc.createElement("input");
|
||||
element.setAttribute("type", "text");
|
||||
element.setAttribute("name", args.fieldName);
|
||||
element.setAttribute("name", args.param);
|
||||
form.appendChild(element);
|
||||
doc.body.appendChild(form);
|
||||
|
||||
/* Open context menu so chrome can access the element */
|
||||
const domWindowUtils =
|
||||
content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
let rect = element.getBoundingClientRect();
|
||||
let left = rect.left + rect.width / 2;
|
||||
let top = rect.top + rect.height / 2;
|
||||
domWindowUtils.sendMouseEvent("contextmenu", left, top, 2,
|
||||
1, 0, false, 0, 0, true);
|
||||
});
|
||||
|
||||
yield popupShownPromise;
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter(`#${id} > input`,
|
||||
{ type : "contextmenu", button : 2 },
|
||||
tab.linkedBrowser);
|
||||
let target = yield contextMenuPromise;
|
||||
|
||||
let target = gContextMenuContentData.popupNode;
|
||||
|
||||
let urlCheck = new Promise((resolve, reject) => {
|
||||
yield new Promise(resolve => {
|
||||
let url = action || tab.linkedBrowser.currentURI.spec;
|
||||
let mm = tab.linkedBrowser.messageManager;
|
||||
let onMessage = (message) => {
|
||||
mm.removeMessageListener("ContextMenu:SearchFieldBookmarkData:Result", onMessage);
|
||||
|
||||
is(message.data.spec, expected,
|
||||
`Bookmark spec for search field named ${fieldName} and baseURI ${baseURI} incorrect`);
|
||||
if (method == "GET") {
|
||||
ok(message.data.spec.endsWith(`${param}=%s`),
|
||||
`Check expected url for field named ${param} and action ${action}`);
|
||||
} else {
|
||||
is(message.data.spec, url,
|
||||
`Check expected url for field named ${param} and action ${action}`);
|
||||
is(message.data.postData, `${param}%3D%25s`,
|
||||
`Check expected POST data for field named ${param} and action ${action}`);
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
mm.addMessageListener("ContextMenu:SearchFieldBookmarkData:Result", onMessage);
|
||||
@ -69,14 +71,11 @@ add_task(function*() {
|
||||
mm.sendAsyncMessage("ContextMenu:SearchFieldBookmarkData", null, { target });
|
||||
});
|
||||
|
||||
yield urlCheck;
|
||||
|
||||
document.getElementById("contentAreaContextMenu").hidePopup();
|
||||
|
||||
yield ContentTask.spawn(browser, null, function* () {
|
||||
let doc = content.document;
|
||||
doc.body.removeChild(doc.getElementById("keyword-form"));
|
||||
});
|
||||
let popupHiddenPromise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
|
||||
contextMenu.hidePopup();
|
||||
yield popupHiddenPromise;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
@ -7,6 +7,8 @@ support-files =
|
||||
skip-if = (os == "linux" || os == "mac") && debug # bug 970052, bug 970053
|
||||
[browser_action_keyword.js]
|
||||
skip-if = os == "linux" # Bug 1188154
|
||||
support-files =
|
||||
print_postdata.sjs
|
||||
[browser_action_keyword_override.js]
|
||||
[browser_action_searchengine.js]
|
||||
[browser_action_searchengine_alias.js]
|
||||
|
@ -1,8 +1,3 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var gOnSearchComplete = null;
|
||||
|
||||
function* promise_first_result(inputText) {
|
||||
yield promiseAutocompleteResultPopup(inputText);
|
||||
|
||||
@ -10,41 +5,47 @@ function* promise_first_result(inputText) {
|
||||
return firstResult;
|
||||
}
|
||||
|
||||
const TEST_URL = "http://mochi.test:8888/browser/browser/base/content/test/urlbar/print_postdata.sjs";
|
||||
|
||||
add_task(function*() {
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("about:mozilla");
|
||||
let tabs = [tab];
|
||||
add_task(function* setup() {
|
||||
yield PlacesUtils.keywords.insert({ keyword: "get",
|
||||
url: TEST_URL + "?q=%s" });
|
||||
yield PlacesUtils.keywords.insert({ keyword: "post",
|
||||
url: TEST_URL,
|
||||
postData: "q=%s" });
|
||||
registerCleanupFunction(function* () {
|
||||
for (let tab of tabs)
|
||||
gBrowser.removeTab(tab);
|
||||
yield PlacesUtils.bookmarks.remove(bm);
|
||||
yield PlacesUtils.keywords.remove("get");
|
||||
yield PlacesUtils.keywords.remove("post");
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
add_task(function* get_keyword() {
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
|
||||
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: "http://example.com/?q=%s",
|
||||
title: "test" });
|
||||
yield PlacesUtils.keywords.insert({ keyword: "keyword",
|
||||
url: "http://example.com/?q=%s" });
|
||||
|
||||
let result = yield promise_first_result("keyword something");
|
||||
let result = yield promise_first_result("get something");
|
||||
isnot(result, null, "Expect a keyword result");
|
||||
|
||||
let types = new Set(result.getAttribute("type").split(/\s+/));
|
||||
Assert.ok(types.has("keyword"));
|
||||
is(result.getAttribute("actiontype"), "keyword", "Expect correct `actiontype` attribute");
|
||||
is(result.getAttribute("title"), "example.com", "Expect correct title");
|
||||
is(result.getAttribute("title"), "mochi.test:8888", "Expect correct title");
|
||||
|
||||
// We need to make a real URI out of this to ensure it's normalised for
|
||||
// comparison.
|
||||
let uri = NetUtil.newURI(result.getAttribute("url"));
|
||||
is(uri.spec, PlacesUtils.mozActionURI("keyword", {url: "http://example.com/?q=something", input: "keyword something"}), "Expect correct url");
|
||||
is(uri.spec, PlacesUtils.mozActionURI("keyword",
|
||||
{ url: TEST_URL + "?q=something",
|
||||
input: "get something"}),
|
||||
"Expect correct url");
|
||||
|
||||
let titleHbox = result._titleText.parentNode.parentNode;
|
||||
ok(titleHbox.classList.contains("ac-title"), "Title hbox element sanity check");
|
||||
is_element_visible(titleHbox, "Title element should be visible");
|
||||
is(result._titleText.textContent, "example.com: something", "Node should contain the name of the bookmark and query");
|
||||
is(result._titleText.textContent, "mochi.test:8888: something",
|
||||
"Node should contain the name of the bookmark and query");
|
||||
|
||||
let urlHbox = result._urlText.parentNode.parentNode;
|
||||
ok(urlHbox.classList.contains("ac-url"), "URL hbox element sanity check");
|
||||
@ -60,22 +61,59 @@ add_task(function*() {
|
||||
let tabPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
EventUtils.synthesizeMouseAtCenter(result, {});
|
||||
yield tabPromise;
|
||||
is(tab.linkedBrowser.currentURI.spec, "http://example.com/?q=something", "Tab should have loaded from clicking on result");
|
||||
is(tab.linkedBrowser.currentURI.spec, TEST_URL + "?q=something",
|
||||
"Tab should have loaded from clicking on result");
|
||||
|
||||
// Middle-click on the result
|
||||
info("Middle-click on result");
|
||||
result = yield promise_first_result("keyword somethingmore");
|
||||
result = yield promise_first_result("get somethingmore");
|
||||
isnot(result, null, "Expect a keyword result");
|
||||
// We need to make a real URI out of this to ensure it's normalised for
|
||||
// comparison.
|
||||
uri = NetUtil.newURI(result.getAttribute("url"));
|
||||
is(uri.spec, PlacesUtils.mozActionURI("keyword", {url: "http://example.com/?q=somethingmore", input: "keyword somethingmore"}), "Expect correct url");
|
||||
is(uri.spec, PlacesUtils.mozActionURI("keyword",
|
||||
{ url: TEST_URL + "?q=somethingmore",
|
||||
input: "get somethingmore" }),
|
||||
"Expect correct url");
|
||||
|
||||
tabPromise = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen");
|
||||
EventUtils.synthesizeMouseAtCenter(result, {button: 1});
|
||||
let tabOpenEvent = yield tabPromise;
|
||||
let newTab = tabOpenEvent.target;
|
||||
tabs.push(newTab);
|
||||
yield BrowserTestUtils.browserLoaded(newTab.linkedBrowser);
|
||||
is(newTab.linkedBrowser.currentURI.spec, "http://example.com/?q=somethingmore", "Tab should have loaded from middle-clicking on result");
|
||||
is(newTab.linkedBrowser.currentURI.spec,
|
||||
TEST_URL + "?q=somethingmore",
|
||||
"Tab should have loaded from middle-clicking on result");
|
||||
});
|
||||
|
||||
|
||||
add_task(function* post_keyword() {
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
|
||||
|
||||
let result = yield promise_first_result("post something");
|
||||
isnot(result, null, "Expect a keyword result");
|
||||
|
||||
let types = new Set(result.getAttribute("type").split(/\s+/));
|
||||
Assert.ok(types.has("keyword"));
|
||||
is(result.getAttribute("actiontype"), "keyword", "Expect correct `actiontype` attribute");
|
||||
is(result.getAttribute("title"), "mochi.test:8888", "Expect correct title");
|
||||
|
||||
is(result.getAttribute("url"),
|
||||
PlacesUtils.mozActionURI("keyword", { url: TEST_URL,
|
||||
input: "post something",
|
||||
"postData": "q=something" }),
|
||||
"Expect correct url");
|
||||
|
||||
// Click on the result
|
||||
info("Normal click on result");
|
||||
let tabPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
EventUtils.synthesizeMouseAtCenter(result, {});
|
||||
yield tabPromise;
|
||||
is(tab.linkedBrowser.currentURI.spec, TEST_URL,
|
||||
"Tab should have loaded from clicking on result");
|
||||
|
||||
let postData = yield ContentTask.spawn(tab.linkedBrowser, null, function* () {
|
||||
return content.document.body.textContent;
|
||||
});
|
||||
is(postData, "q=something", "post data was submitted correctly");
|
||||
});
|
||||
|
22
browser/base/content/test/urlbar/print_postdata.sjs
Normal file
22
browser/base/content/test/urlbar/print_postdata.sjs
Normal file
@ -0,0 +1,22 @@
|
||||
const CC = Components.Constructor;
|
||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream",
|
||||
"setInputStream");
|
||||
|
||||
function handleRequest(request, response) {
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
if (request.method == "GET") {
|
||||
response.write(request.queryString);
|
||||
} else {
|
||||
var body = new BinaryInputStream(request.bodyInputStream);
|
||||
|
||||
var avail;
|
||||
var bytes = [];
|
||||
|
||||
while ((avail = body.available()) > 0)
|
||||
Array.prototype.push.apply(bytes, body.readByteArray(avail));
|
||||
|
||||
var data = String.fromCharCode.apply(null, bytes);
|
||||
response.bodyOutputStream.write(data, data.length);
|
||||
}
|
||||
}
|
@ -444,7 +444,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
break;
|
||||
case "keyword":
|
||||
if (action.params.postData) {
|
||||
postData = getPostDataStream(postData);
|
||||
postData = getPostDataStream(action.params.postData);
|
||||
}
|
||||
mayInheritPrincipal = true;
|
||||
url = action.params.url;
|
||||
|
@ -373,7 +373,7 @@ function openLinkIn(url, where, params) {
|
||||
}
|
||||
|
||||
if (aForceAboutBlankViewerInCurrent) {
|
||||
w.gBrowser.selectedBrowser.createAboutBlankContentViewer(aPrincipal);
|
||||
aCurrentBrowser.createAboutBlankContentViewer(aPrincipal);
|
||||
}
|
||||
|
||||
aCurrentBrowser.loadURIWithFlags(url, {
|
||||
|
@ -471,9 +471,6 @@ extensions.registerSchemaAPI("browserAction", "addon_parent", context => {
|
||||
setIcon: function(details) {
|
||||
let tab = details.tabId !== null ? TabManager.getTab(details.tabId, context) : null;
|
||||
|
||||
// Note: the caller in the child process has already normalized
|
||||
// `details` to not contain an `imageData` property, so the icon can
|
||||
// safely be normalized here without errors.
|
||||
let icon = IconDetails.normalize(details, extension, context);
|
||||
BrowserAction.for(extension).setProperty(tab, "icon", icon);
|
||||
},
|
||||
|
@ -1,26 +0,0 @@
|
||||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
var {
|
||||
IconDetails,
|
||||
} = ExtensionUtils;
|
||||
|
||||
extensions.registerSchemaAPI("browserAction", "addon_child", context => {
|
||||
return {
|
||||
browserAction: {
|
||||
setIcon: function(details) {
|
||||
// This needs to run in the addon process because normalization requires
|
||||
// the use of <canvas>.
|
||||
let normalizedDetails = {
|
||||
tabId: details.tabId,
|
||||
path: IconDetails.normalize(details, context.extension, context),
|
||||
};
|
||||
return context.childManager.callParentAsyncFunction("browserAction.setIcon", [
|
||||
normalizedDetails,
|
||||
]);
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
@ -1,26 +0,0 @@
|
||||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
var {
|
||||
IconDetails,
|
||||
} = ExtensionUtils;
|
||||
|
||||
extensions.registerSchemaAPI("pageAction", "addon_child", context => {
|
||||
return {
|
||||
pageAction: {
|
||||
setIcon: function(details) {
|
||||
// This needs to run in the addon process because normalization requires
|
||||
// the use of <canvas>.
|
||||
let normalizedDetails = {
|
||||
tabId: details.tabId,
|
||||
path: IconDetails.normalize(details, context.extension, context),
|
||||
};
|
||||
return context.childManager.callParentAsyncFunction("pageAction.setIcon", [
|
||||
normalizedDetails,
|
||||
]);
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
@ -260,9 +260,6 @@ extensions.registerSchemaAPI("pageAction", "addon_parent", context => {
|
||||
setIcon(details) {
|
||||
let tab = TabManager.getTab(details.tabId, context);
|
||||
|
||||
// Note: the caller in the child process has already normalized
|
||||
// `details` to not contain an `imageData` property, so the icon can
|
||||
// safely be normalized here without errors.
|
||||
let icon = IconDetails.normalize(details, extension, context);
|
||||
PageAction.for(extension).setProperty(tab, "icon", icon);
|
||||
},
|
||||
|
@ -12,9 +12,7 @@ category webextension-scripts utils chrome://browser/content/ext-utils.js
|
||||
category webextension-scripts windows chrome://browser/content/ext-windows.js
|
||||
|
||||
# scripts that must run in the same process as addon code.
|
||||
category webextension-scripts-addon browserAction chrome://browser/content/ext-c-browserAction.js
|
||||
category webextension-scripts-addon contextMenus chrome://browser/content/ext-c-contextMenus.js
|
||||
category webextension-scripts-addon pageAction chrome://browser/content/ext-c-pageAction.js
|
||||
category webextension-scripts-addon tabs chrome://browser/content/ext-c-tabs.js
|
||||
|
||||
# schemas
|
||||
|
@ -23,7 +23,5 @@ browser.jar:
|
||||
content/browser/ext-tabs.js
|
||||
content/browser/ext-utils.js
|
||||
content/browser/ext-windows.js
|
||||
content/browser/ext-c-browserAction.js
|
||||
content/browser/ext-c-contextMenus.js
|
||||
content/browser/ext-c-pageAction.js
|
||||
content/browser/ext-c-tabs.js
|
||||
|
@ -60,6 +60,7 @@
|
||||
"type": "object",
|
||||
"isInstanceOf": "ImageData",
|
||||
"additionalProperties": { "type": "any" },
|
||||
"postprocess": "convertImageDataToURL",
|
||||
"description": "Pixel data for an image. Must be an ImageData object (for example, from a <code>canvas</code> element)."
|
||||
}
|
||||
],
|
||||
|
@ -49,6 +49,7 @@
|
||||
"type": "object",
|
||||
"isInstanceOf": "ImageData",
|
||||
"additionalProperties": { "type": "any" },
|
||||
"postprocess": "convertImageDataToURL",
|
||||
"description": "Pixel data for an image. Must be an ImageData object (for example, from a <code>canvas</code> element)."
|
||||
}
|
||||
],
|
||||
|
@ -648,12 +648,6 @@ BrowserGlue.prototype = {
|
||||
this._sanitizer.onStartup();
|
||||
// check if we're in safe mode
|
||||
if (Services.appinfo.inSafeMode) {
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1231112#c7 . We need to
|
||||
// register the observer early if we have to migrate tab groups
|
||||
let currentUIVersion = 0;
|
||||
try {
|
||||
currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
|
||||
} catch (ex) {}
|
||||
Services.ww.openWindow(null, "chrome://browser/content/safeMode.xul",
|
||||
"_blank", "chrome,centerscreen,modal,resizable=no", null);
|
||||
}
|
||||
|
@ -643,23 +643,6 @@ var PlacesOrganizer = {
|
||||
|
||||
if (selectedNode && !PlacesUtils.nodeIsSeparator(selectedNode)) {
|
||||
detailsDeck.selectedIndex = 1;
|
||||
// Using the concrete itemId is arguably wrong. The bookmarks API
|
||||
// does allow setting properties for folder shortcuts as well, but since
|
||||
// the UI does not distinct between the couple, we better just show
|
||||
// the concrete item properties for shortcuts to root nodes.
|
||||
let concreteId = PlacesUtils.getConcreteItemId(selectedNode);
|
||||
var isRootItem = concreteId != -1 && PlacesUtils.isRootItem(concreteId);
|
||||
var readOnly = isRootItem ||
|
||||
selectedNode.parent.itemId == PlacesUIUtils.leftPaneFolderId;
|
||||
var useConcreteId = isRootItem ||
|
||||
PlacesUtils.nodeIsTagQuery(selectedNode);
|
||||
var itemId = -1;
|
||||
if (concreteId != -1 && useConcreteId)
|
||||
itemId = concreteId;
|
||||
else if (selectedNode.itemId != -1)
|
||||
itemId = selectedNode.itemId;
|
||||
else
|
||||
itemId = PlacesUtils._uri(selectedNode.uri);
|
||||
|
||||
gEditItemOverlay.initPanel({ node: selectedNode
|
||||
, hiddenRows: ["folderPicker"] });
|
||||
@ -1059,9 +1042,6 @@ var ViewMenu = {
|
||||
var popup = event.target;
|
||||
var pivot = this._clean(popup, startID, endID);
|
||||
|
||||
// If no column is "sort-active", the "Unsorted" item needs to be checked,
|
||||
// so track whether or not we find a column that is sort-active.
|
||||
var isSorted = false;
|
||||
var content = document.getElementById("placeContent");
|
||||
var columns = content.columns;
|
||||
for (var i = 0; i < columns.count; ++i) {
|
||||
@ -1087,7 +1067,6 @@ var ViewMenu = {
|
||||
// This column is the sort key. Its item is checked.
|
||||
if (column.getAttribute("sortDirection") != "") {
|
||||
menuitem.setAttribute("checked", "true");
|
||||
isSorted = true;
|
||||
}
|
||||
}
|
||||
else if (type == "checkbox") {
|
||||
|
@ -147,7 +147,6 @@ let gContainersManager = {
|
||||
let icon = document.getElementById("icon").value;
|
||||
let color = document.getElementById("color").value;
|
||||
let name = document.getElementById("name").value;
|
||||
let userContextId = false;
|
||||
|
||||
if (this.icons.indexOf(icon) == -1) {
|
||||
throw "Internal error. The icon value doesn't match.";
|
||||
|
@ -526,7 +526,6 @@ var DirectoryLinksProvider = {
|
||||
* @return download promise
|
||||
*/
|
||||
reportSitesAction: function DirectoryLinksProvider_reportSitesAction(sites, action, triggeringSiteIndex) {
|
||||
let pastImpressions;
|
||||
// Check if the suggested tile was shown
|
||||
if (action == "view") {
|
||||
sites.slice(0, triggeringSiteIndex + 1).filter(s => s).forEach(site => {
|
||||
@ -544,13 +543,6 @@ var DirectoryLinksProvider = {
|
||||
// suggested tile has targetedSite, or frecent_sites if it was pinned
|
||||
let {frecent_sites, targetedSite, url} = sites[triggeringSiteIndex].link;
|
||||
if (frecent_sites || targetedSite) {
|
||||
// skip past_impressions for "unpin" to avoid chance of tracking
|
||||
if (this._frequencyCaps[url] && action != "unpin") {
|
||||
pastImpressions = {
|
||||
total: this._frequencyCaps[url].totalViews,
|
||||
daily: this._frequencyCaps[url].dailyViews
|
||||
};
|
||||
}
|
||||
this._setFrequencyCapClick(url);
|
||||
}
|
||||
}
|
||||
|
1
config/external/nss/nss.symbols
vendored
1
config/external/nss/nss.symbols
vendored
@ -692,6 +692,7 @@ SSL_SetSRTPCiphers
|
||||
SSL_SetStapledOCSPResponses
|
||||
SSL_SetURL
|
||||
SSL_ShutdownServerSessionIDCache
|
||||
SSL_SignatureSchemePrefSet
|
||||
SSL_SNISocketConfigHook
|
||||
SSL_VersionRangeGet
|
||||
SSL_VersionRangeGetDefault
|
||||
|
@ -65,6 +65,7 @@ if CONFIG['MOZ_THUNDERBIRD'] or CONFIG['MOZ_SUITE']:
|
||||
# Turn on SQLite's assertions in debug builds.
|
||||
if CONFIG['MOZ_DEBUG']:
|
||||
DEFINES['SQLITE_DEBUG'] = 1
|
||||
DEFINES['SQLITE_ENABLE_API_ARMOR'] = True
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'Android':
|
||||
# default to user readable only to fit Android security model
|
||||
|
@ -24,8 +24,8 @@ const { require } = BrowserLoader({
|
||||
window
|
||||
});
|
||||
|
||||
const { createFactory, render, unmountComponentAtNode } =
|
||||
require("devtools/client/shared/vendor/react");
|
||||
const { createFactory } = require("devtools/client/shared/vendor/react");
|
||||
const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
|
||||
|
||||
const AboutDebuggingApp = createFactory(require("./components/aboutdebugging"));
|
||||
|
||||
|
@ -170,8 +170,8 @@ add_task(function* reloadButtonRefreshesMetadata() {
|
||||
|
||||
// Wait for the add-on list to be updated with the reloaded name.
|
||||
const onReInstall = promiseAddonEvent("onInstalled");
|
||||
const onAddonReloaded = waitForMutation(getAddonList(document),
|
||||
{ childList: true, subtree: true });
|
||||
const onAddonReloaded = waitForContentMutation(getAddonList(document));
|
||||
|
||||
const reloadButton = getReloadButton(document, manifestBase.name);
|
||||
reloadButton.click();
|
||||
|
||||
|
@ -39,7 +39,7 @@ add_task(function* () {
|
||||
// Then wait for title update, but on slow test runner, the title may already
|
||||
// be set to the expected value
|
||||
if (newTabTarget.textContent != "foo") {
|
||||
yield waitForMutation(newTabTarget, { childList: true });
|
||||
yield waitForContentMutation(newTabTarget);
|
||||
}
|
||||
|
||||
// Check that the new tab appears in the UI
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
/* eslint-env browser */
|
||||
/* exported openAboutDebugging, changeAboutDebuggingHash, closeAboutDebugging,
|
||||
installAddon, uninstallAddon, waitForMutation, assertHasTarget,
|
||||
installAddon, uninstallAddon, waitForMutation, waitForContentMutation, assertHasTarget,
|
||||
getServiceWorkerList, getTabList, openPanel, waitForInitialAddonList,
|
||||
waitForServiceWorkerRegistered, unregisterServiceWorker,
|
||||
waitForDelayedStartupFinished, setupTestAboutDebuggingWebExtension,
|
||||
@ -229,6 +229,22 @@ function waitForMutation(target, mutationOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that will resolve after receiving a mutation in the subtree of the
|
||||
* provided target. Depending on the current React implementation, a text change might be
|
||||
* observable as a childList mutation or a characterData mutation.
|
||||
*
|
||||
* @param {Node} target
|
||||
* @return {Promise}
|
||||
*/
|
||||
function waitForContentMutation(target) {
|
||||
return waitForMutation(target, {
|
||||
characterData: true,
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an about:debugging TargetList element contains a Target element
|
||||
* corresponding to the specified name.
|
||||
|
@ -42,7 +42,6 @@ var EventEmitter = require("devtools/shared/event-emitter");
|
||||
*
|
||||
* Events:
|
||||
* "new-node-front" when the inner node changed
|
||||
* "before-new-node-front" when the inner node is set to change
|
||||
* "attribute-changed" when an attribute is changed
|
||||
* "detached-front" when the node (or one of its parents) is removed from
|
||||
* the document
|
||||
@ -124,7 +123,6 @@ Selection.prototype = {
|
||||
value = parentNode;
|
||||
}
|
||||
|
||||
this.emit("before-new-node-front", value, reason);
|
||||
this._nodeFront = value;
|
||||
this.emit("new-node-front", value, this.reason);
|
||||
},
|
||||
|
@ -38,5 +38,6 @@ add_task(function* () {
|
||||
let console = yield toolbox.selectTool("webconsole");
|
||||
let { jsterm } = console.hud;
|
||||
let url = yield jsterm.execute("document.location.href");
|
||||
is(url.textContent, URL_2, "The console inspects the second document");
|
||||
// Uses includes as the old console frontend prints a timestamp
|
||||
ok(url.textContent.includes(URL_2), "The console inspects the second document");
|
||||
});
|
||||
|
@ -103,7 +103,6 @@ function Inspector(toolbox) {
|
||||
this.onTextBoxContextMenu = this.onTextBoxContextMenu.bind(this);
|
||||
this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this);
|
||||
this.onNewSelection = this.onNewSelection.bind(this);
|
||||
this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this);
|
||||
this.onDetached = this.onDetached.bind(this);
|
||||
this.onPaneToggleButtonClicked = this.onPaneToggleButtonClicked.bind(this);
|
||||
this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
|
||||
@ -223,7 +222,6 @@ Inspector.prototype = {
|
||||
this.walker.on("new-root", this.onNewRoot);
|
||||
|
||||
this.selection.on("new-node-front", this.onNewSelection);
|
||||
this.selection.on("before-new-node-front", this.onBeforeNewSelection);
|
||||
this.selection.on("detached-front", this.onDetached);
|
||||
|
||||
if (this.target.isLocalTab) {
|
||||
@ -855,16 +853,6 @@ Inspector.prototype = {
|
||||
this._updateProgress = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* When a new node is selected, before the selection has changed.
|
||||
*/
|
||||
onBeforeNewSelection: function (event, node) {
|
||||
if (this.breadcrumbs.indexOf(node) == -1) {
|
||||
// only clear locks if we'd have to update breadcrumbs
|
||||
this.clearPseudoClasses();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When a node is deleted, select its parent node or the defaultNode if no
|
||||
* parent is found (may happen when deleting an iframe inside which the
|
||||
@ -928,7 +916,6 @@ Inspector.prototype = {
|
||||
this.teardownToolbar();
|
||||
this.breadcrumbs.destroy();
|
||||
this.selection.off("new-node-front", this.onNewSelection);
|
||||
this.selection.off("before-new-node-front", this.onBeforeNewSelection);
|
||||
this.selection.off("detached-front", this.onDetached);
|
||||
let markupDestroyer = this._destroyMarkup();
|
||||
this.panelWin.inspector = null;
|
||||
@ -1557,16 +1544,6 @@ Inspector.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear any pseudo-class locks applied to the current hierarchy.
|
||||
*/
|
||||
clearPseudoClasses: function () {
|
||||
if (!this.walker) {
|
||||
return promise.resolve();
|
||||
}
|
||||
return this.walker.clearPseudoClassLocks().catch(this._handleRejectionIfNotDestroyed);
|
||||
},
|
||||
|
||||
/**
|
||||
* Edit the outerHTML of the selected Node.
|
||||
*/
|
||||
|
@ -31,15 +31,29 @@ add_task(function* () {
|
||||
yield selectNode("#div-1", inspector);
|
||||
|
||||
yield togglePseudoClass(inspector);
|
||||
yield assertPseudoAddedToNode(inspector, testActor, view);
|
||||
yield assertPseudoAddedToNode(inspector, testActor, view, "#div-1");
|
||||
|
||||
yield togglePseudoClass(inspector);
|
||||
yield assertPseudoRemovedFromNode(testActor);
|
||||
yield assertPseudoRemovedFromView(inspector, testActor, view);
|
||||
yield assertPseudoRemovedFromNode(testActor, "#div-1");
|
||||
yield assertPseudoRemovedFromView(inspector, testActor, view, "#div-1");
|
||||
|
||||
yield togglePseudoClass(inspector);
|
||||
yield testNavigate(inspector, testActor, view);
|
||||
|
||||
info("Toggle pseudo on the parent and ensure everything is toggled off");
|
||||
yield selectNode("#parent-div", inspector);
|
||||
yield togglePseudoClass(inspector);
|
||||
yield assertPseudoRemovedFromNode(testActor, "#div-1");
|
||||
yield assertPseudoRemovedFromView(inspector, testActor, view, "#div-1");
|
||||
|
||||
yield togglePseudoClass(inspector);
|
||||
info("Assert pseudo is dismissed when toggling it on a sibling node");
|
||||
yield selectNode("#div-2", inspector);
|
||||
yield togglePseudoClass(inspector);
|
||||
yield assertPseudoAddedToNode(inspector, testActor, view, "#div-2");
|
||||
let hasLock = yield testActor.hasPseudoClassLock("#div-1", PSEUDO);
|
||||
ok(!hasLock, "pseudo-class lock has been removed for the previous locked node");
|
||||
|
||||
info("Destroying the toolbox");
|
||||
let tab = toolbox.target.tab;
|
||||
yield toolbox.destroy();
|
||||
@ -47,7 +61,8 @@ add_task(function* () {
|
||||
// As the toolbox get detroyed, we need to fetch a new test-actor
|
||||
testActor = yield getTestActorWithoutToolbox(tab);
|
||||
|
||||
yield assertPseudoRemovedFromNode(testActor);
|
||||
yield assertPseudoRemovedFromNode(testActor, "#div-1");
|
||||
yield assertPseudoRemovedFromNode(testActor, "#div-2");
|
||||
});
|
||||
|
||||
function* togglePseudoClass(inspector) {
|
||||
@ -75,17 +90,14 @@ function* testNavigate(inspector, testActor, ruleview) {
|
||||
ok((yield testActor.hasPseudoClassLock("#div-1", PSEUDO)),
|
||||
"pseudo-class lock is still applied after inspecting ancestor");
|
||||
|
||||
let onPseudo = inspector.selection.once("pseudoclass");
|
||||
yield selectNode("#div-2", inspector);
|
||||
yield onPseudo;
|
||||
|
||||
info("Make sure the pseudoclass is removed after navigating to a " +
|
||||
info("Make sure the pseudoclass is still set after navigating to a " +
|
||||
"non-hierarchy node");
|
||||
ok(!(yield testActor.hasPseudoClassLock("#div-1", PSEUDO)),
|
||||
"pseudo-class lock is removed after inspecting sibling node");
|
||||
ok(yield testActor.hasPseudoClassLock("#div-1", PSEUDO),
|
||||
"pseudo-class lock is still on after inspecting sibling node");
|
||||
|
||||
yield selectNode("#div-1", inspector);
|
||||
yield togglePseudoClass(inspector);
|
||||
}
|
||||
|
||||
function* showPickerOn(selector, inspector) {
|
||||
@ -93,10 +105,10 @@ function* showPickerOn(selector, inspector) {
|
||||
yield inspector.highlighter.showBoxModel(nodeFront);
|
||||
}
|
||||
|
||||
function* assertPseudoAddedToNode(inspector, testActor, ruleview) {
|
||||
info("Make sure the pseudoclass lock is applied to #div-1 and its ancestors");
|
||||
function* assertPseudoAddedToNode(inspector, testActor, ruleview, selector) {
|
||||
info("Make sure the pseudoclass lock is applied to " + selector + " and its ancestors");
|
||||
|
||||
let hasLock = yield testActor.hasPseudoClassLock("#div-1", PSEUDO);
|
||||
let hasLock = yield testActor.hasPseudoClassLock(selector, PSEUDO);
|
||||
ok(hasLock, "pseudo-class lock has been applied");
|
||||
hasLock = yield testActor.hasPseudoClassLock("#parent-div", PSEUDO);
|
||||
ok(hasLock, "pseudo-class lock has been applied");
|
||||
@ -111,8 +123,8 @@ function* assertPseudoAddedToNode(inspector, testActor, ruleview) {
|
||||
is(rules[1]._ruleEditor.rule.selectorText, "div:hover",
|
||||
"rule view is showing " + PSEUDO + " rule");
|
||||
|
||||
info("Show the highlighter on #div-1");
|
||||
yield showPickerOn("#div-1", inspector);
|
||||
info("Show the highlighter on " + selector);
|
||||
yield showPickerOn(selector, inspector);
|
||||
|
||||
info("Check that the infobar selector contains the pseudo-class");
|
||||
let value = yield testActor.getHighlighterNodeTextContent(
|
||||
@ -121,11 +133,11 @@ function* assertPseudoAddedToNode(inspector, testActor, ruleview) {
|
||||
yield inspector.highlighter.hideBoxModel();
|
||||
}
|
||||
|
||||
function* assertPseudoRemovedFromNode(testActor) {
|
||||
function* assertPseudoRemovedFromNode(testActor, selector) {
|
||||
info("Make sure the pseudoclass lock is removed from #div-1 and its " +
|
||||
"ancestors");
|
||||
|
||||
let hasLock = yield testActor.hasPseudoClassLock("#div-1", PSEUDO);
|
||||
let hasLock = yield testActor.hasPseudoClassLock(selector, PSEUDO);
|
||||
ok(!hasLock, "pseudo-class lock has been removed");
|
||||
hasLock = yield testActor.hasPseudoClassLock("#parent-div", PSEUDO);
|
||||
ok(!hasLock, "pseudo-class lock has been removed");
|
||||
@ -133,13 +145,13 @@ function* assertPseudoRemovedFromNode(testActor) {
|
||||
ok(!hasLock, "pseudo-class lock has been removed");
|
||||
}
|
||||
|
||||
function* assertPseudoRemovedFromView(inspector, testActor, ruleview) {
|
||||
function* assertPseudoRemovedFromView(inspector, testActor, ruleview, selector) {
|
||||
info("Check that the ruleview no longer contains the pseudo-class rule");
|
||||
let rules = ruleview.element.querySelectorAll(
|
||||
".ruleview-rule.theme-separator");
|
||||
is(rules.length, 2, "rule view is showing 2 rules after removing lock");
|
||||
|
||||
yield showPickerOn("#div-1", inspector);
|
||||
yield showPickerOn(selector, inspector);
|
||||
|
||||
let value = yield testActor.getHighlighterNodeTextContent(
|
||||
"box-model-infobar-pseudo-classes");
|
||||
|
@ -584,6 +584,22 @@ const Services = {
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* An implementation of Services.wm that provides a shim for
|
||||
* getMostRecentWindow.
|
||||
*/
|
||||
wm: {
|
||||
getMostRecentWindow: function () {
|
||||
// Having the returned object implement openUILinkIn is
|
||||
// sufficient for our purposes.
|
||||
return {
|
||||
openUILinkIn: function (url) {
|
||||
window.open(url, "_blank");
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
20
devtools/client/shared/shim/test/file_service_wm.html
Normal file
20
devtools/client/shared/shim/test/file_service_wm.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
// Silence eslint complaint about the onload below.
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
"use strict";
|
||||
|
||||
function load() {
|
||||
(window.opener || window.parent).hurray();
|
||||
window.close();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload='load()'>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,8 +1,10 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
file_service_wm.html
|
||||
prefs-wrapper.js
|
||||
|
||||
[test_service_appinfo.html]
|
||||
[test_service_focus.html]
|
||||
[test_service_prefs.html]
|
||||
[test_service_prefs_defaults.html]
|
||||
[test_service_wm.html]
|
||||
|
36
devtools/client/shared/shim/test/test_service_wm.html
Normal file
36
devtools/client/shared/shim/test/test_service_wm.html
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1310279
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1310279 - replace Services.wm</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
|
||||
<script type="application/javascript;version=1.8">
|
||||
"use strict";
|
||||
var exports = {}
|
||||
var module = {exports};
|
||||
</script>
|
||||
|
||||
<script type="application/javascript;version=1.8"
|
||||
src="resource://devtools/client/shared/shim/Services.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.8">
|
||||
"use strict";
|
||||
|
||||
function hurray(window) {
|
||||
ok(true, "window loaded");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
Services.wm.getMostRecentWindow().openUILinkIn("file_service_wm.html");
|
||||
|
||||
</script>
|
||||
</body>
|
@ -4,5 +4,6 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DIRS += [
|
||||
'stub-generators'
|
||||
'stub-generators',
|
||||
'stubs'
|
||||
]
|
||||
|
@ -18,18 +18,120 @@ registerCleanupFunction(() => {
|
||||
});
|
||||
|
||||
const { prepareMessage } = require("devtools/client/webconsole/new-console-output/utils/messages");
|
||||
const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index.js");
|
||||
|
||||
const BASE_PATH = "../../../../devtools/client/webconsole/new-console-output/test/fixtures";
|
||||
const TEMP_FILE_PATH = OS.Path.join(`${BASE_PATH}/stub-generators`, "test-tempfile.js");
|
||||
|
||||
let cachedPackets = {};
|
||||
|
||||
function getCleanedPacket(key, packet) {
|
||||
if(Object.keys(cachedPackets).includes(key)) {
|
||||
return cachedPackets[key];
|
||||
}
|
||||
|
||||
// Strip escaped characters.
|
||||
let safeKey = key
|
||||
.replace(/\\n/g, "\n")
|
||||
.replace(/\\r/g, "\r")
|
||||
.replace(/\\\"/g, `\"`)
|
||||
.replace(/\\\'/g, `\'`);
|
||||
|
||||
// If the stub already exist, we want to ignore irrelevant properties
|
||||
// (actor, timeStamp, timer, ...) that might changed and "pollute"
|
||||
// the diff resulting from this stub generation.
|
||||
let res;
|
||||
if(stubPackets.has(safeKey)) {
|
||||
|
||||
let existingPacket = stubPackets.get(safeKey);
|
||||
res = Object.assign({}, packet, {
|
||||
from: existingPacket.from
|
||||
});
|
||||
|
||||
// Clean root timestamp.
|
||||
if(res.timestamp) {
|
||||
res.timestamp = existingPacket.timestamp;
|
||||
}
|
||||
|
||||
if (res.message) {
|
||||
// Clean timeStamp on the message prop.
|
||||
res.message.timeStamp = existingPacket.message.timeStamp;
|
||||
if (res.message.timer) {
|
||||
// Clean timer properties on the message.
|
||||
// Those properties are found on console.time and console.timeEnd calls,
|
||||
// and those time can vary, which is why we need to clean them.
|
||||
if (res.message.timer.started) {
|
||||
res.message.timer.started = existingPacket.message.timer.started;
|
||||
}
|
||||
if (res.message.timer.duration) {
|
||||
res.message.timer.duration = existingPacket.message.timer.duration;
|
||||
}
|
||||
}
|
||||
|
||||
if(Array.isArray(res.message.arguments)) {
|
||||
// Clean actor ids on each message.arguments item.
|
||||
res.message.arguments.forEach((argument, i) => {
|
||||
if (argument && argument.actor) {
|
||||
argument.actor = existingPacket.message.arguments[i].actor;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (res.result) {
|
||||
// Clean actor ids on evaluation result messages.
|
||||
res.result.actor = existingPacket.result.actor;
|
||||
if (res.result.preview) {
|
||||
if(res.result.preview.timestamp) {
|
||||
// Clean timestamp there too.
|
||||
res.result.preview.timestamp = existingPacket.result.preview.timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res.exception) {
|
||||
// Clean actor ids on exception messages.
|
||||
res.exception.actor = existingPacket.exception.actor;
|
||||
if (res.exception.preview) {
|
||||
if(res.exception.preview.timestamp) {
|
||||
// Clean timestamp there too.
|
||||
res.exception.preview.timestamp = existingPacket.exception.preview.timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res.eventActor) {
|
||||
// Clean actor ids, timeStamp and startedDateTime on network messages.
|
||||
res.eventActor.actor = existingPacket.eventActor.actor;
|
||||
res.eventActor.startedDateTime = existingPacket.eventActor.startedDateTime;
|
||||
res.eventActor.timeStamp = existingPacket.eventActor.timeStamp;
|
||||
}
|
||||
|
||||
if (res.pageError) {
|
||||
// Clean timeStamp on pageError messages.
|
||||
res.pageError.timeStamp = existingPacket.pageError.timeStamp;
|
||||
}
|
||||
|
||||
} else {
|
||||
res = packet;
|
||||
}
|
||||
|
||||
cachedPackets[key] = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
function formatPacket(key, packet) {
|
||||
return `
|
||||
stubPackets.set("${key}", ${JSON.stringify(packet, null, "\t")});
|
||||
stubPackets.set("${key}", ${JSON.stringify(getCleanedPacket(key, packet), null, "\t")});
|
||||
`;
|
||||
}
|
||||
|
||||
function formatStub(key, message) {
|
||||
let prepared = prepareMessage(message, {getNextId: () => "1"});
|
||||
function formatStub(key, packet) {
|
||||
let prepared = prepareMessage(
|
||||
getCleanedPacket(key, packet),
|
||||
{getNextId: () => "1"}
|
||||
);
|
||||
|
||||
return `
|
||||
stubPreparedMessages.set("${key}", new ConsoleMessage(${JSON.stringify(prepared, null, "\t")}));
|
||||
`;
|
||||
|
11
devtools/client/webconsole/new-console-output/test/fixtures/stubs/moz.build
vendored
Normal file
11
devtools/client/webconsole/new-console-output/test/fixtures/stubs/moz.build
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'consoleApi.js',
|
||||
'evaluationResult.js',
|
||||
'index.js',
|
||||
'networkEvent.js',
|
||||
'pageError.js',
|
||||
)
|
@ -1752,6 +1752,14 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
||||
return;
|
||||
}
|
||||
|
||||
// There can be only one node locked per pseudo, so dismiss all existing
|
||||
// ones
|
||||
for (let locked of this._activePseudoClassLocks) {
|
||||
if (DOMUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
|
||||
this._removePseudoClassLock(locked, pseudo);
|
||||
}
|
||||
}
|
||||
|
||||
this._addPseudoClassLock(node, pseudo);
|
||||
|
||||
if (!options.parents) {
|
||||
@ -1836,6 +1844,15 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
||||
|
||||
this._removePseudoClassLock(node, pseudo);
|
||||
|
||||
// Remove pseudo class for children as we don't want to allow
|
||||
// turning it on for some childs without setting it on some parents
|
||||
for (let locked of this._activePseudoClassLocks) {
|
||||
if (node.rawNode.contains(locked.rawNode) &&
|
||||
DOMUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
|
||||
this._removePseudoClassLock(locked, pseudo);
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.parents) {
|
||||
return;
|
||||
}
|
||||
|
@ -46,6 +46,14 @@ KeyframeEffect::Constructor(
|
||||
aOptions, aRv);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<KeyframeEffect>
|
||||
KeyframeEffect::Constructor(const GlobalObject& aGlobal,
|
||||
KeyframeEffectReadOnly& aSource,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
return ConstructKeyframeEffect<KeyframeEffect>(aGlobal, aSource, aRv);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<KeyframeEffect>
|
||||
KeyframeEffect::Constructor(
|
||||
const GlobalObject& aGlobal,
|
||||
|
@ -47,6 +47,11 @@ public:
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<KeyframeEffect>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
KeyframeEffectReadOnly& aSource,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// Variant of Constructor that accepts a KeyframeAnimationOptions object
|
||||
// for use with for Animatable.animate.
|
||||
// Not exposed to content.
|
||||
|
@ -273,40 +273,7 @@ KeyframeEffectReadOnly::UpdateProperties(nsStyleContext* aStyleContext)
|
||||
{
|
||||
MOZ_ASSERT(aStyleContext);
|
||||
|
||||
nsTArray<AnimationProperty> properties;
|
||||
if (mTarget) {
|
||||
// When GetComputedKeyframeValues or GetAnimationPropertiesFromKeyframes
|
||||
// calculate computed values from |mKeyframes|, they could possibly
|
||||
// trigger a subsequent restyle in which we rebuild animations. If that
|
||||
// happens we could find that |mKeyframes| is overwritten while it is
|
||||
// being iterated over. Normally that shouldn't happen but just in case we
|
||||
// make a copy of |mKeyframes| first and iterate over that instead.
|
||||
auto keyframesCopy(mKeyframes);
|
||||
|
||||
nsTArray<ComputedKeyframeValues> computedValues =
|
||||
KeyframeUtils::GetComputedKeyframeValues(keyframesCopy,
|
||||
mTarget->mElement,
|
||||
aStyleContext);
|
||||
|
||||
if (mEffectOptions.mSpacingMode == SpacingMode::paced) {
|
||||
KeyframeUtils::ApplySpacing(keyframesCopy, SpacingMode::paced,
|
||||
mEffectOptions.mPacedProperty,
|
||||
computedValues, aStyleContext);
|
||||
}
|
||||
|
||||
properties =
|
||||
KeyframeUtils::GetAnimationPropertiesFromKeyframes(keyframesCopy,
|
||||
computedValues,
|
||||
aStyleContext);
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(SpecifiedKeyframeArraysAreEqual(mKeyframes, keyframesCopy),
|
||||
"Apart from the computed offset members, the keyframes array"
|
||||
" should not be modified");
|
||||
#endif
|
||||
|
||||
mKeyframes.SwapElements(keyframesCopy);
|
||||
}
|
||||
nsTArray<AnimationProperty> properties = BuildProperties(aStyleContext);
|
||||
|
||||
if (mProperties == properties) {
|
||||
return;
|
||||
@ -613,6 +580,87 @@ KeyframeEffectReadOnly::ConstructKeyframeEffect(
|
||||
return effect.forget();
|
||||
}
|
||||
|
||||
template<class KeyframeEffectType>
|
||||
/* static */ already_AddRefed<KeyframeEffectType>
|
||||
KeyframeEffectReadOnly::ConstructKeyframeEffect(const GlobalObject& aGlobal,
|
||||
KeyframeEffectReadOnly& aSource,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aGlobal.Context());
|
||||
if (!doc) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a new KeyframeEffectReadOnly object with aSource's target,
|
||||
// iteration composite operation, composite operation, and spacing mode.
|
||||
// The constructor creates a new AnimationEffect(ReadOnly) object by
|
||||
// aSource's TimingParams.
|
||||
// Note: we don't need to re-throw exceptions since the value specified on
|
||||
// aSource's timing object can be assumed valid.
|
||||
RefPtr<KeyframeEffectType> effect =
|
||||
new KeyframeEffectType(doc,
|
||||
aSource.mTarget,
|
||||
aSource.SpecifiedTiming(),
|
||||
aSource.mEffectOptions);
|
||||
// Copy cumulative change hint. mCumulativeChangeHint should be the same as
|
||||
// the source one because both of targets are the same.
|
||||
effect->mCumulativeChangeHint = aSource.mCumulativeChangeHint;
|
||||
|
||||
// Copy aSource's keyframes and animation properties.
|
||||
// Note: We don't call SetKeyframes directly, which might revise the
|
||||
// computed offsets and rebuild the animation properties.
|
||||
// FIXME: Bug 1314537: We have to make sure SharedKeyframeList is handled
|
||||
// properly.
|
||||
effect->mKeyframes = aSource.mKeyframes;
|
||||
effect->mProperties = aSource.mProperties;
|
||||
return effect.forget();
|
||||
}
|
||||
|
||||
nsTArray<AnimationProperty>
|
||||
KeyframeEffectReadOnly::BuildProperties(nsStyleContext* aStyleContext)
|
||||
{
|
||||
MOZ_ASSERT(aStyleContext);
|
||||
|
||||
nsTArray<AnimationProperty> result;
|
||||
// If mTarget is null, return an empty property array.
|
||||
if (!mTarget) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// When GetComputedKeyframeValues or GetAnimationPropertiesFromKeyframes
|
||||
// calculate computed values from |mKeyframes|, they could possibly
|
||||
// trigger a subsequent restyle in which we rebuild animations. If that
|
||||
// happens we could find that |mKeyframes| is overwritten while it is
|
||||
// being iterated over. Normally that shouldn't happen but just in case we
|
||||
// make a copy of |mKeyframes| first and iterate over that instead.
|
||||
auto keyframesCopy(mKeyframes);
|
||||
|
||||
nsTArray<ComputedKeyframeValues> computedValues =
|
||||
KeyframeUtils::GetComputedKeyframeValues(keyframesCopy,
|
||||
mTarget->mElement,
|
||||
aStyleContext);
|
||||
|
||||
if (mEffectOptions.mSpacingMode == SpacingMode::paced) {
|
||||
KeyframeUtils::ApplySpacing(keyframesCopy, SpacingMode::paced,
|
||||
mEffectOptions.mPacedProperty,
|
||||
computedValues, aStyleContext);
|
||||
}
|
||||
|
||||
result = KeyframeUtils::GetAnimationPropertiesFromKeyframes(keyframesCopy,
|
||||
computedValues,
|
||||
aStyleContext);
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(SpecifiedKeyframeArraysAreEqual(mKeyframes, keyframesCopy),
|
||||
"Apart from the computed offset members, the keyframes array"
|
||||
" should not be modified");
|
||||
#endif
|
||||
|
||||
mKeyframes.SwapElements(keyframesCopy);
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::UpdateTargetRegistration()
|
||||
{
|
||||
@ -716,6 +764,14 @@ KeyframeEffectReadOnly::Constructor(
|
||||
aRv);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<KeyframeEffectReadOnly>
|
||||
KeyframeEffectReadOnly::Constructor(const GlobalObject& aGlobal,
|
||||
KeyframeEffectReadOnly& aSource,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
return ConstructKeyframeEffect<KeyframeEffectReadOnly>(aGlobal, aSource, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::GetTarget(
|
||||
Nullable<OwningElementOrCSSPseudoElement>& aRv) const
|
||||
|
@ -121,14 +121,16 @@ struct AnimationPropertySegment
|
||||
StyleAnimationValue mFromValue, mToValue;
|
||||
Maybe<ComputedTimingFunction> mTimingFunction;
|
||||
|
||||
bool operator==(const AnimationPropertySegment& aOther) const {
|
||||
bool operator==(const AnimationPropertySegment& aOther) const
|
||||
{
|
||||
return mFromKey == aOther.mFromKey &&
|
||||
mToKey == aOther.mToKey &&
|
||||
mFromValue == aOther.mFromValue &&
|
||||
mToValue == aOther.mToValue &&
|
||||
mTimingFunction == aOther.mTimingFunction;
|
||||
}
|
||||
bool operator!=(const AnimationPropertySegment& aOther) const {
|
||||
bool operator!=(const AnimationPropertySegment& aOther) const
|
||||
{
|
||||
return !(*this == aOther);
|
||||
}
|
||||
};
|
||||
@ -152,17 +154,31 @@ struct AnimationProperty
|
||||
|
||||
InfallibleTArray<AnimationPropertySegment> mSegments;
|
||||
|
||||
// The copy constructor/assignment doesn't copy mIsRunningOnCompositor and
|
||||
// mPerformanceWarning.
|
||||
AnimationProperty() = default;
|
||||
AnimationProperty(const AnimationProperty& aOther)
|
||||
: mProperty(aOther.mProperty), mSegments(aOther.mSegments) { }
|
||||
AnimationProperty& operator=(const AnimationProperty& aOther)
|
||||
{
|
||||
mProperty = aOther.mProperty;
|
||||
mSegments = aOther.mSegments;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// NOTE: This operator does *not* compare the mIsRunningOnCompositor member.
|
||||
// This is because AnimationProperty objects are compared when recreating
|
||||
// CSS animations to determine if mutation observer change records need to
|
||||
// be created or not. However, at the point when these objects are compared
|
||||
// the mIsRunningOnCompositor will not have been set on the new objects so
|
||||
// we ignore this member to avoid generating spurious change records.
|
||||
bool operator==(const AnimationProperty& aOther) const {
|
||||
bool operator==(const AnimationProperty& aOther) const
|
||||
{
|
||||
return mProperty == aOther.mProperty &&
|
||||
mSegments == aOther.mSegments;
|
||||
}
|
||||
bool operator!=(const AnimationProperty& aOther) const {
|
||||
bool operator!=(const AnimationProperty& aOther) const
|
||||
{
|
||||
return !(*this == aOther);
|
||||
}
|
||||
};
|
||||
@ -198,6 +214,11 @@ public:
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<KeyframeEffectReadOnly>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
KeyframeEffectReadOnly& aSource,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const;
|
||||
Maybe<NonOwningAnimationTarget> GetTarget() const
|
||||
{
|
||||
@ -320,6 +341,17 @@ protected:
|
||||
const OptionsType& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template<class KeyframeEffectType>
|
||||
static already_AddRefed<KeyframeEffectType>
|
||||
ConstructKeyframeEffect(const GlobalObject& aGlobal,
|
||||
KeyframeEffectReadOnly& aSource,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// Build properties by recalculating from |mKeyframes| using |aStyleContext|
|
||||
// to resolve specified values. This function also applies paced spacing if
|
||||
// needed.
|
||||
nsTArray<AnimationProperty> BuildProperties(nsStyleContext* aStyleContext);
|
||||
|
||||
// This effect is registered with its target element so long as:
|
||||
//
|
||||
// (a) It has a target element, and
|
||||
|
@ -162,7 +162,7 @@ AudioChannelAgent::InitInternal(nsPIDOMWindowInner* aWindow,
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!aWindow)) {
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aWindow->IsInnerWindow());
|
||||
|
@ -469,10 +469,6 @@ include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
for var in ('MOZ_B2G_RIL'):
|
||||
if CONFIG[var]:
|
||||
DEFINES[var] = True
|
||||
|
||||
if CONFIG['MOZ_BUILD_APP'] in ['browser', 'mobile/android', 'xulrunner']:
|
||||
DEFINES['HAVE_SIDEBAR'] = True
|
||||
|
||||
|
@ -1304,7 +1304,9 @@ HTMLCanvasElement::GetCompositorBackendType() const
|
||||
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(OwnerDoc());
|
||||
if (docWidget) {
|
||||
layers::LayerManager* layerManager = docWidget->GetLayerManager();
|
||||
return layerManager->GetCompositorBackendType();
|
||||
if (layerManager) {
|
||||
return layerManager->GetCompositorBackendType();
|
||||
}
|
||||
}
|
||||
|
||||
return LayersBackend::LAYERS_NONE;
|
||||
|
@ -2952,7 +2952,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
|
||||
mWatchManager.Watch(mReadyState, &HTMLMediaElement::UpdateReadyStateInternal);
|
||||
|
||||
mShutdownObserver->Subscribe(this);
|
||||
CreateAudioChannelAgent();
|
||||
MaybeCreateAudioChannelAgent();
|
||||
}
|
||||
|
||||
HTMLMediaElement::~HTMLMediaElement()
|
||||
@ -2993,6 +2993,7 @@ HTMLMediaElement::~HTMLMediaElement()
|
||||
}
|
||||
|
||||
WakeLockRelease();
|
||||
mAudioChannelAgent = nullptr;
|
||||
}
|
||||
|
||||
void HTMLMediaElement::StopSuspendingAfterFirstFrame()
|
||||
@ -3752,10 +3753,6 @@ nsresult HTMLMediaElement::InitializeDecoderAsClone(MediaDecoder* aOriginal)
|
||||
|
||||
LOG(LogLevel::Debug, ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal));
|
||||
|
||||
decoder->SetMediaSeekable(aOriginal->IsMediaSeekable());
|
||||
decoder->SetMediaSeekableOnlyInBufferedRanges(
|
||||
aOriginal->IsMediaSeekableOnlyInBufferedRanges());
|
||||
|
||||
RefPtr<MediaResource> resource =
|
||||
originalResource->CloneData(decoder->GetResourceCallback());
|
||||
|
||||
@ -5759,17 +5756,26 @@ ImageContainer* HTMLMediaElement::GetImageContainer()
|
||||
return container ? container->GetImageContainer() : nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::CreateAudioChannelAgent()
|
||||
bool
|
||||
HTMLMediaElement::MaybeCreateAudioChannelAgent()
|
||||
{
|
||||
if (mAudioChannelAgent) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
mAudioChannelAgent = new AudioChannelAgent();
|
||||
mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(),
|
||||
nsresult rv = mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(),
|
||||
static_cast<int32_t>(mAudioChannel),
|
||||
this);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mAudioChannelAgent = nullptr;
|
||||
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
|
||||
("HTMLMediaElement, Fail to initialize the audio channel agent,"
|
||||
" this = %p\n", this));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -5825,6 +5831,10 @@ HTMLMediaElement::UpdateAudioChannelPlayingState(bool aForcePlaying)
|
||||
aForcePlaying || IsPlayingThroughTheAudioChannel();
|
||||
|
||||
if (playingThroughTheAudioChannel != mPlayingThroughTheAudioChannel) {
|
||||
if (!MaybeCreateAudioChannelAgent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPlayingThroughTheAudioChannel = playingThroughTheAudioChannel;
|
||||
NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel);
|
||||
}
|
||||
@ -6035,7 +6045,7 @@ HTMLMediaElement::IsAllowedToPlay()
|
||||
// If the tab hasn't been activated yet, the media element in that tab can't
|
||||
// be playback now until the tab goes to foreground first time or user clicks
|
||||
// the unblocking tab icon.
|
||||
if (!IsTabActivated()) {
|
||||
if (MaybeCreateAudioChannelAgent() && !IsTabActivated()) {
|
||||
// Even we haven't start playing yet, we still need to notify the audio
|
||||
// channe system because we need to receive the resume notification later.
|
||||
UpdateAudioChannelPlayingState(true /* force to start */);
|
||||
@ -6048,6 +6058,7 @@ HTMLMediaElement::IsAllowedToPlay()
|
||||
bool
|
||||
HTMLMediaElement::IsTabActivated() const
|
||||
{
|
||||
MOZ_ASSERT(mAudioChannelAgent);
|
||||
return !mAudioChannelAgent->ShouldBlockMedia();
|
||||
}
|
||||
|
||||
@ -6459,9 +6470,8 @@ HTMLMediaElement::SetAudibleState(bool aAudible)
|
||||
void
|
||||
HTMLMediaElement::NotifyAudioPlaybackChanged(AudibleChangedReasons aReason)
|
||||
{
|
||||
MOZ_ASSERT(mAudioChannelAgent);
|
||||
|
||||
if (!mAudioChannelAgent->IsPlayingStarted()) {
|
||||
if (MaybeCreateAudioChannelAgent() &&
|
||||
!mAudioChannelAgent->IsPlayingStarted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -6504,6 +6514,7 @@ HTMLMediaElement::MaybeNotifyMediaResumed(SuspendTypes aSuspend)
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mAudioChannelAgent);
|
||||
uint64_t windowID = mAudioChannelAgent->WindowID();
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction([windowID]() -> void {
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
@ -6580,14 +6591,13 @@ HTMLMediaElement::SetMediaInfo(const MediaInfo& aInfo)
|
||||
void
|
||||
HTMLMediaElement::AudioCaptureStreamChangeIfNeeded()
|
||||
{
|
||||
MOZ_ASSERT(mAudioChannelAgent);
|
||||
|
||||
// No need to capture a silence media element.
|
||||
if (!HasAudio()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mAudioChannelAgent->IsPlayingStarted()) {
|
||||
if (MaybeCreateAudioChannelAgent() &&
|
||||
!mAudioChannelAgent->IsPlayingStarted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1229,9 +1229,11 @@ protected:
|
||||
// Notifies the audio channel agent when the element starts or stops playing.
|
||||
void NotifyAudioChannelAgent(bool aPlaying);
|
||||
|
||||
// Creates the audio channel agent in the beginning and this agent would be
|
||||
// used to communicate with the AudioChannelService.
|
||||
void CreateAudioChannelAgent();
|
||||
// True if we create the audio channel agent successfully or we already have
|
||||
// one. The agent is used to communicate with the AudioChannelService. eg.
|
||||
// notify we are playing/audible and receive muted/unmuted/suspend/resume
|
||||
// commands from AudioChannelService.
|
||||
bool MaybeCreateAudioChannelAgent();
|
||||
|
||||
// Determine if the element should be paused because of suspend conditions.
|
||||
bool ShouldElementBePaused();
|
||||
|
@ -171,15 +171,6 @@ MediaDecoder::ResourceCallback::SetInfinite(bool aInfinite)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoder::ResourceCallback::SetMediaSeekable(bool aMediaSeekable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mDecoder) {
|
||||
mDecoder->SetMediaSeekable(aMediaSeekable);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoder::ResourceCallback::NotifyNetworkError()
|
||||
{
|
||||
@ -406,8 +397,6 @@ MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
|
||||
, INIT_CANONICAL(mPlaybackBytesPerSecond, 0.0)
|
||||
, INIT_CANONICAL(mPlaybackRateReliable, true)
|
||||
, INIT_CANONICAL(mDecoderPosition, 0)
|
||||
, INIT_CANONICAL(mMediaSeekable, true)
|
||||
, INIT_CANONICAL(mMediaSeekableOnlyInBufferedRanges, false)
|
||||
, INIT_CANONICAL(mIsVisible, !aOwner->IsHidden())
|
||||
, mTelemetryReported(false)
|
||||
{
|
||||
@ -791,8 +780,8 @@ MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
aInfo->mAudio.mChannels, aInfo->mAudio.mRate,
|
||||
aInfo->HasAudio(), aInfo->HasVideo());
|
||||
|
||||
SetMediaSeekable(aInfo->mMediaSeekable);
|
||||
SetMediaSeekableOnlyInBufferedRanges(aInfo->mMediaSeekableOnlyInBufferedRanges);
|
||||
mMediaSeekable = aInfo->mMediaSeekable;
|
||||
mMediaSeekableOnlyInBufferedRanges = aInfo->mMediaSeekableOnlyInBufferedRanges;
|
||||
mInfo = aInfo.forget();
|
||||
ConstructMediaTracks();
|
||||
|
||||
@ -1286,18 +1275,6 @@ MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
|
||||
mEstimatedDuration = Some(TimeUnit::FromMicroseconds(aDuration));
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoder::SetMediaSeekable(bool aMediaSeekable) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mMediaSeekable = aMediaSeekable;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoder::SetMediaSeekableOnlyInBufferedRanges(bool aMediaSeekableOnlyInBufferedRanges){
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mMediaSeekableOnlyInBufferedRanges = aMediaSeekableOnlyInBufferedRanges;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaDecoder::IsTransportSeekable()
|
||||
{
|
||||
@ -1313,13 +1290,6 @@ MediaDecoder::IsMediaSeekable()
|
||||
return mMediaSeekable;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaDecoder::IsMediaSeekableOnlyInBufferedRanges()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mMediaSeekableOnlyInBufferedRanges;
|
||||
}
|
||||
|
||||
media::TimeIntervals
|
||||
MediaDecoder::GetSeekable()
|
||||
{
|
||||
@ -1333,7 +1303,7 @@ MediaDecoder::GetSeekable()
|
||||
// We can seek in buffered range if the media is seekable. Also, we can seek
|
||||
// in unbuffered ranges if the transport level is seekable (local file or the
|
||||
// server supports range requests, etc.) or in cue-less WebMs
|
||||
if (IsMediaSeekableOnlyInBufferedRanges()) {
|
||||
if (mMediaSeekableOnlyInBufferedRanges) {
|
||||
return GetBuffered();
|
||||
} else if (!IsMediaSeekable()) {
|
||||
return media::TimeIntervals();
|
||||
|
@ -82,7 +82,6 @@ public:
|
||||
/* MediaResourceCallback functions */
|
||||
MediaDecoderOwner* GetMediaOwner() const override;
|
||||
void SetInfinite(bool aInfinite) override;
|
||||
void SetMediaSeekable(bool aMediaSeekable) override;
|
||||
void NotifyNetworkError() override;
|
||||
void NotifyDecodeError() override;
|
||||
void NotifyDataArrived() override;
|
||||
@ -252,17 +251,9 @@ protected:
|
||||
void UpdateEstimatedMediaDuration(int64_t aDuration) override;
|
||||
|
||||
public:
|
||||
// Set a flag indicating whether random seeking is supported
|
||||
void SetMediaSeekable(bool aMediaSeekable);
|
||||
// Set a flag indicating whether seeking is supported only in buffered ranges
|
||||
void SetMediaSeekableOnlyInBufferedRanges(bool aMediaSeekableOnlyInBufferedRanges);
|
||||
|
||||
// Returns true if this media supports random seeking. False for example with
|
||||
// chained ogg files.
|
||||
bool IsMediaSeekable();
|
||||
// Returns true if this media supports seeking only in buffered ranges. True
|
||||
// for example in WebMs with no cues
|
||||
bool IsMediaSeekableOnlyInBufferedRanges();
|
||||
// Returns true if seeking is supported on a transport level (e.g. the server
|
||||
// supports range requests, we are playing a file, etc.).
|
||||
bool IsTransportSeekable();
|
||||
@ -573,7 +564,7 @@ private:
|
||||
|
||||
void OnMediaNotSeekable()
|
||||
{
|
||||
SetMediaSeekable(false);
|
||||
mMediaSeekable = false;
|
||||
}
|
||||
|
||||
void FinishShutdown();
|
||||
@ -669,6 +660,13 @@ protected:
|
||||
// True if we've already fired metadataloaded.
|
||||
bool mFiredMetadataLoaded;
|
||||
|
||||
// True if the media is seekable (i.e. supports random access).
|
||||
bool mMediaSeekable = true;
|
||||
|
||||
// True if the media is only seekable within its buffered ranges
|
||||
// like WebMs with no cues.
|
||||
bool mMediaSeekableOnlyInBufferedRanges = false;
|
||||
|
||||
// Stores media info, including info of audio tracks and video tracks, should
|
||||
// only be accessed from main thread.
|
||||
nsAutoPtr<MediaInfo> mInfo;
|
||||
@ -768,12 +766,6 @@ protected:
|
||||
// back again.
|
||||
Canonical<int64_t> mDecoderPosition;
|
||||
|
||||
// True if the media is seekable (i.e. supports random access).
|
||||
Canonical<bool> mMediaSeekable;
|
||||
|
||||
// True if the media is only seekable within its buffered ranges.
|
||||
Canonical<bool> mMediaSeekableOnlyInBufferedRanges;
|
||||
|
||||
// True if the decoder is visible.
|
||||
Canonical<bool> mIsVisible;
|
||||
|
||||
@ -815,12 +807,6 @@ public:
|
||||
AbstractCanonical<int64_t>* CanonicalDecoderPosition() {
|
||||
return &mDecoderPosition;
|
||||
}
|
||||
AbstractCanonical<bool>* CanonicalMediaSeekable() {
|
||||
return &mMediaSeekable;
|
||||
}
|
||||
AbstractCanonical<bool>* CanonicalMediaSeekableOnlyInBufferedRanges() {
|
||||
return &mMediaSeekableOnlyInBufferedRanges;
|
||||
}
|
||||
AbstractCanonical<bool>* CanonicalIsVisible() {
|
||||
return &mIsVisible;
|
||||
}
|
||||
|
@ -193,26 +193,15 @@ public:
|
||||
virtual State GetState() const = 0;
|
||||
|
||||
// Event handlers for various events.
|
||||
// Return true if the event is handled by this state object.
|
||||
virtual bool HandleDormant(bool aDormant);
|
||||
|
||||
virtual bool HandleCDMProxyReady() { return false; }
|
||||
|
||||
virtual bool HandleAudioDecoded(MediaData* aAudio) { return false; }
|
||||
|
||||
virtual bool HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool HandleEndOfStream() { return false; }
|
||||
|
||||
virtual bool HandleWaitingForData() { return false; }
|
||||
virtual void HandleCDMProxyReady() {}
|
||||
virtual void HandleAudioDecoded(MediaData* aAudio) {}
|
||||
virtual void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) {}
|
||||
virtual void HandleEndOfStream() {}
|
||||
virtual void HandleWaitingForData() {}
|
||||
virtual void HandleAudioCaptured() {}
|
||||
|
||||
virtual RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) = 0;
|
||||
|
||||
virtual bool HandleAudioCaptured() { return false; }
|
||||
|
||||
virtual RefPtr<ShutdownPromise> HandleShutdown();
|
||||
|
||||
virtual void HandleVideoSuspendTimeout() = 0;
|
||||
@ -278,7 +267,6 @@ protected:
|
||||
* Transition to other states when decoding metadata is done:
|
||||
* SHUTDOWN if failing to decode metadata.
|
||||
* WAIT_FOR_CDM if the media is encrypted and CDM is not available.
|
||||
* DORMANT if any pending dormant request.
|
||||
* DECODING_FIRSTFRAME otherwise.
|
||||
*/
|
||||
class MediaDecoderStateMachine::DecodeMetadataState
|
||||
@ -318,12 +306,6 @@ public:
|
||||
return DECODER_STATE_DECODING_METADATA;
|
||||
}
|
||||
|
||||
bool HandleDormant(bool aDormant) override
|
||||
{
|
||||
mPendingDormant = aDormant;
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(false, "Can't seek while decoding metadata.");
|
||||
@ -352,18 +334,12 @@ private:
|
||||
}
|
||||
|
||||
MozPromiseRequestHolder<MediaDecoderReader::MetadataPromise> mMetadataRequest;
|
||||
|
||||
// True if we need to enter dormant state after reading metadata. Note that
|
||||
// we can't enter dormant state until reading metadata is done for some
|
||||
// limitations of the reader.
|
||||
bool mPendingDormant = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Purpose: wait for the CDM to start decoding.
|
||||
*
|
||||
* Transition to other states when CDM is ready:
|
||||
* DORMANT if any pending dormant request.
|
||||
* DECODING_FIRSTFRAME otherwise.
|
||||
*/
|
||||
class MediaDecoderStateMachine::WaitForCDMState
|
||||
@ -372,10 +348,9 @@ class MediaDecoderStateMachine::WaitForCDMState
|
||||
public:
|
||||
explicit WaitForCDMState(Master* aPtr) : StateObject(aPtr) {}
|
||||
|
||||
void Enter(bool aPendingDormant)
|
||||
void Enter()
|
||||
{
|
||||
MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
|
||||
mPendingDormant = aPendingDormant;
|
||||
}
|
||||
|
||||
void Exit() override
|
||||
@ -390,9 +365,7 @@ public:
|
||||
return DECODER_STATE_WAIT_FOR_CDM;
|
||||
}
|
||||
|
||||
bool HandleDormant(bool aDormant) override;
|
||||
|
||||
bool HandleCDMProxyReady() override;
|
||||
void HandleCDMProxyReady() override;
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
|
||||
{
|
||||
@ -414,7 +387,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool mPendingDormant = false;
|
||||
SeekJob mPendingSeek;
|
||||
};
|
||||
|
||||
@ -443,8 +415,8 @@ public:
|
||||
|
||||
void Exit() override
|
||||
{
|
||||
// mPendingSeek is either moved in HandleDormant() or should be rejected
|
||||
// here before transition to SHUTDOWN.
|
||||
// mPendingSeek is either moved when exiting dormant or
|
||||
// should be rejected here before transition to SHUTDOWN.
|
||||
mPendingSeek.RejectIfExists(__func__);
|
||||
}
|
||||
|
||||
@ -453,8 +425,6 @@ public:
|
||||
return DECODER_STATE_DORMANT;
|
||||
}
|
||||
|
||||
bool HandleDormant(bool aDormant) override;
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
|
||||
|
||||
void HandleVideoSuspendTimeout() override
|
||||
@ -467,13 +437,7 @@ public:
|
||||
// Do nothing since we won't resume decoding until exiting dormant.
|
||||
}
|
||||
|
||||
void HandlePlayStateChanged(MediaDecoder::PlayState aPlayState) override
|
||||
{
|
||||
if (aPlayState == MediaDecoder::PLAY_STATE_PLAYING) {
|
||||
// Exit dormant when the user wants to play.
|
||||
HandleDormant(false);
|
||||
}
|
||||
}
|
||||
void HandlePlayStateChanged(MediaDecoder::PlayState aPlayState) override;
|
||||
|
||||
private:
|
||||
SeekJob mPendingSeek;
|
||||
@ -483,7 +447,6 @@ private:
|
||||
* Purpose: decode the 1st audio and video frames to fire the 'loadeddata' event.
|
||||
*
|
||||
* Transition to:
|
||||
* DORMANT if any dormant request.
|
||||
* SHUTDOWN if any decode error.
|
||||
* SEEKING if any pending seek and seek is possible.
|
||||
* DECODING when the 'loadeddata' event is fired.
|
||||
@ -498,7 +461,7 @@ public:
|
||||
|
||||
void Exit() override
|
||||
{
|
||||
// mPendingSeek is either moved before transition to SEEKING or DORMANT,
|
||||
// mPendingSeek is either moved before transition to SEEKING,
|
||||
// or should be rejected here before transition to SHUTDOWN.
|
||||
mPendingSeek.RejectIfExists(__func__);
|
||||
}
|
||||
@ -508,30 +471,25 @@ public:
|
||||
return DECODER_STATE_DECODING_FIRSTFRAME;
|
||||
}
|
||||
|
||||
bool HandleAudioDecoded(MediaData* aAudio) override
|
||||
void HandleAudioDecoded(MediaData* aAudio) override
|
||||
{
|
||||
mMaster->Push(aAudio, MediaData::AUDIO_DATA);
|
||||
MaybeFinishDecodeFirstFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
{
|
||||
mMaster->Push(aVideo, MediaData::VIDEO_DATA);
|
||||
MaybeFinishDecodeFirstFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleEndOfStream() override
|
||||
void HandleEndOfStream() override
|
||||
{
|
||||
MaybeFinishDecodeFirstFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
|
||||
|
||||
bool HandleDormant(bool aDormant) override;
|
||||
|
||||
void HandleVideoSuspendTimeout() override
|
||||
{
|
||||
// Do nothing for we need to decode the 1st video frame to get the dimensions.
|
||||
@ -555,7 +513,7 @@ private:
|
||||
* Purpose: decode audio/video data for playback.
|
||||
*
|
||||
* Transition to:
|
||||
* DORMANT if any dormant request.
|
||||
* DORMANT if playback is paused for a while.
|
||||
* SEEKING if any seek request.
|
||||
* SHUTDOWN if any decode error.
|
||||
* BUFFERING if playback can't continue due to lack of decoded data.
|
||||
@ -610,37 +568,33 @@ public:
|
||||
return DECODER_STATE_DECODING;
|
||||
}
|
||||
|
||||
bool HandleAudioDecoded(MediaData* aAudio) override
|
||||
void HandleAudioDecoded(MediaData* aAudio) override
|
||||
{
|
||||
mMaster->Push(aAudio, MediaData::AUDIO_DATA);
|
||||
MaybeStopPrerolling();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
{
|
||||
mMaster->Push(aVideo, MediaData::VIDEO_DATA);
|
||||
MaybeStopPrerolling();
|
||||
CheckSlowDecoding(aDecodeStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
|
||||
|
||||
bool HandleEndOfStream() override;
|
||||
void HandleEndOfStream() override;
|
||||
|
||||
bool HandleWaitingForData() override
|
||||
void HandleWaitingForData() override
|
||||
{
|
||||
MaybeStopPrerolling();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleAudioCaptured() override
|
||||
void HandleAudioCaptured() override
|
||||
{
|
||||
MaybeStopPrerolling();
|
||||
// MediaSink is changed. Schedule Step() to check if we can start playback.
|
||||
mMaster->ScheduleStateMachine();
|
||||
return true;
|
||||
}
|
||||
|
||||
void HandleVideoSuspendTimeout() override
|
||||
@ -730,6 +684,20 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void EnterDormant()
|
||||
{
|
||||
auto t = mMaster->mMediaSink->IsStarted()
|
||||
? mMaster->GetClock()
|
||||
: mMaster->GetMediaTime();
|
||||
SeekJob seekJob;
|
||||
seekJob.mTarget = SeekTarget(t, SeekTarget::Accurate,
|
||||
MediaDecoderEventVisibility::Suppressed);
|
||||
// SeekJob asserts |mTarget.IsValid() == !mPromise.IsEmpty()| so we
|
||||
// need to create the promise even it is not used at all.
|
||||
RefPtr<MediaDecoder::SeekPromise> unused = seekJob.mPromise.Ensure(__func__);
|
||||
SetState<DormantState>(Move(seekJob));
|
||||
}
|
||||
|
||||
void StartDormantTimer()
|
||||
{
|
||||
auto timeout = MediaPrefs::DormantOnPauseTimeout();
|
||||
@ -738,7 +706,7 @@ private:
|
||||
return;
|
||||
} else if (timeout == 0) {
|
||||
// Enter dormant immediately without scheduling a timer.
|
||||
HandleDormant(true);
|
||||
EnterDormant();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -748,7 +716,7 @@ private:
|
||||
mDormantTimer.Ensure(target,
|
||||
[this] () {
|
||||
mDormantTimer.CompleteRequest();
|
||||
HandleDormant(true);
|
||||
EnterDormant();
|
||||
}, [this] () {
|
||||
mDormantTimer.CompleteRequest();
|
||||
});
|
||||
@ -774,7 +742,6 @@ private:
|
||||
* Purpose: seek to a particular new playback position.
|
||||
*
|
||||
* Transition to:
|
||||
* DORMANT if any dormant request.
|
||||
* SEEKING if any new seek request.
|
||||
* SHUTDOWN if seek failed.
|
||||
* COMPLETED if the new playback position is the end of the media resource.
|
||||
@ -871,18 +838,14 @@ public:
|
||||
return DECODER_STATE_SEEKING;
|
||||
}
|
||||
|
||||
bool HandleDormant(bool aDormant) override;
|
||||
|
||||
bool HandleAudioDecoded(MediaData* aAudio) override
|
||||
void HandleAudioDecoded(MediaData* aAudio) override
|
||||
{
|
||||
MOZ_ASSERT(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
{
|
||||
MOZ_ASSERT(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
|
||||
@ -952,7 +915,6 @@ private:
|
||||
* Purpose: stop playback until enough data is decoded to continue playback.
|
||||
*
|
||||
* Transition to:
|
||||
* DORMANT if any dormant request.
|
||||
* SEEKING if any seek request.
|
||||
* SHUTDOWN if any decode error.
|
||||
* COMPLETED when having decoded all audio/video data.
|
||||
@ -987,25 +949,23 @@ public:
|
||||
return DECODER_STATE_BUFFERING;
|
||||
}
|
||||
|
||||
bool HandleAudioDecoded(MediaData* aAudio) override
|
||||
void HandleAudioDecoded(MediaData* aAudio) override
|
||||
{
|
||||
// This might be the sample we need to exit buffering.
|
||||
// Schedule Step() to check it.
|
||||
mMaster->Push(aAudio, MediaData::AUDIO_DATA);
|
||||
mMaster->ScheduleStateMachine();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
{
|
||||
// This might be the sample we need to exit buffering.
|
||||
// Schedule Step() to check it.
|
||||
mMaster->Push(aVideo, MediaData::VIDEO_DATA);
|
||||
mMaster->ScheduleStateMachine();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleEndOfStream() override;
|
||||
void HandleEndOfStream() override;
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
|
||||
|
||||
@ -1030,7 +990,6 @@ private:
|
||||
* Purpose: play all the decoded data and fire the 'ended' event.
|
||||
*
|
||||
* Transition to:
|
||||
* DORMANT if any dormant request.
|
||||
* SEEKING if any seek request.
|
||||
*/
|
||||
class MediaDecoderStateMachine::CompletedState
|
||||
@ -1102,11 +1061,10 @@ public:
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
|
||||
|
||||
bool HandleAudioCaptured() override
|
||||
void HandleAudioCaptured() override
|
||||
{
|
||||
// MediaSink is changed. Schedule Step() to check if we can start playback.
|
||||
mMaster->ScheduleStateMachine();
|
||||
return true;
|
||||
}
|
||||
|
||||
void HandleVideoSuspendTimeout() override
|
||||
@ -1153,11 +1111,6 @@ public:
|
||||
return DECODER_STATE_SHUTDOWN;
|
||||
}
|
||||
|
||||
bool HandleDormant(bool aDormant) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(false, "Can't seek in shutdown state.");
|
||||
@ -1181,27 +1134,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
MediaDecoderStateMachine::
|
||||
StateObject::HandleDormant(bool aDormant)
|
||||
{
|
||||
if (!aDormant) {
|
||||
return true;
|
||||
}
|
||||
SeekJob seekJob;
|
||||
int64_t seekTargetTime = mMaster->mMediaSink->IsStarted()
|
||||
? mMaster->GetClock() : mMaster->GetMediaTime();
|
||||
|
||||
seekJob.mTarget = SeekTarget(seekTargetTime,
|
||||
SeekTarget::Accurate,
|
||||
MediaDecoderEventVisibility::Suppressed);
|
||||
// SeekJob asserts |mTarget.IsValid() == !mPromise.IsEmpty()| so we
|
||||
// need to create the promise even it is not used at all.
|
||||
RefPtr<MediaDecoder::SeekPromise> unused = seekJob.mPromise.Ensure(__func__);
|
||||
SetState<DormantState>(Move(seekJob));
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<ShutdownPromise>
|
||||
MediaDecoderStateMachine::
|
||||
StateObject::HandleShutdown()
|
||||
@ -1292,6 +1224,8 @@ DecodeMetadataState::OnMetadataRead(MetadataHolder* aMetadata)
|
||||
|
||||
mMaster->mInfo = Some(aMetadata->mInfo);
|
||||
mMaster->mMetadataTags = aMetadata->mTags.forget();
|
||||
mMaster->mMediaSeekable = Info().mMediaSeekable;
|
||||
mMaster->mMediaSeekableOnlyInBufferedRanges = Info().mMediaSeekableOnlyInBufferedRanges;
|
||||
|
||||
if (Info().mMetadataDuration.isSome()) {
|
||||
mMaster->RecomputeDuration();
|
||||
@ -1336,33 +1270,12 @@ DecodeMetadataState::OnMetadataRead(MetadataHolder* aMetadata)
|
||||
if (waitingForCDM) {
|
||||
// Metadata parsing was successful but we're still waiting for CDM caps
|
||||
// to become available so that we can build the correct decryptor/decoder.
|
||||
SetState<WaitForCDMState>(mPendingDormant);
|
||||
} else if (mPendingDormant) {
|
||||
SetState<DormantState>(SeekJob{});
|
||||
SetState<WaitForCDMState>();
|
||||
} else {
|
||||
SetState<DecodingFirstFrameState>(SeekJob{});
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MediaDecoderStateMachine::
|
||||
WaitForCDMState::HandleDormant(bool aDormant)
|
||||
{
|
||||
mPendingDormant = aDormant;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaDecoderStateMachine::
|
||||
DormantState::HandleDormant(bool aDormant)
|
||||
{
|
||||
if (!aDormant) {
|
||||
MOZ_ASSERT(!Info().IsEncrypted() || mMaster->mCDMProxy);
|
||||
SetState<DecodingFirstFrameState>(Move(mPendingSeek));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise>
|
||||
MediaDecoderStateMachine::
|
||||
DormantState::HandleSeek(SeekTarget aTarget)
|
||||
@ -1374,16 +1287,22 @@ DormantState::HandleSeek(SeekTarget aTarget)
|
||||
return SetState<SeekingState>(Move(seekJob));
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
MediaDecoderStateMachine::
|
||||
DormantState::HandlePlayStateChanged(MediaDecoder::PlayState aPlayState)
|
||||
{
|
||||
if (aPlayState == MediaDecoder::PLAY_STATE_PLAYING) {
|
||||
// Exit dormant when the user wants to play.
|
||||
MOZ_ASSERT(!Info().IsEncrypted() || mMaster->mCDMProxy);
|
||||
SetState<DecodingFirstFrameState>(Move(mPendingSeek));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::
|
||||
WaitForCDMState::HandleCDMProxyReady()
|
||||
{
|
||||
if (mPendingDormant) {
|
||||
SetState<DormantState>(Move(mPendingSeek));
|
||||
} else {
|
||||
SetState<DecodingFirstFrameState>(Move(mPendingSeek));
|
||||
}
|
||||
return true;
|
||||
SetState<DecodingFirstFrameState>(Move(mPendingSeek));
|
||||
}
|
||||
|
||||
void
|
||||
@ -1437,16 +1356,6 @@ DecodingFirstFrameState::HandleSeek(SeekTarget aTarget)
|
||||
return SetState<SeekingState>(Move(seekJob));
|
||||
}
|
||||
|
||||
bool
|
||||
MediaDecoderStateMachine::
|
||||
DecodingFirstFrameState::HandleDormant(bool aDormant)
|
||||
{
|
||||
if (aDormant) {
|
||||
SetState<DormantState>(Move(mPendingSeek));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::
|
||||
DecodingFirstFrameState::MaybeFinishDecodeFirstFrame()
|
||||
@ -1511,7 +1420,7 @@ DecodingState::HandleSeek(SeekTarget aTarget)
|
||||
return SetState<SeekingState>(Move(seekJob));
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
MediaDecoderStateMachine::
|
||||
DecodingState::HandleEndOfStream()
|
||||
{
|
||||
@ -1520,7 +1429,6 @@ DecodingState::HandleEndOfStream()
|
||||
} else {
|
||||
MaybeStopPrerolling();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1557,26 +1465,6 @@ DecodingState::MaybeStartBuffering()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MediaDecoderStateMachine::
|
||||
SeekingState::HandleDormant(bool aDormant)
|
||||
{
|
||||
if (!aDormant) {
|
||||
return true;
|
||||
}
|
||||
MOZ_ASSERT(mSeekJob.Exists());
|
||||
// Because both audio and video decoders are going to be reset in this
|
||||
// method later, we treat a VideoOnly seek task as a normal Accurate
|
||||
// seek task so that while it is resumed, both audio and video playback
|
||||
// are handled.
|
||||
if (mSeekJob.mTarget.IsVideoOnly()) {
|
||||
mSeekJob.mTarget.SetType(SeekTarget::Accurate);
|
||||
mSeekJob.mTarget.SetVideoOnly(false);
|
||||
}
|
||||
SetState<DormantState>(Move(mSeekJob));
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise>
|
||||
MediaDecoderStateMachine::
|
||||
SeekingState::HandleSeek(SeekTarget aTarget)
|
||||
@ -1712,7 +1600,7 @@ BufferingState::Step()
|
||||
SetState<DecodingState>();
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
MediaDecoderStateMachine::
|
||||
BufferingState::HandleEndOfStream()
|
||||
{
|
||||
@ -1722,7 +1610,6 @@ BufferingState::HandleEndOfStream()
|
||||
// Check if we can exit buffering.
|
||||
mMaster->ScheduleStateMachine();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise>
|
||||
@ -1776,6 +1663,7 @@ ShutdownState::Enter()
|
||||
master->mAudioQueueListener.Disconnect();
|
||||
master->mVideoQueueListener.Disconnect();
|
||||
master->mMetadataManager.Disconnect();
|
||||
master->mOnMediaNotSeekable.Disconnect();
|
||||
|
||||
// Disconnect canonicals and mirrors before shutting down our task queue.
|
||||
master->mBuffered.DisconnectIfConnected();
|
||||
@ -1790,8 +1678,6 @@ ShutdownState::Enter()
|
||||
master->mPlaybackBytesPerSecond.DisconnectIfConnected();
|
||||
master->mPlaybackRateReliable.DisconnectIfConnected();
|
||||
master->mDecoderPosition.DisconnectIfConnected();
|
||||
master->mMediaSeekable.DisconnectIfConnected();
|
||||
master->mMediaSeekableOnlyInBufferedRanges.DisconnectIfConnected();
|
||||
master->mIsVisible.DisconnectIfConnected();
|
||||
|
||||
master->mDuration.DisconnectAll();
|
||||
@ -1863,8 +1749,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
INIT_MIRROR(mPlaybackBytesPerSecond, 0.0),
|
||||
INIT_MIRROR(mPlaybackRateReliable, true),
|
||||
INIT_MIRROR(mDecoderPosition, 0),
|
||||
INIT_MIRROR(mMediaSeekable, true),
|
||||
INIT_MIRROR(mMediaSeekableOnlyInBufferedRanges, false),
|
||||
INIT_MIRROR(mIsVisible, true),
|
||||
INIT_CANONICAL(mDuration, NullableTimeUnit()),
|
||||
INIT_CANONICAL(mIsShutdown, false),
|
||||
@ -1920,8 +1804,6 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
|
||||
mPlaybackBytesPerSecond.Connect(aDecoder->CanonicalPlaybackBytesPerSecond());
|
||||
mPlaybackRateReliable.Connect(aDecoder->CanonicalPlaybackRateReliable());
|
||||
mDecoderPosition.Connect(aDecoder->CanonicalDecoderPosition());
|
||||
mMediaSeekable.Connect(aDecoder->CanonicalMediaSeekable());
|
||||
mMediaSeekableOnlyInBufferedRanges.Connect(aDecoder->CanonicalMediaSeekableOnlyInBufferedRanges());
|
||||
|
||||
// Initialize watchers.
|
||||
mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated);
|
||||
@ -2301,6 +2183,11 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoder* aDecoder)
|
||||
|
||||
mMetadataManager.Connect(mReader->TimedMetadataEvent(), OwnerThread());
|
||||
|
||||
mOnMediaNotSeekable = mReader->OnMediaNotSeekable().Connect(
|
||||
OwnerThread(), [this] () {
|
||||
mMediaSeekable = false;
|
||||
});
|
||||
|
||||
mMediaSink = CreateMediaSink(mAudioCaptured);
|
||||
|
||||
mCDMProxyPromise.Begin(aDecoder->RequestCDMProxy()->Then(
|
||||
@ -2512,13 +2399,6 @@ void MediaDecoderStateMachine::RecomputeDuration()
|
||||
mDuration = Some(duration);
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::SetDormant(bool aDormant)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
mStateObj->HandleDormant(aDormant);
|
||||
}
|
||||
|
||||
RefPtr<ShutdownPromise>
|
||||
MediaDecoderStateMachine::Shutdown()
|
||||
{
|
||||
@ -2968,7 +2848,7 @@ MediaDecoderStateMachine::FinishDecodeFirstFrame()
|
||||
|
||||
DECODER_LOG("Media duration %lld, "
|
||||
"transportSeekable=%d, mediaSeekable=%d",
|
||||
Duration().ToMicroseconds(), mResource->IsTransportSeekable(), mMediaSeekable.Ref());
|
||||
Duration().ToMicroseconds(), mResource->IsTransportSeekable(), mMediaSeekable);
|
||||
|
||||
// Get potentially updated metadata
|
||||
mReader->ReadUpdatedMetadata(mInfo.ptr());
|
||||
|
@ -281,8 +281,6 @@ private:
|
||||
// constructor immediately after the task queue is created.
|
||||
void InitializationTask(MediaDecoder* aDecoder);
|
||||
|
||||
void SetDormant(bool aDormant);
|
||||
|
||||
void SetAudioCaptured(bool aCaptured);
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget);
|
||||
@ -736,6 +734,12 @@ private:
|
||||
// True if video decoding is suspended.
|
||||
bool mVideoDecodeSuspended;
|
||||
|
||||
// True if the media is seekable (i.e. supports random access).
|
||||
bool mMediaSeekable = true;
|
||||
|
||||
// True if the media is seekable only in buffered ranges.
|
||||
bool mMediaSeekableOnlyInBufferedRanges = false;
|
||||
|
||||
// Track enabling video decode suspension via timer
|
||||
DelayedScheduler mVideoDecodeSuspendTimer;
|
||||
|
||||
@ -752,6 +756,7 @@ private:
|
||||
MediaEventListener mAudioQueueListener;
|
||||
MediaEventListener mVideoQueueListener;
|
||||
MediaEventListener mAudibleListener;
|
||||
MediaEventListener mOnMediaNotSeekable;
|
||||
|
||||
MediaEventProducerExc<nsAutoPtr<MediaInfo>,
|
||||
nsAutoPtr<MetadataTags>,
|
||||
@ -810,12 +815,6 @@ private:
|
||||
// Current decoding position in the stream.
|
||||
Mirror<int64_t> mDecoderPosition;
|
||||
|
||||
// True if the media is seekable (i.e. supports random access).
|
||||
Mirror<bool> mMediaSeekable;
|
||||
|
||||
// True if the media is seekable only in buffered ranges.
|
||||
Mirror<bool> mMediaSeekableOnlyInBufferedRanges;
|
||||
|
||||
// IsVisible, mirrored from the media decoder.
|
||||
Mirror<bool> mIsVisible;
|
||||
|
||||
|
@ -157,29 +157,6 @@ using media::Refcountable;
|
||||
|
||||
static Atomic<bool> sInShutdown;
|
||||
|
||||
static bool
|
||||
HostInDomain(const nsCString &aHost, const nsCString &aPattern)
|
||||
{
|
||||
int32_t patternOffset = 0;
|
||||
int32_t hostOffset = 0;
|
||||
|
||||
// Act on '*.' wildcard in the left-most position in a domain pattern.
|
||||
if (aPattern.Length() > 2 && aPattern[0] == '*' && aPattern[1] == '.') {
|
||||
patternOffset = 2;
|
||||
|
||||
// Ignore the lowest level sub-domain for the hostname.
|
||||
hostOffset = aHost.FindChar('.') + 1;
|
||||
|
||||
if (hostOffset <= 1) {
|
||||
// Reject a match between a wildcard and a TLD or '.foo' form.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
nsDependentCString hostRoot(aHost, hostOffset);
|
||||
return hostRoot.EqualsIgnoreCase(aPattern.BeginReading() + patternOffset);
|
||||
}
|
||||
|
||||
static bool
|
||||
HostIsHttps(nsIURI &docURI)
|
||||
{
|
||||
@ -191,58 +168,6 @@ HostIsHttps(nsIURI &docURI)
|
||||
return isHttps;
|
||||
}
|
||||
|
||||
static bool
|
||||
HostHasPermission(nsIURI &docURI)
|
||||
{
|
||||
nsAdoptingCString hostName;
|
||||
docURI.GetAsciiHost(hostName); //normalize UTF8 to ASCII equivalent
|
||||
nsAdoptingCString domainWhiteList =
|
||||
Preferences::GetCString("media.getusermedia.screensharing.allowed_domains");
|
||||
domainWhiteList.StripWhitespace();
|
||||
|
||||
if (domainWhiteList.IsEmpty() || hostName.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get UTF8 to ASCII domain name normalization service
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIIDNService> idnService =
|
||||
do_GetService("@mozilla.org/network/idn-service;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t begin = 0;
|
||||
uint32_t end = 0;
|
||||
nsCString domainName;
|
||||
/*
|
||||
Test each domain name in the comma separated list
|
||||
after converting from UTF8 to ASCII. Each domain
|
||||
must match exactly or have a single leading '*.' wildcard
|
||||
*/
|
||||
do {
|
||||
end = domainWhiteList.FindChar(',', begin);
|
||||
if (end == (uint32_t)-1) {
|
||||
// Last or only domain name in the comma separated list
|
||||
end = domainWhiteList.Length();
|
||||
}
|
||||
|
||||
rv = idnService->ConvertUTF8toACE(Substring(domainWhiteList, begin, end - begin),
|
||||
domainName);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (HostInDomain(hostName, domainName)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("Failed to convert UTF-8 host to ASCII");
|
||||
}
|
||||
|
||||
begin = end + 1;
|
||||
} while (end < domainWhiteList.Length());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is an implementation of MediaStreamListener. This is used
|
||||
* to Start() and Stop() the underlying MediaEngineSource when MediaStreams
|
||||
@ -2218,8 +2143,7 @@ if (privileged) {
|
||||
!Preferences::GetBool("media.getusermedia.screensharing.allow_on_old_platforms",
|
||||
false) && !IsVistaOrLater()) ||
|
||||
#endif
|
||||
(!privileged && !HostIsHttps(*docURI)) ||
|
||||
(!isChrome && !HostHasPermission(*docURI))) {
|
||||
(!privileged && !HostIsHttps(*docURI))) {
|
||||
RefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(aWindow,
|
||||
NS_LITERAL_STRING("NotAllowedError"));
|
||||
|
@ -122,6 +122,7 @@ private:
|
||||
#endif
|
||||
#ifdef XP_WIN
|
||||
DECL_MEDIA_PREF("media.wmf.enabled", PDMWMFEnabled, bool, true);
|
||||
DECL_MEDIA_PREF("media.wmf.skip-blacklist", PDMWMFSkipBlacklist, bool, false);
|
||||
DECL_MEDIA_PREF("media.decoder-doctor.wmf-disabled-is-failure", DecoderDoctorWMFDisabledIsFailure, bool, false);
|
||||
DECL_MEDIA_PREF("media.wmf.vp9.enabled", PDMWMFVP9DecoderEnabled, bool, true);
|
||||
DECL_MEDIA_PREF("media.wmf.decoder.thread-count", PDMWMFThreadCount, int32_t, -1);
|
||||
|
@ -34,9 +34,6 @@ public:
|
||||
// Notify is duration is known to this MediaResource.
|
||||
virtual void SetInfinite(bool aInfinite) {}
|
||||
|
||||
// Notify if seeking is supported by this MediaResource.
|
||||
virtual void SetMediaSeekable(bool aMediaSeekable) {}
|
||||
|
||||
// Notify that a network error is encountered.
|
||||
virtual void NotifyNetworkError() {}
|
||||
|
||||
|
@ -15,6 +15,9 @@
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "GMPCDMProxy.h"
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#include "mozilla/MediaDrmCDMProxy.h"
|
||||
#endif
|
||||
#include "mozilla/EMEUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
@ -325,6 +328,28 @@ private:
|
||||
WeakPtr<MediaKeys> mMediaKeys;
|
||||
};
|
||||
|
||||
already_AddRefed<CDMProxy>
|
||||
MediaKeys::CreateCDMProxy()
|
||||
{
|
||||
RefPtr<CDMProxy> proxy;
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if (IsWidevineKeySystem(mKeySystem)) {
|
||||
proxy = new MediaDrmCDMProxy(this,
|
||||
mKeySystem,
|
||||
mConfig.mDistinctiveIdentifier == MediaKeysRequirement::Required,
|
||||
mConfig.mPersistentState == MediaKeysRequirement::Required);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
proxy = new GMPCDMProxy(this,
|
||||
mKeySystem,
|
||||
new MediaKeysGMPCrashHelper(this),
|
||||
mConfig.mDistinctiveIdentifier == MediaKeysRequirement::Required,
|
||||
mConfig.mPersistentState == MediaKeysRequirement::Required);
|
||||
}
|
||||
return proxy.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DetailedPromise>
|
||||
MediaKeys::Init(ErrorResult& aRv)
|
||||
{
|
||||
@ -334,11 +359,7 @@ MediaKeys::Init(ErrorResult& aRv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mProxy = new GMPCDMProxy(this,
|
||||
mKeySystem,
|
||||
new MediaKeysGMPCrashHelper(this),
|
||||
mConfig.mDistinctiveIdentifier == MediaKeysRequirement::Required,
|
||||
mConfig.mPersistentState == MediaKeysRequirement::Required);
|
||||
mProxy = CreateCDMProxy();
|
||||
|
||||
// Determine principal (at creation time) of the MediaKeys object.
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetParentObject());
|
||||
|
@ -133,7 +133,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
bool IsInPrivateBrowsing();
|
||||
// Instantiate CDMProxy instance.
|
||||
// It could be MediaDrmCDMProxy (Widevine on Fennec) or GMPCDMProxy (the rest).
|
||||
already_AddRefed<CDMProxy> CreateCDMProxy();
|
||||
|
||||
// Removes promise from mPromises, and returns it.
|
||||
already_AddRefed<DetailedPromise> RetrievePromise(PromiseId aId);
|
||||
|
@ -199,12 +199,11 @@ MediaDrmProxySupport::MediaDrmProxySupport(const nsAString& aKeySystem)
|
||||
: mKeySystem(aKeySystem), mDestroyed(false)
|
||||
{
|
||||
mJavaCallbacks = MediaDrmProxy::NativeMediaDrmProxyCallbacks::New();
|
||||
// TODO: Bug 1306196 will check the pref to determine if it is oop case.
|
||||
// Follow the pref flag PDMAndroidRemoteCodecEnabled returned to determine
|
||||
// it is crossing process CDM or not.
|
||||
|
||||
mBridgeProxy =
|
||||
MediaDrmProxy::Create(mKeySystem,
|
||||
mJavaCallbacks);
|
||||
mJavaCallbacks,
|
||||
MediaPrefs::PDMAndroidRemoteCodecEnabled());
|
||||
}
|
||||
|
||||
MediaDrmProxySupport::~MediaDrmProxySupport()
|
||||
|
@ -24,6 +24,10 @@ using namespace mozilla::layers;
|
||||
|
||||
namespace media {
|
||||
|
||||
// Minimum update frequency is 1/120th of a second, i.e. half the
|
||||
// duration of a 60-fps frame.
|
||||
static const int64_t MIN_UPDATE_INTERVAL_US = 1000000 / (60 * 2);
|
||||
|
||||
VideoSink::VideoSink(AbstractThread* aThread,
|
||||
MediaSink* aAudioSink,
|
||||
MediaQueue<MediaData>& aVideoQueue,
|
||||
@ -441,8 +445,9 @@ VideoSink::UpdateRenderedVideoFrames()
|
||||
}
|
||||
|
||||
int64_t nextFrameTime = frames[1]->mTime;
|
||||
int64_t delta = std::max<int64_t>((nextFrameTime - clockTime), MIN_UPDATE_INTERVAL_US);
|
||||
TimeStamp target = nowTime + TimeDuration::FromMicroseconds(
|
||||
(nextFrameTime - clockTime) / mAudioSink->GetPlaybackParams().mPlaybackRate);
|
||||
delta / mAudioSink->GetPlaybackParams().mPlaybackRate);
|
||||
|
||||
RefPtr<VideoSink> self = this;
|
||||
mUpdateScheduler.Ensure(target, [self] () {
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
#include "MediaInfo.h"
|
||||
#include "MediaPrefs.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
#include "gfx2DGlue.h"
|
||||
@ -66,7 +67,7 @@ const CLSID CLSID_WebmMfVpxDec =
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
||||
LayersBackend
|
||||
GetCompositorBackendType(layers::KnowsCompositor* aKnowsCompositor)
|
||||
{
|
||||
@ -180,8 +181,8 @@ FindDXVABlacklistedDLL(StaticAutoPtr<D3DDLLBlacklistingCache>& aDLLBlacklistingC
|
||||
ClearOnShutdown(&aDLLBlacklistingCache);
|
||||
}
|
||||
|
||||
if (aBlacklist.IsEmpty()) {
|
||||
// Empty blacklist -> No blacklisting.
|
||||
if (aBlacklist.IsEmpty() || MediaPrefs::PDMWMFSkipBlacklist()) {
|
||||
// Empty blacklist, or "media.wmf.skip-blacklist"=true -> No blacklisting.
|
||||
aDLLBlacklistingCache->mBlacklistPref.SetLength(0);
|
||||
aDLLBlacklistingCache->mBlacklistedDLL.SetLength(0);
|
||||
return aDLLBlacklistingCache->mBlacklistedDLL;
|
||||
|
BIN
dom/media/test/bug1301226-odd.wav
Normal file
BIN
dom/media/test/bug1301226-odd.wav
Normal file
Binary file not shown.
1
dom/media/test/bug1301226-odd.wav^headers^
Normal file
1
dom/media/test/bug1301226-odd.wav^headers^
Normal file
@ -0,0 +1 @@
|
||||
Cache-Control: no-store
|
BIN
dom/media/test/bug1301226.wav
Normal file
BIN
dom/media/test/bug1301226.wav
Normal file
Binary file not shown.
1
dom/media/test/bug1301226.wav^headers^
Normal file
1
dom/media/test/bug1301226.wav^headers^
Normal file
@ -0,0 +1 @@
|
||||
Cache-Control: no-store
|
@ -165,6 +165,10 @@ var gPlayTests = [
|
||||
{ name:"wavedata_alaw.wav", type:"audio/x-wav", duration:1.0 },
|
||||
// uLaw compressed wave file
|
||||
{ name:"wavedata_ulaw.wav", type:"audio/x-wav", duration:1.0 },
|
||||
// Data length 0xFFFFFFFF
|
||||
{ name:"bug1301226.wav", type:"audio/x-wav", duration:0.003673 },
|
||||
// Data length 0xFFFFFFFF and odd chunk lengths.
|
||||
{ name:"bug1301226-odd.wav", type:"audio/x-wav", duration:0.003673 },
|
||||
|
||||
// Ogg stream without eof marker
|
||||
{ name:"bug461281.ogg", type:"application/ogg", duration:2.208 },
|
||||
|
@ -391,6 +391,10 @@ support-files =
|
||||
bug883173.vtt
|
||||
bug1066943.webm
|
||||
bug1066943.webm^headers^
|
||||
bug1301226.wav
|
||||
bug1301226.wav^headers^
|
||||
bug1301226-odd.wav
|
||||
bug1301226-odd.wav^headers^
|
||||
can_play_type_dash.js
|
||||
can_play_type_ogg.js
|
||||
can_play_type_wave.js
|
||||
|
@ -11,10 +11,6 @@
|
||||
bug: "1211656"
|
||||
});
|
||||
|
||||
var mustFailWith = (msg, reason, f) =>
|
||||
f().then(() => ok(false, msg + " must fail"),
|
||||
e => is(e.name, reason, msg + " must fail: " + e.message));
|
||||
|
||||
var pushPrefs = (...p) => new Promise(r => SpecialPowers.pushPrefEnv({set: p}, r));
|
||||
|
||||
/**
|
||||
@ -63,15 +59,9 @@
|
||||
}
|
||||
];
|
||||
return Promise.resolve()
|
||||
// Screensharing must work even without "mochi.test," in allowed_domains
|
||||
.then(() => pushPrefs(["media.getusermedia.screensharing.allowed_domains",
|
||||
"mozilla.github.io,*.bugzilla.mozilla.org"]))
|
||||
.then(() => mustFailWith("Screensharing if absent in allowed_domains",
|
||||
"NotAllowedError",
|
||||
() => navigator.mediaDevices.getUserMedia({
|
||||
video: videoConstraints[0], fake: false
|
||||
})))
|
||||
.then(() => pushPrefs(["media.getusermedia.screensharing.allowed_domains",
|
||||
"mozilla.github.io,mochi.test,*.bugzilla.mozilla.org"]))
|
||||
.then(() => getUserMedia(constraints).then(stream => {
|
||||
var playback = new LocalMediaStreamPlayback(testVideo, stream);
|
||||
return playback.playMediaWithDeprecatedStreamStop(false);
|
||||
|
@ -133,8 +133,11 @@ WAVTrackDemuxer::Init()
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
mOffset += aChunkSize; // Skip other irrelevant chunks.
|
||||
}
|
||||
if (mOffset & 1) {
|
||||
// Wave files are 2-byte aligned so we need to round up
|
||||
mOffset += (aChunkSize + 1) & ~1; // Skip other irrelevant chunks.
|
||||
mOffset += 1;
|
||||
}
|
||||
mHeaderParser.Reset();
|
||||
}
|
||||
|
@ -6,9 +6,6 @@
|
||||
|
||||
DIRS += ['interfaces']
|
||||
|
||||
if CONFIG['MOZ_B2G_RIL']:
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit_stats/xpcshell.ini']
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
|
||||
|
||||
|
@ -31,9 +31,6 @@ XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
|
||||
|
||||
if CONFIG['MOZ_B2G_RIL']:
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest-ril.ini']
|
||||
|
||||
if CONFIG['MOZ_TIME_MANAGER']:
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest-time.ini']
|
||||
|
||||
|
@ -34,11 +34,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_SECUREELEMENT']:
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
'tests/unit/xpcshell.ini'
|
||||
]
|
||||
if CONFIG['MOZ_B2G_RIL']:
|
||||
EXTRA_COMPONENTS += [
|
||||
'gonk/UiccConnector.js',
|
||||
'gonk/UiccConnector.manifest',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
[DEFAULT]
|
||||
skip-if = (os == 'linux' && debug && bits == 32) # bug 1311599
|
||||
support-files =
|
||||
head.js
|
||||
file_priming-top.html
|
||||
|
@ -21,9 +21,6 @@
|
||||
#include "AudioManager.h"
|
||||
|
||||
#include "nsIObserverService.h"
|
||||
#ifdef MOZ_B2G_RIL
|
||||
#include "nsIRadioInterfaceLayer.h"
|
||||
#endif
|
||||
#include "nsISettingsService.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
@ -694,9 +691,6 @@ AudioManager::AudioManager()
|
||||
, mA2dpSwitchDone(true)
|
||||
#endif
|
||||
, mObserver(new HeadphoneSwitchObserver())
|
||||
#ifdef MOZ_B2G_RIL
|
||||
, mMuteCallToRIL(false)
|
||||
#endif
|
||||
{
|
||||
for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(kAudioDeviceInfos); ++idx) {
|
||||
mAudioDeviceTableIdMaps.Put(kAudioDeviceInfos[idx].value, idx);
|
||||
@ -754,13 +748,6 @@ AudioManager::AudioManager()
|
||||
NS_WARNING("Failed to add audio-channel-process-changed observer!");
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.moz.mute.call.to_ril", value, "false");
|
||||
if (!strcmp(value, "true")) {
|
||||
mMuteCallToRIL = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
AudioManager::~AudioManager() {
|
||||
@ -815,13 +802,6 @@ AudioManager::GetInstance()
|
||||
NS_IMETHODIMP
|
||||
AudioManager::GetMicrophoneMuted(bool* aMicrophoneMuted)
|
||||
{
|
||||
#ifdef MOZ_B2G_RIL
|
||||
if (mMuteCallToRIL) {
|
||||
// Simply return cached mIsMicMuted if mute call go via RIL.
|
||||
*aMicrophoneMuted = mIsMicMuted;
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (AudioSystem::isMicrophoneMuted(aMicrophoneMuted)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -833,15 +813,6 @@ NS_IMETHODIMP
|
||||
AudioManager::SetMicrophoneMuted(bool aMicrophoneMuted)
|
||||
{
|
||||
if (!AudioSystem::muteMicrophone(aMicrophoneMuted)) {
|
||||
#ifdef MOZ_B2G_RIL
|
||||
if (mMuteCallToRIL) {
|
||||
// Extra mute request to RIL for specific platform.
|
||||
nsCOMPtr<nsIRadioInterfaceLayer> ril = do_GetService("@mozilla.org/ril;1");
|
||||
NS_ENSURE_TRUE(ril, NS_ERROR_FAILURE);
|
||||
ril->SetMicrophoneMuted(aMicrophoneMuted);
|
||||
mIsMicMuted = aMicrophoneMuted;
|
||||
}
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -141,11 +141,6 @@ protected:
|
||||
|
||||
private:
|
||||
nsAutoPtr<mozilla::hal::SwitchObserver> mObserver;
|
||||
#ifdef MOZ_B2G_RIL
|
||||
bool mMuteCallToRIL;
|
||||
// mIsMicMuted is only used for toggling mute call to RIL.
|
||||
bool mIsMicMuted;
|
||||
#endif
|
||||
|
||||
void HandleBluetoothStatusChanged(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
|
@ -39,18 +39,6 @@
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/SettingChangeNotificationBinding.h"
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
#include "mozstumbler/MozStumbler.h"
|
||||
#include "nsIIccInfo.h"
|
||||
#include "nsIMobileConnectionInfo.h"
|
||||
#include "nsIMobileConnectionService.h"
|
||||
#include "nsIMobileCellInfo.h"
|
||||
#include "nsIMobileNetworkInfo.h"
|
||||
#include "nsIRadioInterfaceLayer.h"
|
||||
#include "nsIIccService.h"
|
||||
#include "nsIDataCallManager.h"
|
||||
#endif
|
||||
|
||||
#ifdef AGPS_TYPE_INVALID
|
||||
#define AGPS_HAVE_DUAL_APN
|
||||
#endif
|
||||
@ -70,14 +58,7 @@ using namespace mozilla::dom;
|
||||
static const int kDefaultPeriod = 1000; // ms
|
||||
static bool gDebug_isLoggingEnabled = false;
|
||||
static bool gDebug_isGPSLocationIgnored = false;
|
||||
#ifdef MOZ_B2G_RIL
|
||||
static const char* kNetworkConnStateChangedTopic = "network-connection-state-changed";
|
||||
#endif
|
||||
static const char* kMozSettingsChangedTopic = "mozsettings-changed";
|
||||
#ifdef MOZ_B2G_RIL
|
||||
static const char* kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
|
||||
static const char* kSettingRilDefaultServiceId = "ril.data.defaultServiceId";
|
||||
#endif
|
||||
// Both of these settings can be toggled in the Gaia Developer settings screen.
|
||||
static const char* kSettingDebugEnabled = "geolocation.debugging.enabled";
|
||||
static const char* kSettingDebugGpsIgnored = "geolocation.debugging.gps-locations-ignored";
|
||||
@ -93,11 +74,6 @@ NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider,
|
||||
/* static */ GonkGPSGeolocationProvider* GonkGPSGeolocationProvider::sSingleton = nullptr;
|
||||
GpsCallbacks GonkGPSGeolocationProvider::mCallbacks;
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
AGpsCallbacks GonkGPSGeolocationProvider::mAGPSCallbacks;
|
||||
AGpsRilCallbacks GonkGPSGeolocationProvider::mAGPSRILCallbacks;
|
||||
#endif // MOZ_B2G_RIL
|
||||
|
||||
|
||||
void
|
||||
GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location)
|
||||
@ -156,9 +132,6 @@ GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location)
|
||||
RefPtr<UpdateLocationEvent> event = new UpdateLocationEvent(somewhere);
|
||||
NS_DispatchToMainThread(event);
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
MozStumble(somewhere);
|
||||
#endif
|
||||
}
|
||||
|
||||
class NotifyObserversGPSTask final : public Runnable
|
||||
@ -280,10 +253,6 @@ GonkGPSGeolocationProvider::SetCapabilitiesCallback(uint32_t capabilities)
|
||||
GonkGPSGeolocationProvider::GetSingleton();
|
||||
|
||||
provider->mSupportsScheduling = mCapabilities & GPS_CAPABILITY_SCHEDULING;
|
||||
#ifdef MOZ_B2G_RIL
|
||||
provider->mSupportsMSB = mCapabilities & GPS_CAPABILITY_MSB;
|
||||
provider->mSupportsMSA = mCapabilities & GPS_CAPABILITY_MSA;
|
||||
#endif
|
||||
provider->mSupportsSingleShot = mCapabilities & GPS_CAPABILITY_SINGLE_SHOT;
|
||||
#ifdef GPS_CAPABILITY_ON_DEMAND_TIME
|
||||
provider->mSupportsTimeInjection = mCapabilities & GPS_CAPABILITY_ON_DEMAND_TIME;
|
||||
@ -332,90 +301,9 @@ GonkGPSGeolocationProvider::RequestUtcTimeCallback()
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
void
|
||||
GonkGPSGeolocationProvider::AGPSStatusCallback(AGpsStatus* status)
|
||||
{
|
||||
MOZ_ASSERT(status);
|
||||
|
||||
class AGPSStatusEvent : public Runnable {
|
||||
public:
|
||||
AGPSStatusEvent(AGpsStatusValue aStatus)
|
||||
: mStatus(aStatus)
|
||||
{}
|
||||
NS_IMETHOD Run() override {
|
||||
RefPtr<GonkGPSGeolocationProvider> provider =
|
||||
GonkGPSGeolocationProvider::GetSingleton();
|
||||
|
||||
switch (mStatus) {
|
||||
case GPS_REQUEST_AGPS_DATA_CONN:
|
||||
provider->RequestDataConnection();
|
||||
break;
|
||||
case GPS_RELEASE_AGPS_DATA_CONN:
|
||||
provider->ReleaseDataConnection();
|
||||
break;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
AGpsStatusValue mStatus;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new AGPSStatusEvent(status->status));
|
||||
}
|
||||
|
||||
void
|
||||
GonkGPSGeolocationProvider::AGPSRILSetIDCallback(uint32_t flags)
|
||||
{
|
||||
class RequestSetIDEvent : public Runnable {
|
||||
public:
|
||||
RequestSetIDEvent(uint32_t flags)
|
||||
: mFlags(flags)
|
||||
{}
|
||||
NS_IMETHOD Run() override {
|
||||
RefPtr<GonkGPSGeolocationProvider> provider =
|
||||
GonkGPSGeolocationProvider::GetSingleton();
|
||||
provider->RequestSetID(mFlags);
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
uint32_t mFlags;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new RequestSetIDEvent(flags));
|
||||
}
|
||||
|
||||
void
|
||||
GonkGPSGeolocationProvider::AGPSRILRefLocCallback(uint32_t flags)
|
||||
{
|
||||
class RequestRefLocEvent : public Runnable {
|
||||
public:
|
||||
RequestRefLocEvent()
|
||||
{}
|
||||
NS_IMETHOD Run() override {
|
||||
RefPtr<GonkGPSGeolocationProvider> provider =
|
||||
GonkGPSGeolocationProvider::GetSingleton();
|
||||
provider->SetReferenceLocation();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
if (flags & AGPS_RIL_REQUEST_REFLOC_CELLID) {
|
||||
NS_DispatchToMainThread(new RequestRefLocEvent());
|
||||
}
|
||||
}
|
||||
#endif // MOZ_B2G_RIL
|
||||
|
||||
GonkGPSGeolocationProvider::GonkGPSGeolocationProvider()
|
||||
: mStarted(false)
|
||||
, mSupportsScheduling(false)
|
||||
#ifdef MOZ_B2G_RIL
|
||||
, mSupportsMSB(false)
|
||||
, mSupportsMSA(false)
|
||||
, mRilDataServiceId(0)
|
||||
, mNumberOfRilServices(1)
|
||||
, mObservingNetworkConnStateChange(false)
|
||||
#endif
|
||||
, mObservingSettingsChange(false)
|
||||
, mSupportsSingleShot(false)
|
||||
, mSupportsTimeInjection(false)
|
||||
@ -464,66 +352,6 @@ GonkGPSGeolocationProvider::GetGPSInterface()
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
int32_t
|
||||
GonkGPSGeolocationProvider::GetDataConnectionState()
|
||||
{
|
||||
if (!mRadioInterface) {
|
||||
return nsINetworkInfo::NETWORK_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
int32_t state;
|
||||
mRadioInterface->GetDataCallStateByType(
|
||||
nsINetworkInfo::NETWORK_TYPE_MOBILE_SUPL, &state);
|
||||
return state;
|
||||
}
|
||||
|
||||
void
|
||||
GonkGPSGeolocationProvider::SetAGpsDataConn(nsAString& aApn)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mAGpsInterface);
|
||||
|
||||
bool hasUpdateNetworkAvailability = false;
|
||||
if (mAGpsRilInterface &&
|
||||
mAGpsRilInterface->size >= sizeof(AGpsRilInterface) &&
|
||||
mAGpsRilInterface->update_network_availability) {
|
||||
hasUpdateNetworkAvailability = true;
|
||||
}
|
||||
|
||||
int32_t connectionState = GetDataConnectionState();
|
||||
NS_ConvertUTF16toUTF8 apn(aApn);
|
||||
if (connectionState == nsINetworkInfo::NETWORK_STATE_CONNECTED) {
|
||||
// The definition of availability is
|
||||
// 1. The device is connected to the home network
|
||||
// 2. The device is connected to a foreign network and data
|
||||
// roaming is enabled
|
||||
// RIL turns on/off data connection automatically when the data
|
||||
// roaming setting changes.
|
||||
if (hasUpdateNetworkAvailability) {
|
||||
mAGpsRilInterface->update_network_availability(true, apn.get());
|
||||
}
|
||||
#ifdef AGPS_HAVE_DUAL_APN
|
||||
mAGpsInterface->data_conn_open(AGPS_TYPE_SUPL,
|
||||
apn.get(),
|
||||
AGPS_APN_BEARER_IPV4);
|
||||
#else
|
||||
mAGpsInterface->data_conn_open(apn.get());
|
||||
#endif
|
||||
} else if (connectionState == nsINetworkInfo::NETWORK_STATE_DISCONNECTED) {
|
||||
if (hasUpdateNetworkAvailability) {
|
||||
mAGpsRilInterface->update_network_availability(false, apn.get());
|
||||
}
|
||||
#ifdef AGPS_HAVE_DUAL_APN
|
||||
mAGpsInterface->data_conn_closed(AGPS_TYPE_SUPL);
|
||||
#else
|
||||
mAGpsInterface->data_conn_closed();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MOZ_B2G_RIL
|
||||
|
||||
void
|
||||
GonkGPSGeolocationProvider::RequestSettingValue(const char* aKey)
|
||||
{
|
||||
@ -548,199 +376,6 @@ GonkGPSGeolocationProvider::RequestSettingValue(const char* aKey)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
void
|
||||
GonkGPSGeolocationProvider::RequestDataConnection()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mRadioInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetDataConnectionState() == nsINetworkInfo::NETWORK_STATE_CONNECTED) {
|
||||
// Connection is already established, we don't need to setup again.
|
||||
// We just get supl APN and make AGPS data connection state updated.
|
||||
RequestSettingValue("ril.supl.apn");
|
||||
} else {
|
||||
mRadioInterface->SetupDataCallByType(nsINetworkInfo::NETWORK_TYPE_MOBILE_SUPL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GonkGPSGeolocationProvider::ReleaseDataConnection()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mRadioInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
mRadioInterface->DeactivateDataCallByType(nsINetworkInfo::NETWORK_TYPE_MOBILE_SUPL);
|
||||
}
|
||||
|
||||
void
|
||||
GonkGPSGeolocationProvider::RequestSetID(uint32_t flags)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mRadioInterface ||
|
||||
!mAGpsInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
AGpsSetIDType type = AGPS_SETID_TYPE_NONE;
|
||||
|
||||
nsCOMPtr<nsIIccService> iccService =
|
||||
do_GetService(ICC_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE_VOID(iccService);
|
||||
|
||||
nsCOMPtr<nsIIcc> icc;
|
||||
iccService->GetIccByServiceId(mRilDataServiceId, getter_AddRefs(icc));
|
||||
NS_ENSURE_TRUE_VOID(icc);
|
||||
|
||||
nsAutoString id;
|
||||
|
||||
if (flags & AGPS_RIL_REQUEST_SETID_IMSI) {
|
||||
type = AGPS_SETID_TYPE_IMSI;
|
||||
icc->GetImsi(id);
|
||||
}
|
||||
|
||||
if (flags & AGPS_RIL_REQUEST_SETID_MSISDN) {
|
||||
nsCOMPtr<nsIIccInfo> iccInfo;
|
||||
icc->GetIccInfo(getter_AddRefs(iccInfo));
|
||||
if (iccInfo) {
|
||||
nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
|
||||
if (gsmIccInfo) {
|
||||
type = AGPS_SETID_TYPE_MSISDN;
|
||||
gsmIccInfo->GetMsisdn(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 idBytes(id);
|
||||
mAGpsRilInterface->set_set_id(type, idBytes.get());
|
||||
}
|
||||
|
||||
namespace {
|
||||
int
|
||||
ConvertToGpsRefLocationType(const nsAString& aConnectionType)
|
||||
{
|
||||
const char* GSM_TYPES[] = { "gsm", "gprs", "edge" };
|
||||
const char* UMTS_TYPES[] = { "umts", "hspda", "hsupa", "hspa", "hspa+" };
|
||||
|
||||
for (auto type: GSM_TYPES) {
|
||||
if (aConnectionType.EqualsASCII(type)) {
|
||||
return AGPS_REF_LOCATION_TYPE_GSM_CELLID;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto type: UMTS_TYPES) {
|
||||
if (aConnectionType.EqualsASCII(type)) {
|
||||
return AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
|
||||
}
|
||||
}
|
||||
|
||||
if (gDebug_isLoggingEnabled) {
|
||||
DBG("geo: Unsupported connection type %s\n",
|
||||
NS_ConvertUTF16toUTF8(aConnectionType).get());
|
||||
}
|
||||
return AGPS_REF_LOCATION_TYPE_GSM_CELLID;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
GonkGPSGeolocationProvider::SetReferenceLocation()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mRadioInterface ||
|
||||
!mAGpsRilInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
AGpsRefLocation location;
|
||||
|
||||
nsCOMPtr<nsIMobileConnectionService> service =
|
||||
do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
|
||||
if (!service) {
|
||||
NS_WARNING("Cannot get MobileConnectionService");
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMobileConnection> connection;
|
||||
service->GetItemByServiceId(mRilDataServiceId, getter_AddRefs(connection));
|
||||
NS_ENSURE_TRUE_VOID(connection);
|
||||
|
||||
nsCOMPtr<nsIMobileConnectionInfo> voice;
|
||||
connection->GetVoice(getter_AddRefs(voice));
|
||||
if (voice) {
|
||||
nsAutoString connectionType;
|
||||
nsresult rv = voice->GetType(connectionType);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
location.type = ConvertToGpsRefLocationType(connectionType);
|
||||
|
||||
nsCOMPtr<nsIMobileNetworkInfo> networkInfo;
|
||||
voice->GetNetwork(getter_AddRefs(networkInfo));
|
||||
if (networkInfo) {
|
||||
nsresult result;
|
||||
nsAutoString mcc, mnc;
|
||||
|
||||
networkInfo->GetMcc(mcc);
|
||||
networkInfo->GetMnc(mnc);
|
||||
|
||||
location.u.cellID.mcc = mcc.ToInteger(&result);
|
||||
if (result != NS_OK) {
|
||||
NS_WARNING("Cannot parse mcc to integer");
|
||||
location.u.cellID.mcc = 0;
|
||||
}
|
||||
|
||||
location.u.cellID.mnc = mnc.ToInteger(&result);
|
||||
if (result != NS_OK) {
|
||||
NS_WARNING("Cannot parse mnc to integer");
|
||||
location.u.cellID.mnc = 0;
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("Cannot get mobile network info.");
|
||||
location.u.cellID.mcc = 0;
|
||||
location.u.cellID.mnc = 0;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMobileCellInfo> cell;
|
||||
voice->GetCell(getter_AddRefs(cell));
|
||||
if (cell) {
|
||||
int32_t lac;
|
||||
int64_t cid;
|
||||
|
||||
cell->GetGsmLocationAreaCode(&lac);
|
||||
// The valid range of LAC is 0x0 to 0xffff which is defined in
|
||||
// hardware/ril/include/telephony/ril.h
|
||||
if (lac >= 0x0 && lac <= 0xffff) {
|
||||
location.u.cellID.lac = lac;
|
||||
}
|
||||
|
||||
cell->GetGsmCellId(&cid);
|
||||
// The valid range of cell id is 0x0 to 0xffffffff which is defined in
|
||||
// hardware/ril/include/telephony/ril.h
|
||||
if (cid >= 0x0 && cid <= 0xffffffff) {
|
||||
location.u.cellID.cid = cid;
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("Cannot get mobile gell info.");
|
||||
location.u.cellID.lac = -1;
|
||||
location.u.cellID.cid = -1;
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("Cannot get mobile connection info.");
|
||||
return;
|
||||
}
|
||||
mAGpsRilInterface->set_ref_location(&location, sizeof(location));
|
||||
}
|
||||
|
||||
#endif // MOZ_B2G_RIL
|
||||
|
||||
void
|
||||
GonkGPSGeolocationProvider::InjectLocation(double latitude,
|
||||
double longitude,
|
||||
@ -784,34 +419,12 @@ GonkGPSGeolocationProvider::Init()
|
||||
mCallbacks.request_utc_time_cb = RequestUtcTimeCallback;
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
mAGPSCallbacks.status_cb = AGPSStatusCallback;
|
||||
mAGPSCallbacks.create_thread_cb = CreateThreadCallback;
|
||||
|
||||
mAGPSRILCallbacks.request_setid = AGPSRILSetIDCallback;
|
||||
mAGPSRILCallbacks.request_refloc = AGPSRILRefLocCallback;
|
||||
mAGPSRILCallbacks.create_thread_cb = CreateThreadCallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (mGpsInterface->init(&mCallbacks) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
mAGpsInterface =
|
||||
static_cast<const AGpsInterface*>(mGpsInterface->get_extension(AGPS_INTERFACE));
|
||||
if (mAGpsInterface) {
|
||||
mAGpsInterface->init(&mAGPSCallbacks);
|
||||
}
|
||||
|
||||
mAGpsRilInterface =
|
||||
static_cast<const AGpsRilInterface*>(mGpsInterface->get_extension(AGPS_RIL_INTERFACE));
|
||||
if (mAGpsRilInterface) {
|
||||
mAGpsRilInterface->init(&mAGPSRILCallbacks);
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_DispatchToMainThread(NewRunnableMethod(this, &GonkGPSGeolocationProvider::StartGPS));
|
||||
}
|
||||
|
||||
@ -823,24 +436,8 @@ GonkGPSGeolocationProvider::StartGPS()
|
||||
|
||||
int32_t update = Preferences::GetInt("geo.default.update", kDefaultPeriod);
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
if (mSupportsMSA || mSupportsMSB) {
|
||||
SetupAGPS();
|
||||
}
|
||||
#endif
|
||||
|
||||
int positionMode = GPS_POSITION_MODE_STANDALONE;
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
bool singleShot = false;
|
||||
|
||||
// XXX: If we know this is a single shot request, use MSA can be faster.
|
||||
if (singleShot && mSupportsMSA) {
|
||||
positionMode = GPS_POSITION_MODE_MS_ASSISTED;
|
||||
} else if (mSupportsMSB) {
|
||||
positionMode = GPS_POSITION_MODE_MS_BASED;
|
||||
}
|
||||
#endif
|
||||
if (!mSupportsScheduling) {
|
||||
update = kDefaultPeriod;
|
||||
}
|
||||
@ -856,43 +453,6 @@ GonkGPSGeolocationProvider::StartGPS()
|
||||
mGpsInterface->start();
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
void
|
||||
GonkGPSGeolocationProvider::SetupAGPS()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mAGpsInterface);
|
||||
|
||||
const nsAdoptingCString& suplServer = Preferences::GetCString("geo.gps.supl_server");
|
||||
int32_t suplPort = Preferences::GetInt("geo.gps.supl_port", -1);
|
||||
if (!suplServer.IsEmpty() && suplPort > 0) {
|
||||
mAGpsInterface->set_server(AGPS_TYPE_SUPL, suplServer.get(), suplPort);
|
||||
} else {
|
||||
NS_WARNING("Cannot get SUPL server settings");
|
||||
return;
|
||||
}
|
||||
|
||||
// Request RIL date service ID for correct RadioInterface object first due to
|
||||
// multi-SIM case needs it to handle AGPS related stuffs. For single SIM, 0
|
||||
// will be returned as default RIL data service ID.
|
||||
RequestSettingValue(kSettingRilDefaultServiceId);
|
||||
}
|
||||
|
||||
void
|
||||
GonkGPSGeolocationProvider::UpdateRadioInterface()
|
||||
{
|
||||
nsCOMPtr<nsIRadioInterfaceLayer> ril = do_GetService("@mozilla.org/ril;1");
|
||||
NS_ENSURE_TRUE_VOID(ril);
|
||||
ril->GetRadioInterface(mRilDataServiceId, getter_AddRefs(mRadioInterface));
|
||||
}
|
||||
|
||||
bool
|
||||
GonkGPSGeolocationProvider::IsValidRilServiceId(uint32_t aServiceId)
|
||||
{
|
||||
return aServiceId < mNumberOfRilServices;
|
||||
}
|
||||
#endif // MOZ_B2G_RIL
|
||||
|
||||
|
||||
NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider::NetworkLocationUpdate,
|
||||
nsIGeolocationUpdate)
|
||||
@ -1013,9 +573,6 @@ GonkGPSGeolocationProvider::Startup()
|
||||
}
|
||||
|
||||
mStarted = true;
|
||||
#ifdef MOZ_B2G_RIL
|
||||
mNumberOfRilServices = Preferences::GetUint(kPrefRilNumRadioInterfaces, 1);
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1046,14 +603,6 @@ GonkGPSGeolocationProvider::Shutdown()
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (obs) {
|
||||
nsresult rv;
|
||||
#ifdef MOZ_B2G_RIL
|
||||
rv = obs->RemoveObserver(this, kNetworkConnStateChangedTopic);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("geo: Gonk GPS network state RemoveObserver failed");
|
||||
} else {
|
||||
mObservingNetworkConnStateChange = false;
|
||||
}
|
||||
#endif
|
||||
rv = obs->RemoveObserver(this, kMozSettingsChangedTopic);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("geo: Gonk GPS mozsettings RemoveObserver failed");
|
||||
@ -1115,59 +664,6 @@ GonkGPSGeolocationProvider::Observe(nsISupports* aSubject,
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
if (!strcmp(aTopic, kNetworkConnStateChangedTopic)) {
|
||||
nsCOMPtr<nsINetworkInfo> info = do_QueryInterface(aSubject);
|
||||
if (!info) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsCOMPtr<nsIRilNetworkInfo> rilInfo = do_QueryInterface(aSubject);
|
||||
if (mAGpsRilInterface && mAGpsRilInterface->update_network_state) {
|
||||
int32_t state;
|
||||
int32_t type;
|
||||
info->GetState(&state);
|
||||
info->GetType(&type);
|
||||
bool connected = (state == nsINetworkInfo::NETWORK_STATE_CONNECTED);
|
||||
bool roaming = false;
|
||||
int gpsNetworkType = ConvertToGpsNetworkType(type);
|
||||
if (gpsNetworkType >= 0) {
|
||||
if (rilInfo) {
|
||||
do {
|
||||
nsCOMPtr<nsIMobileConnectionService> service =
|
||||
do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
|
||||
if (!service) {
|
||||
break;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMobileConnection> connection;
|
||||
service->GetItemByServiceId(mRilDataServiceId, getter_AddRefs(connection));
|
||||
if (!connection) {
|
||||
break;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMobileConnectionInfo> voice;
|
||||
connection->GetVoice(getter_AddRefs(voice));
|
||||
if (voice) {
|
||||
voice->GetRoaming(&roaming);
|
||||
}
|
||||
} while (0);
|
||||
}
|
||||
mAGpsRilInterface->update_network_state(
|
||||
connected,
|
||||
gpsNetworkType,
|
||||
roaming,
|
||||
/* extra_info = */ nullptr);
|
||||
}
|
||||
}
|
||||
// No data connection
|
||||
if (!rilInfo) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RequestSettingValue("ril.supl.apn");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!strcmp(aTopic, kMozSettingsChangedTopic)) {
|
||||
// Read changed setting value
|
||||
RootedDictionary<SettingChangeNotification> setting(RootingCx());
|
||||
@ -1189,18 +685,6 @@ GonkGPSGeolocationProvider::Observe(nsISupports* aSubject,
|
||||
setting.mValue.isBoolean() ? setting.mValue.toBoolean() : false;
|
||||
return NS_OK;
|
||||
}
|
||||
#ifdef MOZ_B2G_RIL
|
||||
else if (setting.mKey.EqualsASCII(kSettingRilDefaultServiceId)) {
|
||||
if (!setting.mValue.isNumber() ||
|
||||
!IsValidRilServiceId(setting.mValue.toNumber())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mRilDataServiceId = setting.mValue.toNumber();
|
||||
UpdateRadioInterface();
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -1212,50 +696,6 @@ NS_IMETHODIMP
|
||||
GonkGPSGeolocationProvider::Handle(const nsAString& aName,
|
||||
JS::Handle<JS::Value> aResult)
|
||||
{
|
||||
#ifdef MOZ_B2G_RIL
|
||||
if (aName.EqualsLiteral("ril.supl.apn")) {
|
||||
// When we get the APN, we attempt to call data_call_open of AGPS.
|
||||
if (aResult.isString()) {
|
||||
JSContext *cx = nsContentUtils::GetCurrentJSContext();
|
||||
NS_ENSURE_TRUE(cx, NS_OK);
|
||||
|
||||
// NB: No need to enter a compartment to read the contents of a string.
|
||||
nsAutoJSString apn;
|
||||
if (!apn.init(cx, aResult.toString())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!apn.IsEmpty()) {
|
||||
SetAGpsDataConn(apn);
|
||||
}
|
||||
}
|
||||
} else if (aName.EqualsASCII(kSettingRilDefaultServiceId)) {
|
||||
uint32_t id = 0;
|
||||
JSContext *cx = nsContentUtils::GetCurrentJSContext();
|
||||
NS_ENSURE_TRUE(cx, NS_OK);
|
||||
if (!JS::ToUint32(cx, aResult, &id)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!IsValidRilServiceId(id)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mRilDataServiceId = id;
|
||||
UpdateRadioInterface();
|
||||
|
||||
MOZ_ASSERT(!mObservingNetworkConnStateChange);
|
||||
|
||||
// Now we know which service ID to deal with, observe necessary topic then
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_ENSURE_TRUE(obs, NS_OK);
|
||||
|
||||
if (NS_FAILED(obs->AddObserver(this, kNetworkConnStateChangedTopic, false))) {
|
||||
NS_WARNING("Failed to add network state changed observer!");
|
||||
} else {
|
||||
mObservingNetworkConnStateChange = true;
|
||||
}
|
||||
}
|
||||
#endif // MOZ_B2G_RIL
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,6 @@
|
||||
#include "nsIGeolocationProvider.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIDOMGeoPosition.h"
|
||||
#ifdef MOZ_B2G_RIL
|
||||
#include "nsIRadioInterfaceLayer.h"
|
||||
#endif
|
||||
#include "nsISettingsService.h"
|
||||
|
||||
class nsIThread;
|
||||
@ -64,34 +61,14 @@ private:
|
||||
static void ReleaseWakelockCallback();
|
||||
static pthread_t CreateThreadCallback(const char* name, void (*start)(void*), void* arg);
|
||||
static void RequestUtcTimeCallback();
|
||||
#ifdef MOZ_B2G_RIL
|
||||
static void AGPSStatusCallback(AGpsStatus* status);
|
||||
static void AGPSRILSetIDCallback(uint32_t flags);
|
||||
static void AGPSRILRefLocCallback(uint32_t flags);
|
||||
#endif
|
||||
|
||||
static GpsCallbacks mCallbacks;
|
||||
#ifdef MOZ_B2G_RIL
|
||||
static AGpsCallbacks mAGPSCallbacks;
|
||||
static AGpsRilCallbacks mAGPSRILCallbacks;
|
||||
#endif
|
||||
|
||||
void Init();
|
||||
void StartGPS();
|
||||
void ShutdownGPS();
|
||||
void InjectLocation(double latitude, double longitude, float accuracy);
|
||||
void RequestSettingValue(const char* aKey);
|
||||
#ifdef MOZ_B2G_RIL
|
||||
void UpdateRadioInterface();
|
||||
bool IsValidRilServiceId(uint32_t aServiceId);
|
||||
void SetupAGPS();
|
||||
int32_t GetDataConnectionState();
|
||||
void SetAGpsDataConn(nsAString& aApn);
|
||||
void RequestDataConnection();
|
||||
void ReleaseDataConnection();
|
||||
void RequestSetID(uint32_t flags);
|
||||
void SetReferenceLocation();
|
||||
#endif
|
||||
|
||||
const GpsInterface* GetGPSInterface();
|
||||
|
||||
@ -100,26 +77,11 @@ private:
|
||||
bool mStarted;
|
||||
|
||||
bool mSupportsScheduling;
|
||||
#ifdef MOZ_B2G_RIL
|
||||
bool mSupportsMSB;
|
||||
bool mSupportsMSA;
|
||||
uint32_t mRilDataServiceId;
|
||||
// mNumberOfRilServices indicates how many SIM slots supported on device, and
|
||||
// RadioInterfaceLayer.js takes responsibility to set up the corresponding
|
||||
// preference value.
|
||||
uint32_t mNumberOfRilServices;
|
||||
bool mObservingNetworkConnStateChange;
|
||||
#endif
|
||||
bool mObservingSettingsChange;
|
||||
bool mSupportsSingleShot;
|
||||
bool mSupportsTimeInjection;
|
||||
|
||||
const GpsInterface* mGpsInterface;
|
||||
#ifdef MOZ_B2G_RIL
|
||||
const AGpsInterface* mAGpsInterface;
|
||||
const AGpsRilInterface* mAGpsRilInterface;
|
||||
nsCOMPtr<nsIRadioInterface> mRadioInterface;
|
||||
#endif
|
||||
nsCOMPtr<nsIGeolocationUpdate> mLocationCallback;
|
||||
nsCOMPtr<nsIThread> mInitThread;
|
||||
nsCOMPtr<nsIGeolocationProvider> mNetworkLocationProvider;
|
||||
|
@ -28,9 +28,6 @@
|
||||
#include "TimeZoneSettingObserver.h"
|
||||
#include "AudioManager.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#ifdef MOZ_B2G_RIL
|
||||
#include "mozilla/ipc/Ril.h"
|
||||
#endif
|
||||
#include "mozilla/ipc/KeyStore.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
@ -115,10 +112,6 @@ SystemWorkerManager::Shutdown()
|
||||
|
||||
ShutdownAutoMounter();
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
RilWorker::Shutdown();
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIWifi> wifi(do_QueryInterface(mWifiWorker));
|
||||
if (wifi) {
|
||||
wifi->Shutdown();
|
||||
@ -184,22 +177,7 @@ SystemWorkerManager::RegisterRilWorker(unsigned int aClientId,
|
||||
JS::Handle<JS::Value> aWorker,
|
||||
JSContext *aCx)
|
||||
{
|
||||
#ifndef MOZ_B2G_RIL
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#else
|
||||
NS_ENSURE_TRUE(aWorker.isObject(), NS_ERROR_UNEXPECTED);
|
||||
|
||||
JSAutoCompartment ac(aCx, &aWorker.toObject());
|
||||
|
||||
WorkerCrossThreadDispatcher *wctd =
|
||||
GetWorkerCrossThreadDispatcher(aCx, aWorker);
|
||||
if (!wctd) {
|
||||
NS_WARNING("Failed to GetWorkerCrossThreadDispatcher for ril");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return RilWorker::Register(aClientId, wctd);
|
||||
#endif // MOZ_B2G_RIL
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -89,43 +89,6 @@ EXTRA_JS_MODULES += [
|
||||
'systemlibs.js',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_B2G_RIL']:
|
||||
EXPORTS += [
|
||||
'mozstumbler/MozStumbler.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'mozstumbler/MozStumbler.cpp',
|
||||
'mozstumbler/StumblerLogging.cpp',
|
||||
'mozstumbler/UploadStumbleRunnable.cpp',
|
||||
'mozstumbler/WriteStumbleOnThread.cpp'
|
||||
]
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDataCallInterfaceService.idl',
|
||||
'nsIDataCallManager.idl',
|
||||
'nsIGonkDataCallInterfaceService.idl',
|
||||
'nsIRadioInterfaceLayer.idl',
|
||||
]
|
||||
EXTRA_COMPONENTS += [
|
||||
'DataCallManager.js',
|
||||
'DataCallManager.manifest',
|
||||
'RILSystemMessengerHelper.js',
|
||||
'RILSystemMessengerHelper.manifest',
|
||||
]
|
||||
EXTRA_JS_MODULES += [
|
||||
'ril_consts.js',
|
||||
'ril_worker.js',
|
||||
'ril_worker_buf_object.js',
|
||||
'ril_worker_telephony_request_queue.js',
|
||||
'RILSystemMessenger.jsm',
|
||||
]
|
||||
if not CONFIG['DISABLE_MOZ_RIL_GEOLOC']:
|
||||
EXTRA_COMPONENTS += [
|
||||
'DataCallInterfaceService.js',
|
||||
'DataCallInterfaceService.manifest',
|
||||
'RadioInterfaceLayer.js',
|
||||
'RadioInterfaceLayer.manifest',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
DEFINES['HAVE_ANDROID_OS'] = True
|
||||
|
@ -565,7 +565,7 @@ var interfaceNamesInGlobalScope =
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"HTMLVideoElement",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IdleDeadline",
|
||||
{name: "IdleDeadline", nightly: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBCursor",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
@ -25,9 +25,10 @@ dictionary KeyframeEffectOptions : AnimationEffectTimingProperties {
|
||||
// the first argument since we cannot convert a mixin into a union type
|
||||
// automatically.
|
||||
[Func="nsDocument::IsWebAnimationsEnabled",
|
||||
Constructor((Element or CSSPseudoElement)? target,
|
||||
object? keyframes,
|
||||
optional (unrestricted double or KeyframeEffectOptions) options)]
|
||||
Constructor ((Element or CSSPseudoElement)? target,
|
||||
object? keyframes,
|
||||
optional (unrestricted double or KeyframeEffectOptions) options),
|
||||
Constructor (KeyframeEffectReadOnly source)]
|
||||
interface KeyframeEffectReadOnly : AnimationEffectReadOnly {
|
||||
// Bug 1241783: As with the constructor, we use (Element or CSSPseudoElement)?
|
||||
// for the type of |target| instead of Animatable?
|
||||
@ -36,9 +37,6 @@ interface KeyframeEffectReadOnly : AnimationEffectReadOnly {
|
||||
readonly attribute CompositeOperation composite;
|
||||
readonly attribute DOMString spacing;
|
||||
|
||||
// Not yet implemented:
|
||||
// KeyframeEffect clone();
|
||||
|
||||
// We use object instead of ComputedKeyframe so that we can put the
|
||||
// property-value pairs on the object.
|
||||
[Throws] sequence<object> getKeyframes();
|
||||
@ -66,7 +64,8 @@ partial interface KeyframeEffectReadOnly {
|
||||
[Func="nsDocument::IsWebAnimationsEnabled",
|
||||
Constructor ((Element or CSSPseudoElement)? target,
|
||||
object? keyframes,
|
||||
optional (unrestricted double or KeyframeEffectOptions) options)]
|
||||
optional (unrestricted double or KeyframeEffectOptions) options),
|
||||
Constructor (KeyframeEffectReadOnly source)]
|
||||
interface KeyframeEffect : KeyframeEffectReadOnly {
|
||||
inherit attribute (Element or CSSPseudoElement)? target;
|
||||
inherit attribute IterationCompositeOperation iterationComposite;
|
||||
|
@ -77,6 +77,7 @@ class Animation;
|
||||
class AnimationData;
|
||||
class AsyncCanvasRenderer;
|
||||
class AsyncPanZoomController;
|
||||
class BasicLayerManager;
|
||||
class ClientLayerManager;
|
||||
class Layer;
|
||||
class LayerMetricsWrapper;
|
||||
@ -196,6 +197,9 @@ public:
|
||||
virtual ClientLayerManager* AsClientLayerManager()
|
||||
{ return nullptr; }
|
||||
|
||||
virtual BasicLayerManager* AsBasicLayerManager()
|
||||
{ return nullptr; }
|
||||
|
||||
/**
|
||||
* Returns true if this LayerManager is owned by an nsIWidget,
|
||||
* and is used for drawing into the widget.
|
||||
|
@ -74,6 +74,8 @@ protected:
|
||||
virtual ~BasicLayerManager();
|
||||
|
||||
public:
|
||||
BasicLayerManager* AsBasicLayerManager() override { return this; }
|
||||
|
||||
/**
|
||||
* Set the default target context that will be used when BeginTransaction
|
||||
* is called. This can only be called outside a transaction.
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
|
||||
#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
|
||||
#include "mozilla/layers/LayersTypes.h" // for etc
|
||||
#include "mozilla/widget/CompositorWidget.h" // for WidgetRenderingContext
|
||||
#include "ipc/CompositorBench.h" // for CompositorBench
|
||||
#include "ipc/ShadowLayerUtils.h"
|
||||
#include "mozilla/mozalloc.h" // for operator new, etc
|
||||
@ -911,11 +912,18 @@ LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegi
|
||||
mLastFrameMissedHWC = !!composer2D;
|
||||
}
|
||||
|
||||
mozilla::widget::WidgetRenderingContext widgetContext;
|
||||
#if defined(XP_MACOSX)
|
||||
widgetContext.mLayerManager = this;
|
||||
#elif defined(MOZ_WIDGET_ANDROID)
|
||||
widgetContext.mCompositor = GetCompositor();
|
||||
#endif
|
||||
|
||||
{
|
||||
PROFILER_LABEL("LayerManagerComposite", "PreRender",
|
||||
js::ProfileEntry::Category::GRAPHICS);
|
||||
|
||||
if (!mCompositor->GetWidget()->PreRender(this)) {
|
||||
if (!mCompositor->GetWidget()->PreRender(&widgetContext)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -946,13 +954,13 @@ LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegi
|
||||
}
|
||||
|
||||
if (actualBounds.IsEmpty()) {
|
||||
mCompositor->GetWidget()->PostRender(this);
|
||||
mCompositor->GetWidget()->PostRender(&widgetContext);
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow widget to render a custom background.
|
||||
mCompositor->GetWidget()->DrawWindowUnderlay(
|
||||
this, LayoutDeviceIntRect::FromUnknownRect(actualBounds));
|
||||
&widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds));
|
||||
|
||||
RefPtr<CompositingRenderTarget> previousTarget;
|
||||
if (haveLayerEffects) {
|
||||
@ -980,7 +988,7 @@ LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegi
|
||||
|
||||
// Allow widget to render a custom foreground.
|
||||
mCompositor->GetWidget()->DrawWindowOverlay(
|
||||
this, LayoutDeviceIntRect::FromUnknownRect(actualBounds));
|
||||
&widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds));
|
||||
|
||||
// Debugging
|
||||
RenderDebugOverlay(actualBounds);
|
||||
@ -999,7 +1007,7 @@ LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegi
|
||||
composer2D->Render(mCompositor->GetWidget()->RealWidget());
|
||||
}
|
||||
|
||||
mCompositor->GetWidget()->PostRender(this);
|
||||
mCompositor->GetWidget()->PostRender(&widgetContext);
|
||||
|
||||
RecordFrame();
|
||||
}
|
||||
|
@ -11,13 +11,10 @@ DIRS += [
|
||||
'testshell',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_B2G_RIL']:
|
||||
DIRS += ['ril']
|
||||
|
||||
if CONFIG['MOZ_ENABLE_DBUS']:
|
||||
DIRS += ['dbus']
|
||||
|
||||
if CONFIG['MOZ_B2G_RIL'] or CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
DIRS += ['unixfd', 'unixsocket']
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
|
@ -549,7 +549,7 @@ RegisterAllocator::getMoveGroupAfter(LInstruction* ins)
|
||||
void
|
||||
RegisterAllocator::dumpInstructions()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
#ifdef JS_JITSPEW
|
||||
fprintf(stderr, "Instructions:\n");
|
||||
|
||||
for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
|
||||
@ -610,5 +610,5 @@ RegisterAllocator::dumpInstructions()
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
#endif // DEBUG
|
||||
#endif // JS_JITSPEW
|
||||
}
|
||||
|
@ -8229,8 +8229,8 @@ nsLayoutUtils::DoLogTestDataForPaint(LayerManager* aManager,
|
||||
const std::string& aKey,
|
||||
const std::string& aValue)
|
||||
{
|
||||
if (aManager->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
|
||||
static_cast<ClientLayerManager*>(aManager)->LogTestDataForCurrentPaint(aScrollId, aKey, aValue);
|
||||
if (ClientLayerManager* mgr = aManager->AsClientLayerManager()) {
|
||||
mgr->LogTestDataForCurrentPaint(aScrollId, aKey, aValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,7 +456,7 @@ public:
|
||||
nscoord GetCrossSize() const { return mCrossSize; }
|
||||
nscoord GetCrossPosition() const { return mCrossPosn; }
|
||||
|
||||
nscoord ResolvedAscent() const {
|
||||
nscoord ResolvedAscent(bool aUseFirstBaseline) const {
|
||||
if (mAscent == ReflowOutput::ASK_FOR_BASELINE) {
|
||||
// XXXdholbert We should probably be using the *container's* writing-mode
|
||||
// here, instead of the item's -- though it doesn't much matter right
|
||||
@ -465,8 +465,13 @@ public:
|
||||
// will matter more (& can be expanded/tested) once we officially support
|
||||
// logical directions & vertical writing-modes in flexbox, in bug 1079155
|
||||
// or a dependency.
|
||||
// Use GetFirstLineBaseline(), or just GetBaseline() if that fails.
|
||||
if (!nsLayoutUtils::GetFirstLineBaseline(mWM, mFrame, &mAscent)) {
|
||||
// Use GetFirstLineBaseline() or GetLastLineBaseline() as appropriate,
|
||||
// or just GetLogicalBaseline() if that fails.
|
||||
bool found = aUseFirstBaseline ?
|
||||
nsLayoutUtils::GetFirstLineBaseline(mWM, mFrame, &mAscent) :
|
||||
nsLayoutUtils::GetLastLineBaseline(mWM, mFrame, &mAscent);
|
||||
|
||||
if (!found) {
|
||||
mAscent = mFrame->GetLogicalBaseline(mWM);
|
||||
}
|
||||
}
|
||||
@ -492,7 +497,8 @@ public:
|
||||
// from, so that it can look up the appropriate components from mMargin.)
|
||||
nscoord GetBaselineOffsetFromOuterCrossEdge(
|
||||
AxisEdgeType aEdge,
|
||||
const FlexboxAxisTracker& aAxisTracker) const;
|
||||
const FlexboxAxisTracker& aAxisTracker,
|
||||
bool aUseFirstLineBaseline) const;
|
||||
|
||||
float GetShareOfWeightSoFar() const { return mShareOfWeightSoFar; }
|
||||
|
||||
@ -826,7 +832,8 @@ public:
|
||||
mTotalInnerHypotheticalMainSize(0),
|
||||
mTotalOuterHypotheticalMainSize(0),
|
||||
mLineCrossSize(0),
|
||||
mBaselineOffset(nscoord_MIN)
|
||||
mFirstBaselineOffset(nscoord_MIN),
|
||||
mLastBaselineOffset(nscoord_MIN)
|
||||
{}
|
||||
|
||||
// Returns the sum of our FlexItems' outer hypothetical main sizes.
|
||||
@ -925,8 +932,22 @@ public:
|
||||
*
|
||||
* If there are no baseline-aligned FlexItems, returns nscoord_MIN.
|
||||
*/
|
||||
nscoord GetBaselineOffset() const {
|
||||
return mBaselineOffset;
|
||||
nscoord GetFirstBaselineOffset() const {
|
||||
return mFirstBaselineOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offset within this line where any last baseline-aligned
|
||||
* FlexItems should place their baseline. Opposite the case of the first
|
||||
* baseline offset, this represents a distance from the line's cross-end
|
||||
* edge (since last baseline-aligned items are flush to the cross-end edge).
|
||||
* If we're internally reversing the axes, this instead represents the
|
||||
* distance from the line's cross-start edge.
|
||||
*
|
||||
* If there are no last baseline-aligned FlexItems, returns nscoord_MIN.
|
||||
*/
|
||||
nscoord GetLastBaselineOffset() const {
|
||||
return mLastBaselineOffset;
|
||||
}
|
||||
|
||||
// Runs the "Resolving Flexible Lengths" algorithm from section 9.7 of the
|
||||
@ -965,7 +986,8 @@ private:
|
||||
nscoord mTotalInnerHypotheticalMainSize;
|
||||
nscoord mTotalOuterHypotheticalMainSize;
|
||||
nscoord mLineCrossSize;
|
||||
nscoord mBaselineOffset;
|
||||
nscoord mFirstBaselineOffset;
|
||||
nscoord mLastBaselineOffset;
|
||||
};
|
||||
|
||||
// Information about a strut left behind by a FlexItem that's been collapsed
|
||||
@ -1891,19 +1913,20 @@ FlexItem::FlexItem(ReflowInput& aFlexItemReflowInput,
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
// If the flex item's inline axis is the same as the cross axis, then
|
||||
// 'align-self:baseline' is identical to 'flex-start'. If that's the case, we
|
||||
// just directly convert our align-self value here, so that we don't have to
|
||||
// handle this with special cases elsewhere.
|
||||
// Moreover: for the time being (until we support writing-modes),
|
||||
// all inline axes are horizontal -- so we can just check if the cross axis
|
||||
// is horizontal.
|
||||
// FIXME: Once we support writing-mode (vertical text), this
|
||||
// IsCrossAxisHorizontal check won't be sufficient anymore -- we'll actually
|
||||
// need to compare our inline axis vs. the cross axis.
|
||||
if (mAlignSelf == NS_STYLE_ALIGN_BASELINE &&
|
||||
aAxisTracker.IsCrossAxisHorizontal()) {
|
||||
mAlignSelf = NS_STYLE_ALIGN_FLEX_START;
|
||||
// Map align-self 'baseline' value to 'start' when baseline alignment
|
||||
// is not possible because the FlexItem's writing mode is orthogonal to
|
||||
// the main axis of the container. If that's the case, we just directly
|
||||
// convert our align-self value here, so that we don't have to handle this
|
||||
// with special cases elsewhere.
|
||||
// We are treating this case as one where it is appropriate to use the
|
||||
// fallback values defined at https://www.w3.org/TR/css-align-3/#baseline
|
||||
if (aAxisTracker.IsRowOriented() ==
|
||||
aAxisTracker.GetWritingMode().IsOrthogonalTo(mWM)) {
|
||||
if (mAlignSelf == NS_STYLE_ALIGN_BASELINE) {
|
||||
mAlignSelf = NS_STYLE_ALIGN_FLEX_START;
|
||||
} else if (mAlignSelf == NS_STYLE_ALIGN_LAST_BASELINE) {
|
||||
mAlignSelf = NS_STYLE_ALIGN_FLEX_END;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1976,7 +1999,8 @@ FlexItem::CheckForMinSizeAuto(const ReflowInput& aFlexItemReflowInput,
|
||||
nscoord
|
||||
FlexItem::GetBaselineOffsetFromOuterCrossEdge(
|
||||
AxisEdgeType aEdge,
|
||||
const FlexboxAxisTracker& aAxisTracker) const
|
||||
const FlexboxAxisTracker& aAxisTracker,
|
||||
bool aUseFirstLineBaseline) const
|
||||
{
|
||||
// NOTE: Currently, 'mAscent' (taken from reflow) is an inherently vertical
|
||||
// measurement -- it's the distance from the border-top edge of this FlexItem
|
||||
@ -1990,7 +2014,8 @@ FlexItem::GetBaselineOffsetFromOuterCrossEdge(
|
||||
AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis();
|
||||
mozilla::Side sideToMeasureFrom = kAxisOrientationToSidesMap[crossAxis][aEdge];
|
||||
|
||||
nscoord marginTopToBaseline = ResolvedAscent() + mMargin.top;
|
||||
nscoord marginTopToBaseline = ResolvedAscent(aUseFirstLineBaseline) +
|
||||
mMargin.top;
|
||||
|
||||
if (sideToMeasureFrom == eSideTop) {
|
||||
// Measuring from top (normal case): the distance from the margin-box top
|
||||
@ -3089,15 +3114,19 @@ SingleLineCrossAxisPositionTracker::
|
||||
void
|
||||
FlexLine::ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker)
|
||||
{
|
||||
nscoord crossStartToFurthestBaseline = nscoord_MIN;
|
||||
nscoord crossEndToFurthestBaseline = nscoord_MIN;
|
||||
nscoord crossStartToFurthestFirstBaseline = nscoord_MIN;
|
||||
nscoord crossEndToFurthestFirstBaseline = nscoord_MIN;
|
||||
nscoord crossStartToFurthestLastBaseline = nscoord_MIN;
|
||||
nscoord crossEndToFurthestLastBaseline = nscoord_MIN;
|
||||
nscoord largestOuterCrossSize = 0;
|
||||
for (const FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
|
||||
nscoord curOuterCrossSize =
|
||||
item->GetOuterCrossSize(aAxisTracker.GetCrossAxis());
|
||||
|
||||
if (item->GetAlignSelf() == NS_STYLE_ALIGN_BASELINE &&
|
||||
if ((item->GetAlignSelf() == NS_STYLE_ALIGN_BASELINE ||
|
||||
item->GetAlignSelf() == NS_STYLE_ALIGN_LAST_BASELINE) &&
|
||||
item->GetNumAutoMarginsInAxis(aAxisTracker.GetCrossAxis()) == 0) {
|
||||
const bool useFirst = (item->GetAlignSelf() == NS_STYLE_ALIGN_BASELINE);
|
||||
// FIXME: Once we support "writing-mode", we'll have to do baseline
|
||||
// alignment in vertical flex containers here (w/ horizontal cross-axes).
|
||||
|
||||
@ -3129,16 +3158,24 @@ FlexLine::ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker)
|
||||
|
||||
nscoord crossStartToBaseline =
|
||||
item->GetBaselineOffsetFromOuterCrossEdge(eAxisEdge_Start,
|
||||
aAxisTracker);
|
||||
aAxisTracker,
|
||||
useFirst);
|
||||
nscoord crossEndToBaseline = curOuterCrossSize - crossStartToBaseline;
|
||||
|
||||
// Now, update our "largest" values for these (across all the flex items
|
||||
// in this flex line), so we can use them in computing the line's cross
|
||||
// size below:
|
||||
crossStartToFurthestBaseline = std::max(crossStartToFurthestBaseline,
|
||||
crossStartToBaseline);
|
||||
crossEndToFurthestBaseline = std::max(crossEndToFurthestBaseline,
|
||||
crossEndToBaseline);
|
||||
if (useFirst) {
|
||||
crossStartToFurthestFirstBaseline =
|
||||
std::max(crossStartToFurthestFirstBaseline, crossStartToBaseline);
|
||||
crossEndToFurthestFirstBaseline =
|
||||
std::max(crossEndToFurthestFirstBaseline, crossEndToBaseline);
|
||||
} else {
|
||||
crossStartToFurthestLastBaseline =
|
||||
std::max(crossStartToFurthestLastBaseline, crossStartToBaseline);
|
||||
crossEndToFurthestLastBaseline =
|
||||
std::max(crossEndToFurthestLastBaseline, crossEndToBaseline);
|
||||
}
|
||||
} else {
|
||||
largestOuterCrossSize = std::max(largestOuterCrossSize, curOuterCrossSize);
|
||||
}
|
||||
@ -3148,17 +3185,24 @@ FlexLine::ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker)
|
||||
// end, depending on whether we've flipped the axes) to the furthest
|
||||
// item-baseline. The item(s) with that baseline will be exactly aligned with
|
||||
// the line's edge.
|
||||
mBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
|
||||
crossEndToFurthestBaseline : crossStartToFurthestBaseline;
|
||||
mFirstBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
|
||||
crossEndToFurthestFirstBaseline : crossStartToFurthestFirstBaseline;
|
||||
|
||||
mLastBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
|
||||
crossStartToFurthestLastBaseline : crossEndToFurthestLastBaseline;
|
||||
|
||||
// The line's cross-size is the larger of:
|
||||
// (a) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
|
||||
// all baseline-aligned items with no cross-axis auto margins...
|
||||
// and
|
||||
// (b) largest cross-size of all other children.
|
||||
mLineCrossSize = std::max(crossStartToFurthestBaseline +
|
||||
crossEndToFurthestBaseline,
|
||||
largestOuterCrossSize);
|
||||
// (b) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
|
||||
// all last baseline-aligned items with no cross-axis auto margins...
|
||||
// and
|
||||
// (c) largest cross-size of all other children.
|
||||
mLineCrossSize = std::max(
|
||||
std::max(crossStartToFurthestFirstBaseline + crossEndToFurthestFirstBaseline,
|
||||
crossStartToFurthestLastBaseline + crossEndToFurthestLastBaseline),
|
||||
largestOuterCrossSize);
|
||||
}
|
||||
|
||||
void
|
||||
@ -3288,8 +3332,7 @@ SingleLineCrossAxisPositionTracker::
|
||||
switch (alignSelf) {
|
||||
case NS_STYLE_ALIGN_SELF_START:
|
||||
case NS_STYLE_ALIGN_SELF_END:
|
||||
case NS_STYLE_ALIGN_LAST_BASELINE:
|
||||
NS_WARNING("NYI: align-items/align-self:left/right/self-start/self-end/last baseline");
|
||||
NS_WARNING("NYI: align-items/align-self:left/right/self-start/self-end");
|
||||
MOZ_FALLTHROUGH;
|
||||
case NS_STYLE_ALIGN_FLEX_START:
|
||||
// No space to skip over -- we're done.
|
||||
@ -3302,19 +3345,25 @@ SingleLineCrossAxisPositionTracker::
|
||||
mPosition +=
|
||||
(aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis)) / 2;
|
||||
break;
|
||||
case NS_STYLE_ALIGN_BASELINE: {
|
||||
case NS_STYLE_ALIGN_BASELINE:
|
||||
case NS_STYLE_ALIGN_LAST_BASELINE: {
|
||||
const bool useFirst = (alignSelf == NS_STYLE_ALIGN_BASELINE);
|
||||
|
||||
// Normally, baseline-aligned items are collectively aligned with the
|
||||
// line's cross-start edge; however, if our cross axis is (internally)
|
||||
// reversed, we instead align them with the cross-end edge.
|
||||
// A similar logic holds for last baseline-aligned items, but in reverse.
|
||||
AxisEdgeType baselineAlignEdge =
|
||||
aAxisTracker.AreAxesInternallyReversed() ?
|
||||
aAxisTracker.AreAxesInternallyReversed() == useFirst ?
|
||||
eAxisEdge_End : eAxisEdge_Start;
|
||||
|
||||
nscoord itemBaselineOffset =
|
||||
aItem.GetBaselineOffsetFromOuterCrossEdge(baselineAlignEdge,
|
||||
aAxisTracker);
|
||||
aAxisTracker,
|
||||
useFirst);
|
||||
|
||||
nscoord lineBaselineOffset = aLine.GetBaselineOffset();
|
||||
nscoord lineBaselineOffset = useFirst ? aLine.GetFirstBaselineOffset()
|
||||
: aLine.GetLastBaselineOffset();
|
||||
|
||||
NS_ASSERTION(lineBaselineOffset >= itemBaselineOffset,
|
||||
"failed at finding largest baseline offset");
|
||||
@ -3323,7 +3372,7 @@ SingleLineCrossAxisPositionTracker::
|
||||
// to get the item's baseline to hit the line's baseline offset:
|
||||
nscoord baselineDiff = lineBaselineOffset - itemBaselineOffset;
|
||||
|
||||
if (aAxisTracker.AreAxesInternallyReversed()) {
|
||||
if (aAxisTracker.AreAxesInternallyReversed() == useFirst) {
|
||||
// Advance to align item w/ line's flex-end edge (as in FLEX_END case):
|
||||
mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
|
||||
// ...and step *back* by the baseline adjustment:
|
||||
@ -4276,7 +4325,7 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
|
||||
// measured from):
|
||||
nscoord flexContainerAscent;
|
||||
if (!aAxisTracker.AreAxesInternallyReversed()) {
|
||||
nscoord firstLineBaselineOffset = lines.getFirst()->GetBaselineOffset();
|
||||
nscoord firstLineBaselineOffset = lines.getFirst()->GetFirstBaselineOffset();
|
||||
if (firstLineBaselineOffset == nscoord_MIN) {
|
||||
// No baseline-aligned items in line. Use sentinel value to prompt us to
|
||||
// get baseline from the first FlexItem after we've reflowed it.
|
||||
@ -4313,7 +4362,7 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
|
||||
// at the cross-end edge of that line, which the line's baseline offset is
|
||||
// measured from):
|
||||
if (aAxisTracker.AreAxesInternallyReversed()) {
|
||||
nscoord lastLineBaselineOffset = lines.getLast()->GetBaselineOffset();
|
||||
nscoord lastLineBaselineOffset = lines.getLast()->GetFirstBaselineOffset();
|
||||
if (lastLineBaselineOffset == nscoord_MIN) {
|
||||
// No baseline-aligned items in line. Use sentinel value to prompt us to
|
||||
// get baseline from the last FlexItem after we've reflowed it.
|
||||
@ -4412,10 +4461,11 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
|
||||
|
||||
// If this is our first item and we haven't established a baseline for
|
||||
// the container yet (i.e. if we don't have 'align-self: baseline' on any
|
||||
// children), then use this child's baseline as the container's baseline.
|
||||
// children), then use this child's first baseline as the container's
|
||||
// baseline.
|
||||
if (item == firstItem &&
|
||||
flexContainerAscent == nscoord_MIN) {
|
||||
flexContainerAscent = itemNormalBPos + item->ResolvedAscent();
|
||||
flexContainerAscent = itemNormalBPos + item->ResolvedAscent(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,5 +61,13 @@
|
||||
<i>ital<br/>ic</i>
|
||||
</table>
|
||||
</div>
|
||||
<div class="flexbox">
|
||||
<div class="lime">blk_1line</div
|
||||
><div class="yellow">blk<br/>2lines</div
|
||||
><div class="orange"><span class="super">super</span></div
|
||||
><div class="pink"><span class="sub">sub</span></div
|
||||
><div class="aqua big">big<br/>text<br/>3lines</div
|
||||
><div class="tan"><i>ital<br/>ic</i></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -17,10 +17,15 @@
|
||||
<style>
|
||||
.flexbox {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
border: 1px dashed blue;
|
||||
font: 14px sans-serif;
|
||||
}
|
||||
.base {
|
||||
align-items: baseline;
|
||||
}
|
||||
.lastbase {
|
||||
align-items: last baseline;
|
||||
}
|
||||
|
||||
.big {
|
||||
height: 100px;
|
||||
@ -45,7 +50,15 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="flexbox">
|
||||
<div class="flexbox base">
|
||||
<div class="lime">blk_1line</div>
|
||||
<div class="yellow">blk<br/>2lines</div>
|
||||
<div class="orange"><span class="super">super</span></div>
|
||||
<div class="pink"><span class="sub">sub</span></div>
|
||||
<div class="aqua big">big<br/>text<br/>3lines</div>
|
||||
<i class="tan">ital<br/>ic</i>
|
||||
</div>
|
||||
<div class="flexbox lastbase">
|
||||
<div class="lime">blk_1line</div>
|
||||
<div class="yellow">blk<br/>2lines</div>
|
||||
<div class="orange"><span class="super">super</span></div>
|
||||
|
@ -19,11 +19,16 @@
|
||||
<style>
|
||||
.flexbox {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-wrap: wrap-reverse;
|
||||
border: 1px dashed blue;
|
||||
font: 14px sans-serif;
|
||||
}
|
||||
.base {
|
||||
align-items: baseline;
|
||||
}
|
||||
.lastbase {
|
||||
align-items: last baseline;
|
||||
}
|
||||
|
||||
.big {
|
||||
height: 100px;
|
||||
@ -48,7 +53,15 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="flexbox">
|
||||
<div class="flexbox base">
|
||||
<div class="lime">blk_1line</div>
|
||||
<div class="yellow">blk<br/>2lines</div>
|
||||
<div class="orange"><span class="super">super</span></div>
|
||||
<div class="pink"><span class="sub">sub</span></div>
|
||||
<div class="aqua big">big<br/>text<br/>3lines</div>
|
||||
<i class="tan">ital<br/>ic</i>
|
||||
</div>
|
||||
<div class="flexbox lastbase">
|
||||
<div class="lime">blk_1line</div>
|
||||
<div class="yellow">blk<br/>2lines</div>
|
||||
<div class="orange"><span class="super">super</span></div>
|
||||
|
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!-- Reference case for behavior of the 'baseline' value for align-items and
|
||||
align-self when tested against content with an orthogonal writing-mode.
|
||||
-->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Reftest Reference</title>
|
||||
<link rel="author" title="Brad Werth" href="mailto:bwerth@mozilla.com"/>
|
||||
<style>
|
||||
.container {
|
||||
display: flex;
|
||||
border: 1px dashed blue;
|
||||
font: 14px sans-serif;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.ortho { writing-mode: vertical-rl;
|
||||
width: 17px;
|
||||
height: 40px;
|
||||
float: left; }
|
||||
.offset { margin-top: 10px;
|
||||
margin-bottom: 3px; }
|
||||
|
||||
.start { align-self: flex-start; }
|
||||
.end { align-self: flex-end; }
|
||||
|
||||
.lime { background: lime; }
|
||||
.yellow { background: yellow; }
|
||||
.orange { background: orange; }
|
||||
.pink { background: pink; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="lime ortho start">ortho</div
|
||||
><div class="yellow offset start">one line</div
|
||||
><div class="orange offset start">two<br/>lines</div
|
||||
><div class="pink offset start">offset</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="lime ortho end">ortho</div
|
||||
><div class="yellow offset end">one line</div
|
||||
><div class="orange offset end">two<br/>lines</div
|
||||
><div class="pink offset end">offset</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!-- Testcase for behavior of the 'baseline' value for align-items (and
|
||||
align-self, implicitly). This test baseline-aligns various types of
|
||||
content against content that is exempt from alignment due to an
|
||||
orthognal writing-mode.
|
||||
-->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Test: Baseline alignment of block flex items with 'baseline' value for 'align-items' / 'align-self' against non-parallel writing-modes.</title>
|
||||
<link rel="author" title="Brad Werth" href="mailto:bwerth@mozilla.com"/>
|
||||
<link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#baseline-participation"/>
|
||||
<link rel="match" href="flexbox-align-self-baseline-horiz-006-ref.xhtml"/>
|
||||
<style>
|
||||
.container {
|
||||
display: flex;
|
||||
border: 1px dashed blue;
|
||||
font: 14px sans-serif;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.base { align-items: baseline; }
|
||||
.lastbase { align-items: last baseline; }
|
||||
|
||||
.ortho { writing-mode: vertical-rl;
|
||||
width: 17px;
|
||||
height: 40px; }
|
||||
.offset { margin-top: 10px;
|
||||
margin-bottom: 3px; }
|
||||
|
||||
.lime { background: lime; }
|
||||
.yellow { background: yellow; }
|
||||
.orange { background: orange; }
|
||||
.pink { background: pink; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container base">
|
||||
<div class="lime ortho">ortho</div>
|
||||
<div class="yellow">one line</div>
|
||||
<div class="orange">two<br/>lines</div>
|
||||
<div class="pink offset">offset</div>
|
||||
</div>
|
||||
<div class="container lastbase">
|
||||
<div class="lime ortho">ortho</div>
|
||||
<div class="yellow">one line</div>
|
||||
<div class="orange">two<br/>lines</div>
|
||||
<div class="pink offset">offset</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!-- Reference case for behavior of 'baseline' and 'last baseline' values
|
||||
for align-items and align-self.
|
||||
-->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Reftest Reference</title>
|
||||
<link rel="author" title="Brad Werth" href="mailto:bwerth@mozilla.com"/>
|
||||
<style>
|
||||
.container {
|
||||
display: flex;
|
||||
border: 1px dashed blue;
|
||||
font: 14px sans-serif;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.start { align-self: flex-start; }
|
||||
.end { align-self: flex-end; }
|
||||
|
||||
.offset { margin-top: 10px;
|
||||
margin-bottom: 3px; }
|
||||
|
||||
.lime { background: lime; }
|
||||
.yellow { background: yellow; }
|
||||
.orange { background: orange; }
|
||||
.pink { background: pink; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="lime offset start">one line (first)</div
|
||||
><div class="yellow offset end">one line (last)</div
|
||||
><div class="orange offset end">two<br/>lines and offset (last)</div
|
||||
><div class="pink offset start">offset (first)</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!-- Testcase for behavior of 'baseline' and 'last baseline' values
|
||||
for align-items (and align-self, implicitly). This test confirms
|
||||
non-interference between the 'baseline' and 'last baseline' items.
|
||||
-->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Test: Baseline alignment of block flex items with 'baseline' and 'last-baseline' values for 'align-self' against each other.</title>
|
||||
<link rel="author" title="Brad Werth" href="mailto:bwerth@mozilla.com"/>
|
||||
<link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#baseline-participation"/>
|
||||
<link rel="match" href="flexbox-align-self-baseline-horiz-007-ref.xhtml"/>
|
||||
<style>
|
||||
.container {
|
||||
display: flex;
|
||||
border: 1px dashed blue;
|
||||
font: 14px sans-serif;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.base { align-self: baseline; }
|
||||
.lastbase { align-self: last baseline; }
|
||||
|
||||
.offset { margin-top: 10px;
|
||||
margin-bottom: 3px; }
|
||||
|
||||
.lime { background: lime; }
|
||||
.yellow { background: yellow; }
|
||||
.orange { background: orange; }
|
||||
.pink { background: pink; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="lime base">one line (first)</div>
|
||||
<div class="yellow lastbase">one line (last)</div>
|
||||
<div class="orange offset lastbase">two<br/>lines and offset (last)</div>
|
||||
<div class="pink offset base">offset (first)</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -18,6 +18,8 @@
|
||||
== flexbox-align-self-baseline-horiz-003.xhtml flexbox-align-self-baseline-horiz-003-ref.xhtml
|
||||
== flexbox-align-self-baseline-horiz-004.xhtml flexbox-align-self-baseline-horiz-004-ref.xhtml
|
||||
== flexbox-align-self-baseline-horiz-005.xhtml flexbox-align-self-baseline-horiz-005-ref.xhtml
|
||||
== flexbox-align-self-baseline-horiz-006.xhtml flexbox-align-self-baseline-horiz-006-ref.xhtml
|
||||
== flexbox-align-self-baseline-horiz-007.xhtml flexbox-align-self-baseline-horiz-007-ref.xhtml
|
||||
|
||||
== flexbox-align-self-stretch-vert-001.html flexbox-align-self-stretch-vert-001-ref.html
|
||||
== flexbox-align-self-stretch-vert-002.html flexbox-align-self-stretch-vert-002-ref.html
|
||||
|
@ -391,7 +391,7 @@ public:
|
||||
const gfxMatrix& aTransform,
|
||||
const nsIntRect* aDirtyRect) override
|
||||
{
|
||||
BasicLayerManager* basic = static_cast<BasicLayerManager*>(mLayerManager);
|
||||
BasicLayerManager* basic = mLayerManager->AsBasicLayerManager();
|
||||
basic->SetTarget(&aContext);
|
||||
|
||||
gfxPoint devPixelOffset =
|
||||
@ -850,7 +850,7 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
|
||||
|
||||
/* Paint the child */
|
||||
context.SetMatrix(matrixAutoSaveRestore.Matrix());
|
||||
BasicLayerManager* basic = static_cast<BasicLayerManager*>(aParams.layerManager);
|
||||
BasicLayerManager* basic = aParams.layerManager->AsBasicLayerManager();
|
||||
RefPtr<gfxContext> oldCtx = basic->GetTarget();
|
||||
basic->SetTarget(&context);
|
||||
aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
|
||||
|
@ -39,6 +39,14 @@ public abstract class StreamItem extends RecyclerView.ViewHolder {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
public static class HighlightsTitle extends StreamItem {
|
||||
public static final int LAYOUT_ID = R.layout.activity_stream_main_highlightstitle;
|
||||
|
||||
public HighlightsTitle(View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TopPanel extends StreamItem {
|
||||
public static final int LAYOUT_ID = R.layout.activity_stream_main_toppanel;
|
||||
|
||||
@ -109,7 +117,9 @@ public abstract class StreamItem extends RecyclerView.ViewHolder {
|
||||
menuButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ActivityStreamContextMenu.show(v.getContext(), ActivityStreamContextMenu.MenuMode.HIGHLIGHT,
|
||||
ActivityStreamContextMenu.show(v.getContext(),
|
||||
menuButton,
|
||||
ActivityStreamContextMenu.MenuMode.HIGHLIGHT,
|
||||
title, url, onUrlOpenListener, onUrlOpenInBackgroundListener,
|
||||
vIconView.getWidth(), vIconView.getHeight());
|
||||
}
|
||||
|
@ -47,6 +47,8 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamItem> impl
|
||||
public int getItemViewType(int position) {
|
||||
if (position == 0) {
|
||||
return TopPanel.LAYOUT_ID;
|
||||
} else if (position == 1) {
|
||||
return StreamItem.HighlightsTitle.LAYOUT_ID;
|
||||
} else {
|
||||
return HighlightItem.LAYOUT_ID;
|
||||
}
|
||||
@ -58,6 +60,8 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamItem> impl
|
||||
|
||||
if (type == TopPanel.LAYOUT_ID) {
|
||||
return new TopPanel(inflater.inflate(type, parent, false), onUrlOpenListener, onUrlOpenInBackgroundListener);
|
||||
} else if (type == StreamItem.HighlightsTitle.LAYOUT_ID) {
|
||||
return new StreamItem.HighlightsTitle(inflater.inflate(type, parent, false));
|
||||
} else if (type == HighlightItem.LAYOUT_ID) {
|
||||
return new HighlightItem(inflater.inflate(type, parent, false), onUrlOpenListener, onUrlOpenInBackgroundListener);
|
||||
} else {
|
||||
@ -70,8 +74,8 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamItem> impl
|
||||
throw new IllegalArgumentException("Requested cursor position for invalid item");
|
||||
}
|
||||
|
||||
// We have one blank panel at the top, hence remove that to obtain the cursor position
|
||||
return position - 1;
|
||||
// We have two blank panels at the top, hence remove that to obtain the cursor position
|
||||
return position - 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -114,7 +118,7 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamItem> impl
|
||||
highlightsCount = 0;
|
||||
}
|
||||
|
||||
return highlightsCount + 1;
|
||||
return highlightsCount + 2;
|
||||
}
|
||||
|
||||
public void swapHighlightsCursor(Cursor cursor) {
|
||||
|
@ -8,37 +8,27 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.design.widget.BottomSheetBehavior;
|
||||
import android.support.design.widget.BottomSheetDialog;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.IntentHelper;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.gecko.activitystream.ActivityStream;
|
||||
import org.mozilla.gecko.annotation.RobocopTarget;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.home.HomePager;
|
||||
import org.mozilla.gecko.icons.IconCallback;
|
||||
import org.mozilla.gecko.icons.IconResponse;
|
||||
import org.mozilla.gecko.icons.Icons;
|
||||
import org.mozilla.gecko.util.Clipboard;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.util.UIAsyncTask;
|
||||
import org.mozilla.gecko.widget.FaviconView;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import static org.mozilla.gecko.activitystream.ActivityStream.extractLabel;
|
||||
|
||||
public class ActivityStreamContextMenu
|
||||
extends BottomSheetDialog
|
||||
@RobocopTarget
|
||||
public abstract class ActivityStreamContextMenu
|
||||
implements NavigationView.OnNavigationItemSelectedListener {
|
||||
|
||||
public enum MenuMode {
|
||||
@ -54,66 +44,48 @@ public class ActivityStreamContextMenu
|
||||
final HomePager.OnUrlOpenListener onUrlOpenListener;
|
||||
final HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener;
|
||||
|
||||
boolean isAlreadyBookmarked = false;
|
||||
boolean isAlreadyBookmarked; // default false;
|
||||
|
||||
private ActivityStreamContextMenu(final Context context,
|
||||
final MenuMode mode,
|
||||
final String title, @NonNull final String url,
|
||||
HomePager.OnUrlOpenListener onUrlOpenListener,
|
||||
HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener,
|
||||
final int tilesWidth, final int tilesHeight) {
|
||||
super(context);
|
||||
public abstract MenuItem getItemByID(int id);
|
||||
|
||||
public abstract void show();
|
||||
|
||||
public abstract void dismiss();
|
||||
|
||||
final MenuMode mode;
|
||||
|
||||
/* package-private */ ActivityStreamContextMenu(final Context context,
|
||||
final MenuMode mode,
|
||||
final String title, @NonNull final String url,
|
||||
HomePager.OnUrlOpenListener onUrlOpenListener,
|
||||
HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener) {
|
||||
this.context = context;
|
||||
|
||||
this.mode = mode;
|
||||
|
||||
this.title = title;
|
||||
this.url = url;
|
||||
this.onUrlOpenListener = onUrlOpenListener;
|
||||
this.onUrlOpenInBackgroundListener = onUrlOpenInBackgroundListener;
|
||||
}
|
||||
|
||||
final LayoutInflater inflater = LayoutInflater.from(context);
|
||||
|
||||
final View content = inflater.inflate(R.layout.activity_stream_contextmenu_layout, null);
|
||||
setContentView(content);
|
||||
|
||||
((TextView) findViewById(R.id.title)).setText(title);
|
||||
extractLabel(context, url, false, new ActivityStream.LabelCallback() {
|
||||
public void onLabelExtracted(String label) {
|
||||
((TextView) findViewById(R.id.url)).setText(label);
|
||||
}
|
||||
});
|
||||
|
||||
// Copy layouted parameters from the Highlights / TopSites items to ensure consistency
|
||||
final FaviconView faviconView = (FaviconView) findViewById(R.id.icon);
|
||||
ViewGroup.LayoutParams layoutParams = faviconView.getLayoutParams();
|
||||
layoutParams.width = tilesWidth;
|
||||
layoutParams.height = tilesHeight;
|
||||
faviconView.setLayoutParams(layoutParams);
|
||||
|
||||
Icons.with(context)
|
||||
.pageUrl(url)
|
||||
.skipNetwork()
|
||||
.build()
|
||||
.execute(new IconCallback() {
|
||||
@Override
|
||||
public void onIconResponse(IconResponse response) {
|
||||
faviconView.updateImage(response);
|
||||
}
|
||||
});
|
||||
|
||||
NavigationView navigationView = (NavigationView) findViewById(R.id.menu);
|
||||
navigationView.setNavigationItemSelectedListener(this);
|
||||
|
||||
/**
|
||||
* Must be called before the menu is shown.
|
||||
* <p/>
|
||||
* Your implementation must be ready to return items from getItemByID() before postInit() is
|
||||
* called, i.e. you should probably inflate your menu items before this call.
|
||||
*/
|
||||
protected void postInit() {
|
||||
// Disable "dismiss" for topsites until we have decided on its behaviour for topsites
|
||||
// (currently "dismiss" adds the URL to a highlights-specific blocklist, which the topsites
|
||||
// query has no knowledge of).
|
||||
if (mode == MenuMode.TOPSITE) {
|
||||
final MenuItem dismissItem = navigationView.getMenu().findItem(R.id.dismiss);
|
||||
final MenuItem dismissItem = getItemByID(R.id.dismiss);
|
||||
dismissItem.setVisible(false);
|
||||
}
|
||||
|
||||
// Disable the bookmark item until we know its bookmark state
|
||||
final MenuItem bookmarkItem = navigationView.getMenu().findItem(R.id.bookmark);
|
||||
final MenuItem bookmarkItem = getItemByID(R.id.bookmark);
|
||||
bookmarkItem.setEnabled(false);
|
||||
|
||||
(new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
|
||||
@ -134,10 +106,9 @@ public class ActivityStreamContextMenu
|
||||
}).execute();
|
||||
|
||||
// Only show the "remove from history" item if a page actually has history
|
||||
final MenuItem deleteHistoryItem = navigationView.getMenu().findItem(R.id.delete);
|
||||
final MenuItem deleteHistoryItem = getItemByID(R.id.delete);
|
||||
deleteHistoryItem.setVisible(false);
|
||||
|
||||
|
||||
(new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
|
||||
boolean hasHistory;
|
||||
|
||||
@ -164,26 +135,8 @@ public class ActivityStreamContextMenu
|
||||
}
|
||||
}
|
||||
}).execute();
|
||||
|
||||
BottomSheetBehavior<View> bsBehaviour = BottomSheetBehavior.from((View) content.getParent());
|
||||
bsBehaviour.setPeekHeight(context.getResources().getDimensionPixelSize(R.dimen.activity_stream_contextmenu_peek_height));
|
||||
}
|
||||
|
||||
public static ActivityStreamContextMenu show(Context context,
|
||||
final MenuMode menuMode,
|
||||
final String title, @NonNull final String url,
|
||||
HomePager.OnUrlOpenListener onUrlOpenListener,
|
||||
HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener,
|
||||
final int tilesWidth, final int tilesHeight) {
|
||||
final ActivityStreamContextMenu menu = new ActivityStreamContextMenu(context,
|
||||
menuMode,
|
||||
title, url,
|
||||
onUrlOpenListener, onUrlOpenInBackgroundListener,
|
||||
tilesWidth, tilesHeight);
|
||||
menu.show();
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(MenuItem item) {
|
||||
@ -254,4 +207,33 @@ public class ActivityStreamContextMenu
|
||||
dismiss();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@RobocopTarget
|
||||
public static ActivityStreamContextMenu show(Context context,
|
||||
View anchor,
|
||||
final MenuMode menuMode,
|
||||
final String title, @NonNull final String url,
|
||||
HomePager.OnUrlOpenListener onUrlOpenListener,
|
||||
HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener,
|
||||
final int tilesWidth, final int tilesHeight) {
|
||||
final ActivityStreamContextMenu menu;
|
||||
|
||||
if (!HardwareUtils.isTablet()) {
|
||||
menu = new BottomSheetContextMenu(context,
|
||||
menuMode,
|
||||
title, url,
|
||||
onUrlOpenListener, onUrlOpenInBackgroundListener,
|
||||
tilesWidth, tilesHeight);
|
||||
} else {
|
||||
menu = new PopupContextMenu(context,
|
||||
anchor,
|
||||
menuMode,
|
||||
title, url,
|
||||
onUrlOpenListener, onUrlOpenInBackgroundListener);
|
||||
}
|
||||
|
||||
menu.show();
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,102 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
package org.mozilla.gecko.home.activitystream.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.design.widget.BottomSheetBehavior;
|
||||
import android.support.design.widget.BottomSheetDialog;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.activitystream.ActivityStream;
|
||||
import org.mozilla.gecko.home.HomePager;
|
||||
import org.mozilla.gecko.icons.IconCallback;
|
||||
import org.mozilla.gecko.icons.IconResponse;
|
||||
import org.mozilla.gecko.icons.Icons;
|
||||
import org.mozilla.gecko.widget.FaviconView;
|
||||
|
||||
import static org.mozilla.gecko.activitystream.ActivityStream.extractLabel;
|
||||
|
||||
/* package-private */ class BottomSheetContextMenu
|
||||
extends ActivityStreamContextMenu {
|
||||
|
||||
|
||||
private final BottomSheetDialog bottomSheetDialog;
|
||||
|
||||
private final NavigationView navigationView;
|
||||
|
||||
public BottomSheetContextMenu(final Context context,
|
||||
final MenuMode mode,
|
||||
final String title, @NonNull final String url,
|
||||
HomePager.OnUrlOpenListener onUrlOpenListener,
|
||||
HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener,
|
||||
final int tilesWidth, final int tilesHeight) {
|
||||
|
||||
super(context,
|
||||
mode,
|
||||
title,
|
||||
url,
|
||||
onUrlOpenListener,
|
||||
onUrlOpenInBackgroundListener);
|
||||
|
||||
final LayoutInflater inflater = LayoutInflater.from(context);
|
||||
final View content = inflater.inflate(R.layout.activity_stream_contextmenu_bottomsheet, null);
|
||||
|
||||
bottomSheetDialog = new BottomSheetDialog(context);
|
||||
bottomSheetDialog.setContentView(content);
|
||||
|
||||
((TextView) content.findViewById(R.id.title)).setText(title);
|
||||
|
||||
extractLabel(context, url, false, new ActivityStream.LabelCallback() {
|
||||
public void onLabelExtracted(String label) {
|
||||
((TextView) content.findViewById(R.id.url)).setText(label);
|
||||
}
|
||||
});
|
||||
|
||||
// Copy layouted parameters from the Highlights / TopSites items to ensure consistency
|
||||
final FaviconView faviconView = (FaviconView) content.findViewById(R.id.icon);
|
||||
ViewGroup.LayoutParams layoutParams = faviconView.getLayoutParams();
|
||||
layoutParams.width = tilesWidth;
|
||||
layoutParams.height = tilesHeight;
|
||||
faviconView.setLayoutParams(layoutParams);
|
||||
|
||||
Icons.with(context)
|
||||
.pageUrl(url)
|
||||
.skipNetwork()
|
||||
.build()
|
||||
.execute(new IconCallback() {
|
||||
@Override
|
||||
public void onIconResponse(IconResponse response) {
|
||||
faviconView.updateImage(response);
|
||||
}
|
||||
});
|
||||
|
||||
navigationView = (NavigationView) content.findViewById(R.id.menu);
|
||||
navigationView.setNavigationItemSelectedListener(this);
|
||||
|
||||
super.postInit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem getItemByID(int id) {
|
||||
return navigationView.getMenu().findItem(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
bottomSheetDialog.show();
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
bottomSheetDialog.dismiss();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
package org.mozilla.gecko.home.activitystream.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.home.HomePager;
|
||||
|
||||
/* package-private */ class PopupContextMenu
|
||||
extends ActivityStreamContextMenu {
|
||||
|
||||
private final PopupWindow popupWindow;
|
||||
private final NavigationView navigationView;
|
||||
|
||||
private final View anchor;
|
||||
|
||||
public PopupContextMenu(final Context context,
|
||||
View anchor,
|
||||
final MenuMode mode,
|
||||
final String title,
|
||||
@NonNull final String url,
|
||||
HomePager.OnUrlOpenListener onUrlOpenListener,
|
||||
HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener) {
|
||||
super(context,
|
||||
mode,
|
||||
title,
|
||||
url,
|
||||
onUrlOpenListener,
|
||||
onUrlOpenInBackgroundListener);
|
||||
|
||||
this.anchor = anchor;
|
||||
|
||||
final LayoutInflater inflater = LayoutInflater.from(context);
|
||||
|
||||
View card = inflater.inflate(R.layout.activity_stream_contextmenu_popupmenu, null);
|
||||
navigationView = (NavigationView) card.findViewById(R.id.menu);
|
||||
navigationView.setNavigationItemSelectedListener(this);
|
||||
|
||||
popupWindow = new PopupWindow(context);
|
||||
popupWindow.setContentView(card);
|
||||
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
popupWindow.setFocusable(true);
|
||||
|
||||
super.postInit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem getItemByID(int id) {
|
||||
return navigationView.getMenu().findItem(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
// By default popupWindow follows the pre-material convention of displaying the popup
|
||||
// below a View. We need to shift it over the view:
|
||||
popupWindow.showAsDropDown(anchor,
|
||||
0,
|
||||
-(anchor.getHeight() + anchor.getPaddingBottom()));
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
popupWindow.dismiss();
|
||||
}
|
||||
}
|
@ -92,6 +92,7 @@ class TopSitesCard extends RecyclerView.ViewHolder
|
||||
onUrlOpenListener.onUrlOpen(url, EnumSet.noneOf(HomePager.OnUrlOpenListener.Flags.class));
|
||||
} else if (clickedView == menuButton) {
|
||||
ActivityStreamContextMenu.show(clickedView.getContext(),
|
||||
menuButton,
|
||||
ActivityStreamContextMenu.MenuMode.TOPSITE,
|
||||
title.getText().toString(), url,
|
||||
onUrlOpenListener, onUrlOpenInBackgroundListener,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user