Merge autoland to central, a=merge

MozReview-Commit-ID: Jz9iBkuBrpV
This commit is contained in:
Wes Kocher 2017-08-30 19:52:39 -07:00
Родитель b822d164b0 a216658ae3
Коммит 940bdbc699
230 изменённых файлов: 7411 добавлений и 5718 удалений

Просмотреть файл

@ -64,12 +64,12 @@ MARKUPMAP(form,
roles::FORM)
MARKUPMAP(footer,
New_HyperText,
roles::FOOTER)
New_HTMLHeaderOrFooter,
0)
MARKUPMAP(header,
New_HyperText,
roles::HEADER)
New_HTMLHeaderOrFooter,
0)
MARKUPMAP(h1,
New_HyperText,

Просмотреть файл

@ -161,6 +161,9 @@ static Accessible* New_HTMLFigcaption(nsIContent* aContent, Accessible* aContext
static Accessible* New_HTMLFigure(nsIContent* aContent, Accessible* aContext)
{ return new HTMLFigureAccessible(aContent, aContext->Document()); }
static Accessible* New_HTMLHeaderOrFooter(nsIContent* aContent, Accessible* aContext)
{ return new HTMLHeaderOrFooterAccessible(aContent, aContext->Document()); }
static Accessible* New_HTMLLegend(nsIContent* aContent, Accessible* aContext)
{ return new HTMLLegendAccessible(aContent, aContext->Document()); }

Просмотреть файл

@ -1142,31 +1142,6 @@ HyperTextAccessible::LandmarkRole() const
return nsGkAtoms::navigation;
}
if (mContent->IsAnyOfHTMLElements(nsGkAtoms::header,
nsGkAtoms::footer)) {
// Only map header and footer if they are not descendants of an article
// or section tag.
nsIContent* parent = mContent->GetParent();
while (parent) {
if (parent->IsAnyOfHTMLElements(nsGkAtoms::article, nsGkAtoms::section)) {
break;
}
parent = parent->GetParent();
}
// No article or section elements found.
if (!parent) {
if (mContent->IsHTMLElement(nsGkAtoms::header)) {
return nsGkAtoms::banner;
}
if (mContent->IsHTMLElement(nsGkAtoms::footer)) {
return nsGkAtoms::contentinfo;
}
}
return nullptr;
}
if (mContent->IsHTMLElement(nsGkAtoms::aside)) {
return nsGkAtoms::complementary;
}

Просмотреть файл

@ -202,3 +202,59 @@ HTMLSummaryAccessible::IsWidget() const
{
return true;
}
////////////////////////////////////////////////////////////////////////////////
// HTMLHeaderOrFooterAccessible
////////////////////////////////////////////////////////////////////////////////
NS_IMPL_ISUPPORTS_INHERITED0(HTMLHeaderOrFooterAccessible, HyperTextAccessible)
role
HTMLHeaderOrFooterAccessible::NativeRole()
{
// Only map header and footer if they are direct descendants of the body tag.
// If other sectioning or sectioning root elements, they become sections.
nsIContent* parent = mContent->GetParent();
while (parent) {
if (parent->IsAnyOfHTMLElements(nsGkAtoms::article, nsGkAtoms::aside,
nsGkAtoms::nav, nsGkAtoms::section,
nsGkAtoms::blockquote, nsGkAtoms::details,
nsGkAtoms::dialog, nsGkAtoms::fieldset,
nsGkAtoms::figure, nsGkAtoms::td)) {
break;
}
parent = parent->GetParent();
}
// No sectioning or sectioning root elements found.
if (!parent) {
if (mContent->IsHTMLElement(nsGkAtoms::header)) {
return roles::HEADER;
}
if (mContent->IsHTMLElement(nsGkAtoms::footer)) {
return roles::FOOTER;
}
}
return roles::SECTION;
}
nsIAtom*
HTMLHeaderOrFooterAccessible::LandmarkRole() const
{
if (!HasOwnContent())
return nullptr;
a11y::role r = const_cast<HTMLHeaderOrFooterAccessible*>(this)->Role();
if (r == roles::HEADER) {
return nsGkAtoms::banner;
}
if (r == roles::FOOTER) {
return nsGkAtoms::contentinfo;
}
return nullptr;
}

Просмотреть файл

@ -114,6 +114,26 @@ public:
virtual bool IsWidget() const override;
};
/**
* Used for HTML header and footer elements.
*/
class HTMLHeaderOrFooterAccessible : public HyperTextAccessibleWrap
{
public:
HTMLHeaderOrFooterAccessible(nsIContent* aContent, DocAccessible* aDoc) :
HyperTextAccessibleWrap(aContent, aDoc) {}
NS_DECL_ISUPPORTS_INHERITED
// Accessible
virtual nsIAtom* LandmarkRole() const override;
virtual a11y::role NativeRole() override;
protected:
virtual ~HTMLHeaderOrFooterAccessible() {}
};
} // namespace a11y
} // namespace mozilla

Просмотреть файл

@ -524,12 +524,20 @@
testElm("footer", obj);
obj = {
role: ROLE_FOOTER,
role: ROLE_SECTION,
absentAttributes: { "xml-roles": "contentinfo" },
interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
};
testElm("footer_in_article", obj);
testElm("footer_in_aside", obj);
testElm("footer_in_nav", obj);
testElm("footer_in_section", obj);
testElm("footer_in_blockquote", obj);
testElm("footer_in_details", obj);
testElm("footer_in_dialog", obj);
testElm("footer_in_fieldset", obj);
testElm("footer_in_figure", obj);
testElm("footer_in_td", obj);
// ////////////////////////////////////////////////////////////////////////
// HTML:form
@ -609,12 +617,20 @@
testElm("header", obj);
obj = {
role: ROLE_HEADER,
role: ROLE_SECTION,
absentAttributes: { "xml-roles": "banner" },
interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
};
testElm("header_in_article", obj);
testElm("header_in_aside", obj);
testElm("header_in_nav", obj);
testElm("header_in_section", obj);
testElm("header_in_blockquote", obj);
testElm("header_in_details", obj);
testElm("header_in_dialog", obj);
testElm("header_in_fieldset", obj);
testElm("header_in_figure", obj);
testElm("header_in_td", obj);
// ////////////////////////////////////////////////////////////////////////
// HTML:hr
@ -1471,9 +1487,33 @@
<article>
<footer id="footer_in_article">Some copyright info</footer>
</article>
<aside>
<footer id="footer_in_aside">Some copyright info</footer>
</aside>
<nav>
<footer id="footer_in_nav">Some copyright info</footer>
</nav>
<section>
<footer id="footer_in_section">Some copyright info</footer>
</section>
<blockquote>
<footer id="footer_in_blockquote">Some copyright info</footer>
</blockquote>
<details open="true">
<footer id="footer_in_details">Some copyright info</footer>
</details>
<dialog open="true">
<footer id="footer_in_dialog">Some copyright info</footer>
</dialog>
<fieldset>
<footer id="footer_in_fieldset">Some copyright info</footer>
</fieldset>
<figure>
<footer id="footer_in_figure">Some copyright info</footer>
</figure>
<table><tr><td>
<footer id="footer_in_td">Some copyright info</footer>
</td></tr></table>
<form id="form"></form>
@ -1492,9 +1532,33 @@
<article>
<header id="header_in_article">Not logo</header>
</article>
<aside>
<header id="header_in_aside">Not logo</header>
</aside>
<nav>
<header id="header_in_nav">Not logo</header>
</nav>
<section>
<header id="header_in_section">Not logo</header>
</section>
<blockquote>
<header id="header_in_blockquote">Not logo</header>
</blockquote>
<details open="true">
<header id="header_in_details">Not logo</header>
</details>
<dialog open="true">
<header id="header_in_dialog">Not logo</header>
</dialog>
<fieldset>
<header id="header_in_fieldset">Not logo</header>
</fieldset>
<figure>
<header id="header_in_figure">Not logo</header>
</figure>
<table><tr><td>
<header id="header_in_td">Not logo</header>
</td></tr></table>
<hr id="hr">
<p id="i_container">normal<i>italic</i></p>

Просмотреть файл

@ -47,34 +47,34 @@
}, {
accOrElmOrID: "article_header",
expectedUtterance: [
[{"string": "header"}, "a header within an article"],
["a header within an article", {"string": "header"}]],
["a header within an article"],
["a header within an article"]],
expectedBraille: [
[{"string": "headerAbbr"}, "a header within an article"],
["a header within an article", {"string": "headerAbbr"}]],
["a header within an article"],
["a header within an article"]],
}, {
accOrElmOrID: "article_footer",
expectedUtterance: [
[{"string": "footer"}, "a footer within an article"],
["a footer within an article", {"string": "footer"}]],
["a footer within an article"],
["a footer within an article"]],
expectedBraille: [
[{"string": "footerAbbr"}, "a footer within an article"],
["a footer within an article", {"string": "footerAbbr"}]]
["a footer within an article"],
["a footer within an article"]]
}, {
accOrElmOrID: "section_header",
expectedUtterance: [[{"string": "header"}, "a header within a section"],
["a header within a section", {"string": "header"}]],
expectedUtterance: [["a header within a section"],
["a header within a section"]],
expectedBraille: [
[{"string": "headerAbbr"}, "a header within a section"],
["a header within a section", {"string": "headerAbbr"}]]
["a header within a section"],
["a header within a section"]]
}, {
accOrElmOrID: "section_footer",
expectedUtterance: [
[{"string": "footer"}, "a footer within a section"],
["a footer within a section", {"string": "footer"}]],
["a footer within a section"],
["a footer within a section"]],
expectedBraille: [
[{"string": "footerAbbr"}, "a footer within a section"],
["a footer within a section", {"string": "footerAbbr"}]]
["a footer within a section"],
["a footer within a section"]]
}, {
accOrElmOrID: "aside",
expectedUtterance: [

Просмотреть файл

@ -32,9 +32,9 @@
// Check that landmark elements get accessibles with styled overflow.
testRole("section_overflow", ROLE_SECTION);
testRole("nav_overflow", ROLE_SECTION);
testRole("header_overflow", ROLE_HEADER);
testRole("header_overflow", ROLE_SECTION);
testRole("aside_overflow", ROLE_NOTE);
testRole("footer_overflow", ROLE_FOOTER);
testRole("footer_overflow", ROLE_SECTION);
testRole("article_overflow", ROLE_ARTICLE);
// test html:div

Просмотреть файл

@ -460,6 +460,7 @@ pref("browser.tabs.loadInBackground", true);
pref("browser.tabs.opentabfor.middleclick", true);
pref("browser.tabs.loadDivertedInBackground", false);
pref("browser.tabs.loadBookmarksInBackground", false);
pref("browser.tabs.loadBookmarksInTabs", false);
pref("browser.tabs.tabClipWidth", 140);
#ifdef UNIX_BUT_NOT_MAC
pref("browser.tabs.drawInTitlebar", false);

Просмотреть файл

@ -27,7 +27,7 @@ function onUnload(aEvent) {
}
function appUpdater() {
function appUpdater(options = {}) {
XPCOMUtils.defineLazyServiceGetter(this, "aus",
"@mozilla.org/updates/update-service;1",
"nsIApplicationUpdateService");
@ -38,6 +38,7 @@ function appUpdater() {
"@mozilla.org/updates/update-manager;1",
"nsIUpdateManager");
this.options = options;
this.updateDeck = document.getElementById("updateDeck");
// Hide the update deck when the update window is already open and it's not
@ -186,9 +187,15 @@ appUpdater.prototype =
button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion], 1);
button.accessKey = this.bundle.GetStringFromName("update.downloadAndInstallButton.accesskey");
}
this.updateDeck.selectedPanel = panel;
if (this.options.buttonAutoFocus &&
(!document.commandDispatcher.focusedElement || // don't steal the focus
document.commandDispatcher.focusedElement.localName == "button")) { // except from the other buttons
button.focus();
}
} else {
this.updateDeck.selectedPanel = panel;
}
this.updateDeck.selectedPanel = panel;
},
/**

Просмотреть файл

@ -64,13 +64,7 @@ function init(aEvent) {
}
if (AppConstants.MOZ_UPDATER) {
gAppUpdater = new appUpdater();
let button = gAppUpdater.updateDeck.selectedPanel.querySelector("button");
if (button && (!document.commandDispatcher.focusedElement || // don't steal the focus
document.commandDispatcher.focusedElement.localName == "button")) { // except from the other buttons
button.focus();
}
gAppUpdater = new appUpdater({ buttonAutoFocus: true });
let channelLabel = document.getElementById("currentChannel");
let currentChannelText = document.getElementById("currentChannelText");

Просмотреть файл

@ -667,6 +667,7 @@ html|input.urlbar-input[textoverflow]:not([focused]) {
#urlbar[pageproxystate=invalid] > #page-action-buttons > .urlbar-page-action,
#identity-box.chromeUI ~ #page-action-buttons > .urlbar-page-action,
#urlbar[usertyping] > .urlbar-textbox-container > .urlbar-history-dropmarker,
.urlbar-go-button[pageproxystate="valid"],
.urlbar-go-button:not([parentfocused="true"]),
#urlbar[pageproxystate="invalid"] > #identity-box > #blocked-permissions-container,

Просмотреть файл

@ -1379,10 +1379,6 @@ var gBrowserInit = {
gRemoteControl.updateVisualCue(Marionette.running);
this._uriToLoadPromise.then(uriToLoad => {
gIdentityHandler.initIdentityBlock(uriToLoad);
});
// Wait until chrome is painted before executing code not critical to making the window visible
this._boundDelayedStartup = this._delayedStartup.bind(this);
window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
@ -2775,6 +2771,7 @@ function URLBarSetURI(aURI) {
let isDifferentValidValue = valid && value != gURLBar.value;
gURLBar.value = value;
gURLBar.valueIsTyped = !valid;
gURLBar.removeAttribute("usertyping");
if (isDifferentValidValue) {
gURLBar.selectionStart = gURLBar.selectionEnd = 0;
}
@ -7082,7 +7079,7 @@ var gIdentityHandler = {
* RegExp used to decide if an about url should be shown as being part of
* the browser UI.
*/
_secureInternalUIWhitelist: /^(?:accounts|addons|cache|config|crashes|customizing|downloads|healthreport|home|license|newaddon|permissions|preferences|privatebrowsing|rights|searchreset|sessionrestore|support|welcomeback)(?:[?#]|$)/i,
_secureInternalUIWhitelist: /^(?:accounts|addons|cache|config|crashes|customizing|downloads|healthreport|license|newaddon|permissions|preferences|rights|searchreset|sessionrestore|support|welcomeback)(?:[?#]|$)/i,
get _isBroken() {
return this._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
@ -7712,14 +7709,6 @@ var gIdentityHandler = {
},
setURI(uri) {
// Ignore about:blank loads until the window's initial URL has loaded,
// to avoid hiding the UI that initIdentityBlock could have prepared.
if (this._ignoreAboutBlankUntilFirstLoad) {
if (uri.spec == "about:blank")
return;
this._ignoreAboutBlankUntilFirstLoad = false;
}
this._uri = uri;
try {
@ -7753,30 +7742,6 @@ var gIdentityHandler = {
}
},
/**
* Used to initialize the identity block before first paint to avoid
* flickering when opening a new window showing a secure internal page
* (eg. about:home)
*/
initIdentityBlock(initialURI) {
if (this._uri) {
// Apparently we already loaded something, so there's nothing to do here.
return;
}
if ((typeof initialURI != "string") || !initialURI.startsWith("about:"))
return;
let uri = Services.io.newURI(initialURI);
if (this._secureInternalUIWhitelist.test(uri.pathQueryRef)) {
this._isSecureInternalUI = true;
this._ignoreAboutBlankUntilFirstLoad = true;
this.refreshIdentityBlock();
// The identity label won't be visible without setting this.
gURLBar.setAttribute("pageproxystate", "valid");
}
},
/**
* Click handler for the identity-box element in primary chrome.
*/

Просмотреть файл

@ -729,6 +729,7 @@
// Only animate the "burst" indicating the page has loaded if
// the top-level page is the one that finished loading.
if (aWebProgress.isTopLevel && !aWebProgress.isLoadingDocument &&
Components.isSuccessCode(aStatus) &&
!this.mTabBrowser.tabAnimationsInProgress &&
Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
this.mTab.setAttribute("bursting", "true");

Просмотреть файл

@ -528,7 +528,7 @@ add_task(async function() {
await BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, async function(browser) {
info("Waiting for about:addons tab to open...");
let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons");
let promiseTabLoaded = BrowserTestUtils.browserLoaded(browser, false, "about:addons");
await ContentTask.spawn(browser, null, async function() {
let addOnsButton = content.document.getElementById("addons");
@ -536,10 +536,9 @@ add_task(async function() {
});
await BrowserTestUtils.synthesizeKey(" ", {}, browser);
let tab = await promiseTabOpened;
is(tab.linkedBrowser.currentURI.spec, "about:addons",
await promiseTabLoaded;
is(browser.currentURI.spec, "about:addons",
"Should have seen the about:addons tab");
await BrowserTestUtils.removeTab(tab);
});
});

Просмотреть файл

@ -2,10 +2,10 @@
/**
* Tests that the identity-box shows the chromeUI styling
* when viewing about:home in a new window.
* when viewing such a page in a new window.
*/
add_task(async function() {
let homepage = "about:home";
let homepage = "about:preferences";
await SpecialPowers.pushPrefEnv({
"set": [
["browser.startup.homepage", homepage],

Просмотреть файл

@ -129,8 +129,6 @@ var whitelist = [
{file: "resource://gre/modules/Localization.jsm"},
// Starting from here, files in the whitelist are bugs that need fixing.
// Bug 1339420
{file: "chrome://branding/content/icon128.png"},
// Bug 1339424 (wontfix?)
{file: "chrome://browser/locale/taskbar.properties",
platforms: ["linux", "macosx"]},

Просмотреть файл

@ -1300,6 +1300,11 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
this._value = this.inputField.value;
gBrowser.userTypedValue = this.value;
this.valueIsTyped = true;
if (this.inputField.value) {
this.setAttribute("usertyping", "true");
} else {
this.removeAttribute("usertyping");
}
// Only wait for a result when we are sure to get one. In some
// cases, like when pasting the same exact text, we may not fire
// a new search and we won't get a result.

Просмотреть файл

@ -43,7 +43,9 @@ var gBidiUI = false;
* Determines whether the given url is considered a special URL for new tabs.
*/
function isBlankPageURL(aURL) {
return aURL == "about:blank" || aURL == BROWSER_NEW_TAB_URL;
return aURL == "about:blank" ||
aURL == "about:home" ||
aURL == BROWSER_NEW_TAB_URL;
}
function getBrowserURL() {

Просмотреть файл

@ -40,6 +40,8 @@ let gFaviconLoadDataMap = new Map();
// copied from utilityOverlay.js
const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
const PREF_LOAD_BOOKMARKS_IN_BACKGROUND = "browser.tabs.loadBookmarksInBackground";
const PREF_LOAD_BOOKMARKS_IN_TABS = "browser.tabs.loadBookmarksInTabs";
// This function isn't public both because it's synchronous and because it is
// going to be removed in bug 1072833.
@ -54,25 +56,37 @@ function IsLivemark(aItemId) {
self.ids = new Set(idsVec);
let obs = Object.freeze({
QueryInterface: XPCOMUtils.generateQI(Ci.nsIAnnotationObserver),
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarksObserver]),
onItemAnnotationSet(itemId, annoName) {
if (annoName == LIVEMARK_ANNO)
// Ci.nsINavBookmarkObserver items.
onItemChanged(itemId, property, isAnnoProperty, newValue, lastModified,
itemType, parentId, guid) {
if (isAnnoProperty && property == LIVEMARK_ANNO) {
self.ids.add(itemId);
}
},
onItemAnnotationRemoved(itemId, annoName) {
// If annoName is set to an empty string, the item is gone.
if (annoName == LIVEMARK_ANNO || annoName == "")
self.ids.delete(itemId);
onItemRemoved(itemId) {
// Since the bookmark is removed, we know we can remove any references
// to it from the cache.
self.ids.delete(itemId);
},
onItemAdded() {},
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onItemVisited() {},
onItemMoved() {},
onPageAnnotationSet() { },
onPageAnnotationRemoved() { },
skipDescendantsOnItemRemoval: false,
skipTags: false,
});
PlacesUtils.annotations.addObserver(obs);
PlacesUtils.bookmarks.addObserver(obs);
PlacesUtils.registerShutdownFunction(() => {
PlacesUtils.annotations.removeObserver(obs);
PlacesUtils.bookmarks.removeObserver(obs);
});
}
return self.ids.has(aItemId);
@ -1018,7 +1032,14 @@ this.PlacesUIUtils = {
openNodeWithEvent:
function PUIU_openNodeWithEvent(aNode, aEvent) {
let window = aEvent.target.ownerGlobal;
this._openNodeIn(aNode, window.whereToOpenLink(aEvent, false, true), window);
let where = window.whereToOpenLink(aEvent, false, true);
if (where == "current" && this.loadBookmarksInTabs &&
PlacesUtils.nodeIsBookmark(aNode) && !aNode.uri.startsWith("javascript:")) {
where = "tab";
}
this._openNodeIn(aNode, where, window);
},
/**
@ -1031,7 +1052,7 @@ this.PlacesUIUtils = {
this._openNodeIn(aNode, aWhere, window, aPrivate);
},
_openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aWindow, aPrivate = false) {
_openNodeIn: function PUIU__openNodeIn(aNode, aWhere, aWindow, aPrivate = false) {
if (aNode && PlacesUtils.nodeIsURI(aNode) &&
this.checkURLSecurity(aNode, aWindow)) {
let isBookmark = PlacesUtils.nodeIsBookmark(aNode);
@ -1058,7 +1079,7 @@ this.PlacesUIUtils = {
aWindow.openUILinkIn(aNode.uri, aWhere, {
allowPopups: aNode.uri.startsWith("javascript:"),
inBackground: Services.prefs.getBoolPref("browser.tabs.loadBookmarksInBackground"),
inBackground: this.loadBookmarksInBackground,
private: aPrivate,
});
}
@ -1525,6 +1546,11 @@ XPCOMUtils.defineLazyGetter(PlacesUIUtils, "useAsyncTransactions", function() {
return false;
});
XPCOMUtils.defineLazyPreferenceGetter(PlacesUIUtils, "loadBookmarksInBackground",
PREF_LOAD_BOOKMARKS_IN_BACKGROUND, false);
XPCOMUtils.defineLazyPreferenceGetter(PlacesUIUtils, "loadBookmarksInTabs",
PREF_LOAD_BOOKMARKS_IN_TABS, false);
XPCOMUtils.defineLazyServiceGetter(this, "URIFixup",
"@mozilla.org/docshell/urifixup;1",
"nsIURIFixup");

Просмотреть файл

@ -12,17 +12,8 @@ support-files =
keyword_form.html
[browser_0_library_left_pane_migration.js]
[browser_410196_paste_into_tags.js]
subsuite = clipboard
[browser_416459_cut.js]
subsuite = clipboard
[browser_423515.js]
[browser_425884.js]
[browser_435851_copy_query.js]
subsuite = clipboard
[browser_475045.js]
[browser_555547.js]
[browser_addBookmarkForFrame.js]
[browser_bookmark_folder_moveability.js]
[browser_bookmarklet_windowOpen.js]
support-files =
pageopeningwindow.html
@ -33,6 +24,13 @@ support-files =
[browser_bookmarkProperties_editTagContainer.js]
[browser_bookmarkProperties_readOnlyRoot.js]
[browser_bookmarksProperties.js]
[browser_check_correct_controllers.js]
[browser_click_bookmarks_on_toolbar.js]
[browser_cutting_bookmarks.js]
subsuite = clipboard
[browser_copy_folder_tree.js]
[browser_copy_query_without_tree.js]
subsuite = clipboard
[browser_drag_bookmarks_on_toolbar.js]
[browser_forgetthissite_single.js]
[browser_history_sidebar_search.js]
@ -51,9 +49,12 @@ support-files =
[browser_library_search.js]
[browser_library_views_liveupdate.js]
[browser_markPageAsFollowedLink.js]
[browser_paste_into_tags.js]
subsuite = clipboard
[browser_sidebarpanels_click.js]
skip-if = true # temporarily disabled for breaking the treeview - bug 658744
[browser_sort_in_library.js]
[browser_toolbar_drop_text.js]
[browser_toolbarbutton_menu_context.js]
[browser_views_iconsupdate.js]
support-files =

Просмотреть файл

@ -0,0 +1,130 @@
/* 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/. */
const PREF_LOAD_BOOKMARKS_IN_TABS = "browser.tabs.loadBookmarksInTabs";
const TEST_PAGES = ["about:mozilla", "about:robots"];
var gBookmarkElements = [];
function getToolbarNodeForItemGuid(aItemGuid) {
var children = document.getElementById("PlacesToolbarItems").childNodes;
for (let child of children) {
if (aItemGuid == child._placesNode.bookmarkGuid) {
return child;
}
}
return null;
}
function waitForLoad(browser, url) {
return BrowserTestUtils.browserLoaded(browser, false, url).then(() => {
return BrowserTestUtils.loadURI(browser, "about:blank");
});
}
function waitForNewTab(url, inBackground) {
return BrowserTestUtils.waitForNewTab(gBrowser, url).then(tab => {
if (inBackground) {
Assert.notEqual(tab,
gBrowser.selectedTab, `The new tab is in the background`);
} else {
Assert.equal(tab,
gBrowser.selectedTab, `The new tab is in the foreground`);
}
return BrowserTestUtils.removeTab(tab);
})
}
add_task(async function setup() {
let bookmarks = await Promise.all(TEST_PAGES.map((url, index) => {
return PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
title: `Title ${index}`,
url
});
}));
let toolbar = document.getElementById("PersonalToolbar");
let wasCollapsed = toolbar.collapsed;
if (wasCollapsed) {
await promiseSetToolbarVisibility(toolbar, true);
}
for (let bookmark of bookmarks) {
let element = getToolbarNodeForItemGuid(bookmark.guid);
Assert.notEqual(element, null, "Found node on toolbar");
gBookmarkElements.push(element);
}
registerCleanupFunction(async () => {
gBookmarkElements = [];
if (wasCollapsed) {
await promiseSetToolbarVisibility(toolbar, false);
}
await Promise.all(bookmarks.map(bookmark => {
return PlacesUtils.bookmarks.remove(bookmark);
}));
});
});
add_task(async function click() {
let promise = waitForLoad(gBrowser.selectedBrowser, TEST_PAGES[0]);
EventUtils.synthesizeMouseAtCenter(gBookmarkElements[0], {
button: 0
});
await promise;
promise = waitForNewTab(TEST_PAGES[1], false);
EventUtils.synthesizeMouseAtCenter(gBookmarkElements[1], {
button: 0, accelKey: true
});
await promise;
});
add_task(async function middleclick() {
let promise = waitForNewTab(TEST_PAGES[0], true);
EventUtils.synthesizeMouseAtCenter(gBookmarkElements[0], {
button: 1, shiftKey: true
});
await promise;
promise = waitForNewTab(TEST_PAGES[1], false);
EventUtils.synthesizeMouseAtCenter(gBookmarkElements[1], {
button: 1
});
await promise;
});
add_task(async function clickWithPrefSet() {
await SpecialPowers.pushPrefEnv({set: [
[PREF_LOAD_BOOKMARKS_IN_TABS, true]
]})
let promise = waitForNewTab(TEST_PAGES[0], false);
EventUtils.synthesizeMouseAtCenter(gBookmarkElements[0], {
button: 0
});
await promise;
let placesContext = document.getElementById("placesContext");
promise = BrowserTestUtils.waitForEvent(placesContext, "popupshown");
EventUtils.synthesizeMouseAtCenter(gBookmarkElements[1], {
button: 2,
type: "contextmenu"
});
await promise;
promise = waitForLoad(gBrowser.selectedBrowser, TEST_PAGES[1]);
let open = document.getElementById("placesContext_open");
EventUtils.synthesizeMouseAtCenter(open, {
button: 0
});
await promise;
await SpecialPowers.popPrefEnv();
});

Просмотреть файл

@ -65,10 +65,10 @@ function startTest() {
// TOOLBAR
ok(true, "*** Acting on toolbar bookmarks");
bs.insertBookmark(bs.toolbarFolder,
PlacesUtils._uri("http://tb1.mozilla.org/"),
bs.DEFAULT_INDEX,
"tb1");
id = bs.insertBookmark(bs.toolbarFolder,
PlacesUtils._uri("http://tb1.mozilla.org/"),
bs.DEFAULT_INDEX,
"tb1");
bs.setItemTitle(id, "tb1_edited");
addedBookmarks.push(id);
id = bs.insertBookmark(bs.toolbarFolder,

Просмотреть файл

@ -24,7 +24,6 @@ add_task(async function test() {
let placesItems = document.getElementById("PlacesToolbarItems");
ok(placesItems, "PlacesToolbarItems should not be null");
ok(placesItems.localName == "scrollbox", "PlacesToolbarItems should not be null");
ok(placesItems.childNodes[0], "PlacesToolbarItems must have at least one child");
/**
* Simulates a drop of a URI onto the bookmarks bar.
@ -39,7 +38,12 @@ add_task(async function test() {
let promiseItemAddedNotification = promiseBookmarksNotification(
"onItemAdded", (itemId, parentId, index, type, uri, guid) => uri.spec == url);
EventUtils.synthesizeDrop(placesItems.childNodes[0],
// We use the toolbar as the drag source, as we just need almost any node
// to simulate the drag. The actual data for the drop is passed via the
// drag data. Note: The toolbar is used rather than another bookmark node,
// as we need something that is immovable from a places perspective, as this
// forces the move into a copy.
EventUtils.synthesizeDrop(toolbar,
placesItems,
[[{type: aMimeType,
data: url}]],
@ -80,7 +84,8 @@ add_task(async function test() {
let promiseItemAddedNotification = promiseBookmarksNotification(
"onItemAdded", (itemId, parentId, index, type, uri, guid) => uri.spec == urls[2]);
EventUtils.synthesizeDrop(placesItems.childNodes[0],
// See notes for EventUtils.synthesizeDrop in simulateDragDrop().
EventUtils.synthesizeDrop(toolbar,
placesItems,
[[{type: aMimeType,
data}]],

Просмотреть файл

@ -0,0 +1,40 @@
"use strict";
let {IsLivemark} = Cu.import("resource:///modules/PlacesUIUtils.jsm", {});
add_task(function test_livemark_cache_builtin_folder() {
// This test checks a basic livemark, and also initializes the observer for
// updates to the bookmarks.
Assert.ok(!IsLivemark(PlacesUtils.unfiledBookmarksFolderId),
"unfiled bookmarks should not be seen as a livemark");
});
add_task(async function test_livemark_add_and_remove_items() {
let bookmark = await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
title: "Grandsire",
url: "http://example.com",
});
let bookmarkId = await PlacesUtils.promiseItemId(bookmark.guid);
Assert.ok(!IsLivemark(bookmarkId),
"a simple bookmark should not be seen as a livemark");
let livemark = await PlacesUtils.livemarks.addLivemark({
title: "Stedman",
feedURI: Services.io.newURI("http://livemark.com/"),
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
});
let livemarkId = await PlacesUtils.promiseItemId(livemark.guid);
Assert.ok(IsLivemark(livemarkId),
"a livemark should be reported as a livemark");
// Now remove the livemark.
await PlacesUtils.livemarks.removeLivemark(livemark);
Assert.ok(!IsLivemark(livemarkId),
"the livemark should have been removed from the cache");
});

Просмотреть файл

@ -21,4 +21,5 @@ support-files =
[test_browserGlue_urlbar_defaultbehavior_migration.js]
[test_clearHistory_shutdown.js]
[test_leftpane_corruption_handling.js]
[test_PUIU_livemarksCache.js]
[test_PUIU_makeTransaction.js]

Просмотреть файл

@ -767,16 +767,16 @@ var gMainPane = {
updateBrowserStartupLastSession() {
let pbAutoStartPref = document.getElementById("browser.privatebrowsing.autostart");
let startupPref = document.getElementById("browser.startup.page");
let menu = document.getElementById("browserStartupPage");
let group = document.getElementById("browserStartupPage");
let option = document.getElementById("browserStartupLastSession");
if (pbAutoStartPref.value) {
option.setAttribute("disabled", "true");
if (option.selected) {
menu.selectedItem = document.getElementById("browserStartupHomePage");
group.selectedItem = document.getElementById("browserStartupHomePage");
}
} else {
option.removeAttribute("disabled");
startupPref.updateElements(); // select the correct index in the startup menulist
startupPref.updateElements(); // select the correct radio in the startup group
}
},

Просмотреть файл

@ -321,74 +321,67 @@
<label id="isDefaultLabel" flex="1">&isDefault.label;</label>
</hbox>
</deck>
<separator class="thin"/>
</vbox>
#endif
<html:table id="startupTable">
<html:tr>
<html:td class="label-cell">
<label accesskey="&startupPage2.accesskey;"
control="browserStartupPage">&startupPage2.label;</label>
</html:td>
<html:td class="content-cell">
<menulist id="browserStartupPage"
class="content-cell-item"
preference="browser.startup.page">
<menupopup>
<menuitem label="&startupUserHomePage.label;"
value="1"
id="browserStartupHomePage"/>
<menuitem label="&startupBlankPage.label;"
value="0"
id="browserStartupBlank"/>
<menuitem label="&startupPrevSession.label;"
value="3"
id="browserStartupLastSession"/>
</menupopup>
</menulist>
</html:td>
</html:tr>
<html:tr class="tableGroup">
<html:td class="label-cell">
<label accesskey="&homepage2.accesskey;"
control="browserHomePage">&homepage2.label;</label>
</html:td>
<html:td class="content-cell">
<textbox id="browserHomePage"
class="padded uri-element content-cell-item"
type="autocomplete"
autocompletesearch="unifiedcomplete"
onsyncfrompreference="return gMainPane.syncFromHomePref();"
onsynctopreference="return gMainPane.syncToHomePref(this.value);"
placeholder="&abouthome.pageTitle;"
preference="browser.startup.homepage"/>
</html:td>
</html:tr>
<html:tr class="tableSubGroup">
<html:td class="label-cell" />
<html:td class="content-cell homepage-buttons">
<button id="useCurrent"
class="content-cell-item"
label=""
accesskey="&useCurrentPage.accesskey;"
label1="&useCurrentPage.label;"
label2="&useMultiple.label;"
preference="pref.browser.homepage.disable_button.current_page"/>
<button id="useBookmark"
class="content-cell-item"
label="&chooseBookmark.label;"
accesskey="&chooseBookmark.accesskey;"
preference="pref.browser.homepage.disable_button.bookmark_page"
searchkeywords="&selectBookmark.title; &selectBookmark.label;"/>
<button id="restoreDefaultHomePage"
class="content-cell-item"
label="&restoreDefault.label;"
accesskey="&restoreDefault.accesskey;"
preference="pref.browser.homepage.disable_button.restore_default"/>
</html:td>
</html:tr>
</html:table>
<vbox id="startupPageBox">
<label accesskey="&startupPage2.accesskey;"
control="browserStartupPage">&startupPage2.label;</label>
<radiogroup id="browserStartupPage"
preference="browser.startup.page">
<radio label="&startupUserHomePage.label;"
value="1"
id="browserStartupHomePage"/>
<radio label="&startupBlankPage.label;"
value="0"
id="browserStartupBlank"/>
<radio label="&startupPrevSession.label;"
value="3"
id="browserStartupLastSession"/>
</radiogroup>
</vbox>
</groupbox>
<!-- Home Page -->
<groupbox id="homepageGroup"
data-category="paneGeneral"
hidden="true">
<caption><label>&homepage2.label;</label></caption>
<vbox>
<textbox id="browserHomePage"
class="uri-element"
type="autocomplete"
autocompletesearch="unifiedcomplete"
onsyncfrompreference="return gMainPane.syncFromHomePref();"
onsynctopreference="return gMainPane.syncToHomePref(this.value);"
placeholder="&abouthome.pageTitle;"
preference="browser.startup.homepage"/>
</vbox>
<hbox class="homepage-buttons">
<button id="useCurrent"
flex="1"
class="homepage-button"
label=""
accesskey="&useCurrentPage.accesskey;"
label1="&useCurrentPage.label;"
label2="&useMultiple.label;"
preference="pref.browser.homepage.disable_button.current_page"/>
<button id="useBookmark"
flex="1"
class="homepage-button"
label="&chooseBookmark.label;"
accesskey="&chooseBookmark.accesskey;"
preference="pref.browser.homepage.disable_button.bookmark_page"
searchkeywords="&selectBookmark.title; &selectBookmark.label;"/>
<button id="restoreDefaultHomePage"
flex="1"
class="homepage-button"
label="&restoreDefault.label;"
accesskey="&restoreDefault.accesskey;"
preference="pref.browser.homepage.disable_button.restore_default"/>
</hbox>
</groupbox>
<!-- Tab preferences -->

Просмотреть файл

@ -1102,10 +1102,17 @@ var gPrivacyPane = {
let malware = malwareTable.value
.split(",")
.filter(x => x !== "goog-unwanted-shavar" && x !== "test-unwanted-simple");
.filter(x => x !== "goog-unwanted-proto" &&
x !== "goog-unwanted-shavar" &&
x !== "test-unwanted-simple");
if (blockUncommonUnwanted.checked) {
malware.push("goog-unwanted-shavar");
if (malware.indexOf("goog-malware-shavar") != -1) {
malware.push("goog-unwanted-shavar");
} else {
malware.push("goog-unwanted-proto");
}
malware.push("test-unwanted-simple");
}

Просмотреть файл

@ -12,7 +12,7 @@ add_task(async function() {
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
await evaluateSearchResults("Set Home Page", "startupGroup");
await evaluateSearchResults("Set Home Page", "homepageGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

Просмотреть файл

@ -130,6 +130,7 @@ add_task(async function() {
let child = mainPrefTag.children[i]
if (child.id == "paneGeneral"
|| child.id == "startupGroup"
|| child.id == "homepageGroup"
|| child.id == "languagesGroup"
|| child.id == "fontsGroup"
|| child.id == "downloadsGroup"

Просмотреть файл

@ -59,11 +59,19 @@ add_task(async function() {
await checkPrefSwitch(false);
});
requestLongerTimeout(2);
// test the unwanted/uncommon software warning preference
add_task(async function() {
async function checkPrefSwitch(val1, val2) {
async function checkPrefSwitch(val1, val2, isV2) {
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", val1);
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.block_uncommon", val2);
let testMalwareTable = "goog-malware-" + (isV2 ? "shavar" : "proto");
testMalwareTable += ",test-malware-simple";
if (val1 && val2) {
testMalwareTable += ",goog-unwanted-" + (isV2 ? "shavar" : "proto");
testMalwareTable += ",test-unwanted-simple";
}
Services.prefs.setCharPref("urlclassifier.malwareTable", testMalwareTable);
gBrowser.reload();
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
@ -85,8 +93,13 @@ add_task(async function() {
// when the preference is on, the malware table should include these ids
let malwareTable = Services.prefs.getCharPref("urlclassifier.malwareTable").split(",");
is(malwareTable.includes("goog-unwanted-shavar"), !checked,
"malware table doesn't include goog-unwanted-shavar");
if (isV2) {
is(malwareTable.includes("goog-unwanted-shavar"), !checked,
"malware table doesn't include goog-unwanted-shavar");
} else {
is(malwareTable.includes("goog-unwanted-proto"), !checked,
"malware table doesn't include goog-unwanted-proto");
}
is(malwareTable.includes("test-unwanted-simple"), !checked,
"malware table doesn't include test-unwanted-simple");
let sortedMalware = malwareTable.slice(0);
@ -95,8 +108,13 @@ add_task(async function() {
}
await checkPrefSwitch(true, true);
await checkPrefSwitch(false, true);
await checkPrefSwitch(true, false);
await checkPrefSwitch(false, false);
await checkPrefSwitch(true, true, false);
await checkPrefSwitch(false, true, false);
await checkPrefSwitch(true, false, false);
await checkPrefSwitch(false, false, false);
await checkPrefSwitch(true, true, true);
await checkPrefSwitch(false, true, true);
await checkPrefSwitch(true, false, true);
await checkPrefSwitch(false, false, true);
});

Просмотреть файл

@ -199,10 +199,17 @@ var gSecurityPane = {
let malware = malwareTable.value
.split(",")
.filter(x => x !== "goog-unwanted-shavar" && x !== "test-unwanted-simple");
.filter(x => x !== "goog-unwanted-proto" &&
x !== "goog-unwanted-shavar" &&
x !== "test-unwanted-simple");
if (blockUncommonUnwanted.checked) {
malware.push("goog-unwanted-shavar");
if (malware.indexOf("goog-malware-shavar") != -1) {
malware.push("goog-unwanted-shavar");
} else {
malware.push("goog-unwanted-proto");
}
malware.push("test-unwanted-simple");
}

Просмотреть файл

@ -97,11 +97,19 @@ add_task(async function() {
await checkPrefSwitch(false);
});
requestLongerTimeout(2);
// test the unwanted/uncommon software warning preference
add_task(async function() {
async function checkPrefSwitch(val1, val2) {
async function checkPrefSwitch(val1, val2, isV2) {
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", val1);
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.block_uncommon", val2);
let testMalwareTable = "goog-malware-" + (isV2 ? "shavar" : "proto");
testMalwareTable += ",test-malware-simple";
if (val1 && val2) {
testMalwareTable += ",goog-unwanted-" + (isV2 ? "shavar" : "proto");
testMalwareTable += ",test-unwanted-simple";
}
Services.prefs.setCharPref("urlclassifier.malwareTable", testMalwareTable);
gBrowser.reload();
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
@ -122,8 +130,13 @@ add_task(async function() {
// when the preference is on, the malware table should include these ids
let malwareTable = Services.prefs.getCharPref("urlclassifier.malwareTable").split(",");
is(malwareTable.includes("goog-unwanted-shavar"), !checked,
"malware table doesn't include goog-unwanted-shavar");
if (isV2) {
is(malwareTable.includes("goog-unwanted-shavar"), !checked,
"malware table doesn't include goog-unwanted-shavar");
} else {
is(malwareTable.includes("goog-unwanted-proto"), !checked,
"malware table doesn't include goog-unwanted-proto");
}
is(malwareTable.includes("test-unwanted-simple"), !checked,
"malware table doesn't include test-unwanted-simple");
let sortedMalware = malwareTable.slice(0);
@ -131,8 +144,13 @@ add_task(async function() {
Assert.deepEqual(malwareTable, sortedMalware, "malware table has been sorted");
}
await checkPrefSwitch(true, true);
await checkPrefSwitch(false, true);
await checkPrefSwitch(true, false);
await checkPrefSwitch(false, false);
await checkPrefSwitch(true, true, false);
await checkPrefSwitch(false, true, false);
await checkPrefSwitch(true, false, false);
await checkPrefSwitch(false, false, false);
await checkPrefSwitch(true, true, true);
await checkPrefSwitch(false, true, true);
await checkPrefSwitch(true, false, true);
await checkPrefSwitch(false, false, true);
});

Просмотреть файл

@ -126,9 +126,16 @@ AutofillProfileAutoCompleteSearch.prototype = {
return;
}
let collectionName = isAddressField ? ADDRESSES_COLLECTION_NAME : CREDITCARDS_COLLECTION_NAME;
let infoWithoutElement = Object.assign({}, info);
delete infoWithoutElement.elementWeakRef;
this._getRecords({collectionName, info, searchString}).then((records) => {
let data = {
collectionName: isAddressField ? ADDRESSES_COLLECTION_NAME : CREDITCARDS_COLLECTION_NAME,
info: infoWithoutElement,
searchString,
};
this._getRecords(data).then((records) => {
if (this.forceStop) {
return;
}

Просмотреть файл

@ -58,6 +58,9 @@ this.FormAutofillUtils = {
"tel-extension": "tel",
"email": "email",
"cc-name": "creditCard",
"cc-given-name": "creditCard",
"cc-additional-name": "creditCard",
"cc-family-name": "creditCard",
"cc-number": "creditCard",
"cc-exp-month": "creditCard",
"cc-exp-year": "creditCard",

Просмотреть файл

@ -69,6 +69,7 @@
* cc-given-name,
* cc-additional-name,
* cc-family-name,
* cc-exp,
*
* // metadata
* timeCreated, // in ms
@ -190,6 +191,7 @@ const VALID_CREDIT_CARD_COMPUTED_FIELDS = [
"cc-given-name",
"cc-additional-name",
"cc-family-name",
"cc-exp",
];
const INTERNAL_FIELDS = [

Просмотреть файл

@ -4,8 +4,6 @@
form {
justify-content: center;
/* Add extra space to ensure invalid input box is displayed properly */
padding: 2px;
}
form > label,

Просмотреть файл

@ -15,6 +15,8 @@ p {
form {
flex-wrap: wrap;
/* Add extra space to ensure invalid input box is displayed properly */
padding: 2px;
}
form > label,

Просмотреть файл

@ -128,11 +128,13 @@ add_task(async function test_getAll() {
// Check computed fields.
do_check_eq(creditCards[0]["cc-given-name"], "John");
do_check_eq(creditCards[0]["cc-family-name"], "Doe");
do_check_eq(creditCards[0]["cc-exp"], "2017-04");
// Test with rawData set.
creditCards = profileStorage.creditCards.getAll({rawData: true});
do_check_eq(creditCards[0]["cc-given-name"], undefined);
do_check_eq(creditCards[0]["cc-family-name"], undefined);
do_check_eq(creditCards[0]["cc-exp"], undefined);
// Modifying output shouldn't affect the storage.
creditCards[0]["cc-name"] = "test";

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

До

Ширина:  |  Высота:  |  Размер: 6.0 KiB

Просмотреть файл

@ -32,6 +32,8 @@
border: none;
/* Set to none so no grey contrast background in the high-contrast mode */
background: none;
/* make sure the icon stay above the activity-stream searchbar */
z-index: 10;
}
/* Keyboard focus styling */
@ -43,27 +45,28 @@
}
#onboarding-overlay-button-icon {
width: 36px;
width: 32px;
vertical-align: top;
}
#onboarding-overlay-button::after {
background: #5ce6e6;
font-size: 12px;
border: 1px solid #fff;
background: #0060df;
font-size: 13px;
text-align: center;
color: #10404a;
color: #fff;
box-sizing: content-box;
font-weight: 400;
content: attr(aria-label);
display: inline-block;
offset-inline-start: 39px;
border-radius: 22px;
padding: 5px 12px;
border: 1px solid transparent;
border-radius: 2px;
padding: 10px 16px;
min-width: 100px;
max-width: 140px;
white-space: pre-line;
margin-inline-start: 3px;
margin-top: -5px;
margin-inline-start: 4px;
margin-top: -10px;
box-shadow: -2px 0 5px 0 rgba(74, 74, 79, 0.25);
}
#onboarding-overlay-dialog,
@ -86,7 +89,7 @@
}
.onboarding-close-btn::before {
content: url(chrome://global/skin/icons/close.svg);
content: url("chrome://global/skin/icons/close.svg");
-moz-context-properties: fill, fill-opacity;
fill-opacity: 0;
}
@ -307,14 +310,14 @@
}
.onboarding-tour-action-button {
padding: 10px 20px;
font-size: 15px;
font-weight: 600;
line-height: 21px;
background: #0a84ff;
background: #0060df;
/* With 1px transparent border, could see a border in the high-constrast mode */
border: 1px solid transparent;
border-radius: 0;
border-radius: 2px;
padding: 10px 20px;
font-size: 14px;
font-weight: 600;
line-height: 16px;
color: #fff;
float: inline-end;
margin-inline-end: 26px;
@ -337,12 +340,12 @@
}
.onboarding-tour-action-button:hover:not([disabled]) {
background: #0060df;
background: #003eaa;
cursor: pointer;
}
.onboarding-tour-action-button:active:not([disabled]) {
background: #003EAA;
background: #002275;
}
.onboarding-tour-action-button:disabled {
@ -532,3 +535,9 @@
#onboarding-notification-action-btn:active {
background-color: #dadada;
}
@media (min-resolution: 2dppx) {
#onboarding-notification-tour-icon {
background-image: url("chrome://branding/content/icon128.png");
}
}

Просмотреть файл

@ -457,13 +457,15 @@ class Onboarding {
switch (id) {
case "onboarding-overlay-button":
this.showOverlay();
this.gotoPage(this.selectedTour.id);
break;
case "onboarding-overlay-close-btn":
// If the clicking target is directly on the outer-most overlay,
// that means clicking outside the tour content area.
// Let's toggle the overlay.
case "onboarding-overlay":
this.toggleOverlay();
this.gotoPage(this.selectedTour.id);
this.hideOverlay();
break;
case "onboarding-notification-close-btn":
this.hideNotification();
@ -471,7 +473,7 @@ class Onboarding {
break;
case "onboarding-notification-action-btn":
let tourId = this._notificationBar.dataset.targetTourId;
this.toggleOverlay();
this.showOverlay();
this.gotoPage(tourId);
this._removeTourFromNotificationQueue(tourId);
break;
@ -564,7 +566,7 @@ class Onboarding {
event.preventDefault();
break;
case "Escape":
this.toggleOverlay();
this.hideOverlay();
break;
case "Tab":
let next = this.wrapMoveFocus(target, shiftKey);
@ -615,20 +617,22 @@ class Onboarding {
this._overlayIcon = this._overlay = this._notificationBar = null;
}
toggleOverlay() {
showOverlay() {
if (this._tourItems.length == 0) {
// Lazy loading until first toggle.
this._loadTours(this._tours);
}
this.hideNotification();
this._overlay.classList.toggle("onboarding-opened");
this.toggleModal(this._overlay.classList.contains("onboarding-opened"));
this.toggleModal(this._overlay.classList.toggle("onboarding-opened"));
}
hideOverlay() {
let hiddenCheckbox = this._window.document.getElementById("onboarding-tour-hidden-checkbox");
if (hiddenCheckbox.checked) {
this.hide();
}
this.toggleModal(this._overlay.classList.toggle("onboarding-opened"));
}
/**
@ -991,7 +995,7 @@ class Onboarding {
let img = this._window.document.createElement("img");
img.id = "onboarding-overlay-button-icon";
img.setAttribute("role", "presentation");
img.src = "resource://onboarding/img/overlay-icon.svg";
img.src = "chrome://branding/content/icon64.png";
button.appendChild(img);
return button;
}

Просмотреть файл

@ -10,6 +10,7 @@ skip-if = debug || os == "mac" # Full keyboard navigation on OSX only works if F
[browser_onboarding_notification_2.js]
[browser_onboarding_notification_3.js]
[browser_onboarding_notification_4.js]
[browser_onboarding_notification_click_auto_complete_tour.js]
[browser_onboarding_select_default_tour.js]
[browser_onboarding_tours.js]
[browser_onboarding_tourset.js]

Просмотреть файл

@ -0,0 +1,33 @@
add_task(async function test_show_click_auto_complete_tour_in_notification() {
resetOnboardingDefaultState();
skipMuteNotificationOnFirstSession();
// the second tour is an click-auto-complete tour
await SpecialPowers.pushPrefEnv({set: [
["browser.onboarding.newtour", "customize,library"],
]});
let tab = await openTab(ABOUT_NEWTAB_URL);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
// Trigger CTA button to mark the tour as complete
let expectedPrefUpdates = [
promisePrefUpdated(`browser.onboarding.tour.onboarding-tour-customize.completed`, true),
]
BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-customize", {}, tab.linkedBrowser);
BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-customize-button", {}, tab.linkedBrowser);
await Promise.all(expectedPrefUpdates);
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-close-btn", {}, gBrowser.selectedBrowser);
let { activeNavItemId } = await getCurrentActiveTour(tab.linkedBrowser);
is("onboarding-tour-customize", activeNavItemId, "the active tour should be the previous shown tour");
await reloadTab(tab);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
await promiseTourNotificationOpened(tab.linkedBrowser);
let targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
is("onboarding-tour-library", targetTourId, "correctly show the click-autocomplete-tour in notification");
await BrowserTestUtils.removeTab(tab);
});

Просмотреть файл

@ -28,7 +28,7 @@ let WebCompatReporter = {
init() {
PageActions.addAction(new PageActions.Action({
id: "webcompat-reporter-button",
title: wcStrings.GetStringFromName("wc-reporter.label"),
title: wcStrings.GetStringFromName("wc-reporter.label2"),
iconURL: "chrome://webcompat-reporter/skin/lightbulb.svg",
onCommand: (e) => this.reportIssue(e.target.ownerGlobal),
onShowingInPanel: (buttonNode) => this.onShowingInPanel(buttonNode)

Просмотреть файл

@ -2,10 +2,9 @@
# 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/.
# LOCALIZATION NOTE(wc-reporter.label): This string will be used in the
# Firefox menu panel below its button. Localized length should be considered.
# \u00ad is included at the beginning of the string to disable auto-hyphens.
wc-reporter.label=\u00adReport Site Issue
# LOCALIZATION NOTE(wc-reporter.label2): This string will be used in the
# Firefox page actions menu. Localized length should be considered.
wc-reporter.label2=Report Site Issue…
# LOCALIZATION NOTE(wc-reporter.tooltip): A site compatibility issue is
# a website bug that exists in one browser (Firefox), but not another.
wc-reporter.tooltip=Report a site compatibility issue

Просмотреть файл

@ -469,17 +469,6 @@ notification[value="translation"] menulist > .menulist-dropmarker {
border-top: 1px solid ThreeDShadow;
}
.autocomplete-richlistitem:hover,
treechildren.searchbar-treebody::-moz-tree-row(hover) {
background-color: var(--arrowpanel-dimmed);
border-color: var(--panel-separator-color);
}
.autocomplete-richlistitem[selected],
treechildren.searchbar-treebody::-moz-tree-row(selected) {
background-color: Highlight;
}
.ac-title {
font-size: 1.05em;
}

Просмотреть файл

@ -425,18 +425,6 @@
border-top: 1px solid #C7C7C7;
}
.autocomplete-richlistitem:hover,
treechildren.searchbar-treebody::-moz-tree-row(hover) {
background-color: var(--arrowpanel-dimmed);
border-color: var(--panel-separator-color);
}
.autocomplete-richlistitem[selected],
treechildren.searchbar-treebody::-moz-tree-row(selected) {
background-color: Highlight;
color: HighlightText;
}
.ac-title {
font-size: 14px;
}

Просмотреть файл

@ -154,60 +154,21 @@ separator.thin:not([orient="vertical"]) {
}
/* General Pane */
#startupGroup {
margin-top: 0px !important;
}
#startupTable {
margin-top: 32px;
margin-inline-end: -4px;
border-collapse: collapse;
}
#startupTable > tr > td {
padding: 0; /* remove the padding from html.css */
}
#startupTable > .tableGroup > td {
#startupPageBox {
padding-top: 32px;
}
#startupTable > .tableSubGroup > td {
padding-top: 8px;
#browserHomePage {
margin-inline-start: 0;
margin-inline-end: 0;
}
#startupTable > tr > .label-cell {
text-align: end;
width: 0; /* make the column as small as possible */
.homepage-button:first-of-type {
margin-inline-start: 0;
}
#startupTable > tr > .content-cell:not(:first-child) {
padding-inline-start: 8px;
}
#startupTable > tr > .label-cell > label {
white-space: nowrap;
}
#startupTable > tr > .content-cell > menulist,
#startupTable > tr > .content-cell > textbox {
width: calc(100% - 8px);
margin-left: 4px;
margin-right: 4px;
}
#startupTable > tr > .homepage-buttons {
display: flex;
flex-wrap: wrap;
}
#startupTable > tr > .homepage-buttons > .content-cell-item {
flex-grow: 1;
}
.content-cell-item {
margin: 2px 4px;
.homepage-button:last-of-type {
margin-inline-end: 0;
}
#getStarted {

Просмотреть файл

@ -9,16 +9,27 @@
}
.autocomplete-richlistbox {
padding: 4px;
padding: 4px 3px;
}
.autocomplete-richlistitem {
min-height: 30px;
font: message-box;
border-radius: 2px;
border: 1px solid transparent;
}
:root[uidensity=touch] .autocomplete-richlistitem {
min-height: 40px;
}
.autocomplete-richlistitem:hover,
treechildren.searchbar-treebody::-moz-tree-row(hover) {
background-color: var(--arrowpanel-dimmed);
}
.autocomplete-richlistitem[selected],
treechildren.searchbar-treebody::-moz-tree-row(selected) {
background-color: Highlight;
color: HighlightText;
}

Просмотреть файл

@ -678,18 +678,6 @@ html|span.ac-emphasize-text-url {
}
}
.autocomplete-richlistitem:hover,
treechildren.searchbar-treebody::-moz-tree-row(hover) {
background-color: var(--arrowpanel-dimmed);
border-color: var(--panel-separator-color);
}
.autocomplete-richlistitem[selected],
treechildren.searchbar-treebody::-moz-tree-row(selected) {
background-color: Highlight;
color: HighlightText;
}
.ac-type-icon[type=bookmark] {
list-style-image: url("chrome://browser/skin/bookmark.svg");
-moz-context-properties: fill;

Просмотреть файл

@ -241,10 +241,6 @@ function tunnelToInnerBrowser(outer, inner) {
let { detail } = event;
event.preventDefault();
let uri = Services.io.newURI(detail.url);
let sourceNode = event.dataTransfer.mozSourceNode;
let triggeringPrincipal = sourceNode
? sourceNode.nodePrincipal
: Services.scriptSecurityManager.getSystemPrincipal();
// This API is used mainly because it's near the path used for <a target/> with
// regular browser tabs (which calls `openURIInFrame`). The more elaborate APIs
// that support openers, window features, etc. didn't seem callable from JS and / or
@ -252,7 +248,7 @@ function tunnelToInnerBrowser(outer, inner) {
browserWindow.browserDOMWindow
.openURI(uri, null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
Ci.nsIBrowserDOMWindow.OPEN_NEW,
triggeringPrincipal);
outer.contentPrincipal);
},
stop() {

Просмотреть файл

@ -40,6 +40,7 @@ tags = geolocation
[browser_screenshot_button.js]
[browser_tab_close.js]
[browser_tab_remoteness_change.js]
[browser_target_blank.js]
[browser_toolbox_computed_view.js]
[browser_toolbox_rule_view.js]
[browser_toolbox_swap_browsers.js]

Просмотреть файл

@ -0,0 +1,26 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Ensure target="_blank" link opens a new tab
const TAB_URL = "http://example.com/";
const TEST_URL =
`data:text/html,<a href="${TAB_URL}" target="_blank">Click me</a>`
.replace(/ /g, "%20");
addRDMTask(TEST_URL, function* ({ ui }) {
let store = ui.toolWindow.store;
// Wait until the viewport has been added
yield waitUntilState(store, state => state.viewports.length == 1);
// Click the target="_blank" link and wait for a new tab
yield waitForFrameLoad(ui, TEST_URL);
let newTab = BrowserTestUtils.waitForNewTab(gBrowser, TAB_URL);
spawnViewportTask(ui, {}, function* () {
content.document.querySelector("a").click(); // eslint-disable-line
});
ok(yield newTab, "New tab opened from link");
});

Просмотреть файл

@ -68,6 +68,12 @@ function rotate3dToMatrix(x, y, z, radian) {
];
}
test(function(t) {
var target = addDiv(t);
var dist = getDistance(target, 'transform', 'none', 'none');
assert_equals(dist, 0, 'distance of translate');
}, 'Test distance of none and none');
test(function(t) {
var target = addDiv(t);
var dist = getDistance(target, 'transform', 'translate(100px)', 'none');
@ -304,5 +310,39 @@ test(function(t) {
'distance of transform lists');
}, 'Test distance of transform lists');
test(function(t) {
var target = addDiv(t);
var dist = getDistance(target, 'transform',
'translate(100px) rotate(180deg)',
'translate(50px) rotate(90deg) scale(5) skew(1rad)');
assert_approx_equals(dist,
Math.sqrt(50 * 50 +
Math.PI / 2 * Math.PI / 2 +
4 * 4 * 2 +
1 * 1),
epsilon,
'distance of transform lists');
}, 'Test distance of transform lists where one has extra items');
test(function(t) {
var target = addDiv(t);
var dist = getDistance(target, 'transform',
'translate(1000px) rotate3d(1, 0, 0, 180deg)',
'translate(1000px) scale3d(2.5, 0.5, 1)');
assert_equals(dist, Math.sqrt(Math.PI * Math.PI + 1.5 * 1.5 + 0.5 * 0.5),
'distance of transform lists');
}, 'Test distance of mismatched transform lists');
test(function(t) {
var target = addDiv(t);
var dist = getDistance(target, 'transform',
'translate(100px) skew(1rad)',
'translate(1000px) rotate3d(0, 1, 0, -2rad)');
assert_approx_equals(dist,
Math.sqrt(900 * 900 + 1 * 1 + 2 * 2),
epsilon,
'distance of transform lists');
}, 'Test distance of mismatched transform lists with skew function');
</script>
</html>

Просмотреть файл

@ -449,10 +449,12 @@ private:
* to an nsIChannel, which holds a reference to this listener.
* We break the reference cycle in OnStartRequest by clearing mElement.
*/
class HTMLMediaElement::MediaLoadListener final : public nsIStreamListener,
public nsIChannelEventSink,
public nsIInterfaceRequestor,
public nsIObserver
class HTMLMediaElement::MediaLoadListener final
: public nsIStreamListener
, public nsIChannelEventSink
, public nsIInterfaceRequestor
, public nsIObserver
, public nsIThreadRetargetableStreamListener
{
~MediaLoadListener() {}
@ -462,6 +464,7 @@ class HTMLMediaElement::MediaLoadListener final : public nsIStreamListener,
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIOBSERVER
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
public:
explicit MediaLoadListener(HTMLMediaElement* aElement)
@ -477,9 +480,13 @@ private:
const uint32_t mLoadID;
};
NS_IMPL_ISUPPORTS(HTMLMediaElement::MediaLoadListener, nsIRequestObserver,
nsIStreamListener, nsIChannelEventSink,
nsIInterfaceRequestor, nsIObserver)
NS_IMPL_ISUPPORTS(HTMLMediaElement::MediaLoadListener,
nsIRequestObserver,
nsIStreamListener,
nsIChannelEventSink,
nsIInterfaceRequestor,
nsIObserver,
nsIThreadRetargetableStreamListener)
NS_IMETHODIMP
HTMLMediaElement::MediaLoadListener::Observe(nsISupports* aSubject,
@ -621,6 +628,18 @@ HTMLMediaElement::MediaLoadListener::AsyncOnChannelRedirect(nsIChannel* aOldChan
return NS_OK;
}
NS_IMETHODIMP
HTMLMediaElement::MediaLoadListener::CheckListenerChain()
{
MOZ_ASSERT(mNextListener);
nsCOMPtr<nsIThreadRetargetableStreamListener> retargetable =
do_QueryInterface(mNextListener);
if (retargetable) {
return retargetable->CheckListenerChain();
}
return NS_ERROR_NO_INTERFACE;
}
NS_IMETHODIMP
HTMLMediaElement::MediaLoadListener::GetInterface(const nsIID& aIID,
void** aResult)

Просмотреть файл

@ -25,6 +25,7 @@
#include "nsGkAtoms.h"
#include "PrincipalChangeObserver.h"
#include "nsStubMutationObserver.h"
#include "MediaSegment.h" // for PrincipalHandle
// X.h on Linux #defines CurrentTime as 0L, so we have to #undef it here.
#ifdef CurrentTime

Просмотреть файл

@ -5,7 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AudioNotificationReceiver.h"
#include "AudioStream.h" // for AudioStream
#include "mozilla/Logging.h" // for LazyLogModule
#include "mozilla/StaticMutex.h" // for StaticMutex
#include "mozilla/StaticPtr.h" // for StaticAutoPtr
@ -25,41 +24,43 @@ namespace audio {
/*
* A list containing all clients subscribering the device-changed notifications.
*/
static StaticAutoPtr<nsTArray<AudioStream*>> sSubscribers;
static StaticAutoPtr<nsTArray<DeviceChangeListener*>> sSubscribers;
static StaticMutex sMutex;
/*
* AudioNotificationReceiver Implementation
*/
/* static */ void
AudioNotificationReceiver::Register(AudioStream* aAudioStream)
AudioNotificationReceiver::Register(DeviceChangeListener* aDeviceChangeListener)
{
MOZ_ASSERT(XRE_IsContentProcess());
StaticMutexAutoLock lock(sMutex);
if (!sSubscribers) {
sSubscribers = new nsTArray<AudioStream*>();
sSubscribers = new nsTArray<DeviceChangeListener*>();
}
sSubscribers->AppendElement(aAudioStream);
sSubscribers->AppendElement(aDeviceChangeListener);
ANR_LOG("The AudioStream: %p is registered successfully.", aAudioStream);
ANR_LOG("The DeviceChangeListener: %p is registered successfully.",
aDeviceChangeListener);
}
/* static */ void
AudioNotificationReceiver::Unregister(AudioStream* aAudioStream)
AudioNotificationReceiver::Unregister(DeviceChangeListener* aDeviceChangeListener)
{
MOZ_ASSERT(XRE_IsContentProcess());
StaticMutexAutoLock lock(sMutex);
MOZ_ASSERT(!sSubscribers->IsEmpty(), "No subscriber.");
sSubscribers->RemoveElement(aAudioStream);
sSubscribers->RemoveElement(aDeviceChangeListener);
if (sSubscribers->IsEmpty()) {
// Clear the static pointer here to prevent memory leak.
sSubscribers = nullptr;
}
ANR_LOG("The AudioStream: %p is unregistered successfully.", aAudioStream);
ANR_LOG("The DeviceChangeListener: %p is unregistered successfully.",
aDeviceChangeListener);
}
/* static */ void
@ -69,13 +70,14 @@ AudioNotificationReceiver::NotifyDefaultDeviceChanged()
StaticMutexAutoLock lock(sMutex);
// Do nothing when there is no AudioStream.
// Do nothing when there is no DeviceChangeListener.
if (!sSubscribers) {
return;
}
for (AudioStream* stream : *sSubscribers) {
ANR_LOG("Notify the AudioStream: %p that the default device has been changed.", stream);
for (DeviceChangeListener* stream : *sSubscribers) {
ANR_LOG("Notify the DeviceChangeListener: %p "
"that the default device has been changed.", stream);
stream->ResetDefaultDevice();
}
}

Просмотреть файл

@ -13,7 +13,7 @@
* Chrome Process ContentProcess 1
* ------------------ ------------------
*
* AudioNotification AudioStream 1 AudioStream N
* AudioNotification DeviceChangeListener 1 DeviceChangeListener N
* | ^ | ^ ^
* (4)| |(2) |(3) |(8) .
* v | v | v
@ -27,7 +27,7 @@
* . | |
* . | |
* . +------------------------------------+
* .
* . PContent IPC
* .
* . Content Process M
* . ------------------
@ -41,7 +41,7 @@
* 1) Initailize the AudioNotificationSender when ContentParent is created.
* 2) Create an AudioNotification to get the device-changed signal
* from the system.
* 3) Register the AudioStream to AudioNotificationReceiver when it's created.
* 3) Register the DeviceChangeListener to AudioNotificationReceiver when it's created.
* 4) When the default device is changed, AudioNotification get the signal and
* 5) Pass this message by AudioNotificationSender.
* 6) The AudioNotificationSender sends the device-changed notification via
@ -54,27 +54,37 @@
* a) There is only one AudioNotificationSender and AudioNotification
* in a chrome process.
* b) There is only one AudioNotificationReceiver and might be many
* AudioStreams in a content process.
* DeviceChangeListeners in a content process.
* c) There might be many ContentParent in a chrome process.
* d) There is only one ContentChild in a content process.
* e) All the Audiostreams are registered in the AudioNotificationReceiver.
* e) All the DeviceChangeListeners are registered in the AudioNotificationReceiver.
* f) All the ContentParents are registered in the AudioNotificationSender.
*/
namespace mozilla {
class AudioStream;
namespace audio {
// The base class that provides a ResetDefaultDevice interface that
// will be called in AudioNotificationReceiver::NotifyDefaultDeviceChanged
// when it receives device-changed notification from the chrome process.
class DeviceChangeListener
{
protected:
virtual ~DeviceChangeListener() {};
public:
// The subclass shoule provide its own implementation switching the
// audio stream to the new default output device.
virtual void ResetDefaultDevice() = 0;
};
class AudioNotificationReceiver final
{
public:
// Add the AudioStream into the subscribers list.
static void Register(AudioStream* aAudioStream);
// Add the DeviceChangeListener into the subscribers list.
static void Register(DeviceChangeListener* aDeviceChangeListener);
// Remove the AudioStream from the subscribers list.
static void Unregister(AudioStream* aAudioStream);
// Remove the DeviceChangeListener from the subscribers list.
static void Unregister(DeviceChangeListener* aDeviceChangeListener);
// Notify all the streams that the default device has been changed.
static void NotifyDefaultDeviceChanged();

Просмотреть файл

@ -233,6 +233,18 @@ struct AudioChunk {
(&mChannelData);
}
/**
* ChannelFloatsForWrite() should be used only when mBuffer is owned solely
* by the calling thread.
*/
template<typename T>
T* ChannelDataForWrite(size_t aChannel)
{
MOZ_ASSERT(AudioSampleTypeToFormat<T>::Format == mBufferFormat);
MOZ_ASSERT(!mBuffer->IsShared());
return static_cast<T*>(const_cast<void*>(mChannelData[aChannel]));
}
PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
StreamTime mDuration; // in frames within the buffer

Просмотреть файл

@ -19,9 +19,6 @@
#include "nsPrintfCString.h"
#include "gfxPrefs.h"
#include "AudioConverter.h"
#if defined(XP_WIN)
#include "mozilla/audio/AudioNotificationReceiver.h"
#endif
namespace mozilla {
@ -475,6 +472,7 @@ AudioStream::Shutdown()
mState = SHUTDOWN;
}
#if defined(XP_WIN)
void
AudioStream::ResetDefaultDevice()
{
@ -489,6 +487,7 @@ AudioStream::ResetDefaultDevice()
mState = ERRORED;
}
}
#endif
int64_t
AudioStream::GetPosition()

Просмотреть файл

@ -17,6 +17,10 @@
#include "CubebUtils.h"
#include "soundtouch/SoundTouchFactory.h"
#if defined(XP_WIN)
#include "mozilla/audio/AudioNotificationReceiver.h"
#endif
namespace mozilla {
struct CubebDestroyPolicy
@ -150,6 +154,9 @@ public:
// GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels},
// SetMicrophoneActive is thread-safe without external synchronization.
class AudioStream final
#if defined(XP_WIN)
: public audio::DeviceChangeListener
#endif
{
virtual ~AudioStream();
@ -210,8 +217,10 @@ public:
// Resume audio playback.
void Resume();
// Reset stream to default device.
void ResetDefaultDevice();
#if defined(XP_WIN)
// Reset stream to the default device.
void ResetDefaultDevice() override;
#endif
// Return the position in microseconds of the audio frame being played by
// the audio hardware, compensated for playback rate change. Thread-safe.

Просмотреть файл

@ -16,6 +16,7 @@
#include "mozilla/AbstractThread.h"
#include "mozilla/Preferences.h"
#include "mozilla/SharedThreadPool.h"
#include "mozilla/SystemGroup.h"
#include "mozilla/TaskQueue.h"
#include "mozilla/Telemetry.h"
#include "mozilla/dom/ContentChild.h"

Просмотреть файл

@ -112,6 +112,9 @@ uint32_t sCubebMSGLatencyInFrames;
bool sCubebPlaybackLatencyPrefSet;
bool sCubebMSGLatencyPrefSet;
bool sAudioStreamInitEverSucceeded = false;
#ifdef MOZ_CUBEB_REMOTING
bool sCubebSandbox;
#endif
StaticAutoPtr<char> sBrandName;
StaticAutoPtr<char> sCubebBackendName;
@ -249,11 +252,11 @@ void PrefChanged(const char* aPref, void* aClosure)
}
#ifdef MOZ_CUBEB_REMOTING
else if (strcmp(aPref, PREF_CUBEB_SANDBOX) == 0) {
bool cubebSandbox = false;
Preferences::GetBool(aPref, cubebSandbox);
MOZ_LOG(gCubebLog, LogLevel::Verbose, ("%s: %s", PREF_CUBEB_SANDBOX, cubebSandbox ? "true" : "false"));
StaticMutexAutoLock lock(sMutex);
sCubebSandbox = Preferences::GetBool(aPref);
MOZ_LOG(gCubebLog, LogLevel::Verbose, ("%s: %s", PREF_CUBEB_SANDBOX, sCubebSandbox ? "true" : "false"));
if (cubebSandbox && !sServerHandle && XRE_IsParentProcess()) {
if (sCubebSandbox && !sServerHandle && XRE_IsParentProcess()) {
MOZ_LOG(gCubebLog, LogLevel::Debug, ("Starting cubeb server..."));
StartSoundServer();
}
@ -390,11 +393,9 @@ cubeb* GetCubebContextUnlocked()
}
#ifdef MOZ_CUBEB_REMOTING
bool cubebSandbox = false;
Preferences::GetBool(PREF_CUBEB_SANDBOX, cubebSandbox);
MOZ_LOG(gCubebLog, LogLevel::Info, ("%s: %s", PREF_CUBEB_SANDBOX, cubebSandbox ? "true" : "false"));
MOZ_LOG(gCubebLog, LogLevel::Info, ("%s: %s", PREF_CUBEB_SANDBOX, sCubebSandbox ? "true" : "false"));
int rv = cubebSandbox
int rv = sCubebSandbox
? audioipc_client_init(&sCubebContext, sBrandName)
: cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
#else // !MOZ_CUBEB_REMOTING

Просмотреть файл

@ -13,6 +13,7 @@
#include <algorithm>
#include "nsAnonymousTemporaryFile.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/SystemGroup.h"
#include "nsXULAppAPI.h"
namespace mozilla {

Просмотреть файл

@ -574,11 +574,21 @@ AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl)
, mFromFallback(false)
{
LOG(LogLevel::Debug, ("AudioCallbackDriver ctor for graph %p", aGraphImpl));
#if defined(XP_WIN)
if (XRE_IsContentProcess()) {
audio::AudioNotificationReceiver::Register(this);
}
#endif
}
AudioCallbackDriver::~AudioCallbackDriver()
{
MOZ_ASSERT(mPromisesForOperation.IsEmpty());
#if defined(XP_WIN)
if (XRE_IsContentProcess()) {
audio::AudioNotificationReceiver::Unregister(this);
}
#endif
}
bool IsMacbookOrMacbookAir()
@ -857,6 +867,16 @@ AudioCallbackDriver::WakeUp()
mGraphImpl->GetMonitor().Notify();
}
#if defined(XP_WIN)
void
AudioCallbackDriver::ResetDefaultDevice()
{
if (cubeb_stream_reset_default_device(mAudioStream) != CUBEB_OK) {
NS_WARNING("Could not reset cubeb stream to default output device.");
}
}
#endif
/* static */ long
AudioCallbackDriver::DataCallback_s(cubeb_stream* aStream,
void* aUser,

Просмотреть файл

@ -15,6 +15,10 @@
#include "mozilla/SharedThreadPool.h"
#include "mozilla/StaticPtr.h"
#if defined(XP_WIN)
#include "mozilla/audio/AudioNotificationReceiver.h"
#endif
struct cubeb_stream;
template <>
@ -376,6 +380,9 @@ enum AsyncCubebOperation {
*/
class AudioCallbackDriver : public GraphDriver,
public MixerCallbackReceiver
#if defined(XP_WIN)
, public audio::DeviceChangeListener
#endif
{
public:
explicit AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl);
@ -389,6 +396,9 @@ public:
void RemoveCallback() override;
void WaitForNextIteration() override;
void WakeUp() override;
#if defined(XP_WIN)
void ResetDefaultDevice() override;
#endif
/* Static wrapper function cubeb calls back. */
static long DataCallback_s(cubeb_stream * aStream,

Просмотреть файл

@ -13,7 +13,7 @@
#include "nsTArray.h"
#include "ImageTypes.h"
#include "MediaData.h"
#include "StreamTracks.h" // for TrackID
#include "TrackID.h" // for TrackID
#include "TimeUnits.h"
#include "mozilla/gfx/Point.h" // for gfx::IntSize
#include "mozilla/gfx/Rect.h" // for gfx::IntRect

Просмотреть файл

@ -122,8 +122,11 @@ ChannelMediaResource::~ChannelMediaResource()
// a new listener, so notifications from the old channel are discarded
// and don't confuse us.
NS_IMPL_ISUPPORTS(ChannelMediaResource::Listener,
nsIRequestObserver, nsIStreamListener, nsIChannelEventSink,
nsIInterfaceRequestor)
nsIRequestObserver,
nsIStreamListener,
nsIChannelEventSink,
nsIInterfaceRequestor,
nsIThreadRetargetableStreamListener)
nsresult
ChannelMediaResource::Listener::OnStartRequest(nsIRequest* aRequest,
@ -174,7 +177,13 @@ ChannelMediaResource::Listener::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
}
nsresult
ChannelMediaResource::Listener::GetInterface(const nsIID & aIID, void **aResult)
ChannelMediaResource::Listener::CheckListenerChain()
{
return NS_OK;
}
nsresult
ChannelMediaResource::Listener::GetInterface(const nsIID& aIID, void** aResult)
{
return QueryInterface(aIID, aResult);
}

Просмотреть файл

@ -13,6 +13,7 @@
#include "nsIStreamListener.h"
#include "nsIChannelEventSink.h"
#include "nsIInterfaceRequestor.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "Intervals.h"
#include "MediaCache.h"
#include "MediaContainerType.h"
@ -503,9 +504,11 @@ public:
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
class Listener final : public nsIStreamListener,
public nsIInterfaceRequestor,
public nsIChannelEventSink
class Listener final
: public nsIStreamListener
, public nsIInterfaceRequestor
, public nsIChannelEventSink
, public nsIThreadRetargetableStreamListener
{
~Listener() {}
public:
@ -516,6 +519,7 @@ public:
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
void Revoke() { mResource = nullptr; }

Просмотреть файл

@ -14,6 +14,7 @@
namespace mozilla {
class AudioBlockBuffer;
class ThreadSharedFloatArrayBufferList;
/**
* Base class for objects with a thread-safe refcount and a virtual
@ -26,6 +27,10 @@ public:
bool IsShared() { return mRefCnt.get() > 1; }
virtual AudioBlockBuffer* AsAudioBlockBuffer() { return nullptr; };
virtual ThreadSharedFloatArrayBufferList* AsThreadSharedFloatArrayBufferList()
{
return nullptr;
};
virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
@ -53,17 +58,20 @@ class SharedBuffer : public ThreadSharedObject {
public:
void* Data() { return this + 1; }
static already_AddRefed<SharedBuffer> Create(size_t aSize, const fallible_t&)
{
void* m = operator new(AllocSize(aSize), fallible);
if (!m) {
return nullptr;
}
RefPtr<SharedBuffer> p = new (m) SharedBuffer();
return p.forget();
}
static already_AddRefed<SharedBuffer> Create(size_t aSize)
{
CheckedInt<size_t> size = sizeof(SharedBuffer);
size += aSize;
if (!size.isValid()) {
MOZ_CRASH();
}
void* m = moz_xmalloc(size.value());
void* m = operator new(AllocSize(aSize));
RefPtr<SharedBuffer> p = new (m) SharedBuffer();
NS_ASSERTION((reinterpret_cast<char*>(p.get() + 1) - reinterpret_cast<char*>(p.get())) % 4 == 0,
"SharedBuffers should be at least 4-byte aligned");
return p.forget();
}
@ -73,7 +81,22 @@ public:
}
private:
SharedBuffer() {}
static size_t
AllocSize(size_t aDataSize)
{
CheckedInt<size_t> size = sizeof(SharedBuffer);
size += aDataSize;
if (!size.isValid()) {
MOZ_CRASH();
}
return size.value();
}
SharedBuffer()
{
NS_ASSERTION((reinterpret_cast<char*>(this + 1) - reinterpret_cast<char*>(this)) % 4 == 0,
"SharedBuffers should be at least 4-byte aligned");
}
};
} // namespace mozilla

Просмотреть файл

@ -8,23 +8,10 @@
#include "MediaSegment.h"
#include "nsAutoPtr.h"
#include "TrackID.h"
namespace mozilla {
/**
* Unique ID for track within a StreamTracks. Tracks from different
* StreamTrackss may have the same ID; this matters when appending StreamTrackss,
* since tracks with the same ID are matched. Only IDs greater than 0 are allowed.
*/
typedef int32_t TrackID;
const TrackID TRACK_NONE = 0;
const TrackID TRACK_INVALID = -1;
const TrackID TRACK_ANY = -2;
inline bool IsTrackIDExplicit(const TrackID& aId) {
return aId > TRACK_NONE;
}
inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate,
TrackRate aInRate,
TrackTicks aTicks)

27
dom/media/TrackID.h Normal file
Просмотреть файл

@ -0,0 +1,27 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_TRACK_ID_H_
#define MOZILLA_TRACK_ID_H_
namespace mozilla {
/**
* Unique ID for track within a StreamTracks. Tracks from different
* StreamTrackss may have the same ID; this matters when appending StreamTrackss,
* since tracks with the same ID are matched. Only IDs greater than 0 are allowed.
*/
typedef int32_t TrackID;
const TrackID TRACK_NONE = 0;
const TrackID TRACK_INVALID = -1;
const TrackID TRACK_ANY = -2;
inline bool IsTrackIDExplicit(const TrackID& aId) {
return aId > TRACK_NONE;
}
} // namespace mozilla
#endif // MOZILLA_TRACK_ID_H_

Просмотреть файл

@ -40,6 +40,7 @@
#include "mozilla/dom/PluginCrashedEvent.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/Attributes.h"
#include "mozilla/SystemGroup.h"
namespace mozilla {

Просмотреть файл

@ -19,6 +19,7 @@
#include "base/task.h"
#include "nsIObserverService.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/SystemGroup.h"
namespace mozilla {

Просмотреть файл

@ -40,6 +40,7 @@
#include "GMPDecoderModule.h"
#include <limits>
#include "MediaPrefs.h"
#include "mozilla/SystemGroup.h"
using mozilla::ipc::Transport;

Просмотреть файл

@ -4,29 +4,83 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "HLSDecoder.h"
#include "AndroidBridge.h"
#include "DecoderTraits.h"
#include "GeneratedJNINatives.h"
#include "GeneratedJNIWrappers.h"
#include "HLSDemuxer.h"
#include "HLSResource.h"
#include "HLSUtils.h"
#include "MediaContainerType.h"
#include "MediaDecoderStateMachine.h"
#include "MediaFormatReader.h"
#include "MediaPrefs.h"
#include "MediaShutdownManager.h"
#include "nsContentUtils.h"
#include "nsNetUtil.h"
using namespace mozilla::java;
namespace mozilla {
class HLSResourceCallbacksSupport
: public GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport>
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSResourceCallbacksSupport)
public:
typedef GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport> NativeCallbacks;
using NativeCallbacks::DisposeNative;
using NativeCallbacks::AttachNative;
HLSResourceCallbacksSupport(HLSDecoder* aResource);
void Detach();
void OnDataArrived();
void OnError(int aErrorCode);
private:
~HLSResourceCallbacksSupport() {}
HLSDecoder* mDecoder;
};
HLSResourceCallbacksSupport::HLSResourceCallbacksSupport(HLSDecoder* aDecoder)
{
MOZ_ASSERT(aDecoder);
mDecoder = aDecoder;
}
void
HLSDecoder::Shutdown()
HLSResourceCallbacksSupport::Detach()
{
MOZ_ASSERT(NS_IsMainThread());
if (mResource) {
mResource->Detach();
mDecoder = nullptr;
}
void
HLSResourceCallbacksSupport::OnDataArrived()
{
MOZ_ASSERT(NS_IsMainThread());
if (mDecoder) {
HLS_DEBUG("HLSResourceCallbacksSupport", "OnDataArrived");
mDecoder->NotifyDataArrived();
}
MediaDecoder::Shutdown();
}
void
HLSResourceCallbacksSupport::OnError(int aErrorCode)
{
MOZ_ASSERT(NS_IsMainThread());
if (mDecoder) {
HLS_DEBUG("HLSResourceCallbacksSupport", "onError(%d)", aErrorCode);
// Since HLS source should be from the Internet, we treat all resource errors
// from GeckoHlsPlayer as network errors.
mDecoder->NetworkError();
}
}
HLSDecoder::HLSDecoder(MediaDecoderInit& aInit)
: MediaDecoder(aInit)
{
}
MediaDecoderStateMachine*
@ -34,16 +88,13 @@ HLSDecoder::CreateStateMachine()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mResource);
auto resourceWrapper = mResource->GetResourceWrapper();
MOZ_ASSERT(resourceWrapper);
MediaFormatReaderInit init;
init.mVideoFrameContainer = GetVideoFrameContainer();
init.mKnowsCompositor = GetCompositor();
init.mCrashHelper = GetOwner()->CreateGMPCrashHelper();
init.mFrameStats = mFrameStats;
mReader =
new MediaFormatReader(init, new HLSDemuxer(resourceWrapper->GetPlayerId()));
new MediaFormatReader(init, new HLSDemuxer(mHLSResourceWrapper->GetPlayerId()));
return new MediaDecoderStateMachine(this, mReader);
}
@ -65,15 +116,22 @@ nsresult
HLSDecoder::Load(nsIChannel* aChannel)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mResource);
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(mURI));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mResource = MakeUnique<HLSResource>(this, aChannel, uri);
mChannel = aChannel;
nsCString spec;
Unused << mURI->GetSpec(spec);;
HLSResourceCallbacksSupport::Init();
mJavaCallbacks = GeckoHLSResourceWrapper::Callbacks::New();
mCallbackSupport = new HLSResourceCallbacksSupport(this);
HLSResourceCallbacksSupport::AttachNative(mJavaCallbacks, mCallbackSupport);
mHLSResourceWrapper = java::GeckoHLSResourceWrapper::Create(NS_ConvertUTF8toUTF16(spec),
mJavaCallbacks);
MOZ_ASSERT(mHLSResourceWrapper);
rv = MediaShutdownManager::Instance().Register(this);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -90,16 +148,20 @@ void
HLSDecoder::AddSizeOfResources(ResourceSizes* aSizes)
{
MOZ_ASSERT(NS_IsMainThread());
if (mResource) {
aSizes->mByteSize += mResource->SizeOfIncludingThis(aSizes->mMallocSizeOf);
}
// TODO: track JAVA wrappers.
}
already_AddRefed<nsIPrincipal>
HLSDecoder::GetCurrentPrincipal()
{
MOZ_ASSERT(NS_IsMainThread());
return mResource ? mResource->GetCurrentPrincipal() : nullptr;
nsCOMPtr<nsIPrincipal> principal;
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
if (!secMan || !mChannel) {
return nullptr;
}
secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(principal));
return principal.forget();
}
nsresult
@ -107,8 +169,7 @@ HLSDecoder::Play()
{
MOZ_ASSERT(NS_IsMainThread());
HLS_DEBUG("HLSDecoder", "MediaElement called Play");
auto resourceWrapper = mResource->GetResourceWrapper();
resourceWrapper->Play();
mHLSResourceWrapper->Play();
return MediaDecoder::Play();
}
@ -117,8 +178,7 @@ HLSDecoder::Pause()
{
MOZ_ASSERT(NS_IsMainThread());
HLS_DEBUG("HLSDecoder", "MediaElement called Pause");
auto resourceWrapper = mResource->GetResourceWrapper();
resourceWrapper->Pause();
mHLSResourceWrapper->Pause();
return MediaDecoder::Pause();
}
@ -126,18 +186,35 @@ void
HLSDecoder::Suspend()
{
MOZ_ASSERT(NS_IsMainThread());
if (mResource) {
mResource->Suspend();
}
HLS_DEBUG("HLSDecoder", "Should suspend the resource fetching.");
mHLSResourceWrapper->Suspend();
}
void
HLSDecoder::Resume()
{
MOZ_ASSERT(NS_IsMainThread());
if (mResource) {
mResource->Resume();
HLS_DEBUG("HLSDecoder", "Should resume the resource fetching.");
mHLSResourceWrapper->Resume();
}
void
HLSDecoder::Shutdown()
{
HLS_DEBUG("HLSDecoder", "Shutdown");
if (mCallbackSupport) {
mCallbackSupport->Detach();
mCallbackSupport = nullptr;
}
if (mHLSResourceWrapper) {
mHLSResourceWrapper->Destroy();
mHLSResourceWrapper = nullptr;
}
if (mJavaCallbacks) {
HLSResourceCallbacksSupport::DisposeNative(mJavaCallbacks);
mJavaCallbacks = nullptr;
}
MediaDecoder::Shutdown();
}
} // namespace mozilla

Просмотреть файл

@ -7,21 +7,17 @@
#ifndef HLSDecoder_h_
#define HLSDecoder_h_
#include "HLSResource.h"
#include "MediaDecoder.h"
namespace mozilla {
class HLSResourceCallbacksSupport;
class HLSDecoder final : public MediaDecoder
{
public:
// MediaDecoder interface.
explicit HLSDecoder(MediaDecoderInit& aInit)
: MediaDecoder(aInit)
{
}
void Shutdown() override;
explicit HLSDecoder(MediaDecoderInit& aInit);
// Returns true if the HLS backend is pref'ed on.
static bool IsEnabled();
@ -42,8 +38,11 @@ public:
bool IsTransportSeekable() override { return true; }
void Suspend() override;
void Resume() override;
void Shutdown() override;
private:
friend class HLSResourceCallbacksSupport;
void PinForSeek() override {}
void UnpinForSeek() override {}
@ -58,7 +57,11 @@ private:
bool IsLiveStream() override final { return false; }
UniquePtr<HLSResource> mResource;
nsCOMPtr<nsIChannel> mChannel;
nsCOMPtr<nsIURI> mURI;
java::GeckoHLSResourceWrapper::GlobalRef mHLSResourceWrapper;
java::GeckoHLSResourceWrapper::Callbacks::GlobalRef mJavaCallbacks;
RefPtr<HLSResourceCallbacksSupport> mCallbackSupport;
};
} // namespace mozilla

Просмотреть файл

@ -1,117 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "HLSDecoder.h"
#include "HLSResource.h"
#include "HLSUtils.h"
using namespace mozilla::java;
namespace mozilla {
HLSResourceCallbacksSupport::HLSResourceCallbacksSupport(HLSResource* aResource)
{
MOZ_ASSERT(aResource);
mResource = aResource;
}
void
HLSResourceCallbacksSupport::Detach()
{
MOZ_ASSERT(NS_IsMainThread());
mResource = nullptr;
}
void
HLSResourceCallbacksSupport::OnDataArrived()
{
MOZ_ASSERT(NS_IsMainThread());
if (mResource) {
mResource->onDataAvailable();
}
}
void
HLSResourceCallbacksSupport::OnError(int aErrorCode)
{
MOZ_ASSERT(NS_IsMainThread());
if (mResource) {
mResource->onError(aErrorCode);
}
}
HLSResource::HLSResource(HLSDecoder* aDecoder,
nsIChannel* aChannel,
nsIURI* aURI)
: mDecoder(aDecoder)
, mChannel(aChannel)
, mURI(aURI)
{
nsCString spec;
nsresult rv = aURI->GetSpec(spec);
(void)rv;
HLSResourceCallbacksSupport::Init();
mJavaCallbacks = GeckoHLSResourceWrapper::Callbacks::New();
mCallbackSupport = new HLSResourceCallbacksSupport(this);
HLSResourceCallbacksSupport::AttachNative(mJavaCallbacks, mCallbackSupport);
mHLSResourceWrapper = java::GeckoHLSResourceWrapper::Create(NS_ConvertUTF8toUTF16(spec),
mJavaCallbacks);
MOZ_ASSERT(mHLSResourceWrapper);
}
void
HLSResource::onDataAvailable()
{
HLS_DEBUG("HLSResource", "onDataAvailable");
if (mDecoder) {
mDecoder->NotifyDataArrived();
}
}
void
HLSResource::onError(int aErrorCode)
{
HLS_DEBUG("HLSResource", "onError(%d)", aErrorCode);
// Since HLS source should be from the Internet, we treat all resource errors
// from GeckoHlsPlayer as network errors.
if (mDecoder) {
mDecoder->NetworkError();
}
}
void
HLSResource::Suspend()
{
MOZ_ASSERT(NS_IsMainThread(), "Don't call on non-main thread");
HLS_DEBUG("HLSResource", "Should suspend the resource fetching.");
mHLSResourceWrapper->Suspend();
}
void HLSResource::Resume()
{
MOZ_ASSERT(NS_IsMainThread(), "Don't call on non-main thread");
HLS_DEBUG("HLSResource", "Should resume the resource fetching.");
mHLSResourceWrapper->Resume();
}
HLSResource::~HLSResource()
{
HLS_DEBUG("HLSResource", "~HLSResource()");
if (mCallbackSupport) {
mCallbackSupport->Detach();
mCallbackSupport = nullptr;
}
if (mHLSResourceWrapper) {
mHLSResourceWrapper->Destroy();
mHLSResourceWrapper = nullptr;
}
if (mJavaCallbacks) {
HLSResourceCallbacksSupport::DisposeNative(mJavaCallbacks);
mJavaCallbacks = nullptr;
}
}
} // namespace mozilla

Просмотреть файл

@ -1,93 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef HLSResource_h_
#define HLSResource_h_
#include "GeneratedJNINatives.h"
#include "GeneratedJNIWrappers.h"
#include "HLSUtils.h"
#include "nsContentUtils.h"
using namespace mozilla::java;
namespace mozilla {
class HLSDecoder;
class HLSResource;
class HLSResourceCallbacksSupport
: public GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport>
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSResourceCallbacksSupport)
public:
typedef GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport> NativeCallbacks;
using NativeCallbacks::DisposeNative;
using NativeCallbacks::AttachNative;
HLSResourceCallbacksSupport(HLSResource* aResource);
void Detach();
void OnDataArrived();
void OnError(int aErrorCode);
private:
~HLSResourceCallbacksSupport() {}
HLSResource* mResource;
};
class HLSResource final
{
public:
HLSResource(HLSDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI);
~HLSResource();
void Suspend();
void Resume();
already_AddRefed<nsIPrincipal> GetCurrentPrincipal()
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
nsCOMPtr<nsIPrincipal> principal;
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
if (!secMan || !mChannel)
return nullptr;
secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(principal));
return principal.forget();
}
java::GeckoHLSResourceWrapper::GlobalRef GetResourceWrapper() {
return mHLSResourceWrapper;
}
void Detach() { mDecoder = nullptr; }
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
// TODO: track JAVA wrappers.
return 0;
}
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
private:
friend class HLSResourceCallbacksSupport;
void onDataAvailable();
void onError(int aErrorCode);
HLSDecoder* mDecoder;
nsCOMPtr<nsIChannel> mChannel;
nsCOMPtr<nsIURI> mURI;
java::GeckoHLSResourceWrapper::GlobalRef mHLSResourceWrapper;
java::GeckoHLSResourceWrapper::Callbacks::GlobalRef mJavaCallbacks;
RefPtr<HLSResourceCallbacksSupport> mCallbackSupport;
};
} // namespace mozilla
#endif /* HLSResource_h_ */

Просмотреть файл

@ -7,14 +7,12 @@
EXPORTS += [
'HLSDecoder.h',
'HLSDemuxer.h',
'HLSResource.h',
'HLSUtils.h',
]
UNIFIED_SOURCES += [
'HLSDecoder.cpp',
'HLSDemuxer.cpp',
'HLSResource.cpp',
'HLSUtils.cpp',
]

Просмотреть файл

@ -17,7 +17,15 @@ sync protocol PVideoDecoderManager
{
manages PVideoDecoder;
parent:
sync PVideoDecoder(VideoInfo info, TextureFactoryIdentifier identifier) returns (bool success);
// aBlacklistedD3D11Driver and aBlacklistedD3D9Driver are used to read back the blacklisted driver information
// from GPU process to content process.
// We should have added a new sync method to read back this information but, in that way, we also introduce one
// more sync IPC call.
// Considering that this information is only used for telemetry usage in bug 1393392 and should be removed once
// we have collected enough data, we add these two return values here for convenience.
sync PVideoDecoder(VideoInfo info, TextureFactoryIdentifier identifier) returns (bool success,
nsCString aBlacklistedD3D11Driver,
nsCString aBlacklistedD3D9Driver);
sync Readback(SurfaceDescriptorGPUVideo sd) returns (SurfaceDescriptor aResult);

Просмотреть файл

@ -6,6 +6,7 @@
#include "VideoDecoderChild.h"
#include "VideoDecoderManagerChild.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/Telemetry.h"
#include "base/thread.h"
#include "MediaInfo.h"
#include "ImageContainer.h"
@ -19,6 +20,24 @@ using namespace ipc;
using namespace layers;
using namespace gfx;
#ifdef XP_WIN
static void
ReportUnblacklistingTelemetry(bool isGPUProcessCrashed,
const nsCString& aD3D11BlacklistedDriver,
const nsCString& aD3D9BlacklistedDriver)
{
const nsCString& blacklistedDLL = !aD3D11BlacklistedDriver.IsEmpty()
? aD3D11BlacklistedDriver
: aD3D9BlacklistedDriver;
if (!blacklistedDLL.IsEmpty()) {
Telemetry::Accumulate(Telemetry::VIDEO_UNBLACKINGLISTING_DXVA_DRIVER_RUNTIME_STATUS,
blacklistedDLL,
isGPUProcessCrashed ? 1 : 0);
}
}
#endif // XP_WIN
VideoDecoderChild::VideoDecoderChild()
: mThread(VideoDecoderManagerChild::GetManagerThread())
, mCanSend(false)
@ -144,6 +163,12 @@ VideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy)
}));
}
mCanSend = false;
#ifdef XP_WIN
ReportUnblacklistingTelemetry(aWhy == AbnormalShutdown,
mBlacklistedD3D11Driver,
mBlacklistedD3D9Driver);
#endif // XP_WIN
}
bool
@ -173,7 +198,9 @@ VideoDecoderChild::InitIPDL(const VideoInfo& aVideoInfo,
mIPDLSelfRef = this;
bool success = false;
if (manager->SendPVideoDecoderConstructor(this, aVideoInfo, aIdentifier,
&success)) {
&success,
&mBlacklistedD3D11Driver,
&mBlacklistedD3D9Driver)) {
mCanSend = true;
}
return success;

Просмотреть файл

@ -79,6 +79,9 @@ private:
// caller.
bool mNeedNewDecoder;
MediaDataDecoder::DecodedData mDecodedData;
nsCString mBlacklistedD3D11Driver;
nsCString mBlacklistedD3D9Driver;
};
} // namespace dom

Просмотреть файл

@ -117,7 +117,9 @@ VideoDecoderManagerChild::GetManagerAbstractThread()
PVideoDecoderChild*
VideoDecoderManagerChild::AllocPVideoDecoderChild(const VideoInfo& aVideoInfo,
const layers::TextureFactoryIdentifier& aIdentifier,
bool* aSuccess)
bool* aSuccess,
nsCString* /* not used */,
nsCString* /* not used */)
{
return new VideoDecoderChild();
}

Просмотреть файл

@ -72,7 +72,9 @@ protected:
PVideoDecoderChild* AllocPVideoDecoderChild(const VideoInfo& aVideoInfo,
const layers::TextureFactoryIdentifier& aIdentifier,
bool* aSuccess) override;
bool* aSuccess,
nsCString* aBlacklistedD3D11Driver,
nsCString* aBlacklistedD3D9Driver) override;
bool DeallocPVideoDecoderChild(PVideoDecoderChild* actor) override;
private:

Просмотреть файл

@ -24,6 +24,12 @@
#endif
namespace mozilla {
#ifdef XP_WIN
extern const nsCString GetFoundD3D11BlacklistedDLL();
extern const nsCString GetFoundD3D9BlacklistedDLL();
#endif // XP_WIN
namespace dom {
using namespace ipc;
@ -193,15 +199,24 @@ VideoDecoderManagerParent::ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyRea
PVideoDecoderParent*
VideoDecoderManagerParent::AllocPVideoDecoderParent(const VideoInfo& aVideoInfo,
const layers::TextureFactoryIdentifier& aIdentifier,
bool* aSuccess)
bool* aSuccess,
nsCString* aBlacklistedD3D11Driver,
nsCString* aBlacklistedD3D9Driver)
{
RefPtr<TaskQueue> decodeTaskQueue = new TaskQueue(
SharedThreadPool::Get(NS_LITERAL_CSTRING("VideoDecoderParent"), 4),
"VideoDecoderParent::mDecodeTaskQueue");
return new VideoDecoderParent(
auto* parent = new VideoDecoderParent(
this, aVideoInfo, aIdentifier,
sManagerTaskQueue, decodeTaskQueue, aSuccess);
#ifdef XP_WIN
*aBlacklistedD3D11Driver = GetFoundD3D11BlacklistedDLL();
*aBlacklistedD3D9Driver = GetFoundD3D9BlacklistedDLL();
#endif // XP_WIN
return parent;
}
bool

Просмотреть файл

@ -31,7 +31,11 @@ public:
bool OnManagerThread();
protected:
PVideoDecoderParent* AllocPVideoDecoderParent(const VideoInfo& aVideoInfo, const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess) override;
PVideoDecoderParent* AllocPVideoDecoderParent(const VideoInfo& aVideoInfo,
const layers::TextureFactoryIdentifier& aIdentifier,
bool* aSuccess,
nsCString* aBlacklistedD3D11Driver,
nsCString* aBlacklistedD3D9Driver) override;
bool DeallocPVideoDecoderParent(PVideoDecoderParent* actor) override;
mozilla::ipc::IPCResult RecvReadback(const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) override;

Просмотреть файл

@ -147,6 +147,7 @@ EXPORTS += [
'StreamTracks.h',
'ThreadPoolCOMListener.h',
'TimeUnits.h',
'TrackID.h',
'TrackUnionStream.h',
'VideoFrameContainer.h',
'VideoLimits.h',

Просмотреть файл

@ -9,6 +9,7 @@
#include "AppleVTLinker.h"
#include "mozilla/ArrayUtils.h"
#include "nsDebug.h"
#include "PlatformDecoderModule.h" // for sPDMLog
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))

Просмотреть файл

@ -410,6 +410,26 @@ FindD3D9BlacklistedDLL()
"media.wmf.disable-d3d9-for-dlls");
}
const nsCString
GetFoundD3D11BlacklistedDLL()
{
if (sD3D11BlacklistingCache) {
return sD3D11BlacklistingCache->mBlacklistedDLL;
}
return nsCString();
}
const nsCString
GetFoundD3D9BlacklistedDLL()
{
if (sD3D9BlacklistingCache) {
return sD3D9BlacklistingCache->mBlacklistedDLL;
}
return nsCString();
}
class CreateDXVAManagerEvent : public Runnable
{
public:
@ -424,12 +444,14 @@ public:
NS_IMETHOD Run() override {
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
const bool deblacklistingForTelemetry =
XRE_IsGPUProcess() && gfxPrefs::PDMWMFDeblacklistingForTelemetryInGPUProcess();
nsACString* failureReason = &mFailureReason;
nsCString secondFailureReason;
if (mBackend == LayersBackend::LAYERS_D3D11 &&
gfxPrefs::PDMWMFAllowD3D11() && IsWin8OrLater()) {
const nsCString& blacklistedDLL = FindD3D11BlacklistedDLL();
if (!blacklistedDLL.IsEmpty()) {
if (!deblacklistingForTelemetry && !blacklistedDLL.IsEmpty()) {
failureReason->AppendPrintf("D3D11 blacklisted with DLL %s",
blacklistedDLL.get());
} else {
@ -446,7 +468,7 @@ public:
}
const nsCString& blacklistedDLL = FindD3D9BlacklistedDLL();
if (!blacklistedDLL.IsEmpty()) {
if (!deblacklistingForTelemetry && !blacklistedDLL.IsEmpty()) {
mFailureReason.AppendPrintf("D3D9 blacklisted with DLL %s",
blacklistedDLL.get());
} else {

Просмотреть файл

@ -160,15 +160,22 @@ AudioBuffer::AudioBuffer(nsPIDOMWindowInner* aWindow,
uint32_t aNumberOfChannels,
uint32_t aLength,
float aSampleRate,
already_AddRefed<ThreadSharedFloatArrayBufferList>
aInitialContents)
ErrorResult& aRv)
: mOwnerWindow(do_GetWeakReference(aWindow)),
mSharedChannels(aInitialContents),
mLength(aLength),
mSampleRate(aSampleRate)
{
MOZ_ASSERT(!mSharedChannels ||
mSharedChannels->GetChannels() == aNumberOfChannels);
// Note that a buffer with zero channels is permitted here for the sake of
// AudioProcessingEvent, where channel counts must match parameters passed
// to createScriptProcessor(), one of which may be zero.
if (aSampleRate < WebAudioUtils::MinSampleRate ||
aSampleRate > WebAudioUtils::MaxSampleRate ||
aNumberOfChannels > WebAudioUtils::MaxChannelCount ||
!aLength || aLength > INT32_MAX) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
mSharedChannels.mDuration = aLength;
mJSChannels.SetLength(aNumberOfChannels);
mozilla::HoldJSObjects(this);
AudioBufferMemoryTracker::RegisterAudioBuffer(this);
@ -204,6 +211,21 @@ AudioBuffer::ClearJSChannels()
mJSChannels.Clear();
}
void
AudioBuffer::SetSharedChannels(
already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer)
{
RefPtr<ThreadSharedFloatArrayBufferList> buffer = aBuffer;
uint32_t channelCount = buffer->GetChannels();
mSharedChannels.mChannelData.SetLength(channelCount);
for (uint32_t i = 0; i < channelCount; ++i) {
mSharedChannels.mChannelData[i] = buffer->GetData(i);
}
mSharedChannels.mBuffer = buffer.forget();
mSharedChannels.mVolume = 1.0f;
mSharedChannels.mBufferFormat = AUDIO_FORMAT_FLOAT32;
}
/* static */ already_AddRefed<AudioBuffer>
AudioBuffer::Create(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels,
uint32_t aLength, float aSampleRate,
@ -211,20 +233,34 @@ AudioBuffer::Create(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels,
aInitialContents,
ErrorResult& aRv)
{
// Note that a buffer with zero channels is permitted here for the sake of
// AudioProcessingEvent, where channel counts must match parameters passed
// to createScriptProcessor(), one of which may be zero.
if (aSampleRate < WebAudioUtils::MinSampleRate ||
aSampleRate > WebAudioUtils::MaxSampleRate ||
aNumberOfChannels > WebAudioUtils::MaxChannelCount ||
!aLength || aLength > INT32_MAX) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
RefPtr<ThreadSharedFloatArrayBufferList> initialContents = aInitialContents;
RefPtr<AudioBuffer> buffer =
new AudioBuffer(aWindow, aNumberOfChannels, aLength, aSampleRate, aRv);
if (aRv.Failed()) {
return nullptr;
}
if (initialContents) {
MOZ_ASSERT(initialContents->GetChannels() == aNumberOfChannels);
buffer->SetSharedChannels(initialContents.forget());
}
return buffer.forget();
}
/* static */ already_AddRefed<AudioBuffer>
AudioBuffer::Create(nsPIDOMWindowInner* aWindow, float aSampleRate,
AudioChunk&& aInitialContents)
{
AudioChunk initialContents = aInitialContents;
ErrorResult rv;
RefPtr<AudioBuffer> buffer =
new AudioBuffer(aWindow, aNumberOfChannels, aLength, aSampleRate,
Move(aInitialContents));
new AudioBuffer(aWindow, initialContents.ChannelCount(),
initialContents.mDuration, aSampleRate, rv);
if (rv.Failed()) {
return nullptr;
}
buffer->mSharedChannels = Move(aInitialContents);
return buffer.forget();
}
@ -235,6 +271,22 @@ AudioBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return AudioBufferBinding::Wrap(aCx, this, aGivenProto);
}
static void
CopyChannelDataToFloat(const AudioChunk& aChunk, uint32_t aChannel,
uint32_t aSrcOffset, float* aOutput, uint32_t aLength)
{
MOZ_ASSERT(aChunk.mVolume == 1.0f);
if (aChunk.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
mozilla::PodCopy(aOutput,
aChunk.ChannelData<float>()[aChannel] + aSrcOffset,
aLength);
} else {
MOZ_ASSERT(aChunk.mBufferFormat == AUDIO_FORMAT_S16);
ConvertAudioSamples(aChunk.ChannelData<int16_t>()[aChannel] + aSrcOffset,
aOutput, aLength);
}
}
bool
AudioBuffer::RestoreJSChannelData(JSContext* aJSContext)
{
@ -248,23 +300,24 @@ AudioBuffer::RestoreJSChannelData(JSContext* aJSContext)
// into it. We could avoid this with additional JS APIs to construct
// an array (or ArrayBuffer) containing initial data.
JS::Rooted<JSObject*> array(aJSContext,
JS_NewFloat32Array(aJSContext, mLength));
JS_NewFloat32Array(aJSContext, Length()));
if (!array) {
return false;
}
if (mSharedChannels) {
if (!mSharedChannels.IsNull()) {
// "4. Attach ArrayBuffers containing copies of the data to the
// AudioBuffer, to be returned by the next call to getChannelData."
const float* data = mSharedChannels->GetData(i);
JS::AutoCheckCannotGC nogc;
bool isShared;
mozilla::PodCopy(JS_GetFloat32ArrayData(array, &isShared, nogc), data, mLength);
float* jsData = JS_GetFloat32ArrayData(array, &isShared, nogc);
MOZ_ASSERT(!isShared); // Was created as unshared above
CopyChannelDataToFloat(mSharedChannels, i, 0, jsData, Length());
}
mJSChannels[i] = array;
}
mSharedChannels = nullptr;
mSharedChannels.mBuffer = nullptr;
mSharedChannels.mChannelData.Clear();
return true;
}
@ -279,35 +332,37 @@ AudioBuffer::CopyFromChannel(const Float32Array& aDestination, uint32_t aChannel
CheckedInt<uint32_t> end = aStartInChannel;
end += length;
if (aChannelNumber >= NumberOfChannels() ||
!end.isValid() || end.value() > mLength) {
!end.isValid() || end.value() > Length()) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
JS::AutoCheckCannotGC nogc;
JSObject* channelArray = mJSChannels[aChannelNumber];
const float* sourceData = nullptr;
if (channelArray) {
if (JS_GetTypedArrayLength(channelArray) != mLength) {
if (JS_GetTypedArrayLength(channelArray) != Length()) {
// The array's buffer was detached.
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
bool isShared = false;
sourceData = JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
const float* sourceData =
JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
// The sourceData arrays should all have originated in
// RestoreJSChannelData, where they are created unshared.
MOZ_ASSERT(!isShared);
} else if (mSharedChannels) {
sourceData = mSharedChannels->GetData(aChannelNumber);
PodMove(aDestination.Data(), sourceData + aStartInChannel, length);
return;
}
if (sourceData) {
PodMove(aDestination.Data(), sourceData + aStartInChannel, length);
} else {
PodZero(aDestination.Data(), length);
if (!mSharedChannels.IsNull()) {
CopyChannelDataToFloat(mSharedChannels, aChannelNumber, aStartInChannel,
aDestination.Data(), length);
return;
}
PodZero(aDestination.Data(), length);
}
void
@ -321,7 +376,7 @@ AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
CheckedInt<uint32_t> end = aStartInChannel;
end += length;
if (aChannelNumber >= NumberOfChannels() ||
!end.isValid() || end.value() > mLength) {
!end.isValid() || end.value() > Length()) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
@ -333,7 +388,7 @@ AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
JS::AutoCheckCannotGC nogc;
JSObject* channelArray = mJSChannels[aChannelNumber];
if (JS_GetTypedArrayLength(channelArray) != mLength) {
if (JS_GetTypedArrayLength(channelArray) != Length()) {
// The array's buffer was detached.
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
@ -373,7 +428,7 @@ AudioBuffer::StealJSArrayDataIntoSharedChannels(JSContext* aJSContext)
// invoker."
for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
JSObject* channelArray = mJSChannels[i];
if (!channelArray || mLength != JS_GetTypedArrayLength(channelArray)) {
if (!channelArray || Length() != JS_GetTypedArrayLength(channelArray)) {
// Either empty buffer or one of the arrays' buffers was detached.
return nullptr;
}
@ -414,11 +469,17 @@ AudioBuffer::StealJSArrayDataIntoSharedChannels(JSContext* aJSContext)
return result.forget();
}
ThreadSharedFloatArrayBufferList*
const AudioChunk&
AudioBuffer::GetThreadSharedChannelsForRate(JSContext* aJSContext)
{
if (!mSharedChannels) {
mSharedChannels = StealJSArrayDataIntoSharedChannels(aJSContext);
if (mSharedChannels.IsNull()) {
// mDuration is set in constructor
RefPtr<ThreadSharedFloatArrayBufferList> buffer =
StealJSArrayDataIntoSharedChannels(aJSContext);
if (buffer) {
SetSharedChannels(buffer.forget());
}
}
return mSharedChannels;
@ -429,9 +490,7 @@ AudioBuffer::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t amount = aMallocSizeOf(this);
amount += mJSChannels.ShallowSizeOfExcludingThis(aMallocSizeOf);
if (mSharedChannels) {
amount += mSharedChannels->SizeOfIncludingThis(aMallocSizeOf);
}
amount += mSharedChannels.SizeOfExcludingThis(aMallocSizeOf, false);
return amount;
}

Просмотреть файл

@ -7,6 +7,7 @@
#ifndef AudioBuffer_h_
#define AudioBuffer_h_
#include "AudioSegment.h"
#include "nsWrapperCache.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Attributes.h"
@ -53,6 +54,11 @@ public:
nullptr, aRv);
}
// Non-unit AudioChunk::mVolume is not supported
static already_AddRefed<AudioBuffer>
Create(nsPIDOMWindowInner* aWindow, float aSampleRate,
AudioChunk&& aInitialContents);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AudioBuffer)
@ -77,12 +83,12 @@ public:
uint32_t Length() const
{
return mLength;
return mSharedChannels.mDuration;
}
double Duration() const
{
return mLength / static_cast<double> (mSampleRate);
return Length() / static_cast<double> (mSampleRate);
}
uint32_t NumberOfChannels() const
@ -105,18 +111,19 @@ public:
ErrorResult& aRv);
/**
* Returns a ThreadSharedFloatArrayBufferList containing the sample data.
* Can return null if there is no data.
* Returns a reference to an AudioChunk containing the sample data.
* The AudioChunk can have a null buffer if there is no data.
*/
ThreadSharedFloatArrayBufferList* GetThreadSharedChannelsForRate(JSContext* aContext);
const AudioChunk& GetThreadSharedChannelsForRate(JSContext* aContext);
protected:
AudioBuffer(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels,
uint32_t aLength, float aSampleRate,
already_AddRefed<ThreadSharedFloatArrayBufferList>
aInitialContents);
uint32_t aLength, float aSampleRate, ErrorResult& aRv);
~AudioBuffer();
void
SetSharedChannels(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer);
bool RestoreJSChannelData(JSContext* aJSContext);
already_AddRefed<ThreadSharedFloatArrayBufferList>
@ -124,15 +131,15 @@ protected:
void ClearJSChannels();
nsWeakPtr mOwnerWindow;
// Float32Arrays
AutoTArray<JS::Heap<JSObject*>, 2> mJSChannels;
// mSharedChannels aggregates the data from mJSChannels. This is non-null
// if and only if the mJSChannels' buffers are detached.
RefPtr<ThreadSharedFloatArrayBufferList> mSharedChannels;
// if and only if the mJSChannels' buffers are detached, but its mDuration
// member keeps the buffer length regardless of whether the buffer is
// provided by mJSChannels or mSharedChannels.
AudioChunk mSharedChannels;
uint32_t mLength;
nsWeakPtr mOwnerWindow;
float mSampleRate;
};

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше