Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2017-05-05 15:25:16 +02:00
commit 170faef00b
308 changed files with 5191 additions and 2549 deletions

View File

@ -31,6 +31,16 @@ jobs:
- date
when: [] # never (hook only)
- name: nightly-desktop-win64
job:
type: decision-task
treeherder-symbol: Nd-Win64
triggered-by: nightly
target-tasks-method: nightly_win64
run-on-projects:
- date
when: [] # never (hook only)
- name: nightly-android
job:
type: decision-task

View File

@ -2,7 +2,8 @@
* 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/. */
// Note: this file is included in aboutDialog.xul if MOZ_UPDATER is defined.
// Note: this file is included in aboutDialog.xul and preferences/advanced.xul
// if MOZ_UPDATER is defined.
/* import-globals-from aboutDialog.js */

View File

@ -29,7 +29,7 @@ with Files("sync/**"):
BUG_COMPONENT = ("Firefox", "Sync")
with Files("test/alerts/**"):
BUG_COMPONENT = ("Toolkit", "Notification and Alerts")
BUG_COMPONENT = ("Toolkit", "Notifications and Alerts")
with Files("test/appUpdate/**"):
BUG_COMPONENT = ("Toolkit", "Application Update")

View File

@ -215,15 +215,15 @@ nsContextMenu.prototype = {
initNavigationItems: function CM_initNavigationItems() {
var shouldShow = !(this.isContentSelected || this.onLink || this.onImage ||
this.onCanvas || this.onVideo || this.onAudio ||
this.onTextInput || this.onSocial);
this.onTextInput) && this.inTabBrowser;
this.showItem("context-navigation", shouldShow);
this.showItem("context-sep-navigation", shouldShow);
let stopped = XULBrowserWindow.stopCommand.getAttribute("disabled") == "true";
let stopReloadItem = "";
if (shouldShow || this.onSocial) {
stopReloadItem = (stopped || this.onSocial) ? "reload" : "stop";
if (shouldShow || !this.inTabBrowser) {
stopReloadItem = (stopped || !this.inTabBrowser) ? "reload" : "stop";
}
this.showItem("context-reload", stopReloadItem == "reload");
@ -290,9 +290,12 @@ nsContextMenu.prototype = {
this.onImage || this.onCanvas ||
this.onVideo || this.onAudio ||
this.onLink || this.onTextInput);
var showInspect = !this.onSocial && gPrefService.getBoolPref("devtools.inspector.enabled");
var showInspect = this.inTabBrowser && gPrefService.getBoolPref("devtools.inspector.enabled");
this.showItem("context-viewsource", shouldShow);
this.showItem("context-viewinfo", shouldShow);
// The page info is broken for WebExtension popups, as the browser is
// destroyed when the popup is closed.
this.setItemAttr("context-viewinfo", "disabled", this.webExtBrowserType === "popup");
this.showItem("inspect-separator", showInspect);
this.showItem("context-inspect", showInspect);
@ -341,6 +344,9 @@ nsContextMenu.prototype = {
.disabled = !this.hasBGImage;
this.showItem("context-viewimageinfo", this.onImage);
// The image info popup is broken for WebExtension popups, since the browser
// is destroyed when the popup is closed.
this.setItemAttr("context-viewimageinfo", "disabled", this.webExtBrowserType === "popup");
this.showItem("context-viewimagedesc", this.onImage && this.imageDescURL !== "");
},
@ -350,11 +356,12 @@ nsContextMenu.prototype = {
this.showItem(bookmarkPage,
!(this.isContentSelected || this.onTextInput || this.onLink ||
this.onImage || this.onVideo || this.onAudio || this.onSocial ||
this.onCanvas));
this.onCanvas || this.inWebExtBrowser));
bookmarkPage.setAttribute("tooltiptext", bookmarkPage.getAttribute("buttontooltiptext"));
this.showItem("context-bookmarklink", (this.onLink && !this.onMailtoLink &&
!this.onSocial) || this.onPlainTextLink);
!this.onSocial && !this.onMozExtLink) ||
this.onPlainTextLink);
this.showItem("context-keywordfield",
this.onTextInput && this.onKeywordField);
this.showItem("frame", this.inFrame);
@ -398,13 +405,14 @@ nsContextMenu.prototype = {
let shareEnabled = shareButton && !shareButton.disabled && !this.onSocial;
let pageShare = shareEnabled && !(this.isContentSelected ||
this.onTextInput || this.onLink || this.onImage ||
this.onVideo || this.onAudio || this.onCanvas);
this.onVideo || this.onAudio || this.onCanvas ||
this.inWebExtBrowser);
this.showItem("context-sharepage", pageShare);
this.showItem("context-shareselect", shareEnabled && this.isContentSelected);
this.showItem("context-sharelink", shareEnabled && (this.onLink || this.onPlainTextLink) && !this.onMailtoLink);
this.showItem("context-sharelink", shareEnabled && (this.onLink || this.onPlainTextLink) && !this.onMailtoLink && !this.onMozExtLink);
this.showItem("context-shareimage", shareEnabled && this.onImage);
this.showItem("context-sharevideo", shareEnabled && this.onVideo);
this.setItemAttr("context-sharevideo", "disabled", !this.mediaURL || this.mediaURL.startsWith("blob:"));
this.setItemAttr("context-sharevideo", "disabled", !this.mediaURL || this.mediaURL.startsWith("blob:") || this.mediaURL.startsWith("moz-extension:"));
},
initSpellingItems() {
@ -677,6 +685,10 @@ nsContextMenu.prototype = {
this.onCTPPlugin = false;
this.canSpellCheck = false;
this.onPassword = false;
this.webExtBrowserType = "";
this.inWebExtBrowser = false;
this.inTabBrowser = true;
this.onMozExtLink = false;
if (this.isRemote) {
this.selectionInfo = gContextMenuContentData.selectionInfo;
@ -713,6 +725,10 @@ nsContextMenu.prototype = {
this.frameOuterWindowID = WebNavigationFrames.getFrameId(ownerDoc.defaultView);
}
this.onSocial = !!this.browser.getAttribute("origin");
this.webExtBrowserType = this.browser.getAttribute("webextension-view-type");
this.inWebExtBrowser = !!this.webExtBrowserType;
this.inTabBrowser = this.browser.ownerGlobal.gBrowser ?
!!this.browser.ownerGlobal.gBrowser.getTabForBrowser(this.browser) : false;
// Check if we are in a synthetic document (stand alone image, video, etc.).
this.inSyntheticDoc = ownerDoc.mozSyntheticDocument;
@ -869,6 +885,7 @@ nsContextMenu.prototype = {
this.linkTextStr = this.getLinkText();
this.linkProtocol = this.getLinkProtocol();
this.onMailtoLink = (this.linkProtocol == "mailto");
this.onMozExtLink = (this.linkProtocol == "moz-extension");
this.onSaveableLink = this.isLinkSaveable( this.link );
this.linkHasNoReferrer = BrowserUtils.linkHasNoReferrer(elem);
try {
@ -1033,8 +1050,11 @@ nsContextMenu.prototype = {
}
if (!this.isRemote) {
params.frameOuterWindowID = WebNavigationFrames.getFrameId(this.target.ownerGlobal);
// Propagate the frameOuterWindowID value saved when
// the context menu has been opened.
params.frameOuterWindowID = this.frameOuterWindowID;
}
// If we want to change userContextId, we must be sure that we don't
// propagate the referrer.
if ("userContextId" in params &&
@ -1911,7 +1931,7 @@ nsContextMenu.prototype = {
_getTelemetryPageContextInfo() {
let rv = [];
for (let k of ["isContentSelected", "onLink", "onImage", "onCanvas", "onVideo", "onAudio",
"onTextInput", "onSocial"]) {
"onTextInput", "onSocial", "inWebExtBrowser", "inTabBrowser"]) {
if (this[k]) {
rv.push(k.replace(/^(?:is|on)(.)/, (match, firstLetter) => firstLetter.toLowerCase()));
}

View File

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"plugin:mozilla/browser-test"
]
};

View File

@ -0,0 +1,6 @@
[DEFAULT]
support-files =
!/browser/base/content/test/general/contextmenu_common.js
subtst_contextmenu_webext.html
[browser_contextmenu_mozextension.js]

View File

@ -0,0 +1,82 @@
"use strict";
var { SocialService } = Cu.import("resource:///modules/SocialService.jsm", {});
let contextMenu;
let hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled");
let hasContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
// A social share provider
let manifest = {
name: "provider 1",
origin: "https://example.com",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png",
shareURL: "https://example.com/browser/browser/base/content/test/social/share.html"
};
add_task(function* test_setup() {
const example_base = "http://example.com/browser/browser/base/content/test/contextMenu/";
const url = example_base + "subtst_contextmenu_webext.html";
yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/";
const contextmenu_common = chrome_base + "contextmenu_common.js";
/* import-globals-from ../general/contextmenu_common.js */
Services.scriptloader.loadSubScript(contextmenu_common, this);
// Enable social sharing functions in the browser, so the context menu item is shown.
CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
yield new Promise((resolve) => SocialService.addProvider(manifest, resolve));
ok(SocialShare.shareButton && !SocialShare.shareButton.disabled, "Sharing is enabled");
});
add_task(function* test_link() {
// gets hidden for this case.
yield test_contextmenu("#link",
["context-openlinkintab", true,
...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
// We need a blank entry here because the containers submenu is
// dynamically generated with no ids.
...(hasContainers ? ["", null] : []),
"context-openlink", true,
"context-openlinkprivate", true,
"---", null,
"context-savelink", true,
"context-copylink", true,
"context-searchselect", true]);
});
add_task(function* test_video() {
yield test_contextmenu("#video",
["context-media-play", null,
"context-media-mute", null,
"context-media-playbackrate", null,
["context-media-playbackrate-050x", null,
"context-media-playbackrate-100x", null,
"context-media-playbackrate-125x", null,
"context-media-playbackrate-150x", null,
"context-media-playbackrate-200x", null], null,
"context-media-loop", null,
"context-media-showcontrols", null,
"context-video-fullscreen", null,
"---", null,
"context-viewvideo", null,
"context-copyvideourl", null,
"---", null,
"context-savevideo", null,
"context-sharevideo", false,
"context-video-saveimage", null,
"context-sendvideo", null,
"context-castvideo", null,
[], null
]);
});
add_task(function* test_cleanup() {
lastElementSelector = null;
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
yield new Promise((resolve) => {
return SocialService.disableProvider(manifest.origin, resolve);
});
});

View File

@ -0,0 +1,12 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Subtest for browser context menu</title>
</head>
<body>
Browser context menu subtest.
<a href="moz-extension://foo-bar/tab.html" id="link">Link to an extension resource</a>
<video src="moz-extension://foo-bar/video.ogg" id="video"></video>
</body>
</html>

View File

@ -6,3 +6,4 @@ support-files =
skip-if = os == "linux" # Bug 1329991 - test fails intermittently on Linux builds
[browser_selectpopup_colors.js]
skip-if = os == "linux" # Bug 1329991 - test fails intermittently on Linux builds
[browser_selectpopup_searchfocus.js]

View File

@ -0,0 +1,42 @@
let SELECT =
"<html><body><select id='one'>";
for (let i = 0; i < 75; i++) {
SELECT +=
` <option>${i}${i}${i}${i}${i}</option>`;
}
SELECT +=
' <option selected="true">{"end": "true"}</option>' +
"</select></body></html>";
add_task(function* setup() {
yield SpecialPowers.pushPrefEnv({
"set": [
["dom.select_popup_in_parent.enabled", true],
["dom.forms.selectSearch", true]
]
});
});
add_task(function* test_focus_on_search_shouldnt_close_popup() {
const pageUrl = "data:text/html," + escape(SELECT);
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
let menulist = document.getElementById("ContentSelectDropdown");
let selectPopup = menulist.menupopup;
let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
yield popupShownPromise;
let searchInput = selectPopup.querySelector("textbox[type='search']");
searchInput.scrollIntoView();
let searchFocused = BrowserTestUtils.waitForEvent(searchInput, "focus");
yield EventUtils.synthesizeMouseAtCenter(searchInput, {}, window);
yield searchFocused;
is(selectPopup.state, "open", "select popup should still be open after clicking on the search field");
yield hideSelectPopup(selectPopup, "escape");
yield BrowserTestUtils.removeTab(tab);
});

View File

@ -235,8 +235,6 @@ var whitelist = new Set([
{file: "resource://gre/modules/Manifest.jsm"},
// Bug 1351089
{file: "resource://gre/modules/PresentationDeviceInfoManager.jsm"},
// Bug 1351091
{file: "resource://gre/modules/Profiler.jsm"},
// Bug 1351658
{file: "resource://gre/modules/PropertyListUtils.jsm", platforms: ["linux", "win"]},
// Bug 1351097

View File

@ -1,9 +1,11 @@
[DEFAULT]
support-files =
dummy_page.html
test_bug1358314.html
[browser_abandonment_telemetry.js]
[browser_allow_process_switches_despite_related_browser.js]
[browser_contextmenu_openlink_after_tabnavigated.js]
[browser_tabCloseProbes.js]
[browser_tabSpinnerProbe.js]
skip-if = !e10s # Tab spinner is e10s only.

View File

@ -0,0 +1,43 @@
"use strict";
const example_base = "http://example.com/browser/browser/base/content/test/tabs/";
add_task(function* test_contextmenu_openlink_after_tabnavigated() {
let url = example_base + "test_bug1358314.html";
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
const contextMenu = document.getElementById("contentAreaContextMenu");
let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
yield BrowserTestUtils.synthesizeMouse("a", 0, 0, {
type: "contextmenu",
button: 2,
}, gBrowser.selectedBrowser);
yield awaitPopupShown;
info("Popup Shown");
info("Navigate the tab with the opened context menu");
gBrowser.selectedBrowser.loadURI("about:blank");
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
let awaitNewTabOpen = BrowserTestUtils.waitForNewTab(gBrowser, "http://example.com/");
info("Click the 'open link in new tab' menu item");
let openLinkMenuItem = contextMenu.querySelector("#context-openlinkintab");
openLinkMenuItem.click();
info("Wait for the new tab to be opened");
const newTab = yield awaitNewTabOpen;
// Close the contextMenu popup if it has not been closed yet.
contextMenu.hidePopup();
yield BrowserTestUtils.browserLoaded(newTab.linkedBrowser);
const newTabURL = yield ContentTask.spawn(newTab.linkedBrowser, null, function* () {
return content.location.href;
});
is(newTabURL, "http://example.com/", "Got the expected URL loaded in the new tab");
yield BrowserTestUtils.removeTab(newTab);
yield BrowserTestUtils.removeTab(tab);
});

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<p>Test page</p>
<a href="/">Link</a>
</body>
</html>

View File

@ -17,6 +17,7 @@ MOCHITEST_CHROME_MANIFESTS += [
BROWSER_CHROME_MANIFESTS += [
'content/test/alerts/browser.ini',
'content/test/captivePortal/browser.ini',
'content/test/contextMenu/browser.ini',
'content/test/forms/browser.ini',
'content/test/general/browser.ini',
'content/test/newtab/browser.ini',

View File

@ -233,6 +233,7 @@ class BasePopup {
browser.setAttribute("class", "webextension-popup-browser");
browser.setAttribute("webextension-view-type", "popup");
browser.setAttribute("tooltip", "aHTMLTooltip");
browser.setAttribute("contextmenu", "contentAreaContextMenu");
if (this.extension.remote) {
browser.setAttribute("remote", "true");
@ -286,6 +287,9 @@ class BasePopup {
setupBrowser(browser);
let mm = browser.messageManager;
// Sets the context information for context menus.
mm.loadFrameScript("chrome://browser/content/content.js", true);
mm.loadFrameScript(
"chrome://extensions/content/ext-browser-content.js", false);

View File

@ -321,7 +321,7 @@ this.browserAction = class extends ExtensionAPI {
if (pendingPopup) {
if (pendingPopup.window === window && pendingPopup.popupURL === popupURL) {
if (!this.blockParser) {
if (!blockParser) {
pendingPopup.unblockParser();
}

View File

@ -31,6 +31,7 @@ support-files =
[browser_ext_browserAction_area.js]
[browser_ext_browserAction_context.js]
[browser_ext_browserAction_contextMenu.js]
[browser_ext_browserAction_disabled.js]
[browser_ext_browserAction_pageAction_icon.js]
[browser_ext_browserAction_pageAction_icon_permissions.js]
@ -73,6 +74,7 @@ skip-if = debug || asan # Bug 1354681
[browser_ext_optionsPage_browser_style.js]
[browser_ext_optionsPage_privileges.js]
[browser_ext_pageAction_context.js]
[browser_ext_pageAction_contextMenu.js]
[browser_ext_pageAction_popup.js]
[browser_ext_pageAction_popup_resize.js]
[browser_ext_pageAction_simple.js]
@ -93,6 +95,7 @@ skip-if = debug || asan # Bug 1354681
[browser_ext_sessions_restore.js]
[browser_ext_sidebarAction.js]
[browser_ext_sidebarAction_context.js]
[browser_ext_sidebarAction_contextMenu.js]
[browser_ext_sidebarAction_tabs.js]
[browser_ext_sidebarAction_windows.js]
[browser_ext_simple.js]

View File

@ -0,0 +1,106 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
let extData = {
manifest: {
"permissions": ["contextMenus"],
"browser_action": {
"default_popup": "popup.html",
},
},
useAddonManager: "temporary",
files: {
"popup.html": `
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"/>
</head>
<body>
<span id="text">A Test Popup</span>
<img id="testimg" src="data:image/svg+xml,<svg></svg>" height="10" width="10">
</body></html>
`,
},
background: function() {
browser.contextMenus.create({
id: "clickme-page",
title: "Click me!",
contexts: ["all"],
});
},
};
let contextMenuItems = {
"context-navigation": "hidden",
"context-sep-navigation": "hidden",
"context-viewsource": "",
"context-viewinfo": "disabled",
"inspect-separator": "hidden",
"context-inspect": "hidden",
"context-bookmarkpage": "hidden",
"context-sharepage": "hidden",
};
add_task(function* browseraction_popup_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
yield clickBrowserAction(extension, window);
let contentAreaContextMenu = yield openContextMenuInPopup(extension);
let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
is(item.length, 1, "contextMenu item for page was found");
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* browseraction_popup_contextmenu_hidden_items() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
yield clickBrowserAction(extension);
let contentAreaContextMenu = yield openContextMenuInPopup(extension, "#text");
let item, state;
for (const itemID in contextMenuItems) {
item = contentAreaContextMenu.querySelector(`#${itemID}`);
state = contextMenuItems[itemID];
if (state !== "") {
ok(item[state], `${itemID} is ${state}`);
if (state !== "hidden") {
ok(!item.hidden, `Disabled ${itemID} is not hidden`);
}
} else {
ok(!item.hidden, `${itemID} is not hidden`);
ok(!item.disabled, `${itemID} is not disabled`);
}
}
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* browseraction_popup_image_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
yield clickBrowserAction(extension);
let contentAreaContextMenu = yield openContextMenuInPopup(extension, "#testimg");
let item = contentAreaContextMenu.querySelector("#context-viewimageinfo");
ok(!item.hidden);
ok(item.disabled);
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});

View File

@ -0,0 +1,116 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
let extData = {
manifest: {
"permissions": ["contextMenus"],
"page_action": {
"default_popup": "popup.html",
},
},
useAddonManager: "temporary",
files: {
"popup.html": `
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"/>
</head>
<body>
<span id="text">A Test Popup</span>
<img id="testimg" src="data:image/svg+xml,<svg></svg>" height="10" width="10">
</body></html>
`,
},
background: function() {
browser.contextMenus.create({
id: "clickme-page",
title: "Click me!",
contexts: ["all"],
});
browser.tabs.query({active: true, currentWindow: true}, tabs => {
const tabId = tabs[0].id;
browser.pageAction.show(tabId).then(() => {
browser.test.sendMessage("action-shown");
});
});
},
};
let contextMenuItems = {
"context-navigation": "hidden",
"context-sep-navigation": "hidden",
"context-viewsource": "",
"context-viewinfo": "disabled",
"inspect-separator": "hidden",
"context-inspect": "hidden",
"context-bookmarkpage": "hidden",
"context-sharepage": "hidden",
};
add_task(function* pageaction_popup_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
yield extension.awaitMessage("action-shown");
yield clickPageAction(extension, window);
let contentAreaContextMenu = yield openContextMenuInPopup(extension);
let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
is(item.length, 1, "contextMenu item for page was found");
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* pageaction_popup_contextmenu_hidden_items() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
yield extension.awaitMessage("action-shown");
yield clickPageAction(extension, window);
let contentAreaContextMenu = yield openContextMenuInPopup(extension, "#text");
let item, state;
for (const itemID in contextMenuItems) {
item = contentAreaContextMenu.querySelector(`#${itemID}`);
state = contextMenuItems[itemID];
if (state !== "") {
ok(item[state], `${itemID} is ${state}`);
if (state !== "hidden") {
ok(!item.hidden, `Disabled ${itemID} is not hidden`);
}
} else {
ok(!item.hidden, `${itemID} is not hidden`);
ok(!item.disabled, `${itemID} is not disabled`);
}
}
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* pageaction_popup_image_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
yield extension.awaitMessage("action-shown");
yield clickPageAction(extension, window);
let contentAreaContextMenu = yield openContextMenuInPopup(extension, "#testimg");
let item = contentAreaContextMenu.querySelector("#context-viewimageinfo");
ok(!item.hidden);
ok(item.disabled);
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});

View File

@ -4,7 +4,6 @@
let extData = {
manifest: {
"permissions": ["contextMenus"],
"sidebar_action": {
"default_panel": "sidebar.html",
},
@ -31,12 +30,6 @@ let extData = {
},
background: function() {
browser.contextMenus.create({
id: "clickme-page",
title: "Click me!",
contexts: ["all"],
});
browser.test.onMessage.addListener(msg => {
if (msg === "set-panel") {
browser.sidebarAction.setPanel({panel: ""}).then(() => {
@ -103,20 +96,6 @@ add_task(function* sidebar_empty_panel() {
yield extension.unload();
});
add_task(function* sidebar_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
// Test sidebar is opened on install
yield extension.awaitMessage("sidebar");
let contentAreaContextMenu = yield openContextMenuInSidebar();
let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
is(item.length, 1, "contextMenu item for page was found");
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* cleanup() {
// This is set on initial sidebar install.
Services.prefs.clearUserPref("extensions.sidebar-button.shown");

View File

@ -0,0 +1,119 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
let extData = {
manifest: {
"permissions": ["contextMenus"],
"sidebar_action": {
"default_panel": "sidebar.html",
},
},
useAddonManager: "temporary",
files: {
"sidebar.html": `
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"/>
<script src="sidebar.js"></script>
</head>
<body>
<span id="text">A Test Sidebar</span>
<img id="testimg" src="data:image/svg+xml,<svg></svg>" height="10" width="10">
</body></html>
`,
"sidebar.js": function() {
window.onload = () => {
browser.test.sendMessage("sidebar");
};
},
},
background: function() {
browser.contextMenus.create({
id: "clickme-page",
title: "Click me!",
contexts: ["all"],
});
},
};
let contextMenuItems = {
"context-navigation": "hidden",
"context-sep-navigation": "hidden",
"context-viewsource": "",
"context-viewinfo": "",
"inspect-separator": "hidden",
"context-inspect": "hidden",
"context-bookmarkpage": "hidden",
"context-sharepage": "hidden",
};
add_task(function* sidebar_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
// Test sidebar is opened on install
yield extension.awaitMessage("sidebar");
let contentAreaContextMenu = yield openContextMenuInSidebar();
let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
is(item.length, 1, "contextMenu item for page was found");
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* sidebar_contextmenu_hidden_items() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
// Test sidebar is opened on install
yield extension.awaitMessage("sidebar");
let contentAreaContextMenu = yield openContextMenuInSidebar("#text");
let item, state;
for (const itemID in contextMenuItems) {
item = contentAreaContextMenu.querySelector(`#${itemID}`);
state = contextMenuItems[itemID];
if (state !== "") {
ok(item[state], `${itemID} is ${state}`);
if (state !== "hidden") {
ok(!item.hidden, `Disabled ${itemID} is not hidden`);
}
} else {
ok(!item.hidden, `${itemID} is not hidden`);
ok(!item.disabled, `${itemID} is not disabled`);
}
}
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* sidebar_image_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
// Test sidebar is opened on install
yield extension.awaitMessage("sidebar");
let contentAreaContextMenu = yield openContextMenuInSidebar("#testimg");
let item = contentAreaContextMenu.querySelector("#context-viewimageinfo");
ok(!item.hidden);
ok(!item.disabled);
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* cleanup() {
// This is set on initial sidebar install.
Services.prefs.clearUserPref("extensions.sidebar-button.shown");
});

View File

@ -8,7 +8,8 @@
* getBrowserActionPopup getPageActionPopup
* closeBrowserAction closePageAction
* promisePopupShown promisePopupHidden
* openContextMenu closeContextMenu openContextMenuInSidebar
* openContextMenu closeContextMenu
* openContextMenuInSidebar openContextMenuInPopup
* openExtensionContextMenu closeExtensionContextMenu
* openActionContextMenu openSubmenu closeActionContextMenu
* openTabContextMenu closeTabContextMenu
@ -232,6 +233,16 @@ function closeBrowserAction(extension, win = window) {
return Promise.resolve();
}
async function openContextMenuInPopup(extension, selector = "body") {
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
let browser = await awaitExtensionPanel(extension);
let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
await BrowserTestUtils.synthesizeMouseAtCenter(selector, {type: "mousedown", button: 2}, browser);
await BrowserTestUtils.synthesizeMouseAtCenter(selector, {type: "contextmenu"}, browser);
await popupShownPromise;
return contentAreaContextMenu;
}
async function openContextMenuInSidebar(selector = "body") {
let contentAreaContextMenu = SidebarUI.browser.contentDocument.getElementById("contentAreaContextMenu");
let browser = SidebarUI.browser.contentDocument.getElementById("webext-panels-browser");

View File

@ -1,6 +1,7 @@
[DEFAULT]
support-files =
../../../../../toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
../../../../../toolkit/components/extensions/test/mochitest/file_sample.html
tags = webextensions
[test_ext_all_apis.html]

View File

@ -3,6 +3,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* import-globals-from preferences.js */
/* import-globals-from ../../../base/content/aboutDialog-appUpdater.js */
// Load DownloadUtils module for convertByteUnits
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
@ -20,7 +21,56 @@ var gAdvancedPane = {
this._inited = true;
let version = AppConstants.MOZ_APP_VERSION_DISPLAY;
// Include the build ID if this is an "a#" (nightly) build
if (/a\d+$/.test(version)) {
let buildID = Services.appinfo.appBuildID;
let year = buildID.slice(0, 4);
let month = buildID.slice(4, 6);
let day = buildID.slice(6, 8);
version += ` (${year}-${month}-${day})`;
}
// Append "(32-bit)" or "(64-bit)" build architecture to the version number:
let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
let archResource = Services.appinfo.is64Bit
? "aboutDialog.architecture.sixtyFourBit"
: "aboutDialog.architecture.thirtyTwoBit";
let arch = bundle.GetStringFromName(archResource);
version += ` (${arch})`;
document.getElementById("version").textContent = version;
// Show a release notes link if we have a URL.
let relNotesLink = document.getElementById("releasenotes");
let relNotesPrefType = Services.prefs.getPrefType("app.releaseNotesURL");
if (relNotesPrefType != Services.prefs.PREF_INVALID) {
let relNotesURL = Services.urlFormatter.formatURLPref("app.releaseNotesURL");
if (relNotesURL != "about:blank") {
relNotesLink.href = relNotesURL;
relNotesLink.hidden = false;
}
}
let distroId = Services.prefs.getCharPref("distribution.id", "");
if (distroId) {
let distroVersion = Services.prefs.getCharPref("distribution.version");
let distroIdField = document.getElementById("distributionId");
distroIdField.value = distroId + " - " + distroVersion;
distroIdField.hidden = false;
let distroAbout = Services.prefs.getStringPref("distribution.about", "");
if (distroAbout) {
let distroField = document.getElementById("distribution");
distroField.value = distroAbout;
distroField.hidden = false;
}
}
if (AppConstants.MOZ_UPDATER) {
gAppUpdater = new appUpdater();
let onUnload = () => {
window.removeEventListener("unload", onUnload);
Services.prefs.removeObserver("app.update.", this);

View File

@ -4,6 +4,10 @@
<!-- Advanced panel -->
#ifdef MOZ_UPDATER
<script type="application/javascript" src="chrome://browser/content/aboutDialog-appUpdater.js"/>
#endif
<script type="application/javascript"
src="chrome://browser/content/preferences/in-content/advanced.js"/>
@ -52,23 +56,16 @@
<!-- Update -->
<groupbox id="updateApp" data-category="paneAdvanced" hidden="true">
<caption><label>&updateApplication.label;</label></caption>
#ifdef MOZ_UPDATER
<description>&updateApplication.description;</description>
<hbox align="start">
<vbox flex="1">
<radiogroup id="updateRadioGroup">
<radio id="autoDesktop"
value="auto"
label="&updateAuto2.label;"
accesskey="&updateAuto2.accesskey;"/>
<radio value="checkOnly"
label="&updateCheckChoose2.label;"
accesskey="&updateCheckChoose2.accesskey;"/>
<radio value="manual"
label="&updateManual2.label;"
accesskey="&updateManual2.accesskey;"/>
</radiogroup>
<description>
&updateApplication.version.pre;<label id="version"/>&updateApplication.version.post;
<label id="releasenotes" class="learnMore text-link" hidden="true">&releaseNotes.link;</label>
</description>
<description id="distribution" class="text-blurb" hidden="true"/>
<description id="distributionId" class="text-blurb" hidden="true"/>
</vbox>
#ifdef MOZ_UPDATER
<spacer flex="1"/>
<vbox>
<button id="showUpdateHistory"
@ -77,7 +74,112 @@
accesskey="&updateHistory2.accesskey;"
preference="app.update.disable_button.showUpdateHistory"/>
</vbox>
#endif
</hbox>
#ifdef MOZ_UPDATER
<vbox id="updateBox">
<deck id="updateDeck" orient="vertical">
<hbox id="checkForUpdates" align="center">
<spacer flex="1"/>
<button id="checkForUpdatesButton"
label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
oncommand="gAppUpdater.checkForUpdates();"/>
</hbox>
<hbox id="downloadAndInstall" align="center">
<spacer flex="1"/>
<button id="downloadAndInstallButton"
oncommand="gAppUpdater.startDownload();"/>
<!-- label and accesskey will be filled by JS -->
</hbox>
<hbox id="apply" align="center">
<spacer flex="1"/>
<button id="updateButton"
label="&update.updateButton.label3;"
accesskey="&update.updateButton.accesskey;"
oncommand="gAppUpdater.buttonRestartAfterDownload();"/>
</hbox>
<hbox id="checkingForUpdates" align="center">
<image class="update-throbber"/><label>&update.checkingForUpdates;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
disabled="true"/>
</hbox>
<hbox id="downloading" align="center">
<image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
</hbox>
<hbox id="applying" align="center">
<image class="update-throbber"/><label>&update.applying;</label>
</hbox>
<hbox id="downloadFailed" align="center">
<label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
oncommand="gAppUpdater.checkForUpdates();"/>
</hbox>
<hbox id="adminDisabled" align="center">
<label>&update.adminDisabled;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
disabled="true"/>
</hbox>
<hbox id="noUpdatesFound" align="center">
<label>&update.noUpdatesFound;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
oncommand="gAppUpdater.checkForUpdates();"/>
</hbox>
<hbox id="otherInstanceHandlingUpdates" align="center">
<label>&update.otherInstanceHandlingUpdates;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
disabled="true"/>
</hbox>
<hbox id="manualUpdate" align="center">
<label>&update.manual.start;</label><label id="manualLink" class="text-link"/><label>&update.manual.end;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
disabled="true"/>
</hbox>
<hbox id="unsupportedSystem" align="center">
<label>&update.unsupported.start;</label><label id="unsupportedLink" class="text-link">&update.unsupported.linkText;</label><label>&update.unsupported.end;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
disabled="true"/>
</hbox>
<hbox id="restarting" align="center">
<label>&update.restarting;</label>
<spacer flex="1"/>
<button label="&update.updateButton.label3;"
accesskey="&update.updateButton.accesskey;"
disabled="true"/>
</hbox>
</deck>
</vbox>
#endif
<separator/>
#ifdef MOZ_UPDATER
<description>&updateApplication.description;</description>
<radiogroup id="updateRadioGroup">
<radio id="autoDesktop"
value="auto"
label="&updateAuto2.label;"
accesskey="&updateAuto2.accesskey;"/>
<radio value="checkOnly"
label="&updateCheckChoose2.label;"
accesskey="&updateCheckChoose2.accesskey;"/>
<radio value="manual"
label="&updateManual2.label;"
accesskey="&updateManual2.accesskey;"/>
</radiogroup>
#ifdef MOZ_MAINTENANCE_SERVICE
<checkbox id="useService"
label="&useService.label;"

View File

@ -37,6 +37,8 @@
"chrome://browser/locale/preferences/applications.dtd">
<!ENTITY % advancedDTD SYSTEM
"chrome://browser/locale/preferences/advanced.dtd">
<!ENTITY % aboutDialogDTD SYSTEM "chrome://browser/locale/aboutDialog.dtd" >
%aboutDialogDTD;
%brandDTD;
%globalPreferencesDTD;
%preferencesDTD;

View File

@ -110,14 +110,19 @@ add_task(function*() {
});
add_task(function*() {
mockUpdateManager.register();
yield openPreferencesViaOpenPreferencesAPI("advanced", { leaveOpen: true });
let doc = gBrowser.selectedBrowser.contentDocument;
let showBtn = doc.getElementById("showUpdateHistory");
let dialogOverlay = doc.getElementById("dialogOverlay");
// XXX: For unknown reasons, this mock cannot be loaded by
// XPCOMUtils.defineLazyServiceGetter() called in aboutDialog-appUpdater.js.
// It is registered here so that we could assert update history subdialog
// without stopping the preferences advanced pane from loading.
// See bug 1361929.
mockUpdateManager.register();
// Test the dialog window opens
is(dialogOverlay.style.visibility, "", "The dialog should be invisible");
showBtn.doCommand();

View File

@ -32,7 +32,7 @@
class="showNormal"
label="&privatebrowsingpage.openPrivateWindow.label;"
accesskey="&privatebrowsingpage.openPrivateWindow.accesskey;"/>
<div class="showPrivate about-content-container">
<div class="showPrivate container">
<h1 class="title">
<span id="title">&privateBrowsing.title;</span>
<span id="titleTracking">&privateBrowsing.title.tracking;</span>

View File

@ -46,28 +46,9 @@ const prefObserver = {
}
};
const appStartupObserver = {
register() {
Services.obs.addObserver(this, "sessionstore-windows-restored", false); // eslint-disable-line mozilla/no-useless-parameters
},
unregister() {
Services.obs.removeObserver(this, "sessionstore-windows-restored", false); // eslint-disable-line mozilla/no-useless-parameters
},
observe() {
appStartupDone();
this.unregister();
}
}
const APP_STARTUP = 1;
function startup(data, reason) { // eslint-disable-line no-unused-vars
if (reason === APP_STARTUP) {
appStartupObserver.register();
} else {
appStartupDone();
}
appStartupDone();
prefObserver.register();
addonResourceURI = data.resourceURI;
// eslint-disable-next-line promise/catch-or-return

View File

@ -199,7 +199,7 @@ this.main = (function() {
}
browser.tabs.onUpdated.addListener(catcher.watchFunction((id, info, tab) => {
if (info.url && tab.active) {
if (info.url) {
if (urlEnabled(info.url)) {
enableButton(tab.id);
} else if (hasSeenOnboarding) {
@ -208,20 +208,6 @@ this.main = (function() {
}
}, true));
browser.tabs.onActivated.addListener(catcher.watchFunction(({tabId, windowId}) => {
catcher.watchPromise(browser.tabs.get(tabId).then((tab) => {
// onActivated may fire before the url is set
if (!tab.url) {
return;
}
if (urlEnabled(tab.url)) {
enableButton(tabId);
} else if (hasSeenOnboarding) {
disableButton(tabId);
}
}), true);
}));
communication.register("sendEvent", (sender, ...args) => {
catcher.watchPromise(sendEvent(...args));
// We don't wait for it to complete:

View File

@ -3,6 +3,10 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!ENTITY aboutDialog.title "About &brandFullName;">
<!-- LOCALIZATION NOTE (update.*):
# These strings are also used in the update pane of preferences.
# See about:preferences#advanced.
-->
<!-- LOCALIZATION NOTE (update.checkForUpdatesButton.*, update.updateButton.*):
# Only one button is present at a time.
# The button when displayed is located directly under the Firefox version in
@ -91,7 +95,7 @@
<!ENTITY update.unsupported.linkText "Learn more">
<!ENTITY update.unsupported.end "">
<!-- LOCALIZATION NOTE (update.downloading.start,update.downloading.end): update.downloading.start and
<!-- LOCALIZATION NOTE (update.downloading.start,update.downloading.end): update.downloading.start and
update.downloading.end all go into one line, with the amount downloaded inserted in between. As this
is all in one line, try to make the localized text short (see bug 596813 for screenshots). The is
the "em dash" (long dash).

View File

@ -81,7 +81,19 @@
<!ENTITY updateTab.label "Update">
<!-- LOCALIZATION NOTE (updateApplication.label):
Strings from aboutDialog.dtd are displayed in this section of the preferences.
Please check for possible accesskey conflicts.
-->
<!ENTITY updateApplication.label "&brandShortName; Updates">
<!-- LOCALIZATION NOTE (updateApplication.version.*): updateApplication.version.pre
# is followed by a version number, keep the trailing space or replace it with a
# different character as needed. updateApplication.version.post is displayed
# after the version number, and is empty on purpose for English. You can use it
# if required by your language.
-->
<!ENTITY updateApplication.version.pre "Version ">
<!ENTITY updateApplication.version.post "">
<!ENTITY updateApplication.description "Allow &brandShortName; to">
<!ENTITY updateAuto2.label "Automatically install updates (recommended for improved security)">
<!ENTITY updateAuto2.accesskey "A">

View File

@ -568,6 +568,23 @@ description > html|a {
}
}
#updateDeck > hbox > label {
margin-inline-end: 5px ! important;
}
.update-throbber {
width: 16px;
min-height: 16px;
margin-inline-end: 3px;
list-style-image: url("chrome://global/skin/icons/loading.png");
}
@media (min-resolution: 1.1dppx) {
.update-throbber {
list-style-image: url("chrome://global/skin/icons/loading@2x.png");
}
}
.help-button {
position: fixed;
left: 0;

View File

@ -176,7 +176,6 @@
skin/classic/browser/places/bookmarks-notification-finish.png (../shared/places/bookmarks-notification-finish.png)
skin/classic/browser/places/bookmarks-notification-finish@2x.png (../shared/places/bookmarks-notification-finish@2x.png)
skin/classic/browser/privatebrowsing/aboutPrivateBrowsing.css (../shared/privatebrowsing/aboutPrivateBrowsing.css)
skin/classic/browser/privatebrowsing/check.svg (../shared/privatebrowsing/check.svg)
skin/classic/browser/privatebrowsing/favicon.svg (../shared/privatebrowsing/favicon.svg)
skin/classic/browser/privatebrowsing/private-browsing.svg (../shared/privatebrowsing/private-browsing.svg)
skin/classic/browser/privatebrowsing/tracking-protection-off.svg (../shared/privatebrowsing/tracking-protection-off.svg)

View File

@ -5,52 +5,26 @@
@import url("chrome://global/skin/in-content/info-pages.css");
:root {
--color-grey-lightest: #fbfbfb;
--color-grey: #b1b1b1;
--color-blue: #0996f8;
--color-blue-dark: #0670cc;
--color-blue-darker: #005bab;
--icon-margin: 64px;
}
html.private {
--in-content-page-color: #beb8cc;
--in-content-text-color: #beb8cc;
--in-content-page-background: #1c023c;
}
body {
padding: 40px;
--in-content-page-color: white;
--in-content-text-color: white;
--in-content-page-background: #25003e;
}
a:link {
color: var(--color-blue);
text-decoration: none;
}
a:hover {
color: var(--color-blue-dark);
color: inherit;
text-decoration: underline;
}
a:hover:active {
color: var(--color-blue-darker);
}
a:visited {
color: var(--color-blue-darker);
}
.about-content-container {
max-width: 780px;
.container {
max-width: 48em;
}
.section-main {
margin-bottom: 48px;
margin-inline-start: var(--icon-margin);
padding-inline-start: 24px;
}
.section-main:last-child {
@ -67,8 +41,8 @@ p {
.list-row > ul > li {
float: left;
width: 220px;
line-height: 1.5em;
width: 16em;
line-height: 2em;
margin-inline-start: 1em;
margin-bottom: 0;
}
@ -79,17 +53,15 @@ p {
.title {
background-image: url("chrome://browser/skin/privatebrowsing/private-browsing.svg");
background-size: 64px;
background-position: left, center;
font-weight: lighter;
line-height: 1.5em;
min-height: 64px;
margin-inline-start: 0;
padding-inline-start: calc(var(--icon-margin) + 24px);
background-position: left center;
background-size: 2em;
line-height: 2em;
margin-inline-start: calc(-2em - 10px);
padding-inline-start: calc(2em + 10px);
}
.title:dir(rtl) {
background-position: right, center;
background-position: right center;
}
.about-subheader {
@ -97,12 +69,13 @@ p {
align-items: center;
font-size: 1.5em;
font-weight: lighter;
min-height: 32px;
background-image: url("chrome://browser/skin/privatebrowsing/tracking-protection.svg");
background-repeat: no-repeat;
background-size: 32px;
margin-inline-start: calc(var(--icon-margin) - 32px);
padding-inline-start: 56px;
background-position: left center;
background-size: 1.5em;
line-height: 1.5em;
margin-inline-start: calc(-1.5em - 10px);
padding-inline-start: calc(1.5em + 10px);
}
.about-subheader:dir(rtl) {
@ -114,23 +87,17 @@ p {
}
.about-info {
font-size: .875em;
font-size: .9em;
}
.tpTitle {
margin-inline-end: 12px;
}
.private strong {
color: var(--color-grey-lightest);
font-weight: normal;
}
a.button {
padding: 5px 40px;
background-color: #381e59;
border: 1px solid #43256a;
border-radius: 4px;
padding: 3px 20px;
background-color: #8000d7;
border: 1px solid #6000a1;
text-decoration: none;
display: inline-block;
}
@ -155,69 +122,42 @@ a.button {
.toggle + .toggle-btn {
box-sizing: border-box;
cursor: pointer;
min-width: 60px;
height: 24px;
border-radius: 12px;
min-width: 42px;
height: 26px;
border-radius: 13px;
background-color: var(--color-grey);
border: 1px var(--color-grey) solid;
padding: 2px;
}
.toggle + .toggle-btn::after,
.toggle + .toggle-btn::before {
position: relative;
display: block;
content: "";
width: 19px;
height: 100%;
padding: 1px;
}
.toggle + .toggle-btn::after {
position: relative;
display: block;
content: "";
width: 24px;
height: 100%;
left: 0;
box-shadow: 0 0 1px 1px hsla(0, 0%, 0%, .1),
0 1px 0 hsla(0, 0%, 0%, .2);
border-radius: 50%;
background: white;
transition: left .2s ease;
}
.toggle + .toggle-btn::before {
float: left;
left: 9px;
visibility: hidden;
background-size: 16px;
background-repeat: no-repeat;
background-color: transparent;
background-image: url("chrome://browser/skin/privatebrowsing/check.svg");
}
.toggle + .toggle-btn:dir(rtl)::after {
left: auto;
right: 0;
transition-property: right;
}
.toggle + .toggle-btn:dir(rtl)::before {
float: right;
left: auto;
right: 9px;
}
.toggle:checked + .toggle-btn {
background: #3fc455;
border: 1px solid #269939;
background: #16da00;
}
.toggle:checked + .toggle-btn::after {
left: 35px;
left: 16px;
}
.toggle:checked + .toggle-btn:dir(rtl)::after {
right: 35px;
}
.toggle:checked + .toggle-btn::before {
visibility: visible;
left: auto;
right: 16px;
}
.toggle:-moz-focusring + .toggle-btn {

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="#fff" d="M30.057,9.752L15.9,23.909h0l-4.044,4.045-4.045-4.045h0l-6.068-6.067,4.045-4.045,6.068,6.067L26.012,5.707Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 475 B

View File

@ -175,7 +175,8 @@ function waitForSources(dbg, ...sources) {
sources.map(url => {
function sourceExists(state) {
return getSources(state).some(s => {
return s.get("url").includes(url);
let u = s.get("url");
return u && u.includes(url);
});
}
@ -212,8 +213,9 @@ function assertPausedLocation(dbg, source, line) {
is(location.get("line"), line);
// Check the debug line
let cm = dbg.win.document.querySelector(".CodeMirror").CodeMirror;
ok(
dbg.win.cm.lineInfo(line - 1).wrapClass.includes("debug-line"),
cm.lineInfo(line - 1).wrapClass.includes("debug-line"),
"Line is highlighted as paused"
);
}
@ -356,7 +358,10 @@ function findSource(dbg, url) {
}
const sources = dbg.selectors.getSources(dbg.getState());
const source = sources.find(s => s.get("url").includes(url));
const source = sources.find(s => {
let u = s.get("url");
return u && u.includes(url);
});
if (!source) {
throw new Error("Unable to find source: " + url);

View File

@ -23,6 +23,7 @@ support-files =
browser_toolbox_options_enable_serviceworkers_testing_frame_script.js
browser_toolbox_options_enable_serviceworkers_testing.html
serviceworker.js
test_browser_toolbox_debugger.js
[browser_browser_toolbox.js]
[browser_browser_toolbox_debugger.js]

View File

@ -2,10 +2,19 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// This test asserts that the new debugger works from the browser toolbox process
// Its pass a big piece of Javascript string to the browser toolbox process via
// MOZ_TOOLBOX_TEST_SCRIPT env variable. It does that as test resources fetched from
// chrome://mochitests/ package isn't available from browser toolbox process.
// On debug test runner, it takes about 50s to run the test.
requestLongerTimeout(4);
const { setInterval, clearInterval } = require("sdk/timers");
const { fetch } = require("devtools/shared/DevToolsUtils");
const debuggerHeadURL = CHROME_URL_ROOT + "../../debugger/new/test/mochitest/head.js";
const testScriptURL = CHROME_URL_ROOT + "test_browser_toolbox_debugger.js";
add_task(function* runTest() {
yield new Promise(done => {
@ -41,73 +50,64 @@ add_task(function* runTest() {
// which lives in another process. So do not try to use any scope variable!
let env = Components.classes["@mozilla.org/process/environment;1"]
.getService(Components.interfaces.nsIEnvironment);
let testScript = function () {
const { Task } = Components.utils.import("resource://gre/modules/Task.jsm", {});
dump("Opening the browser toolbox and debugger panel\n");
let window, document;
let testUrl = "http://mozilla.org/browser-toolbox-test.js";
Task.spawn(function* () {
dump("Waiting for debugger load\n");
let panel = yield toolbox.selectTool("jsdebugger");
let window = panel.panelWin;
let document = window.document;
yield window.once(window.EVENTS.SOURCE_SHOWN);
dump("Loaded, selecting the test script to debug\n");
let item = document.querySelector(`.dbg-source-item[tooltiptext="${testUrl}"]`);
let onSourceShown = window.once(window.EVENTS.SOURCE_SHOWN);
item.click();
yield onSourceShown;
dump("Selected, setting a breakpoint\n");
let { Sources, editor } = window.DebuggerView;
let onBreak = window.once(window.EVENTS.FETCHED_SCOPES);
editor.emit("gutterClick", 1);
yield onBreak;
dump("Paused, asserting breakpoint position\n");
let url = Sources.selectedItem.attachment.source.url;
if (url != testUrl) {
throw new Error("Breaking on unexpected script: " + url);
// First inject a very minimal head, with simplest assertion methods
// and very common globals
let testHead = (function () {
const info = msg => dump(msg + "\n");
const is = (a, b, description) => {
let msg = "'" + JSON.stringify(a) + "' is equal to '" + JSON.stringify(b) + "'";
if (description) {
msg += " - " + description;
}
let cursor = editor.getCursor();
if (cursor.line != 1) {
throw new Error("Breaking on unexpected line: " + cursor.line);
if (a !== b) {
msg = "FAILURE: " + msg;
dump(msg + "\n");
throw new Error(msg);
} else {
msg = "SUCCESS: " + msg;
dump(msg + "\n");
}
dump("Now, stepping over\n");
let stepOver = window.document.querySelector("#step-over");
let onFetchedScopes = window.once(window.EVENTS.FETCHED_SCOPES);
stepOver.click();
yield onFetchedScopes;
dump("Stepped, asserting step position\n");
url = Sources.selectedItem.attachment.source.url;
if (url != testUrl) {
throw new Error("Stepping on unexpected script: " + url);
};
const ok = (a, description) => {
let msg = "'" + JSON.stringify(a) + "' is true";
if (description) {
msg += " - " + description;
}
cursor = editor.getCursor();
if (cursor.line != 2) {
throw new Error("Stepping on unexpected line: " + cursor.line);
if (!a) {
msg = "FAILURE: " + msg;
dump(msg + "\n");
throw new Error(msg);
} else {
msg = "SUCCESS: " + msg;
dump(msg + "\n");
}
};
const registerCleanupFunction = () => {};
dump("Resume script execution\n");
let resume = window.document.querySelector("#resume");
let onResume = toolbox.target.once("thread-resumed");
resume.click();
yield onResume;
const Cu = Components.utils;
const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
}).toSource().replace(/^\(function \(\) \{|\}\)$/g, "");
// Stringify testHead's function and remove `(function {` prefix and `})` suffix
// to ensure inner symbols gets exposed to next pieces of code
dump("Close the browser toolbox\n");
toolbox.destroy();
// Then inject new debugger head file
let { content } = yield fetch(debuggerHeadURL);
let debuggerHead = content;
// We remove its import of shared-head, which isn't available in browser toolbox process
// And isn't needed thanks to testHead's symbols
debuggerHead = debuggerHead.replace(/Services.scriptloader.loadSubScript[^\)]*\);/, "");
}).catch(error => {
dump("Error while running code in the browser toolbox process:\n");
dump(error + "\n");
dump("stack:\n" + error.stack + "\n");
});
};
env.set("MOZ_TOOLBOX_TEST_SCRIPT", "new " + testScript);
// Finally, fetch the debugger test script that is going to be execute in the browser
// toolbox process
let testScript = (yield fetch(testScriptURL)).content;
let source =
"try {" + testHead + debuggerHead + testScript + "} catch (e) {" +
" dump('Exception: '+ e + ' at ' + e.fileName + ':' + " +
" e.lineNumber + '\\nStack: ' + e.stack + '\\n');" +
"}";
env.set("MOZ_TOOLBOX_TEST_SCRIPT", source);
registerCleanupFunction(() => {
env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
});

View File

@ -0,0 +1,48 @@
/* global toolbox */
info(`START: ${new Error().lineNumber}`);
let testUrl = "http://mozilla.org/browser-toolbox-test.js";
Task.spawn(function* () {
Services.prefs.clearUserPref("devtools.debugger.tabs")
Services.prefs.clearUserPref("devtools.debugger.pending-selected-location")
info("Waiting for debugger load");
yield toolbox.selectTool("jsdebugger");
let dbg = createDebuggerContext(toolbox);
let window = dbg.win;
let document = window.document;
yield waitForSources(dbg, testUrl);
// yield waitForSourceCount(dbg, 6);
info("Loaded, selecting the test script to debug");
// First expand the domain
let domain = [...document.querySelectorAll(".tree-node")].find(node => {
return node.textContent == "mozilla.org";
});
let arrow = domain.querySelector(".arrow");
arrow.click();
let script = [...document.querySelectorAll(".tree-node")].find(node => {
return node.textContent.includes("browser-toolbox-test.js");
});
script = script.querySelector(".node");
script.click();
let onPaused = waitForPaused(dbg);
yield addBreakpoint(dbg, "browser-toolbox-test.js", 2);
yield onPaused;
assertPausedLocation(dbg, "browser-toolbox-test.js", 2);
yield stepIn(dbg);
assertPausedLocation(dbg, "browser-toolbox-test.js", 3);
yield resume(dbg);
info("Close the browser toolbox");
toolbox.destroy();
});

View File

@ -70,7 +70,7 @@ function setPrefDefaults() {
Services.prefs.setBoolPref("devtools.scratchpad.enabled", true);
// Bug 1225160 - Using source maps with browser debugging can lead to a crash
Services.prefs.setBoolPref("devtools.debugger.source-maps-enabled", false);
Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
Services.prefs.setBoolPref("devtools.debugger.client-source-maps-enabled", true);
}
@ -157,16 +157,16 @@ function bindToolboxHandlers() {
}
function setupThreadListeners(panel) {
updateBadgeText(panel._controller.activeThread.state == "paused");
updateBadgeText(panel._selectors().getPause(panel._getState()));
let onPaused = updateBadgeText.bind(null, true);
let onResumed = updateBadgeText.bind(null, false);
panel.target.on("thread-paused", onPaused);
panel.target.on("thread-resumed", onResumed);
gToolbox.target.on("thread-paused", onPaused);
gToolbox.target.on("thread-resumed", onResumed);
panel.once("destroyed", () => {
panel.off("thread-paused", onPaused);
panel.off("thread-resumed", onResumed);
gToolbox.target.off("thread-paused", onPaused);
gToolbox.target.off("thread-resumed", onResumed);
});
}

View File

@ -72,6 +72,7 @@ skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keybo
[browser_inspector_highlighter-cancel.js]
[browser_inspector_highlighter-comments.js]
[browser_inspector_highlighter-cssgrid_01.js]
[browser_inspector_highlighter-cssgrid_02.js]
[browser_inspector_highlighter-csstransform_01.js]
[browser_inspector_highlighter-csstransform_02.js]
[browser_inspector_highlighter-embed.js]

View File

@ -0,0 +1,44 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Test that grid layouts without items don't cause grid highlighter errors.
const TEST_URL = `
<style type='text/css'>
.grid {
display: grid;
grid-template-columns: 20px 20px;
grid-gap: 15px;
}
</style>
<div class="grid"></div>
`;
const HIGHLIGHTER_TYPE = "CssGridHighlighter";
add_task(function* () {
let {inspector, testActor} = yield openInspectorForURL(
"data:text/html;charset=utf-8," + encodeURIComponent(TEST_URL));
let front = inspector.inspector;
let highlighter = yield front.getHighlighterByType(HIGHLIGHTER_TYPE);
info("Try to show the highlighter on the grid container");
let node = yield getNodeFront(".grid", inspector);
yield highlighter.show(node);
let hidden = yield testActor.getHighlighterNodeAttribute(
"css-grid-canvas", "hidden", highlighter);
ok(!hidden, "The highlighter is visible");
info("Hiding the highlighter");
yield highlighter.hide();
hidden = yield testActor.getHighlighterNodeAttribute(
"css-grid-canvas", "hidden", highlighter);
ok(hidden, "The highlighter is hidden");
yield highlighter.finalize();
});

View File

@ -501,7 +501,7 @@ netmonitor.toolbar.cookies=Cookies
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
netmonitor.toolbar.setCookies=Set-Cookies
# LOCALIZATION NOTE (netmonitor.toolbar.cookies): This is the label displayed
# LOCALIZATION NOTE (netmonitor.toolbar.scheme): This is the label displayed
# in the network table toolbar, above the "scheme" column.
netmonitor.toolbar.scheme=Scheme

View File

@ -116,10 +116,6 @@ function processFlagFilter(type, value) {
}
}
function getSizeOrder(size) {
return Math.round(Math.log10(size));
}
function isFlagFilterMatch(item, { type, value, negative }) {
let match = true;
let { responseCookies = { cookies: [] } } = item;
@ -160,11 +156,11 @@ function isFlagFilterMatch(item, { type, value, negative }) {
if (item.fromCache) {
match = false;
} else {
match = getSizeOrder(value) === getSizeOrder(item.transferredSize);
match = isSizeMatch(value, item.transferredSize);
}
break;
case "size":
match = getSizeOrder(value) === getSizeOrder(item.contentSize);
match = isSizeMatch(value, item.contentSize);
break;
case "larger-than":
match = item.contentSize > value;
@ -216,6 +212,10 @@ function isFlagFilterMatch(item, { type, value, negative }) {
return match;
}
function isSizeMatch(value, size) {
return value >= (size - size / 10) && value < (size + size / 10);
}
function isTextFilterMatch({ url }, text) {
let lowerCaseUrl = url.toLowerCase();
let lowerCaseText = text.toLowerCase();

View File

@ -695,6 +695,18 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
return this.currentNode.getGridFragments().length > 0;
},
/**
* Is a given grid fragment valid? i.e. does it actually have tracks? In some cases, we
* may have a fragment that defines column tracks but doesn't have any rows (or vice
* versa). In which case we do not want to draw anything for that fragment.
*
* @param {Object} fragment
* @return {Boolean}
*/
isValidFragment(fragment) {
return fragment.cols.tracks.length && fragment.rows.tracks.length;
},
/**
* The AutoRefreshHighlighter's _hasMoved method returns true only if the
* element's quads have changed. Override it so it also returns true if the
@ -740,8 +752,7 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
// Start drawing the grid fragments.
for (let i = 0; i < this.gridData.length; i++) {
let fragment = this.gridData[i];
this.renderFragment(fragment);
this.renderFragment(this.gridData[i]);
}
// Display the grid area highlights if needed.
@ -1030,6 +1041,10 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
},
renderFragment(fragment) {
if (!this.isValidFragment(fragment)) {
return;
}
this.renderLines(fragment.cols, COLUMNS, "left", "top", "height",
this.getFirstRowLinePos(fragment),
this.getLastRowLinePos(fragment));

View File

@ -201,7 +201,7 @@ contextEmptyReply=Command prefix is unset
# LOCALIZATION NOTE (connectDesc, connectManual, connectPrefixDesc,
# connectMethodDesc, connectUrlDesc, connectDupReply): These strings describe
# the 'connect' command and all its available parameters. A 'prefix' is an
# the 'connect' command and all its available parameters. A 'prefix' is an
# alias for the remote server (think of it as a "connection name"), and it
# allows to identify a specific server when connected to multiple remote
# servers.
@ -230,20 +230,9 @@ disconnectPrefixDesc=Parent prefix for imported commands
# commands removed.
disconnectReply=Removed %S commands.
# LOCALIZATION NOTE (globalDesc, globalWindowDesc, globalOutput): These
# strings describe the 'global' command and its parameters
globalDesc=Change the JS global
globalWindowDesc=The new window/global
globalOutput=JS global is now %S
# LOCALIZATION NOTE: These strings describe the 'clear' command
clearDesc=Clear the output area
# LOCALIZATION NOTE (langDesc, langOutput): These strings describe the 'lang'
# command and its parameters
langDesc=Enter commands in different languages
langOutput=You are now using %S
# LOCALIZATION NOTE (prefDesc, prefManual, prefListDesc, prefListManual,
# prefListSearchDesc, prefListSearchManual, prefShowDesc, prefShowManual,
# prefShowSettingDesc, prefShowSettingManual): These strings describe the
@ -292,12 +281,6 @@ prefOutputFilter=Filter
prefOutputName=Name
prefOutputValue=Value
# LOCALIZATION NOTE (introDesc, introManual): These strings describe the
# 'intro' command. The localization of 'Got it!' should be the same used in
# introTextGo.
introDesc=Show the opening message
introManual=Redisplay the message that is shown to new users until they click the Got it! button
# LOCALIZATION NOTE (introTextOpening3, introTextCommands, introTextKeys2,
# introTextF1Escape, introTextGo): These strings are displayed when the user
# first opens the developer toolbar to explain the command line, and is shown

View File

@ -21,6 +21,7 @@ interface nsIDocShellTreeItem;
interface nsIStructuredCloneContainer;
interface nsIBFCacheEntry;
interface nsIPrincipal;
interface nsISHistory;
%{C++
#include "nsRect.h"
@ -326,6 +327,11 @@ interface nsISHEntry : nsISupports
* if true == "manual", false == "auto".
*/
attribute boolean scrollRestorationIsManual;
/**
* Set the session history it belongs to. It's only set on root entries.
*/
[noscript] void setSHistory(in nsISHistory aSHistory);
};
[scriptable, uuid(bb66ac35-253b-471f-a317-3ece940f04c5)]

View File

@ -96,6 +96,16 @@ interface nsISHistoryInternal: nsISupports
*/
void evictAllContentViewers();
/**
* Add a BFCache entry to expiration tracker so it gets evicted on expiration.
*/
void addToExpirationTracker(in nsIBFCacheEntry aEntry);
/**
* Remove a BFCache entry from expiration tracker.
*/
void removeFromExpirationTracker(in nsIBFCacheEntry aEntry);
/**
* Remove dynamic entries found at given index.
*

View File

@ -5,17 +5,21 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsSHEntry.h"
#include <algorithm>
#include "nsDocShellEditorData.h"
#include "nsIContentViewer.h"
#include "nsIDocShellLoadInfo.h"
#include "nsIDocShellTreeItem.h"
#include "nsDocShellEditorData.h"
#include "nsSHEntryShared.h"
#include "nsILayoutHistoryState.h"
#include "nsIContentViewer.h"
#include "nsIStructuredCloneContainer.h"
#include "nsIInputStream.h"
#include "nsILayoutHistoryState.h"
#include "nsIStructuredCloneContainer.h"
#include "nsIURI.h"
#include "nsSHEntryShared.h"
#include "nsSHistory.h"
#include "mozilla/net/ReferrerPolicy.h"
#include <algorithm>
namespace dom = mozilla::dom;
@ -23,13 +27,13 @@ static uint32_t gEntryID = 0;
nsSHEntry::nsSHEntry()
: mShared(new nsSHEntryShared())
, mLoadReplace(false)
, mReferrerPolicy(mozilla::net::RP_Unset)
, mLoadType(0)
, mID(gEntryID++)
, mScrollPositionX(0)
, mScrollPositionY(0)
, mParent(nullptr)
, mLoadReplace(false)
, mURIWasModified(false)
, mIsSrcdocEntry(false)
, mScrollRestorationIsManual(false)
@ -40,7 +44,6 @@ nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
: mShared(aOther.mShared)
, mURI(aOther.mURI)
, mOriginalURI(aOther.mOriginalURI)
, mLoadReplace(aOther.mLoadReplace)
, mReferrerURI(aOther.mReferrerURI)
, mReferrerPolicy(aOther.mReferrerPolicy)
, mTitle(aOther.mTitle)
@ -50,12 +53,13 @@ nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
, mScrollPositionX(0) // XXX why not copy?
, mScrollPositionY(0) // XXX why not copy?
, mParent(aOther.mParent)
, mURIWasModified(aOther.mURIWasModified)
, mStateData(aOther.mStateData)
, mIsSrcdocEntry(aOther.mIsSrcdocEntry)
, mScrollRestorationIsManual(false)
, mSrcdocData(aOther.mSrcdocData)
, mBaseURI(aOther.mBaseURI)
, mLoadReplace(aOther.mLoadReplace)
, mURIWasModified(aOther.mURIWasModified)
, mIsSrcdocEntry(aOther.mIsSrcdocEntry)
, mScrollRestorationIsManual(false)
{
}
@ -958,3 +962,10 @@ nsSHEntry::SetLastTouched(uint32_t aLastTouched)
mShared->mLastTouched = aLastTouched;
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::SetSHistory(nsISHistory* aSHistory)
{
mShared->mSHistory = do_GetWeakReference(aSHistory);
return NS_OK;
}

View File

@ -7,15 +7,13 @@
#ifndef nsSHEntry_h
#define nsSHEntry_h
// Helper Classes
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
#include "nsString.h"
#include "mozilla/Attributes.h"
// Interfaces needed
#include "nsISHEntry.h"
#include "nsCOMPtr.h"
#include "nsISHContainer.h"
#include "nsISHEntry.h"
#include "nsString.h"
#include "mozilla/Attributes.h"
class nsSHEntryShared;
class nsIInputStream;
@ -49,7 +47,6 @@ private:
// See nsSHEntry.idl for comments on these members.
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIURI> mOriginalURI;
bool mLoadReplace;
nsCOMPtr<nsIURI> mReferrerURI;
uint32_t mReferrerPolicy;
nsString mTitle;
@ -60,12 +57,13 @@ private:
int32_t mScrollPositionY;
nsISHEntry* mParent;
nsCOMArray<nsISHEntry> mChildren;
bool mURIWasModified;
nsCOMPtr<nsIStructuredCloneContainer> mStateData;
bool mIsSrcdocEntry;
bool mScrollRestorationIsManual;
nsString mSrcdocData;
nsCOMPtr<nsIURI> mBaseURI;
bool mLoadReplace;
bool mURIWasModified;
bool mIsSrcdocEntry;
bool mScrollRestorationIsManual;
};
#endif /* nsSHEntry_h */

View File

@ -6,20 +6,21 @@
#include "nsSHEntryShared.h"
#include "nsIDOMDocument.h"
#include "nsISHistory.h"
#include "nsISHistoryInternal.h"
#include "nsIDocument.h"
#include "nsIWebNavigation.h"
#include "nsArray.h"
#include "nsDocShellEditorData.h"
#include "nsIContentViewer.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsDocShellEditorData.h"
#include "nsThreadUtils.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsILayoutHistoryState.h"
#include "nsISHistory.h"
#include "nsISHistoryInternal.h"
#include "nsIWebNavigation.h"
#include "nsThreadUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/Preferences.h"
#include "nsArray.h"
namespace dom = mozilla::dom;
@ -29,78 +30,27 @@ uint64_t gSHEntrySharedID = 0;
} // namespace
#define CONTENT_VIEWER_TIMEOUT_SECONDS "browser.sessionhistory.contentViewerTimeout"
// Default this to time out unused content viewers after 30 minutes
#define CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT (30 * 60)
typedef nsExpirationTracker<nsSHEntryShared, 3> HistoryTrackerBase;
class HistoryTracker final : public HistoryTrackerBase
{
public:
explicit HistoryTracker(uint32_t aTimeout)
: HistoryTrackerBase(1000 * aTimeout / 2, "HistoryTracker")
{
}
protected:
virtual void NotifyExpired(nsSHEntryShared* aObj)
{
RemoveObject(aObj);
aObj->Expire();
}
};
static HistoryTracker* gHistoryTracker = nullptr;
void
nsSHEntryShared::EnsureHistoryTracker()
{
if (!gHistoryTracker) {
// nsExpirationTracker doesn't allow one to change the timer period,
// so just set it once when the history tracker is used for the first time.
gHistoryTracker = new HistoryTracker(
mozilla::Preferences::GetUint(CONTENT_VIEWER_TIMEOUT_SECONDS,
CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT));
}
}
void
nsSHEntryShared::Shutdown()
{
delete gHistoryTracker;
gHistoryTracker = nullptr;
}
nsSHEntryShared::nsSHEntryShared()
: mDocShellID({0})
, mLastTouched(0)
, mID(gSHEntrySharedID++)
, mViewerBounds(0, 0, 0, 0)
, mIsFrameNavigation(false)
, mSaveLayoutState(true)
, mSticky(true)
, mDynamicallyCreated(false)
, mLastTouched(0)
, mID(gSHEntrySharedID++)
, mExpired(false)
, mViewerBounds(0, 0, 0, 0)
{
}
nsSHEntryShared::~nsSHEntryShared()
{
RemoveFromExpirationTracker();
#ifdef DEBUG
if (gHistoryTracker) {
// Check that we're not still on track to expire. We shouldn't be, because
// we just removed ourselves!
nsExpirationTracker<nsSHEntryShared, 3>::Iterator iterator(gHistoryTracker);
nsSHEntryShared* elem;
while ((elem = iterator.Next()) != nullptr) {
NS_ASSERTION(elem != this, "Found dead entry still in the tracker!");
}
}
#endif
if (mContentViewer) {
RemoveFromBFCacheSync();
}
@ -131,8 +81,9 @@ nsSHEntryShared::Duplicate(nsSHEntryShared* aEntry)
void
nsSHEntryShared::RemoveFromExpirationTracker()
{
if (gHistoryTracker && GetExpirationState()->IsTracked()) {
gHistoryTracker->RemoveObject(this);
nsCOMPtr<nsISHistoryInternal> shistory = do_QueryReferent(mSHistory);
if (shistory && GetExpirationState()->IsTracked()) {
shistory->RemoveFromExpirationTracker(this);
}
}
@ -173,34 +124,6 @@ nsSHEntryShared::DropPresentationState()
mEditorData = nullptr;
}
void
nsSHEntryShared::Expire()
{
// This entry has timed out. If we still have a content viewer, we need to
// evict it.
if (!mContentViewer) {
return;
}
nsCOMPtr<nsIDocShell> container;
mContentViewer->GetContainer(getter_AddRefs(container));
nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container);
if (!treeItem) {
return;
}
// We need to find the root DocShell since only that object has an
// SHistory and we need the SHistory to evict content viewers
nsCOMPtr<nsIDocShellTreeItem> root;
treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
nsCOMPtr<nsISHistory> history;
webNav->GetSessionHistory(getter_AddRefs(history));
nsCOMPtr<nsISHistoryInternal> historyInt = do_QueryInterface(history);
if (!historyInt) {
return;
}
historyInt->EvictExpiredContentViewerForEntry(this);
}
nsresult
nsSHEntryShared::SetContentViewer(nsIContentViewer* aViewer)
{
@ -214,8 +137,13 @@ nsSHEntryShared::SetContentViewer(nsIContentViewer* aViewer)
mContentViewer = aViewer;
if (mContentViewer) {
EnsureHistoryTracker();
gHistoryTracker->AddObject(this);
// mSHistory is only set for root entries, but in general bfcache only
// applies to root entries as well. BFCache for subframe navigation has been
// disabled since 2005 in bug 304860.
nsCOMPtr<nsISHistoryInternal> shistory = do_QueryReferent(mSHistory);
if (shistory) {
shistory->AddToExpirationTracker(this);
}
nsCOMPtr<nsIDOMDocument> domDoc;
mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));

View File

@ -7,14 +7,16 @@
#ifndef nsSHEntryShared_h__
#define nsSHEntryShared_h__
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
#include "nsExpirationTracker.h"
#include "nsIBFCacheEntry.h"
#include "nsIMutationObserver.h"
#include "nsExpirationTracker.h"
#include "nsRect.h"
#include "nsString.h"
#include "nsWeakPtr.h"
#include "mozilla/Attributes.h"
class nsSHEntry;
@ -53,12 +55,9 @@ private:
friend class nsSHEntry;
friend class HistoryTracker;
static already_AddRefed<nsSHEntryShared> Duplicate(nsSHEntryShared* aEntry);
void RemoveFromExpirationTracker();
void Expire();
nsresult SyncPresentationState();
void DropPresentationState();
@ -73,10 +72,7 @@ private:
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
nsCString mContentType;
bool mIsFrameNavigation;
bool mSaveLayoutState;
bool mSticky;
bool mDynamicallyCreated;
nsCOMPtr<nsISupports> mCacheKey;
uint32_t mLastTouched;
@ -86,12 +82,20 @@ private:
nsCOMPtr<nsIContentViewer> mContentViewer;
nsCOMPtr<nsIDocument> mDocument;
nsCOMPtr<nsILayoutHistoryState> mLayoutHistoryState;
bool mExpired;
nsCOMPtr<nsISupports> mWindowState;
nsIntRect mViewerBounds;
nsCOMPtr<nsIMutableArray> mRefreshURIList;
nsExpirationState mExpirationState;
nsAutoPtr<nsDocShellEditorData> mEditorData;
nsWeakPtr mSHistory;
bool mIsFrameNavigation;
bool mSaveLayoutState;
bool mSticky;
bool mDynamicallyCreated;
// This flag is about necko cache, not bfcache.
bool mExpired;
};
#endif

View File

@ -8,8 +8,8 @@
#include "nsISHEntry.h"
nsSHTransaction::nsSHTransaction()
: mPersist(true)
, mPrev(nullptr)
: mPrev(nullptr)
, mPersist(true)
{
}

View File

@ -7,10 +7,7 @@
#ifndef nsSHTransaction_h
#define nsSHTransaction_h
// Helper Classes
#include "nsCOMPtr.h"
// Needed interfaces
#include "nsISHTransaction.h"
class nsISHEntry;
@ -27,11 +24,10 @@ protected:
virtual ~nsSHTransaction();
protected:
bool mPersist;
nsISHTransaction* mPrev; // Weak Reference
nsCOMPtr<nsISHTransaction> mNext;
nsCOMPtr<nsISHEntry> mSHEntry;
bool mPersist;
};
#endif /* nsSHTransaction_h */

View File

@ -5,41 +5,43 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsSHistory.h"
#include <algorithm>
// Helper Classes
#include "mozilla/Preferences.h"
#include "mozilla/StaticPtr.h"
// Interfaces Needed
#include "nsILayoutHistoryState.h"
#include "nsCOMArray.h"
#include "nsComponentManagerUtils.h"
#include "nsDocShell.h"
#include "nsIContentViewer.h"
#include "nsIDocShell.h"
#include "nsIDocShellLoadInfo.h"
#include "nsISHContainer.h"
#include "nsIDocShellTreeItem.h"
#include "nsIURI.h"
#include "nsIContentViewer.h"
#include "nsILayoutHistoryState.h"
#include "nsIObserverService.h"
#include "mozilla/Services.h"
#include "nsISHContainer.h"
#include "nsISHEntry.h"
#include "nsISHistoryListener.h"
#include "nsISHTransaction.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsTArray.h"
#include "nsCOMArray.h"
#include "nsDocShell.h"
#include "prsystem.h"
#include "mozilla/Attributes.h"
#include "mozilla/LinkedList.h"
#include "nsISHEntry.h"
#include "nsISHTransaction.h"
#include "nsISHistoryListener.h"
#include "nsComponentManagerUtils.h"
#include "nsNetUtil.h"
// For calculating max history entries and max cachable contentviewers
#include "prsystem.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/TabGroup.h"
using namespace mozilla;
#define PREF_SHISTORY_SIZE "browser.sessionhistory.max_entries"
#define PREF_SHISTORY_MAX_TOTAL_VIEWERS "browser.sessionhistory.max_total_viewers"
#define CONTENT_VIEWER_TIMEOUT_SECONDS "browser.sessionhistory.contentViewerTimeout"
// Default this to time out unused content viewers after 30 minutes
#define CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT (30 * 60)
static const char* kObservedPrefs[] = {
PREF_SHISTORY_SIZE,
@ -233,10 +235,10 @@ nsSHistory::nsSHistory()
: mIndex(-1)
, mLength(0)
, mRequestedIndex(-1)
, mIsPartial(false)
, mGlobalIndexOffset(0)
, mEntriesInFollowingPartialHistories(0)
, mRootDocShell(nullptr)
, mIsPartial(false)
{
// Add this new SHistory object to the list
gSHistoryList.insertBack(this);
@ -254,6 +256,7 @@ NS_INTERFACE_MAP_BEGIN(nsSHistory)
NS_INTERFACE_MAP_ENTRY(nsISHistory)
NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
NS_INTERFACE_MAP_ENTRY(nsISHistoryInternal)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END
// static
@ -382,6 +385,8 @@ nsSHistory::AddEntry(nsISHEntry* aSHEntry, bool aPersist)
{
NS_ENSURE_ARG(aSHEntry);
aSHEntry->SetSHistory(this);
// If we have a root docshell, update the docshell id of the root shentry to
// match the id of that docshell
if (mRootDocShell) {
@ -1299,6 +1304,30 @@ nsSHistory::EvictExpiredContentViewerForEntry(nsIBFCacheEntry* aEntry)
return NS_OK;
}
NS_IMETHODIMP
nsSHistory::AddToExpirationTracker(nsIBFCacheEntry* aEntry)
{
RefPtr<nsSHEntryShared> entry = static_cast<nsSHEntryShared*>(aEntry);
if (!mHistoryTracker || !entry) {
return NS_ERROR_FAILURE;
}
mHistoryTracker->AddObject(entry);
return NS_OK;
}
NS_IMETHODIMP
nsSHistory::RemoveFromExpirationTracker(nsIBFCacheEntry* aEntry)
{
RefPtr<nsSHEntryShared> entry = static_cast<nsSHEntryShared*>(aEntry);
if (!mHistoryTracker || !entry) {
return NS_ERROR_FAILURE;
}
mHistoryTracker->RemoveObject(entry);
return NS_OK;
}
// Evicts all content viewers in all history objects. This is very
// inefficient, because it requires a linear search through all SHistory
// objects for each viewer to be evicted. However, this method is called
@ -1899,6 +1928,23 @@ NS_IMETHODIMP
nsSHistory::SetRootDocShell(nsIDocShell* aDocShell)
{
mRootDocShell = aDocShell;
// Init mHistoryTracker on setting mRootDocShell so we can bind its event
// target to the tabGroup.
if (mRootDocShell) {
nsCOMPtr<nsPIDOMWindowOuter> win = mRootDocShell->GetWindow();
if (!win) {
return NS_ERROR_UNEXPECTED;
}
RefPtr<mozilla::dom::TabGroup> tabGroup = win->TabGroup();
mHistoryTracker = mozilla::MakeUnique<HistoryTracker>(
this,
mozilla::Preferences::GetUint(CONTENT_VIEWER_TIMEOUT_SECONDS,
CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT),
tabGroup->EventTargetFor(mozilla::TaskCategory::Other));
}
return NS_OK;
}

View File

@ -8,15 +8,18 @@
#define nsSHistory_h
#include "nsCOMPtr.h"
#include "nsExpirationTracker.h"
#include "nsIPartialSHistoryListener.h"
#include "nsISHistory.h"
#include "nsISHistoryInternal.h"
#include "nsIWebNavigation.h"
#include "nsISimpleEnumerator.h"
#include "nsIWebNavigation.h"
#include "nsSHEntryShared.h"
#include "nsTObserverArray.h"
#include "nsWeakPtr.h"
#include "nsIPartialSHistoryListener.h"
#include "nsWeakReference.h"
#include "mozilla/LinkedList.h"
#include "mozilla/UniquePtr.h"
class nsIDocShell;
class nsSHEnumerator;
@ -27,9 +30,37 @@ class nsISHTransaction;
class nsSHistory final : public mozilla::LinkedListElement<nsSHistory>,
public nsISHistory,
public nsISHistoryInternal,
public nsIWebNavigation
public nsIWebNavigation,
public nsSupportsWeakReference
{
public:
// The timer based history tracker is used to evict bfcache on expiration.
class HistoryTracker final : public nsExpirationTracker<nsSHEntryShared, 3>
{
public:
explicit HistoryTracker(nsSHistory* aSHistory,
uint32_t aTimeout,
nsIEventTarget* aEventTarget)
: nsExpirationTracker(1000 * aTimeout / 2, "HistoryTracker", aEventTarget)
{
MOZ_ASSERT(aSHistory);
mSHistory = aSHistory;
}
protected:
virtual void NotifyExpired(nsSHEntryShared* aObj)
{
RemoveObject(aObj);
mSHistory->EvictExpiredContentViewerForEntry(aObj);
}
private:
// HistoryTracker is owned by nsSHistory; it always outlives HistoryTracker
// so it's safe to use raw pointer here.
nsSHistory* mSHistory;
};
nsSHistory();
NS_DECL_ISUPPORTS
NS_DECL_NSISHISTORY
@ -85,14 +116,14 @@ private:
// otherwise comparison is done to aIndex - 1.
bool RemoveDuplicate(int32_t aIndex, bool aKeepNext);
// Track all bfcache entries and evict on expiration.
mozilla::UniquePtr<HistoryTracker> mHistoryTracker;
nsCOMPtr<nsISHTransaction> mListRoot;
int32_t mIndex;
int32_t mLength;
int32_t mRequestedIndex;
// Set to true if attached to a grouped session history.
bool mIsPartial;
// The number of entries before this session history object.
int32_t mGlobalIndexOffset;
@ -108,6 +139,9 @@ private:
// Weak reference. Do not refcount this.
nsIDocShell* mRootDocShell;
// Set to true if attached to a grouped session history.
bool mIsPartial;
// Max viewers allowed total, across all SHistory objects
static int32_t sHistoryMaxTotalViewers;
};

View File

@ -49,6 +49,7 @@ support-files =
[browser_bug1206879.js]
[browser_bug1309900_crossProcessHistoryNavigation.js]
[browser_bug1347823.js]
[browser_bug134911.js]
[browser_bug234628-1.js]
[browser_bug234628-10.js]

View File

@ -0,0 +1,67 @@
/**
* Test that session history's expiration tracker would remove bfcache on
* expiration.
*/
// With bfcache not expired.
add_task(async function testValidCache() {
// Make an unrealistic large timeout.
await SpecialPowers.pushPrefEnv({
set: [["browser.sessionhistory.contentViewerTimeout", 86400]]
});
await BrowserTestUtils.withNewTab(
{gBrowser, url: "data:text/html;charset=utf-8,page1"},
async function(browser) {
// Make a simple modification for bfcache testing.
await ContentTask.spawn(browser, null, () => {
content.document.body.textContent = "modified";
});
// Load a random page.
BrowserTestUtils.loadURI(browser, "data:text/html;charset=utf-8,page2");
await BrowserTestUtils.browserLoaded(browser);
// Go back and verify text content.
let awaitPageShow = BrowserTestUtils.waitForContentEvent(browser, "pageshow");
browser.goBack();
await awaitPageShow;
await ContentTask.spawn(browser, null, () => {
is(content.document.body.textContent, "modified");
});
});
});
// With bfcache expired.
add_task(async function testExpiredCache() {
// Make bfcache timeout in 1 sec.
await SpecialPowers.pushPrefEnv({
set: [["browser.sessionhistory.contentViewerTimeout", 1]]
});
await BrowserTestUtils.withNewTab(
{gBrowser, url: "data:text/html;charset=utf-8,page1"},
async function(browser) {
// Make a simple modification for bfcache testing.
await ContentTask.spawn(browser, null, () => {
content.document.body.textContent = "modified";
});
// Load a random page.
BrowserTestUtils.loadURI(browser, "data:text/html;charset=utf-8,page2");
await BrowserTestUtils.browserLoaded(browser);
// Wait for 3 times of expiration timeout, hopefully it's evicted...
await new Promise(resolve => {
setTimeout(resolve, 3000);
});
// Go back and verify text content.
let awaitPageShow = BrowserTestUtils.waitForContentEvent(browser, "pageshow");
browser.goBack();
await awaitPageShow;
await ContentTask.spawn(browser, null, () => {
is(content.document.body.textContent, "page1");
});
});
});

View File

@ -2690,10 +2690,12 @@ nsDOMWindowUtils::ComputeAnimationDistance(nsIDOMElement* aElement,
return NS_ERROR_ILLEGAL_VALUE;
}
nsIPresShell* shell = element->GetComposedDoc()->GetShell();
RefPtr<nsStyleContext> styleContext = shell
? nsComputedDOMStyle::GetStyleContext(element, nullptr, shell)
: nullptr;
RefPtr<nsStyleContext> styleContext;
nsIDocument* doc = element->GetComposedDoc();
if (doc && doc->GetShell()) {
styleContext =
nsComputedDOMStyle::GetStyleContext(element, nullptr, doc->GetShell());
}
*aResult = v1.ComputeDistance(property, v2, styleContext);
return NS_OK;
}

View File

@ -107,7 +107,7 @@ CaptureStreamTestHelper.prototype = {
*/
isOpaquePixelNot: function(px, refColor, threshold) {
px[3] = refColor.data[3];
return h.isPixelNot(px, refColor, threshold);
return this.isPixelNot(px, refColor, threshold);
},
/*

View File

@ -45,8 +45,8 @@ GamepadEventChannelParent::GamepadEventChannelParent()
RefPtr<GamepadPlatformService> service =
GamepadPlatformService::GetParentService();
MOZ_ASSERT(service);
service->AddChannelParent(this);
mBackgroundThread = NS_GetCurrentThread();
service->AddChannelParent(this);
}
mozilla::ipc::IPCResult

View File

@ -5249,6 +5249,10 @@ void HTMLMediaElement::DecodeError(const MediaResult& aError)
AudioTracks()->EmptyTracks();
VideoTracks()->EmptyTracks();
if (mIsLoadingFromSourceChildren) {
if (mDecoder) {
// Shut down the exiting decoder before loading the next source child.
ShutdownDecoder();
}
mErrorSink->ResetError();
if (mSourceLoadCandidate) {
DispatchAsyncSourceError(mSourceLoadCandidate);

View File

@ -2658,20 +2658,14 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
INIT_CANONICAL(mCurrentPosition, TimeUnit::Zero()),
INIT_CANONICAL(mPlaybackOffset, 0),
INIT_CANONICAL(mIsAudioDataAudible, false)
#ifdef XP_WIN
, mShouldUseHiResTimers(Preferences::GetBool("media.hi-res-timers.enabled", true))
#endif
{
MOZ_COUNT_CTOR(MediaDecoderStateMachine);
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
InitVideoQueuePrefs();
#ifdef XP_WIN
// Ensure high precision timers are enabled on Windows, otherwise the state
// machine isn't woken up at reliable intervals to set the next frame, and we
// drop frames while painting. Note that multiple calls to this function
// per-process is OK, provided each call is matched by a corresponding
// timeEndPeriod() call.
timeBeginPeriod(1);
#endif
}
#undef INIT_WATCHABLE
@ -2684,7 +2678,7 @@ MediaDecoderStateMachine::~MediaDecoderStateMachine()
MOZ_COUNT_DTOR(MediaDecoderStateMachine);
#ifdef XP_WIN
timeEndPeriod(1);
MOZ_ASSERT(!mHiResTimersRequested);
#endif
}
@ -2895,6 +2889,12 @@ MediaDecoderStateMachine::StopPlayback()
if (IsPlaying()) {
mMediaSink->SetPlaying(false);
MOZ_ASSERT(!IsPlaying());
#ifdef XP_WIN
if (mHiResTimersRequested) {
mHiResTimersRequested = false;
timeEndPeriod(1);
}
#endif
}
}
@ -2918,6 +2918,20 @@ void MediaDecoderStateMachine::MaybeStartPlayback()
mOnPlaybackEvent.Notify(MediaEventType::PlaybackStarted);
StartMediaSink();
#ifdef XP_WIN
if (!mHiResTimersRequested && mShouldUseHiResTimers) {
mHiResTimersRequested = true;
// Ensure high precision timers are enabled on Windows, otherwise the state
// machine isn't woken up at reliable intervals to set the next frame, and we
// drop frames while painting. Note that each call must be matched by a
// corresponding timeEndPeriod() call. Enabling high precision timers causes
// the CPU to wake up more frequently on Windows 7 and earlier, which causes
// more CPU load and battery use. So we only enable high precision timers
// when we're actually playing.
timeBeginPeriod(1);
}
#endif
if (!IsPlaying()) {
mMediaSink->SetPlaying(true);
MOZ_ASSERT(IsPlaying());

View File

@ -779,6 +779,17 @@ public:
{
return &mIsAudioDataAudible;
}
#ifdef XP_WIN
// Whether we've called timeBeginPeriod(1) to request high resolution
// timers. We request high resolution timers when playback starts, and
// turn them off when playback is paused. Enabling high resolution
// timers can cause higher CPU usage and battery drain on Windows 7.
bool mHiResTimersRequested = false;
// Whether we should enable high resolution timers. This is initialized at
// MDSM construction, and mirrors the value of media.hi-res-timers.enabled.
const bool mShouldUseHiResTimers;
#endif
};
} // namespace mozilla

View File

@ -242,6 +242,17 @@ public:
return bytes.forget();
}
already_AddRefed<MediaByteBuffer> CachedReadAt(int64_t aOffset, uint32_t aCount)
{
RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
bool ok = bytes->SetLength(aCount, fallible);
NS_ENSURE_TRUE(ok, nullptr);
char* curr = reinterpret_cast<char*>(bytes->Elements());
nsresult rv = ReadFromCache(curr, aOffset, aCount);
NS_ENSURE_SUCCESS(rv, nullptr);
return bytes.forget();
}
// Report the current offset in bytes from the start of the stream.
// This is used to approximate where we currently are in the playback of a
// media.

View File

@ -725,7 +725,7 @@ GMPParent::ReadChromiumManifestFile(nsIFile* aFile)
}
// DOM JSON parsing needs to run on the main thread.
return InvokeAsync<nsString&&>(
return InvokeAsync(
mMainThread, this, __func__,
&GMPParent::ParseChromiumManifest, NS_ConvertUTF8toUTF16(json));
}

View File

@ -642,7 +642,7 @@ GeckoMediaPluginServiceParent::AsyncAddPluginDirectory(const nsAString& aDirecto
nsString dir(aDirectory);
RefPtr<GeckoMediaPluginServiceParent> self = this;
return InvokeAsync<nsString&&>(
return InvokeAsync(
thread, this, __func__,
&GeckoMediaPluginServiceParent::AddOnGMPThread, dir)
->Then(

View File

@ -417,7 +417,7 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
}
StartUpdating();
mTrackBuffersManager->AppendData(data, mCurrentAttributes)
mTrackBuffersManager->AppendData(data.forget(), mCurrentAttributes)
->Then(mAbstractMainThread, __func__, this,
&SourceBuffer::AppendDataCompletedWithSuccess,
&SourceBuffer::AppendDataErrored)

View File

@ -47,7 +47,7 @@ protected:
class AppendBufferTask : public SourceBufferTask {
public:
AppendBufferTask(MediaByteBuffer* aData,
AppendBufferTask(already_AddRefed<MediaByteBuffer> aData,
const SourceBufferAttributes& aAttributes)
: mBuffer(aData)
, mAttributes(aAttributes)

View File

@ -116,24 +116,24 @@ TrackBuffersManager::~TrackBuffersManager()
}
RefPtr<TrackBuffersManager::AppendPromise>
TrackBuffersManager::AppendData(MediaByteBuffer* aData,
TrackBuffersManager::AppendData(already_AddRefed<MediaByteBuffer> aData,
const SourceBufferAttributes& aAttributes)
{
MOZ_ASSERT(NS_IsMainThread());
MSE_DEBUG("Appending %" PRIuSIZE " bytes", aData->Length());
RefPtr<MediaByteBuffer> data(aData);
MSE_DEBUG("Appending %" PRIuSIZE " bytes", data->Length());
mEnded = false;
return InvokeAsync<RefPtr<MediaByteBuffer>, SourceBufferAttributes&&>(
GetTaskQueue(), this, __func__,
&TrackBuffersManager::DoAppendData, aData, aAttributes);
return InvokeAsync(GetTaskQueue(), this, __func__,
&TrackBuffersManager::DoAppendData, data.forget(), aAttributes);
}
RefPtr<TrackBuffersManager::AppendPromise>
TrackBuffersManager::DoAppendData(MediaByteBuffer* aData,
TrackBuffersManager::DoAppendData(already_AddRefed<MediaByteBuffer> aData,
const SourceBufferAttributes& aAttributes)
{
RefPtr<AppendBufferTask> task = new AppendBufferTask(aData, aAttributes);
RefPtr<AppendBufferTask> task = new AppendBufferTask(Move(aData), aAttributes);
RefPtr<AppendPromise> p = task->mPromise.Ensure(__func__);
QueueTask(task);

View File

@ -98,7 +98,7 @@ public:
// Buffer Append Algorithm
// 3.5.5 Buffer Append Algorithm.
// http://w3c.github.io/media-source/index.html#sourcebuffer-buffer-append
RefPtr<AppendPromise> AppendData(MediaByteBuffer* aData,
RefPtr<AppendPromise> AppendData(already_AddRefed<MediaByteBuffer> aData,
const SourceBufferAttributes& aAttributes);
// Queue a task to abort any pending AppendData.
@ -174,7 +174,7 @@ private:
friend class MediaSourceDemuxer;
~TrackBuffersManager();
// All following functions run on the taskqueue.
RefPtr<AppendPromise> DoAppendData(MediaByteBuffer* aData,
RefPtr<AppendPromise> DoAppendData(already_AddRefed<MediaByteBuffer> aData,
const SourceBufferAttributes& aAttributes);
void ScheduleSegmentParserLoop();
void SegmentParserLoop();

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<script>
var htmlAudio = new Audio(URL.createObjectURL(new window.MediaSource()));
(new window.AudioContext("ringer")).createMediaElementSource(htmlAudio);
(new window.AudioContext("alarm")).createMediaElementSource(htmlAudio);
</script>
</head>
</html>

View File

@ -81,8 +81,6 @@ load 1157994.html
load 1158427.html
load 1185176.html
load 1185192.html
load 1223670.html
load 1228484.html
load 1304948.html
load 1319486.html
load 1291702.html

View File

@ -908,55 +908,94 @@ AudioStreamHelper.prototype = {
}
}
function VideoStreamHelper() {
this._helper = new CaptureStreamTestHelper2D(50,50);
this._canvas = this._helper.createAndAppendElement('canvas', 'source_canvas');
// Make sure this is initted
this._helper.drawColor(this._canvas, this._helper.green);
this._stream = this._canvas.captureStream(10);
}
class VideoFrameEmitter {
constructor(color1, color2) {
this._helper = new CaptureStreamTestHelper2D(50,50);
this._canvas = this._helper.createAndAppendElement('canvas', 'source_canvas');
this._color1 = color1 ? color1 : this._helper.green;
this._color2 = color2 ? color2 : this._helper.red;
// Make sure this is initted
this._helper.drawColor(this._canvas, this._color1);
this._stream = this._canvas.captureStream();
this._started = false;
}
VideoStreamHelper.prototype = {
stream: function() {
stream() {
return this._stream;
},
}
startCapturingFrames: function() {
var i = 0;
var helper = this;
return setInterval(function() {
start() {
if (this._started) {
return;
}
let i = 0;
this._started = true;
this._intervalId = setInterval(() => {
try {
helper._helper.drawColor(helper._canvas,
i ? helper._helper.green : helper._helper.red);
this._helper.drawColor(this._canvas, i ? this._color1: this._color2);
i = 1 - i;
helper._stream.requestFrame();
} catch (e) {
// ignore; stream might have shut down, and we don't bother clearing
// the setInterval.
}
}, 500);
},
}
waitForFrames: function(canvas, timeout_value) {
var intervalId = this.startCapturingFrames();
timeout_value = timeout_value || 8000;
stop() {
if (this._started) {
clearInterval(this._intervalId);
this._started = false;
}
}
}
return addFinallyToPromise(timeout(
Promise.all([
this._helper.waitForPixelColor(canvas, this._helper.green, 128,
canvas.id + " should become green"),
this._helper.waitForPixelColor(canvas, this._helper.red, 128,
canvas.id + " should become red")
]),
timeout_value,
"Timed out waiting for frames")).finally(() => clearInterval(intervalId));
},
class VideoStreamHelper {
constructor() {
this._helper = new CaptureStreamTestHelper2D(50,50);
}
verifyNoFrames: function(canvas) {
return this.waitForFrames(canvas).then(
() => ok(false, "Color should not change"),
() => ok(true, "Color should not change")
);
checkHasFrame(video, offsetX, offsetY, threshold) {
const h = this._helper;
return h.waitForPixel(video, offsetX, offsetY, px => {
let result = h.isOpaquePixelNot(px, h.black, threshold);
info("Checking that we have a frame, got [" +
Array.slice(px) + "]. Ref=[" +
Array.slice(h.black.data) + "]. Threshold=" + threshold +
". Pass=" + result);
return result;
});
}
async checkVideoPlaying(video, offsetX, offsetY, threshold) {
const h = this._helper;
await this.checkHasFrame(video, offsetX, offsetY, threshold);
let startPixel = { data: h.getPixel(video, offsetX, offsetY)
, name: "startcolor"
};
return h.waitForPixel(video, offsetX, offsetY, px => {
let result = h.isPixelNot(px, startPixel, threshold)
info("Checking playing, [" +
Array.slice(px) + "] vs [" + Array.slice(startPixel.data) +
"]. Threshold=" + threshold + " Pass=" + result);
return result;
});
}
async checkVideoPaused(video, offsetX, offsetY, threshold, timeout) {
const h = this._helper;
await this.checkHasFrame(video, offsetX, offsetY, threshold);
let startPixel = { data: h.getPixel(video, offsetX, offsetY)
, name: "startcolor"
};
const changed = await h.waitForPixel(video, offsetX, offsetY, px => {
let result = h.isOpaquePixelNot(px, startPixel, threshold);
info("Checking paused, [" +
Array.slice(px) + "] vs [" + Array.slice(startPixel.data) +
"]. Threshold=" + threshold + " Pass=" + result);
return result;
}, timeout);
ok(!changed, "Frame shouldn't change within " + timeout / 1000 + " seconds.");
}
}

View File

@ -1004,13 +1004,13 @@ PeerConnectionWrapper.prototype = {
return Promise.all(constraintsList.map(constraints => {
return getUserMedia(constraints).then(stream => {
if (constraints.audio) {
stream.getAudioTracks().map(track => {
stream.getAudioTracks().forEach(track => {
info(this + " gUM local stream " + stream.id +
" with audio track " + track.id);
});
}
if (constraints.video) {
stream.getVideoTracks().map(track => {
stream.getVideoTracks().forEach(track => {
info(this + " gUM local stream " + stream.id +
" with video track " + track.id);
});

View File

@ -2,7 +2,6 @@
<html>
<head>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
<script type="application/javascript" src="head.js"></script>
</head>
<body>
<pre id="test">

View File

@ -2,7 +2,6 @@
<html>
<head>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
<script type="application/javascript" src="head.js"></script>
</head>
<body>
<pre id="test">

View File

@ -3,7 +3,6 @@
<head>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
<script type="application/javascript" src="head.js"></script>
</head>
<body>
<pre id="test">
@ -24,43 +23,11 @@ const offsetX = 20;
const offsetY = 20;
const threshold = 16;
const pausedTimeout = 1000;
const h = new CaptureStreamTestHelper2D(50, 50);
var checkHasFrame = video => h.waitForPixel(video, offsetX, offsetY, px => {
let result = h.isOpaquePixelNot(px, h.black, threshold);
info("Checking that we have a frame, got [" +
Array.slice(px) + "]. Pass=" + result);
return result;
});
var checkVideoPlaying = video => checkHasFrame(video)
.then(() => {
let startPixel = { data: h.getPixel(video, offsetX, offsetY)
, name: "startcolor"
};
return h.waitForPixel(video, offsetX, offsetY, px => {
let result = h.isPixelNot(px, startPixel, threshold)
info("Checking playing, [" + Array.slice(px) + "] vs [" +
Array.slice(startPixel.data) + "]. Pass=" + result);
return result;
});
});
var checkVideoPaused = video => checkHasFrame(video)
.then(() => {
let startPixel = { data: h.getPixel(video, offsetX, offsetY)
, name: "startcolor"
};
return h.waitForPixel(video, offsetX, offsetY, px => {
let result = h.isOpaquePixelNot(px, startPixel, threshold);
info("Checking paused, [" + Array.slice(px) + "] vs [" +
Array.slice(startPixel.data) + "]. Pass=" + result);
return result;
}, pausedTimeout);
}).then(result => ok(!result, "Frame shouldn't change within " + pausedTimeout / 1000 + " seconds."));
let h;
runTest(() => getUserMedia({video: true, fake: true})
.then(stream => {
h = new VideoStreamHelper();
gUMVideoElement =
createMediaElement("video", "gUMVideo");
gUMVideoElement.srcObject = stream;
@ -80,43 +47,45 @@ runTest(() => getUserMedia({video: true, fake: true})
let osc = createOscillatorStream(new AudioContext(), 1000);
captureStreamElement.srcObject.addTrack(osc.getTracks()[0]);
return checkVideoPlaying(captureStreamElement);
return h.checkVideoPlaying(captureStreamElement, 10, 10, 16);
})
.then(() => {
info("Video flowing. Pausing.");
gUMVideoElement.pause();
return checkVideoPaused(captureStreamElement);
return h.checkVideoPaused(captureStreamElement, 10, 10, 16, pausedTimeout);
})
.then(() => {
info("Video stopped flowing. Playing.");
gUMVideoElement.play();
return checkVideoPlaying(captureStreamElement);
return h.checkVideoPlaying(captureStreamElement, 10, 10, 16);
})
.then(() => {
info("Video flowing. Removing source.");
var stream = gUMVideoElement.srcObject;
gUMVideoElement.srcObject = null;
return checkVideoPaused(captureStreamElement).then(() => stream);
return h.checkVideoPaused(captureStreamElement, 10, 10, 16, pausedTimeout)
.then(() => stream);
})
.then(stream => {
info("Video stopped flowing. Setting source.");
gUMVideoElement.srcObject = stream;
return checkVideoPlaying(captureStreamElement);
return h.checkVideoPlaying(captureStreamElement, 10, 10, 16);
})
.then(() => {
info("Video flowing. Changing source by track manipulation. Remove first.");
var track = gUMVideoElement.srcObject.getTracks()[0];
gUMVideoElement.srcObject.removeTrack(track);
return checkVideoPaused(captureStreamElement).then(() => track);
return h.checkVideoPaused(captureStreamElement, 10, 10, 16, pausedTimeout)
.then(() => track);
})
.then(track => {
info("Video paused. Changing source by track manipulation. Add first.");
gUMVideoElement.srcObject.addTrack(track);
gUMVideoElement.play();
return checkVideoPlaying(captureStreamElement);
return h.checkVideoPlaying(captureStreamElement, 10, 10, 16);
})
.then(() => {
gUMVideoElement.srcObject.getTracks().forEach(t => t.stop());

View File

@ -1,7 +1,6 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>

View File

@ -11,9 +11,8 @@
title: "Renegotiation: add second audio stream"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
const test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_ADD_SECOND_STREAM(test) {
@ -21,10 +20,22 @@
[{audio: true}]);
return test.pcLocal.getAllUserMedia([{audio: true}]);
},
],
[
function PC_REMOTE_CHECK_ADDED_TRACK(test) {
// We test both tracks to avoid an ordering problem
is(test.pcRemote._pc.getReceivers().length, 2,
"pcRemote should have two receivers");
return Promise.all(test.pcRemote._pc.getReceivers().map(r => {
const analyser = new AudioStreamAnalyser(
new AudioContext(), new MediaStream([r.track]));
const freq = analyser.binIndexForFrequency(TEST_AUDIO_FREQ);
return analyser.waitForAnalysisSuccess(arr => arr[freq] > 200);
}));
},
]
);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});

View File

@ -11,11 +11,9 @@
title: "Renegotiation: add second audio stream, no bundle"
});
var test;
runNetworkTest(function (options) {
options = options || { };
runNetworkTest(function (options = {}) {
options.bundle = false;
test = new PeerConnectionTest(options);
const test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_ADD_SECOND_STREAM(test) {
@ -29,6 +27,19 @@
function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
test.pcRemote.expectIceChecking();
},
],
[
function PC_REMOTE_CHECK_ADDED_TRACK(test) {
// We test both tracks to avoid an ordering problem
is(test.pcRemote._pc.getReceivers().length, 2,
"pcRemote should have two receivers");
return Promise.all(test.pcRemote._pc.getReceivers().map(r => {
const analyser = new AudioStreamAnalyser(
new AudioContext(), new MediaStream([r.track]));
const freq = analyser.binIndexForFrequency(TEST_AUDIO_FREQ);
return analyser.waitForAnalysisSuccess(arr => arr[freq] > 200);
}));
},
]
);

View File

@ -2,6 +2,7 @@
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
</head>
<body>
<pre id="test">
@ -11,21 +12,30 @@
title: "Renegotiation: add second video stream"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
const test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_ADD_SECOND_STREAM(test) {
test.setMediaConstraints([{video: true}, {video: true}],
[{video: true}]);
return test.pcLocal.getAllUserMedia([{video: true}]);
// Use fake:true here since the native fake device on linux doesn't
// change color as needed by checkVideoPlaying() below.
return test.pcLocal.getAllUserMedia([{video: true, fake: true}]);
},
],
[
function PC_REMOTE_CHECK_VIDEO_FLOW(test) {
const h = new VideoStreamHelper();
is(test.pcRemote.remoteMediaElements.length, 2,
"Should have two remote media elements after renegotiation");
return Promise.all(test.pcRemote.remoteMediaElements.map(video =>
h.checkVideoPlaying(video, 10, 10, 16)));
},
]
);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{video: true}], [{video: true}]);
test.setMediaConstraints([{video: true, fake: true}], [{video: true}]);
test.run();
});
</script>

View File

@ -2,6 +2,7 @@
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
</head>
<body>
<pre id="test">
@ -11,11 +12,9 @@
title: "Renegotiation: add second video stream, no bundle"
});
var test;
runNetworkTest(function (options) {
options = options || { };
runNetworkTest(function (options = {}) {
options.bundle = false;
test = new PeerConnectionTest(options);
const test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_ADD_SECOND_STREAM(test) {
@ -24,16 +23,26 @@
// Since this is a NoBundle variant, adding a track will cause us to
// go back to checking.
test.pcLocal.expectIceChecking();
return test.pcLocal.getAllUserMedia([{video: true}]);
// Use fake:true here since the native fake device on linux doesn't
// change color as needed by checkVideoPlaying() below.
return test.pcLocal.getAllUserMedia([{video: true, fake: true}]);
},
function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
test.pcRemote.expectIceChecking();
},
],
[
function PC_REMOTE_CHECK_VIDEO_FLOW(test) {
const h = new VideoStreamHelper();
is(test.pcRemote.remoteMediaElements.length, 2,
"Should have two remote media elements after renegotiation");
return Promise.all(test.pcRemote.remoteMediaElements.map(video =>
h.checkVideoPlaying(video, 10, 10, 16)));
},
]
);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{video: true}], [{video: true}]);
test.setMediaConstraints([{video: true, fake: true}], [{video: true}]);
test.run();
});
</script>

View File

@ -1,7 +1,6 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>

View File

@ -11,20 +11,42 @@
title: "Renegotiation: remove audio track"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
const test = new PeerConnectionTest(options);
let receivedTrack, analyser, freq;
addRenegotiation(test.chain,
[
function PC_REMOTE_SETUP_ANALYSER(test) {
is(test.pcRemote._pc.getReceivers().length, 1,
"pcRemote should have one receiver before renegotiation");
receivedTrack = test.pcRemote._pc.getReceivers()[0].track;
is(receivedTrack.readyState, "live",
"The received track should be live");
analyser = new AudioStreamAnalyser(
new AudioContext(), new MediaStream([receivedTrack]));
freq = analyser.binIndexForFrequency(TEST_AUDIO_FREQ);
return analyser.waitForAnalysisSuccess(arr => arr[freq] > 200);
},
function PC_LOCAL_REMOVE_AUDIO_TRACK(test) {
test.setOfferOptions({ offerToReceiveAudio: true });
return test.pcLocal.removeSender(0);
},
],
[
function PC_REMOTE_CHECK_FLOW_STOPPED(test) {
is(test.pcRemote._pc.getReceivers().length, 0,
"pcRemote should have no more receivers");
is(receivedTrack.readyState, "ended",
"The received track should have ended");
return analyser.waitForAnalysisSuccess(arr => arr[freq] < 50);
},
]
);
// TODO(bug 1093835): figure out how to verify that media stopped flowing from pcLocal
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});

View File

@ -11,11 +11,16 @@
title: "Renegotiation: remove then add audio track"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
const test = new PeerConnectionTest(options);
let originalTrack;
addRenegotiation(test.chain,
[
function PC_REMOTE_FIND_RECEIVER(test) {
is(test.pcRemote._pc.getReceivers().length, 1,
"pcRemote should have one receiver");
originalTrack = test.pcRemote._pc.getReceivers()[0].track;
},
function PC_LOCAL_REMOVE_AUDIO_TRACK(test) {
return test.pcLocal.removeSender(0);
},
@ -26,10 +31,22 @@
test.pcLocal.disableRtpCountChecking = true;
return test.pcLocal.getAllUserMedia([{audio: true}]);
},
],
[
function PC_REMOTE_CHECK_ADDED_TRACK(test) {
is(test.pcRemote._pc.getReceivers().length, 1,
"pcRemote should still have one receiver");
const track = test.pcRemote._pc.getReceivers()[0].track;
isnot(originalTrack.id, track.id, "Receiver should have changed");
const analyser = new AudioStreamAnalyser(
new AudioContext(), new MediaStream([track]));
const freq = analyser.binIndexForFrequency(TEST_AUDIO_FREQ);
return analyser.waitForAnalysisSuccess(arr => arr[freq] > 200);
},
]
);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});

View File

@ -11,11 +11,16 @@
title: "Renegotiation: remove then add audio track"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
const test = new PeerConnectionTest(options);
let originalTrack;
addRenegotiation(test.chain,
[
function PC_REMOTE_FIND_RECEIVER(test) {
is(test.pcRemote._pc.getReceivers().length, 1,
"pcRemote should have one receiver");
originalTrack = test.pcRemote._pc.getReceivers()[0].track;
},
function PC_LOCAL_REMOVE_AUDIO_TRACK(test) {
// The new track's pipeline will start with a packet count of
// 0, but the remote side will keep its old pipeline and packet
@ -26,13 +31,25 @@
function PC_LOCAL_ADD_AUDIO_TRACK(test) {
return test.pcLocal.getAllUserMedia([{audio: true}]);
},
],
[
function PC_REMOTE_CHECK_ADDED_TRACK(test) {
is(test.pcRemote._pc.getReceivers().length, 1,
"pcRemote should still have one receiver");
const track = test.pcRemote._pc.getReceivers()[0].track;
isnot(originalTrack.id, track.id, "Receiver should have changed");
const analyser = new AudioStreamAnalyser(
new AudioContext(), new MediaStream([track]));
const freq = analyser.binIndexForFrequency(TEST_AUDIO_FREQ);
return analyser.waitForAnalysisSuccess(arr => arr[freq] > 200);
},
]
);
test.chain.insertAfterEach('PC_LOCAL_CREATE_OFFER',
PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER);
PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});

View File

@ -2,6 +2,7 @@
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
</head>
<body>
<pre id="test">
@ -11,25 +12,47 @@
title: "Renegotiation: remove then add video track"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
const test = new PeerConnectionTest(options);
const helper = new VideoStreamHelper();
var originalTrack;
addRenegotiation(test.chain,
[
function PC_LOCAL_REMOVE_AUDIO_TRACK(test) {
function PC_REMOTE_FIND_RECEIVER(test) {
is(test.pcRemote._pc.getReceivers().length, 1,
"pcRemote should have one receiver");
originalTrack = test.pcRemote._pc.getReceivers()[0].track;
},
function PC_LOCAL_REMOVE_VIDEO_TRACK(test) {
// The new track's pipeline will start with a packet count of
// 0, but the remote side will keep its old pipeline and packet
// count.
test.pcLocal.disableRtpCountChecking = true;
return test.pcLocal.removeSender(0);
},
function PC_LOCAL_ADD_AUDIO_TRACK(test) {
return test.pcLocal.getAllUserMedia([{video: true}]);
function PC_LOCAL_ADD_VIDEO_TRACK(test) {
// Use fake:true here since the native fake device on linux doesn't
// change color as needed by checkVideoPlaying() below.
return test.pcLocal.getAllUserMedia([{video: true, fake: true}]);
},
],
[
function PC_REMOTE_CHECK_ADDED_TRACK(test) {
is(test.pcRemote._pc.getReceivers().length, 1,
"pcRemote should still have one receiver");
const track = test.pcRemote._pc.getReceivers()[0].track;
isnot(originalTrack.id, track.id, "Receiver should have changed");
const vOriginal = test.pcRemote.remoteMediaElements.find(
elem => elem.id.includes(originalTrack.id));
const vAdded = test.pcRemote.remoteMediaElements.find(
elem => elem.id.includes(track.id));
ok(vOriginal.ended, "Original video element should have ended");
return helper.checkVideoPlaying(vAdded, 10, 10, 16);
},
]
);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{video: true}], [{video: true}]);
test.run();
});

View File

@ -2,6 +2,7 @@
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
</head>
<body>
<pre id="test">
@ -11,28 +12,50 @@
title: "Renegotiation: remove then add video track, no bundle"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
const test = new PeerConnectionTest(options);
const helper = new VideoStreamHelper();
var originalTrack;
addRenegotiation(test.chain,
[
function PC_LOCAL_REMOVE_AUDIO_TRACK(test) {
function PC_REMOTE_FIND_RECEIVER(test) {
is(test.pcRemote._pc.getReceivers().length, 1,
"pcRemote should have one receiver");
originalTrack = test.pcRemote._pc.getReceivers()[0].track;
},
function PC_LOCAL_REMOVE_VIDEO_TRACK(test) {
// The new track's pipeline will start with a packet count of
// 0, but the remote side will keep its old pipeline and packet
// count.
test.pcLocal.disableRtpCountChecking = true;
return test.pcLocal.removeSender(0);
},
function PC_LOCAL_ADD_AUDIO_TRACK(test) {
return test.pcLocal.getAllUserMedia([{video: true}]);
function PC_LOCAL_ADD_VIDEO_TRACK(test) {
// Use fake:true here since the native fake device on linux doesn't
// change color as needed by checkVideoPlaying() below.
return test.pcLocal.getAllUserMedia([{video: true, fake: true}]);
},
],
[
function PC_REMOTE_CHECK_ADDED_TRACK(test) {
is(test.pcRemote._pc.getReceivers().length, 1,
"pcRemote should still have one receiver");
const track = test.pcRemote._pc.getReceivers()[0].track;
isnot(originalTrack.id, track.id, "Receiver should have changed");
const vOriginal = test.pcRemote.remoteMediaElements.find(
elem => elem.id.includes(originalTrack.id));
const vAdded = test.pcRemote.remoteMediaElements.find(
elem => elem.id.includes(track.id));
ok(vOriginal.ended, "Original video element should have ended");
return helper.checkVideoPlaying(vAdded, 10, 10, 16);
},
]
);
test.chain.insertAfterEach('PC_LOCAL_CREATE_OFFER',
PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER);
PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER);
// TODO(bug 1093835): figure out how to verify if media flows through the new stream
test.setMediaConstraints([{video: true}], [{video: true}]);
test.run();
});

View File

@ -11,21 +11,41 @@
title: "Renegotiation: remove video track"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
const test = new PeerConnectionTest(options);
let receivedTrack, element;
addRenegotiation(test.chain,
[
function PC_REMOTE_SETUP_HELPER(test) {
is(test.pcRemote._pc.getReceivers().length, 1,
"pcRemote should have one receiver before renegotiation");
receivedTrack = test.pcRemote._pc.getReceivers()[0].track;
is(receivedTrack.readyState, "live",
"The received track should be live");
element = createMediaElement("video", "pcRemoteReceivedVideo");
element.srcObject = new MediaStream([receivedTrack]);
return haveEvent(element, "loadeddata");
},
function PC_LOCAL_REMOVE_VIDEO_TRACK(test) {
test.setOfferOptions({ offerToReceiveVideo: true });
test.setMediaConstraints([], [{video: true}]);
return test.pcLocal.removeSender(0);
},
],
[
function PC_REMOTE_CHECK_FLOW_STOPPED(test) {
is(test.pcRemote._pc.getReceivers().length, 0,
"pcRemote should have no more receivers");
is(receivedTrack.readyState, "ended",
"The received track should have ended");
is(element.ended, true,
"Element playing the removed track should have ended");
},
]
);
// TODO(bug 1093835): figure out how to verify that media stopped flowing from pcLocal
test.setMediaConstraints([{video: true}], [{video: true}]);
test.run();
});

View File

@ -2,6 +2,7 @@
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
</head>
<body>
<pre id="test">
@ -11,33 +12,74 @@
title: "Renegotiation: replaceTrack followed by adding a second video stream"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
const test = new PeerConnectionTest(options);
test.setMediaConstraints([{video:true}], [{video:true}]);
const helper = new VideoStreamHelper();
const emitter1 = new VideoFrameEmitter(CaptureStreamTestHelper.prototype.red,
CaptureStreamTestHelper.prototype.green);
const emitter2 = new VideoFrameEmitter(CaptureStreamTestHelper.prototype.blue,
CaptureStreamTestHelper.prototype.grey);
test.chain.replace("PC_LOCAL_GUM", [
function PC_LOCAL_ADDTRACK(test) {
test.pcLocal.attachLocalStream(emitter1.stream());
emitter1.start();
},
]);
addRenegotiation(test.chain,
[
function PC_LOCAL_REPLACE_VIDEO_TRACK_THEN_ADD_SECOND_STREAM(test) {
var oldstream = test.pcLocal._pc.getLocalStreams()[0];
var oldtrack = oldstream.getVideoTracks()[0];
var sender = test.pcLocal._pc.getSenders()[0];
return navigator.mediaDevices.getUserMedia({video:true})
.then(newstream => {
var newtrack = newstream.getVideoTracks()[0];
return test.pcLocal.senderReplaceTrack(0, newtrack, newstream.id);
})
emitter1.stop();
emitter2.start();
const newstream = emitter2.stream();
const newtrack = newstream.getVideoTracks()[0];
return test.pcLocal.senderReplaceTrack(0, newtrack, newstream.id)
.then(() => {
test.setMediaConstraints([{video: true}, {video: true}],
[{video: true}]);
return test.pcLocal.getAllUserMedia([{video: true}]);
// Use fake:true here since the native fake device on linux
// doesn't change color as needed by checkVideoPlaying() below.
return test.pcLocal.getAllUserMedia([{video: true, fake: true}]);
});
},
],
[
function PC_REMOTE_CHECK_ORIGINAL_TRACK_ENDED(test) {
const vremote = test.pcRemote.remoteMediaElements.find(
elem => elem.id.includes(emitter1.stream().getTracks()[0].id));
if (!vremote) {
return Promise.reject(new Error("Couldn't find video element"));
}
ok(vremote.ended, "Original track should have ended after renegotiation");
},
function PC_REMOTE_CHECK_REPLACED_TRACK_FLOW(test) {
const vremote = test.pcRemote.remoteMediaElements.find(
elem => elem.id.includes(test.pcLocal._pc.getSenders()[0].track.id));
if (!vremote) {
return Promise.reject(new Error("Couldn't find video element"));
}
return addFinallyToPromise(helper.checkVideoPlaying(vremote, 10, 10, 16))
.finally(() => emitter2.stop())
.then(() => {
const px = helper._helper.getPixel(vremote, 10, 10);
const isBlue = helper._helper.isPixel(
px, CaptureStreamTestHelper.prototype.blue, 5);
const isGrey = helper._helper.isPixel(
px, CaptureStreamTestHelper.prototype.grey, 5);
ok(isBlue || isGrey, "replaced track should be blue or grey");
});
},
function PC_REMOTE_CHECK_ADDED_TRACK_FLOW(test) {
const vremote = test.pcRemote.remoteMediaElements.find(
elem => elem.id.includes(test.pcLocal._pc.getSenders()[1].track.id));
if (!vremote) {
return Promise.reject(new Error("Couldn't find video element"));
}
return helper.checkVideoPlaying(vremote, 10, 10, 16);
},
]
);
// TODO(bug 1093835):
// figure out how to verify if media flows through the new stream
// figure out how to verify that media stopped flowing from old stream
test.run();
});
</script>

View File

@ -13,21 +13,20 @@
visible: true
});
var test;
var pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p});
const pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p});
function addRIDExtension(pc, extensionId) {
var receivers = pc._pc.getReceivers();
const receivers = pc._pc.getReceivers();
is(receivers.length, 1, "We have exactly one RTP receiver");
var receiver = receivers[0];
const receiver = receivers[0];
SpecialPowers.wrap(pc._pc).mozAddRIDExtension(receiver, extensionId);
}
function selectRecvRID(pc, rid) {
var receivers = pc._pc.getReceivers();
const receivers = pc._pc.getReceivers();
is(receivers.length, 1, "We have exactly one RTP receiver");
var receiver = receivers[0];
const receiver = receivers[0];
SpecialPowers.wrap(pc._pc).mozAddRIDFilter(receiver, rid);
}
@ -38,23 +37,24 @@
// the 80Kbps+overhead needed for the two simulcast streams.
// 100Kbps was apparently too low.
['media.peerconnection.video.min_bitrate_estimate', 180*1000]).then(() => {
var helper;
let emitter, helper;
test = new PeerConnectionTest({bundle: false});
test.setMediaConstraints([{video: true}], [{video: true}]);
test.chain.replace("PC_REMOTE_GUM", [
function PC_REMOTE_CANVAS_CAPTURESTREAM(test) {
emitter = new VideoFrameEmitter();
helper = new VideoStreamHelper();
test.pcRemote.attachLocalStream(helper.stream());
test.pcRemote.attachLocalStream(emitter.stream());
}
]);
test.chain.insertAfter('PC_REMOTE_GET_OFFER', [
function PC_REMOTE_SET_RIDS(test) {
var senders = test.pcRemote._pc.getSenders();
const senders = test.pcRemote._pc.getSenders();
is(senders.length, 1, "We have exactly one RTP sender");
var sender = senders[0];
const sender = senders[0];
ok(sender.track, "Sender has a track");
return sender.setParameters({
@ -88,7 +88,7 @@
// has been created.
test.chain.insertAfter('PC_LOCAL_SET_REMOTE_DESCRIPTION',[
function PC_LOCAL_SET_RTP_FIRST_RID(test) {
var extmap_id = test._local_offer.sdp.match(
const extmap_id = test._local_offer.sdp.match(
"a=extmap:([0-9+])/recvonly urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id");
ok(extmap_id, "Local offer has extmap id for simulcast: " + extmap_id[1]);
// Cause pcLocal to filter out everything but RID "bar", only
@ -99,14 +99,16 @@
]);
test.chain.append([
function PC_LOCAL_WAIT_FOR_FRAMES() {
var vremote = test.pcLocal.remoteMediaElements[0];
async function PC_LOCAL_WAIT_FOR_FRAMES() {
const vremote = test.pcLocal.remoteMediaElements[0];
ok(vremote, "Should have remote video element for pcLocal");
return helper.waitForFrames(vremote);
emitter.start();
await helper.checkVideoPlaying(vremote, 10, 10, 16);
emitter.stop();
},
function PC_LOCAL_CHECK_SIZE_1() {
var vlocal = test.pcRemote.localMediaElements[0];
var vremote = test.pcLocal.remoteMediaElements[0];
const vlocal = test.pcRemote.localMediaElements[0];
const vremote = test.pcLocal.remoteMediaElements[0];
ok(vlocal, "Should have local video element for pcRemote");
ok(vremote, "Should have remote video element for pcLocal");
ok(vlocal.videoWidth > 0, "source width is positive");
@ -122,21 +124,25 @@
function PC_LOCAL_WAIT_FOR_SECOND_MEDIA_FLOW(test) {
return test.pcLocal.waitForMediaFlow();
},
function PC_LOCAL_WAIT_FOR_FRAMES_2() {
var vremote = test.pcLocal.remoteMediaElements[0];
async function PC_LOCAL_WAIT_FOR_FRAMES_2() {
const vremote = test.pcLocal.remoteMediaElements[0];
ok(vremote, "Should have remote video element for pcLocal");
return helper.waitForFrames(vremote);
emitter.start();
await helper.checkVideoPlaying(vremote, 10, 10, 16);
emitter.stop();
},
// For some reason, even though we're getting a 25x25 stream, sometimes
// the resolution isn't updated on the video element on the first frame.
function PC_LOCAL_WAIT_FOR_FRAMES_3() {
var vremote = test.pcLocal.remoteMediaElements[0];
async function PC_LOCAL_WAIT_FOR_FRAMES_3() {
const vremote = test.pcLocal.remoteMediaElements[0];
ok(vremote, "Should have remote video element for pcLocal");
return helper.waitForFrames(vremote);
emitter.start();
await helper.checkVideoPlaying(vremote, 10, 10, 16);
emitter.stop();
},
function PC_LOCAL_CHECK_SIZE_2() {
var vlocal = test.pcRemote.localMediaElements[0];
var vremote = test.pcLocal.remoteMediaElements[0];
const vlocal = test.pcRemote.localMediaElements[0];
const vremote = test.pcLocal.remoteMediaElements[0];
ok(vlocal, "Should have local video element for pcRemote");
ok(vremote, "Should have remote video element for pcLocal");
ok(vlocal.videoWidth > 0, "source width is positive");

View File

@ -13,21 +13,20 @@
visible: true
});
var test;
var pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p});
const pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p});
function addRIDExtension(pc, extensionId) {
var receivers = pc._pc.getReceivers();
const receivers = pc._pc.getReceivers();
is(receivers.length, 1, "We have exactly one RTP receiver");
var receiver = receivers[0];
const receiver = receivers[0];
SpecialPowers.wrap(pc._pc).mozAddRIDExtension(receiver, extensionId);
}
function selectRecvRID(pc, rid) {
var receivers = pc._pc.getReceivers();
const receivers = pc._pc.getReceivers();
is(receivers.length, 1, "We have exactly one RTP receiver");
var receiver = receivers[0];
const receiver = receivers[0];
SpecialPowers.wrap(pc._pc).mozAddRIDFilter(receiver, rid);
}
@ -38,23 +37,24 @@
// the 80Kbps+overhead needed for the two simulcast streams.
// 100Kbps was apparently too low.
['media.peerconnection.video.min_bitrate_estimate', 180*1000]).then(() => {
var helper;
let emitter, helper;
test = new PeerConnectionTest({bundle: false});
const test = new PeerConnectionTest({bundle: false});
test.setMediaConstraints([{video: true}], []);
test.chain.replace("PC_LOCAL_GUM", [
function PC_LOCAL_CANVAS_CAPTURESTREAM(test) {
emitter = new VideoFrameEmitter();
helper = new VideoStreamHelper();
test.pcLocal.attachLocalStream(helper.stream());
test.pcLocal.attachLocalStream(emitter.stream());
}
]);
test.chain.insertBefore('PC_LOCAL_CREATE_OFFER', [
function PC_LOCAL_SET_RIDS(test) {
var senders = test.pcLocal._pc.getSenders();
const senders = test.pcLocal._pc.getSenders();
is(senders.length, 1, "We have exactly one RTP sender");
var sender = senders[0];
const sender = senders[0];
ok(sender.track, "Sender has a track");
return sender.setParameters({
@ -80,7 +80,7 @@
// has been created.
test.chain.insertAfter('PC_REMOTE_SET_LOCAL_DESCRIPTION',[
function PC_REMOTE_SET_RTP_FIRST_RID(test) {
var extmap_id = test.originalOffer.sdp.match(
const extmap_id = test.originalOffer.sdp.match(
"a=extmap:([0-9+])/sendonly urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id");
ok(extmap_id, "Original offer has extmap id for simulcast: " + extmap_id[1]);
// Cause pcRemote to filter out everything but RID "foo", only
@ -91,14 +91,16 @@
]);
test.chain.append([
function PC_REMOTE_WAIT_FOR_FRAMES() {
var vremote = test.pcRemote.remoteMediaElements[0];
async function PC_REMOTE_WAIT_FOR_FRAMES() {
const vremote = test.pcRemote.remoteMediaElements[0];
ok(vremote, "Should have remote video element for pcRemote");
return helper.waitForFrames(vremote);
emitter.start();
await helper.checkVideoPlaying(vremote, 10, 10, 16);
emitter.stop();
},
function PC_REMOTE_CHECK_SIZE_1() {
var vlocal = test.pcLocal.localMediaElements[0];
var vremote = test.pcRemote.remoteMediaElements[0];
const vlocal = test.pcLocal.localMediaElements[0];
const vremote = test.pcRemote.remoteMediaElements[0];
ok(vlocal, "Should have local video element for pcLocal");
ok(vremote, "Should have remote video element for pcRemote");
ok(vlocal.videoWidth > 0, "source width is positive");
@ -114,21 +116,25 @@
function PC_REMOTE_WAIT_FOR_SECOND_MEDIA_FLOW(test) {
return test.pcRemote.waitForMediaFlow();
},
function PC_REMOTE_WAIT_FOR_FRAMES_2() {
var vremote = test.pcRemote.remoteMediaElements[0];
async function PC_REMOTE_WAIT_FOR_FRAMES_2() {
const vremote = test.pcRemote.remoteMediaElements[0];
ok(vremote, "Should have remote video element for pcRemote");
return helper.waitForFrames(vremote);
emitter.start();
await helper.checkVideoPlaying(vremote, 10, 10, 16);
emitter.stop();
},
// For some reason, even though we're getting a 25x25 stream, sometimes
// the resolution isn't updated on the video element on the first frame.
function PC_REMOTE_WAIT_FOR_FRAMES_3() {
var vremote = test.pcRemote.remoteMediaElements[0];
async function PC_REMOTE_WAIT_FOR_FRAMES_3() {
const vremote = test.pcRemote.remoteMediaElements[0];
ok(vremote, "Should have remote video element for pcRemote");
return helper.waitForFrames(vremote);
emitter.start();
await helper.checkVideoPlaying(vremote, 10, 10, 16);
emitter.stop();
},
function PC_REMOTE_CHECK_SIZE_2() {
var vlocal = test.pcLocal.localMediaElements[0];
var vremote = test.pcRemote.remoteMediaElements[0];
const vlocal = test.pcLocal.localMediaElements[0];
const vremote = test.pcRemote.remoteMediaElements[0];
ok(vlocal, "Should have local video element for pcLocal");
ok(vremote, "Should have remote video element for pcRemote");
ok(vlocal.videoWidth > 0, "source width is positive");

View File

@ -15,14 +15,14 @@
var test;
runNetworkTest(function (options) {
var helper;
const emitter = new VideoFrameEmitter();
const helper = new VideoStreamHelper();
test = new PeerConnectionTest(options);
test.chain.replace("PC_LOCAL_GUM", [
function PC_LOCAL_CANVAS_CAPTURESTREAM(test) {
helper = new VideoStreamHelper();
test.pcLocal.attachLocalStream(helper.stream());
test.pcLocal.attachLocalStream(emitter.stream());
}
]);
@ -30,7 +30,9 @@
function PC_REMOTE_WAIT_FOR_FRAMES() {
var vremote = test.pcRemote.remoteMediaElements[0];
ok(vremote, "Should have remote video element for pcRemote");
return helper.waitForFrames(vremote);
emitter.start();
return addFinallyToPromise(helper.checkVideoPlaying(vremote, 10, 10, 16))
.finally(() => emitter.stop());
}
]);
@ -47,7 +49,9 @@
function PC_REMOTE_ENSURE_NO_FRAMES() {
var vremote = test.pcRemote.remoteMediaElements[0];
ok(vremote, "Should have remote video element for pcRemote");
return helper.verifyNoFrames(vremote);
emitter.start();
return addFinallyToPromise(helper.checkVideoPaused(vremote, 10, 10, 16, 5000))
.finally(() => emitter.stop());
},
]);
@ -60,7 +64,9 @@
function PC_REMOTE_WAIT_FOR_FRAMES_2() {
var vremote = test.pcRemote.remoteMediaElements[0];
ok(vremote, "Should have remote video element for pcRemote");
return helper.waitForFrames(vremote);
emitter.start();
return addFinallyToPromise(helper.checkVideoPlaying(vremote, 10, 10, 16))
.finally(() => emitter.stop());
}
]);

View File

@ -197,16 +197,6 @@ AudioContext::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
/* static */ already_AddRefed<AudioContext>
AudioContext::Constructor(const GlobalObject& aGlobal,
ErrorResult& aRv)
{
return AudioContext::Constructor(aGlobal,
AudioChannelService::GetDefaultAudioChannel(),
aRv);
}
/* static */ already_AddRefed<AudioContext>
AudioContext::Constructor(const GlobalObject& aGlobal,
AudioChannel aChannel,
ErrorResult& aRv)
{
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
if (!window) {
@ -214,7 +204,9 @@ AudioContext::Constructor(const GlobalObject& aGlobal,
return nullptr;
}
RefPtr<AudioContext> object = new AudioContext(window, false, aChannel);
RefPtr<AudioContext> object =
new AudioContext(window, false,
AudioChannelService::GetDefaultAudioChannel());
aRv = object->Init();
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;

View File

@ -154,12 +154,6 @@ public:
static already_AddRefed<AudioContext>
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
// Constructor for regular AudioContext. A default audio channel is needed.
static already_AddRefed<AudioContext>
Constructor(const GlobalObject& aGlobal,
AudioChannel aChannel,
ErrorResult& aRv);
// Constructor for offline AudioContext
static already_AddRefed<AudioContext>
Constructor(const GlobalObject& aGlobal,

View File

@ -449,7 +449,7 @@ void WebMBufferedState::UpdateIndex(const MediaByteRangeSet& aRanges, MediaResou
while (length > 0) {
static const uint32_t BLOCK_SIZE = 1048576;
uint32_t block = std::min(length, BLOCK_SIZE);
RefPtr<MediaByteBuffer> bytes = aResource->MediaReadAt(offset, block);
RefPtr<MediaByteBuffer> bytes = aResource->CachedReadAt(offset, block);
if (!bytes) {
break;
}

View File

@ -8,9 +8,13 @@
#include "nsTArray.h"
#include "MediaDataDemuxer.h"
#include "MediaResource.h"
#include "NesteggPacketHolder.h"
#include "mozilla/Move.h"
#include <deque>
#include <stdint.h>
typedef struct nestegg nestegg;
namespace mozilla {

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