Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-09-04 15:26:07 +02:00
Родитель e1f59fae62 3809a1cc80
Коммит a2239c2de6
341 изменённых файлов: 3129 добавлений и 3587 удалений

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

@ -4,9 +4,7 @@
# 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/.
LIBRARY_NAME = 'IA2Marshal'
FORCE_SHARED_LIB = True
SharedLibrary('IA2Marshal')
DEFINES['REGISTER_PROXY_DLL'] = True

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

@ -4,7 +4,7 @@
# 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/.
LIBRARY_NAME = 'AccessibleMarshal'
SharedLibrary('AccessibleMarshal')
GENERATED_SOURCES += [
'dlldata.c',
@ -16,8 +16,6 @@ GENERATED_SOURCES += [
'ISimpleDOMText_p.c',
]
FORCE_SHARED_LIB = True
DEFINES['REGISTER_PROXY_DLL'] = True
DEFFILE = SRCDIR + '/AccessibleMarshal.def'

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

@ -6,9 +6,9 @@
if not CONFIG['LIBXUL_SDK']:
if CONFIG['GAIADIR']:
PROGRAM = CONFIG['MOZ_APP_NAME'] + "-bin"
Program(CONFIG['MOZ_APP_NAME'] + "-bin")
else:
PROGRAM = CONFIG['MOZ_APP_NAME']
Program(CONFIG['MOZ_APP_NAME'])
if CONFIG['MOZ_B2G_LOADER']:
SOURCES += [
'B2GLoader.cpp',

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

@ -4,7 +4,7 @@
# 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/.
PROGRAM = CONFIG['MOZ_APP_NAME']
Program(CONFIG['MOZ_APP_NAME'])
if CONFIG['OS_ARCH'] == 'WINNT':
SOURCES += [

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

@ -6,7 +6,7 @@
DIRS += ['profile/extensions']
PROGRAM = CONFIG['MOZ_APP_NAME']
Program(CONFIG['MOZ_APP_NAME'])
SOURCES += [
'nsBrowserApp.cpp',

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

@ -80,10 +80,13 @@ WebappsStore.prototype = {
},
_feedStore: function() {
if (!this._webAppsActor) {
return promise.resolve();
}
this._listenToApps();
return this._getAllApps()
.then(this._getRunningApps.bind(this))
.then(this._getAppsIcons.bind(this))
.then(this._getAppsIcons.bind(this));
},
_listenToApps: function() {

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

@ -32,30 +32,12 @@ function performTest() {
info("process name: " + gProcess._dbgProcess.processName);
info("process sig: " + gProcess._dbgProcess.processSignature);
ok(gProcess._dbgProfile,
ok(gProcess._dbgProfilePath,
"The remote debugger profile wasn't created properly!");
ok(gProcess._dbgProfile.localDir,
"The remote debugger profile doesn't have a localDir...");
ok(gProcess._dbgProfile.rootDir,
"The remote debugger profile doesn't have a rootDir...");
ok(gProcess._dbgProfile.name,
"The remote debugger profile doesn't have a name...");
is(gProcess._dbgProfilePath, OS.Path.join(OS.Constants.Path.profileDir, "chrome_debugger_profile"),
"The remote debugger profile isn't where we expect it!");
info("profile localDir: " + gProcess._dbgProfile.localDir.path);
info("profile rootDir: " + gProcess._dbgProfile.rootDir.path);
info("profile name: " + gProcess._dbgProfile.name);
let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
.createInstance(Ci.nsIToolkitProfileService);
let profile = profileService.getProfileByName(gProcess._dbgProfile.name);
ok(profile,
"The remote debugger profile wasn't *actually* created properly!");
is(profile.localDir.path, gProcess._dbgProfile.localDir.path,
"The remote debugger profile doesn't have the correct localDir!");
is(profile.rootDir.path, gProcess._dbgProfile.rootDir.path,
"The remote debugger profile doesn't have the correct rootDir!");
info("profile path: " + gProcess._dbgProfilePath);
gProcess.close();
}
@ -68,9 +50,7 @@ function aOnClose() {
info("process exit value: " + gProcess._dbgProcess.exitValue);
info("profile localDir: " + gProcess._dbgProfile.localDir.path);
info("profile rootDir: " + gProcess._dbgProfile.rootDir.path);
info("profile name: " + gProcess._dbgProfile.name);
info("profile path: " + gProcess._dbgProfilePath);
finish();
}

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

@ -8,7 +8,7 @@
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
const DBG_XUL = "chrome://browser/content/devtools/framework/toolbox-process-window.xul";
const CHROME_DEBUGGER_PROFILE_NAME = "-chrome-debugger";
const CHROME_DEBUGGER_PROFILE_NAME = "chrome_debugger_profile";
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm")
@ -154,54 +154,33 @@ BrowserToolboxProcess.prototype = {
_initProfile: function() {
dumpn("Initializing the chrome toolbox user profile.");
let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
.createInstance(Ci.nsIToolkitProfileService);
let profileName;
let debuggingProfileDir = Services.dirsvc.get("ProfLD", Ci.nsIFile);
debuggingProfileDir.append(CHROME_DEBUGGER_PROFILE_NAME);
try {
// Attempt to get the required chrome debugging profile name string.
profileName = profileService.selectedProfile.name + CHROME_DEBUGGER_PROFILE_NAME;
dumpn("Using chrome toolbox profile name: " + profileName);
} catch (e) {
// Requested profile string could not be retrieved.
profileName = CHROME_DEBUGGER_PROFILE_NAME;
let msg = "Querying the current profile failed. " + e.name + ": " + e.message;
dumpn(msg);
Cu.reportError(msg);
}
let profileObject;
try {
// Attempt to get the required chrome debugging profile toolkit object.
profileObject = profileService.getProfileByName(profileName);
dumpn("Using chrome toolbox profile object: " + profileObject);
// The profile exists but the corresponding folder may have been deleted.
var enumerator = Services.dirsvc.get("ProfD", Ci.nsIFile).parent.directoryEntries;
while (enumerator.hasMoreElements()) {
let profileDir = enumerator.getNext().QueryInterface(Ci.nsIFile);
if (profileDir.leafName.contains(profileName)) {
// Requested profile was found and the folder exists.
this._dbgProfile = profileObject;
return;
}
debuggingProfileDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
} catch (ex) {
if (ex.result !== Cr.NS_ERROR_FILE_ALREADY_EXISTS) {
dumpn("Error trying to create a profile directory, failing.");
dumpn("Error: " + (ex.message || ex));
return;
}
// Requested profile was found but the folder was deleted. Cleanup needed.
profileObject.remove(true);
dumpn("The already existing chrome toolbox profile was invalid.");
} catch (e) {
// Requested profile object was not found.
let msg = "Creating a profile failed. " + e.name + ": " + e.message;
dumpn(msg);
Cu.reportError(msg);
}
// Create a new chrome debugging profile.
this._dbgProfile = profileService.createProfile(null, profileName);
profileService.flush();
this._dbgProfilePath = debuggingProfileDir.path;
dumpn("Finished creating the chrome toolbox user profile.");
dumpn("Flushed profile service with: " + profileName);
// We would like to copy prefs into this new profile...
let prefsFile = debuggingProfileDir.clone();
prefsFile.append("prefs.js");
// ... but unfortunately, when we run tests, it seems the starting profile
// clears out the prefs file before re-writing it, and in practice the
// file is empty when we get here. So just copying doesn't work in that
// case.
// We could force a sync pref flush and then copy it... but if we're doing
// that, we might as well just flush directly to the new profile, which
// always works:
Services.prefs.savePrefFile(prefsFile);
dumpn("Finished creating the chrome toolbox user profile at: " + this._dbgProfilePath);
},
/**
@ -219,7 +198,7 @@ BrowserToolboxProcess.prototype = {
}
dumpn("Running chrome debugging process.");
let args = ["-no-remote", "-foreground", "-P", this._dbgProfile.name, "-chrome", xulURI];
let args = ["-no-remote", "-foreground", "-profile", this._dbgProfilePath, "-chrome", xulURI];
process.runwAsync(args, args.length, { observe: () => this.close() });

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

@ -1419,7 +1419,7 @@ CssRuleView.prototype = {
this.element.appendChild(editor.element);
} else {
for (let rule of rules) {
if (rule.selectorText === "element") {
if (rule.domRule.type === ELEMENT_STYLE) {
let referenceElement = rule.editor.element.nextSibling;
this.element.insertBefore(editor.element, referenceElement);
break;

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

@ -84,7 +84,7 @@ function CheckLockState() {
if (AppManager.connection &&
AppManager.connection.status == Connection.Status.CONNECTED &&
AppManager.deviceFront) {
AppManager.preferenceFront) {
// ADB check
if (AppManager.selectedRuntime instanceof USBRuntime) {

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

@ -114,7 +114,8 @@ let UI = {
// not focused.
if (AppManager.selectedProject &&
AppManager.selectedProject.type != "mainProcess" &&
AppManager.selectedProject.type != "runtimeApp") {
AppManager.selectedProject.type != "runtimeApp" &&
AppManager.selectedProject.type != "tab") {
AppManager.validateProject(AppManager.selectedProject);
}
},
@ -138,6 +139,7 @@ let UI = {
break;
case "project-is-not-running":
case "project-is-running":
case "list-tabs-response":
this.updateCommands();
break;
case "runtime":
@ -562,6 +564,9 @@ let UI = {
// If connected and a project is selected
if (AppManager.selectedProject.type == "runtimeApp") {
playCmd.removeAttribute("disabled");
} else if (AppManager.selectedProject.type == "tab") {
playCmd.removeAttribute("disabled");
stopCmd.setAttribute("disabled", "true");
} else if (AppManager.selectedProject.type == "mainProcess") {
playCmd.setAttribute("disabled", "true");
stopCmd.setAttribute("disabled", "true");
@ -592,16 +597,18 @@ let UI = {
let runtimePanelButton = document.querySelector("#runtime-panel-button");
if (AppManager.connection.status == Connection.Status.CONNECTED) {
screenshotCmd.removeAttribute("disabled");
permissionsCmd.removeAttribute("disabled");
if (AppManager.deviceFront) {
detailsCmd.removeAttribute("disabled");
permissionsCmd.removeAttribute("disabled");
screenshotCmd.removeAttribute("disabled");
}
disconnectCmd.removeAttribute("disabled");
detailsCmd.removeAttribute("disabled");
runtimePanelButton.setAttribute("active", "true");
} else {
screenshotCmd.setAttribute("disabled", "true");
permissionsCmd.setAttribute("disabled", "true");
disconnectCmd.setAttribute("disabled", "true");
detailsCmd.setAttribute("disabled", "true");
permissionsCmd.setAttribute("disabled", "true");
screenshotCmd.setAttribute("disabled", "true");
disconnectCmd.setAttribute("disabled", "true");
runtimePanelButton.removeAttribute("active");
}
@ -822,7 +829,13 @@ let Cmds = {
let runtimeappsHeaderNode = document.querySelector("#panel-header-runtimeapps");
if (AppManager.connection.status == Connection.Status.CONNECTED) {
let sortedApps = AppManager.webAppsStore.object.all;
sortedApps = sortedApps.sort((a, b) => {
return a.name > b.name;
});
let mainProcess = AppManager.isMainProcessDebuggable();
if (AppManager.connection.status == Connection.Status.CONNECTED &&
(sortedApps.length > 0 || mainProcess)) {
runtimeappsHeaderNode.removeAttribute("hidden");
} else {
runtimeappsHeaderNode.setAttribute("hidden", "true");
@ -833,7 +846,7 @@ let Cmds = {
runtimeAppsNode.firstChild.remove();
}
if (AppManager.isMainProcessDebuggable()) {
if (mainProcess) {
let panelItemNode = document.createElement("toolbarbutton");
panelItemNode.className = "panel-item";
panelItemNode.setAttribute("label", Strings.GetStringFromName("mainProcess_label"));
@ -849,10 +862,6 @@ let Cmds = {
}, true);
}
let sortedApps = AppManager.webAppsStore.object.all;
sortedApps = sortedApps.sort((a, b) => {
return a.name > b.name;
});
for (let i = 0; i < sortedApps.length; i++) {
let app = sortedApps[i];
let panelItemNode = document.createElement("toolbarbutton");
@ -871,9 +880,63 @@ let Cmds = {
}, true);
}
// Build the tab list right now, so it's fast...
this._buildProjectPanelTabs();
// But re-list them and rebuild, in case any tabs navigated since the last
// time they were listed.
AppManager.listTabs().then(() => {
this._buildProjectPanelTabs();
});
return deferred.promise;
},
_buildProjectPanelTabs: function() {
let tabs = AppManager.tabStore.tabs;
let tabsHeaderNode = document.querySelector("#panel-header-tabs");
if (AppManager.connection.status == Connection.Status.CONNECTED &&
tabs.length > 0) {
tabsHeaderNode.removeAttribute("hidden");
} else {
tabsHeaderNode.setAttribute("hidden", "true");
}
let tabsNode = document.querySelector("#project-panel-tabs");
while (tabsNode.hasChildNodes()) {
tabsNode.firstChild.remove();
}
for (let i = 0; i < tabs.length; i++) {
let tab = tabs[i];
let url = new URL(tab.url);
// Wanted to use nsIFaviconService here, but it only works for visited
// tabs, so that's no help for any remote tabs. Maybe some favicon wizard
// knows how to get high-res favicons easily, or we could offer actor
// support for this (bug 1061654).
tab.favicon = url.origin + "/favicon.ico";
tab.name = tab.title || Strings.GetStringFromName("project_tab_loading");
if (url.protocol.startsWith("http")) {
tab.name = url.hostname + ": " + tab.name;
}
let panelItemNode = document.createElement("toolbarbutton");
panelItemNode.className = "panel-item";
panelItemNode.setAttribute("label", tab.name);
panelItemNode.setAttribute("image", tab.favicon);
tabsNode.appendChild(panelItemNode);
panelItemNode.addEventListener("click", () => {
UI.hidePanels();
AppManager.selectedProject = {
type: "tab",
app: tab,
icon: tab.favicon,
location: tab.url,
name: tab.name
};
}, true);
}
},
showRuntimePanel: function() {
AppManager.scanForWiFiRuntimes();
@ -924,6 +987,8 @@ let Cmds = {
return UI.busyUntil(AppManager.installAndRunProject(), "installing and running app");
case "runtimeApp":
return UI.busyUntil(AppManager.runRuntimeApp(), "running app");
case "tab":
return UI.busyUntil(AppManager.reloadTab(), "reloading tab");
}
return promise.reject();
},

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

@ -117,7 +117,7 @@
<hbox id="panel-buttons-container">
<toolbarbutton id="project-panel-button" class="panel-button no-project" command="cmd_showProjectPanel">
<image class="panel-button-image"/>
<label class="panel-button-label" value="&projectButton_label;"/>
<label class="panel-button-label" value="&projectButton_label;" crop="end"/>
<image class="panel-button-anchor"/>
</toolbarbutton>
<spacer flex="1"/>
@ -143,6 +143,8 @@
<vbox id="project-panel-projects"></vbox>
<label class="panel-header" id="panel-header-runtimeapps" hidden="true">&projectPanel_runtimeApps;</label>
<vbox flex="1" id="project-panel-runtimeapps"/>
<label class="panel-header" id="panel-header-tabs" hidden="true">&projectPanel_tabs;</label>
<vbox flex="1" id="project-panel-tabs"/>
</vbox>
</panel>

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

@ -15,6 +15,7 @@ const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.
const {TextEncoder, OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
const {AppProjects} = require("devtools/app-manager/app-projects");
const WebappsStore = require("devtools/app-manager/webapps-store");
const TabStore = require("devtools/webide/tab-store");
const {AppValidator} = require("devtools/app-manager/app-validator");
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
const AppActorFront = require("devtools/app-actor-front");
@ -25,6 +26,7 @@ const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
const {USBRuntime, WiFiRuntime, SimulatorRuntime,
gLocalRuntime, gRemoteRuntime} = require("devtools/webide/runtimes");
const discovery = require("devtools/toolkit/discovery/discovery");
const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/webide.properties");
@ -47,6 +49,11 @@ exports.AppManager = AppManager = {
this.onWebAppsStoreready = this.onWebAppsStoreready.bind(this);
this.webAppsStore = new WebappsStore(this.connection);
this.webAppsStore.on("store-ready", this.onWebAppsStoreready);
this.tabStore = new TabStore(this.connection);
this.onTabNavigate = this.onTabNavigate.bind(this);
this.onTabClosed = this.onTabClosed.bind(this);
this.tabStore.on("navigate", this.onTabNavigate);
this.tabStore.on("closed", this.onTabClosed);
this.runtimeList = {
usb: [],
@ -81,6 +88,10 @@ exports.AppManager = AppManager = {
this.webAppsStore.off("store-ready", this.onWebAppsStoreready);
this.webAppsStore.destroy();
this.webAppsStore = null;
this.tabStore.off("navigate", this.onTabNavigate);
this.tabStore.off("closed", this.onTabClosed);
this.tabStore.destroy();
this.tabStore = null;
this.connection.off(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
this._listTabsResponse = null;
this.connection.disconnect();
@ -192,7 +203,8 @@ exports.AppManager = AppManager = {
},
isProjectRunning: function() {
if (this.selectedProject.type == "mainProcess") {
if (this.selectedProject.type == "mainProcess" ||
this.selectedProject.type == "tab") {
return true;
}
let manifest = this.getProjectManifestURL(this.selectedProject);
@ -209,7 +221,51 @@ exports.AppManager = AppManager = {
}
},
listTabs: function() {
return this.tabStore.listTabs();
},
// TODO: Merge this into TabProject as part of project-agnostic work
onTabNavigate: function() {
if (this.selectedProject.type !== "tab") {
return;
}
let tab = this.selectedProject.app = this.tabStore.selectedTab;
let uri = NetUtil.newURI(tab.url);
// Wanted to use nsIFaviconService here, but it only works for visited
// tabs, so that's no help for any remote tabs. Maybe some favicon wizard
// knows how to get high-res favicons easily, or we could offer actor
// support for this (bug 1061654).
tab.favicon = uri.prePath + "/favicon.ico";
tab.name = tab.title || Strings.GetStringFromName("project_tab_loading");
if (uri.scheme.startsWith("http")) {
tab.name = uri.host + ": " + tab.name;
}
this.selectedProject.location = tab.url;
this.selectedProject.name = tab.name;
this.selectedProject.icon = tab.favicon;
this.update("project-validated");
},
onTabClosed: function() {
if (this.selectedProject.type !== "tab") {
return;
}
this.selectedProject = null;
},
reloadTab: function() {
if (this.selectedProject && this.selectedProject.type != "tab") {
return promise.reject("tried to reload non-tab project");
}
return this.getTarget().then(target => {
target.activeTab.reload();
});
},
getTarget: function() {
let client = this.connection.client;
if (this.selectedProject.type == "mainProcess") {
return devtools.TargetFactory.forRemoteTab({
form: this._listTabsResponse,
@ -218,13 +274,16 @@ exports.AppManager = AppManager = {
});
}
if (this.selectedProject.type == "tab") {
return this.tabStore.getTargetForTab();
}
let manifest = this.getProjectManifestURL(this.selectedProject);
if (!manifest) {
console.error("Can't find manifestURL for selected project");
return promise.reject();
}
let client = this.connection.client;
let actor = this._listTabsResponse.webappsActor;
return Task.spawn(function* () {
// Once we asked the app to launch, the app isn't necessary completely loaded.
@ -270,6 +329,9 @@ exports.AppManager = AppManager = {
if (value != this.selectedProject) {
this._selectedProject = value;
// Clear out tab store's selected state, if any
this.tabStore.selectedTab = null;
if (this.selectedProject) {
if (this.selectedProject.type == "runtimeApp") {
this.runRuntimeApp();
@ -278,6 +340,9 @@ exports.AppManager = AppManager = {
this.selectedProject.type == "hosted") {
this.validateProject(this.selectedProject);
}
if (this.selectedProject.type == "tab") {
this.tabStore.selectedTab = this.selectedProject.app;
}
}
this.update("project");
@ -300,7 +365,8 @@ exports.AppManager = AppManager = {
this._selectedRuntime = value;
if (!value && this.selectedProject &&
(this.selectedProject.type == "mainProcess" ||
this.selectedProject.type == "runtimeApp")) {
this.selectedProject.type == "runtimeApp" ||
this.selectedProject.type == "tab")) {
this.selectedProject = null;
}
this.update("runtime");

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

@ -0,0 +1,157 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { Cu } = require("chrome");
const EventEmitter = require("devtools/toolkit/event-emitter");
const { Connection } = require("devtools/client/connection-manager");
const { Promise: promise } =
Cu.import("resource://gre/modules/Promise.jsm", {});
const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
const { devtools } =
Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const _knownTabStores = new WeakMap();
let TabStore;
module.exports = TabStore = function(connection) {
// If we already know about this connection,
// let's re-use the existing store.
if (_knownTabStores.has(connection)) {
return _knownTabStores.get(connection);
}
_knownTabStores.set(connection, this);
EventEmitter.decorate(this);
this._resetStore();
this.destroy = this.destroy.bind(this);
this._onStatusChanged = this._onStatusChanged.bind(this);
this._connection = connection;
this._connection.once(Connection.Events.DESTROYED, this.destroy);
this._connection.on(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
this._onTabListChanged = this._onTabListChanged.bind(this);
this._onTabNavigated = this._onTabNavigated.bind(this);
this._onStatusChanged();
return this;
};
TabStore.prototype = {
destroy: function() {
if (this._connection) {
// While this.destroy is bound using .once() above, that event may not
// have occurred when the TabStore client calls destroy, so we
// manually remove it here.
this._connection.off(Connection.Events.DESTROYED, this.destroy);
this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
_knownTabStores.delete(this._connection);
this._connection = null;
}
},
_resetStore: function() {
this.response = null;
this.tabs = [];
},
_onStatusChanged: function() {
if (this._connection.status == Connection.Status.CONNECTED) {
// Watch for changes to remote browser tabs
this._connection.client.addListener("tabListChanged",
this._onTabListChanged);
this._connection.client.addListener("tabNavigated",
this._onTabNavigated);
this.listTabs();
} else {
if (this._connection.client) {
this._connection.client.removeListener("tabListChanged",
this._onTabListChanged);
this._connection.client.removeListener("tabNavigated",
this._onTabNavigated);
}
this._resetStore();
}
},
_onTabListChanged: function() {
this.listTabs();
},
_onTabNavigated: function(e, { from, title, url }) {
if (!this._selectedTab || from !== this._selectedTab.actor) {
return;
}
this._selectedTab.url = url;
this._selectedTab.title = title;
this.emit("navigate");
},
listTabs: function() {
if (!this._connection || !this._connection.client) {
return promise.reject();
}
let deferred = promise.defer();
this._connection.client.listTabs(response => {
if (response.error) {
this._connection.disconnect();
deferred.reject(response.error);
return;
}
this.response = response;
this.tabs = response.tabs;
this._checkSelectedTab();
deferred.resolve(response);
});
return deferred.promise;
},
// TODO: Tab "selection" should really take place by creating a TabProject
// which is the selected project. This should be done as part of the
// project-agnostic work.
_selectedTab: null,
get selectedTab() {
return this._selectedTab;
},
set selectedTab(tab) {
this._selectedTab = tab;
// Attach to the tab to follow navigation events
if (this._selectedTab) {
this.getTargetForTab();
}
},
_checkSelectedTab: function() {
if (!this._selectedTab) {
return;
}
let alive = this.tabs.some(tab => {
return tab.actor === this._selectedTab.actor;
});
if (!alive) {
this.emit("closed");
}
},
getTargetForTab: function() {
let store = this;
return Task.spawn(function*() {
// If you connect to a tab, then detach from it, the root actor may have
// de-listed the actors that belong to the tab. This breaks the toolbox
// if you try to connect to the same tab again. To work around this
// issue, we force a "listTabs" request before connecting to a tab.
yield store.listTabs();
return devtools.TargetFactory.forRemoteTab({
form: store._selectedTab,
client: store._connection.client,
chrome: false
});
});
},
};

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

@ -10,11 +10,13 @@ DIRS += [
'themes',
]
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
EXTRA_JS_MODULES.devtools.webide += [
'modules/addons.js',
'modules/app-manager.js',
'modules/remote-resources.js',
'modules/runtimes.js'
'modules/runtimes.js',
'modules/tab-store.js',
]

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

@ -0,0 +1,9 @@
[DEFAULT]
subsuite = devtools
support-files =
addons/simulators.json
head.js
templates.json
[browser_tabs.js]
skip-if = true # Fails on TBPL, to be fixed in bug 1062611

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

@ -0,0 +1,55 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
function test() {
waitForExplicitFinish();
SimpleTest.requestCompleteLog();
Task.spawn(function() {
const { DebuggerServer } =
Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
DebuggerServer.init(function () { return true; });
DebuggerServer.addBrowserActors();
let tab = yield addTab("about:newtab");
let win = yield openWebIDE();
yield connectToLocal(win);
is(Object.keys(DebuggerServer._connections).length, 1, "Locally connected");
yield selectTabProject(win);
let project = win.AppManager.selectedProject;
is(project.location, "about:newtab", "Location is correct");
is(project.name, "New Tab", "Name is correct");
yield closeWebIDE(win);
DebuggerServer.destroy();
yield removeTab(tab);
finish();
});
}
function connectToLocal(win) {
let deferred = promise.defer();
win.AppManager.connection.once(
win.Connection.Events.CONNECTED,
() => deferred.resolve());
win.document.querySelectorAll(".runtime-panel-item-custom")[1].click();
return deferred.promise;
}
function selectTabProject(win) {
return Task.spawn(function() {
yield win.AppManager.listTabs();
win.Cmds.showProjectPanel();
yield nextTick();
let tabsNode = win.document.querySelector("#project-panel-tabs");
let tabNode = tabsNode.querySelectorAll(".panel-item")[1];
tabNode.click();
});
}

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

@ -14,7 +14,12 @@ const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const {require} = devtools;
const {AppProjects} = require("devtools/app-manager/app-projects");
const TEST_BASE = "chrome://mochitests/content/chrome/browser/devtools/webide/test/";
let TEST_BASE;
if (window.location === "chrome://browser/content/browser.xul") {
TEST_BASE = "chrome://mochitests/content/browser/browser/devtools/webide/test/";
} else {
TEST_BASE = "chrome://mochitests/content/chrome/browser/devtools/webide/test/";
}
Services.prefs.setBoolPref("devtools.webide.enabled", true);
Services.prefs.setBoolPref("devtools.webide.enableLocalRuntime", true);
@ -110,3 +115,41 @@ function documentIsLoaded(doc) {
}
return deferred.promise;
}
function addTab(aUrl, aWindow) {
info("Adding tab: " + aUrl);
let deferred = promise.defer();
let targetWindow = aWindow || window;
let targetBrowser = targetWindow.gBrowser;
targetWindow.focus();
let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
let linkedBrowser = tab.linkedBrowser;
linkedBrowser.addEventListener("load", function onLoad() {
linkedBrowser.removeEventListener("load", onLoad, true);
info("Tab added and finished loading: " + aUrl);
deferred.resolve(tab);
}, true);
return deferred.promise;
}
function removeTab(aTab, aWindow) {
info("Removing tab.");
let deferred = promise.defer();
let targetWindow = aWindow || window;
let targetBrowser = targetWindow.gBrowser;
let tabContainer = targetBrowser.tabContainer;
tabContainer.addEventListener("TabClose", function onClose(aEvent) {
tabContainer.removeEventListener("TabClose", onClose, false);
info("Tab removed and finished closing.");
deferred.resolve();
}, false);
targetBrowser.removeTab(aTab);
return deferred.promise;
}

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

@ -55,7 +55,8 @@ header {
header > div {
vertical-align: top;
display: inline-block;
display: flex;
flex-direction: column;
}
#icon {
@ -73,6 +74,9 @@ h1, #type {
h1 {
font-size: 20px;
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
#type {

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

@ -65,6 +65,10 @@ window.busy-determined #action-busy-undetermined {
/* Panel buttons - projects */
#project-panel-button {
-moz-box-pack: start;
}
#project-panel-button > .panel-button-image {
width: 13px;
height: 13px;
@ -81,6 +85,10 @@ window.busy-determined #action-busy-undetermined {
-moz-image-region: rect(260px,338px,286px,312px);
}
#project-panel-button > .panel-button-label {
max-width: 150px;
}
/* Panel buttons - runtime */
#runtime-panel-button > .panel-button-image {
@ -136,6 +144,7 @@ panel > vbox {
panel > .panel-arrowcontainer > .panel-arrowcontent {
padding: 12px 0;
min-width: 200px;
max-width: 400px;
}
.panel-item {

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

@ -62,6 +62,7 @@
<!ENTITY projectPanel_myProjects "My Projects">
<!ENTITY projectPanel_runtimeApps "Runtime Apps">
<!ENTITY projectPanel_tabs "Tabs">
<!ENTITY runtimePanel_USBDevices "USB Devices">
<!ENTITY runtimePanel_WiFiDevices "Wi-Fi Devices">
<!ENTITY runtimePanel_simulators "Simulators">

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

@ -22,6 +22,10 @@ importHostedApp_header=Enter Manifest URL
notification_showTroubleShooting_label=troubleshooting
notification_showTroubleShooting_accesskey=t
# LOCALIZATION NOTE (project_tab_loading): This is shown as a temporary tab
# title for browser tab projects when the tab is still loading.
project_tab_loading=Loading…
# These messages appear in a notification box when an error occur.
error_cantInstallNotFullyConnected=Can't install project. Not fully connected.

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

@ -4,7 +4,7 @@
# 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/.
PROGRAM = 'CommandExecuteHandler'
Program('CommandExecuteHandler')
SOURCES += [
'CEHHelper.cpp',

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

@ -4,7 +4,7 @@
# 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/.
PROGRAM = 'linktool'
Program('linktool')
SOURCES += [
'linktool.cpp',

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

@ -4,7 +4,7 @@
# 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/.
PROGRAM = 'metrotestharness'
Program('metrotestharness')
SOURCES += [
'metrotestharness.cpp',

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

@ -12,6 +12,7 @@
background-image: linear-gradient(90deg, #a0dfff 0%, #ceeeff 100%);
border: 0px solid rgba(0,148,221,.5);
box-shadow: 0 1px 5px 0 rgba(0,0,0,.5), inset 0 1px 1px 0 #fff;
color: rgb(51,51,51);
}
#customization-tipPanel > .panel-arrowcontainer > .panel-arrowcontent:-moz-locale-dir(rtl) {

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

@ -108,7 +108,7 @@ libs:: $(topsrcdir)/tools/rb/fix_macosx_stack.py
endif
ifeq ($(OS_ARCH),Linux)
libs:: $(topsrcdir)/tools/rb/fix-linux-stack.pl
libs:: $(topsrcdir)/tools/rb/fix_linux_stack.py
$(INSTALL) $< $(DIST)/bin
endif

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

@ -68,7 +68,6 @@ _IS_CYGWIN = False
#endif
#expand _IS_CAMINO = __IS_CAMINO__ != 0
#expand _BIN_SUFFIX = __BIN_SUFFIX__
#expand _PERL = __PERL__
#expand _DEFAULT_APP = "./" + __BROWSER_PATH__
#expand _CERTS_SRC_DIR = __CERTS_SRC_DIR__
@ -143,7 +142,6 @@ class Automation(object):
IS_CYGWIN = _IS_CYGWIN
IS_CAMINO = _IS_CAMINO
BIN_SUFFIX = _BIN_SUFFIX
PERL = _PERL
UNIXISH = not IS_WIN32 and not IS_MAC
@ -671,7 +669,6 @@ class Automation(object):
def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath):
""" Look for timeout or crashes and return the status after the process terminates """
stackFixerProcess = None
stackFixerFunction = None
didTimeout = False
hitMaxTime = False
@ -694,12 +691,12 @@ class Automation(object):
stackFixerFunction = lambda line: stackFixerModule.fixSymbols(line)
del sys.path[0]
elif self.IS_DEBUG_BUILD and self.IS_LINUX:
# Run logsource through fix-linux-stack.pl (uses addr2line)
# Run each line through a function in fix_linux_stack.py (uses addr2line)
# This method is preferred for developer machines, so we don't have to run "make buildsymbols".
stackFixerProcess = self.Process([self.PERL, os.path.join(utilityPath, "fix-linux-stack.pl")],
stdin=logsource,
stdout=subprocess.PIPE)
logsource = stackFixerProcess.stdout
sys.path.insert(0, utilityPath)
import fix_linux_stack as stackFixerModule
stackFixerFunction = lambda line: stackFixerModule.fixSymbols(line)
del sys.path[0]
# With metro browser runs this script launches the metro test harness which launches the browser.
# The metro test harness hands back the real browser process id via log output which we need to
@ -725,7 +722,7 @@ class Automation(object):
self.log.info("INFO | automation.py | metro browser sub process id detected: %s", browserProcessId)
if not hitMaxTime and maxTime and datetime.now() - startTime > timedelta(seconds = maxTime):
# Kill the application, but continue reading from stack fixer so as not to deadlock on stackFixerProcess.wait().
# Kill the application.
hitMaxTime = True
self.log.info("TEST-UNEXPECTED-FAIL | %s | application ran for longer than allowed maximum time of %d seconds", self.lastTestSeen, int(maxTime))
self.killAndGetStack(proc.pid, utilityPath, debuggerInfo)
@ -743,11 +740,6 @@ class Automation(object):
self.lastTestSeen = "Main app process exited normally"
if status != 0 and not didTimeout and not hitMaxTime:
self.log.info("TEST-UNEXPECTED-FAIL | %s | Exited with code %d during test run", self.lastTestSeen, status)
if stackFixerProcess is not None:
fixerStatus = stackFixerProcess.wait()
automationutils.printstatus(status, "stackFixerProcess")
if fixerStatus != 0 and not didTimeout and not hitMaxTime:
self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Stack fixer process exited with code %d during test run", fixerStatus)
return status
def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs):

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

@ -39,20 +39,20 @@ Static Libraries
================
To build a static library, other than defining the source files (see above), one
just needs to define a library name with the ``LIBRARY_NAME`` variable.
just needs to define a library name with the ``Library`` template.
LIBRARY_NAME = 'foo'
Library('foo')
The library file name will be ``libfoo.a`` on UNIX systems and ``foo.lib`` on
Windows.
If the static library needs to aggregate other static libraries, a list of
``LIBRARY_NAME`` can be added to the ``USE_LIBS`` variable. Like ``SOURCES``, it
``Library`` names can be added to the ``USE_LIBS`` variable. Like ``SOURCES``, it
requires the appended list to be alphanumerically ordered.
USE_LIBS += ['bar', 'baz']
If there are multiple directories containing the same ``LIBRARY_NAME``, it is
If there are multiple directories containing the same ``Library`` name, it is
possible to disambiguate by prefixing with the path to the wanted one (relative
or absolute):
@ -61,7 +61,7 @@ or absolute):
'../relative/baz',
]
Note that the leaf name in those paths is the ``LIBRARY_NAME``, not an actual
Note that the leaf name in those paths is the ``Library`` name, not an actual
file name.
Note that currently, the build system may not create an actual library for
@ -84,10 +84,10 @@ be linked to that bigger library, with the ``FINAL_LIBRARY`` variable.
FINAL_LIBRARY = 'xul'
The ``FINAL_LIBRARY`` value must match a unique ``LIBRARY_NAME`` somewhere
The ``FINAL_LIBRARY`` value must match a unique ``Library`` name somewhere
in the tree.
As a special rule, those intermediate libraries don't need a ``LIBRARY_NAME``
As a special rule, those intermediate libraries don't need a ``Library`` name
for themselves.
@ -95,15 +95,15 @@ Shared Libraries
================
Sometimes, we want shared libraries, a.k.a. dynamic libraries. Such libraries
are defined with the same variables as static libraries, with the addition of
the ``FORCE_SHARED_LIB`` boolean variable:
are defined similarly to static libraries, using the ``SharedLibrary`` template
instead of ``Library``.
FORCE_SHARED_LIB = True
SharedLibrary('foo')
When this variable is set, no static library is built. See further below to
When this template is used, no static library is built. See further below to
build both types of libraries.
With a ``LIBRARY_NAME`` of ``foo``, the library file name will be
With a ``SharedLibrary`` name of ``foo``, the library file name will be
``libfoo.dylib`` on OSX, ``libfoo.so`` on ELF systems (Linux, etc.), and
``foo.dll`` on Windows. On Windows, there is also an import library named
``foo.lib``, used on the linker command line. ``libfoo.dylib`` and
@ -111,51 +111,61 @@ With a ``LIBRARY_NAME`` of ``foo``, the library file name will be
systems.
On OSX, one may want to create a special kind of dynamic library: frameworks.
This is done with the ``IS_FRAMEWORK`` boolean variable.
This is done with the ``Framework`` template.
IS_FRAMEWORK = True
Framework('foo')
With a ``LIBRARY_NAME`` of ``foo``, the framework file name will be ``foo``.
This variable however affects the behavior on all platforms, so it needs to
With a ``Framework`` name of ``foo``, the framework file name will be ``foo``.
This template however affects the behavior on all platforms, so it needs to
be set only on OSX.
Another special kind of library, XPCOM-specific, are XPCOM components. One can
build such a component with the ``IS_COMPONENT`` boolean variable.
build such a component with the ``XPCOMBinaryComponent`` template.
IS_COMPONENT = True
XPCOMBinaryComponent('foo')
Executables
===========
Executables, a.k.a. programs, are, in the simplest form, defined with the
``PROGRAM`` variable.
``Program`` template.
PROGRAM = 'foobar'
Program('foobar')
On UNIX systems, the executable file name will be ``foobar``, while on Windows,
it will be ``foobar.exe``.
Like static and shared libraries, the build system can be instructed to link
libraries to the executable with ``USE_LIBS``, listing various ``LIBRARY_NAME``.
libraries to the executable with ``USE_LIBS``, listing various ``Library``
names.
In some cases, we want to create an executable per source file in the current
directory, in which case we can use the ``SIMPLE_PROGRAMS`` list:
directory, in which case we can use the ``SimplePrograms`` template
SIMPLE_PROGRAMS = [
SimplePrograms([
'FirstProgram',
'SecondProgram',
]
])
The corresponding ``SOURCES`` must match:
Contrary to ``Program``, which requires corresponding ``SOURCES``, when using
``SimplePrograms``, the corresponding ``SOURCES`` are implied. If the
corresponding ``sources`` have an extension different from ``.cpp``, it is
possible to specify the proper extension:
SOURCES += [
'FirstProgram.cpp',
'SecondProgram.c',
]
SimplePrograms([
'ThirdProgram',
'FourthProgram',
], ext='.c')
Similar to ``SIMPLE_PROGRAMS``, is ``CPP_UNIT_TESTS``, which defines, with the
same rules, C++ unit tests programs.
Please note this construct was added for compatibility with what already lives
in the mozilla tree ; it is recommended not to add new simple programs with
sources with a different extension than ``.cpp``.
Similar to ``SimplePrograms``, is the ``CppUnitTests`` template, which defines,
with the same rules, C++ unit tests programs. Like ``SimplePrograms``, it takes
an ``ext`` argument to specify the extension for the corresponding ``SOURCES``,
if it's different from ``.cpp``.
Linking with system libraries
@ -189,10 +199,10 @@ Libraries from third party build system
=======================================
Some libraries in the tree are not built by the moz.build-governed build
system, and there is no ``LIBRARY_NAME`` corresponding to them.
system, and there is no ``Library`` corresponding to them.
However, ``USE_LIBS`` allows to reference such libraries by giving a full
path (like when disambiguating identical ``LIBRARY_NAME``). The same naming
path (like when disambiguating identical ``Library`` names). The same naming
rules apply as other uses of ``USE_LIBS``, so only the library name without
prefix and suffix shall be given.
@ -217,12 +227,12 @@ When both types of libraries are required, one needs to set both
But because static libraries and Windows import libraries have the same file
names, either the static or the shared library name needs to be different
than ``LIBRARY_NAME``.
than the name given to the ``Library`` template.
The ``STATIC_LIBRARY_NAME`` and ``SHARED_LIBRARY_NAME`` variables can be used
to change either the static or the shared library name.
LIBRARY_NAME = 'foo'
Library('foo')
STATIC_LIBRARY_NAME = 'foo_s'
With the above, on Windows, ``foo_s.lib`` will be the static library,
@ -231,25 +241,25 @@ With the above, on Windows, ``foo_s.lib`` will be the static library,
In some cases, for convenience, it is possible to set both
``STATIC_LIBRARY_NAME`` and ``SHARED_LIBRARY_NAME``. For example:
LIBRARY_NAME = 'mylib'
Library('mylib')
STATIC_LIBRARY_NAME = 'mylib_s'
SHARED_LIBRARY_NAME = CONFIG['SHARED_NAME']
This allows to use ``mylib`` in the ``USE_LIBS`` of another library or
executable.
When refering to a ``LIBRARY_NAME`` building both types of libraries in
When refering to a ``Library`` name building both types of libraries in
``USE_LIBS``, the shared library is chosen to be linked. But sometimes,
it is wanted to link the static version, in which case the ``LIBRARY_NAME``
it is wanted to link the static version, in which case the ``Library`` name
needs to be prefixed with ``static:`` in ``USE_LIBS``
a/moz.build:
LIBRARY_NAME = 'mylib'
Library('mylib')
FORCE_SHARED_LIB = True
FORCE_STATIC_LIB = True
STATIC_LIBRARY_NAME = 'mylib_s'
b/moz.build:
PROGRAM = 'myprog'
Program('myprog')
USE_LIBS += [
'static:mylib',
]
@ -262,18 +272,18 @@ The ``SDK_LIBRARY`` boolean variable defines whether the library in the current
directory is going to be installed in the SDK.
The ``SONAME`` variable declares a "shared object name" for the library. It
defaults to the ``LIBRARY_NAME`` or the ``SHARED_LIBRARY_NAME`` if set. When
defaults to the ``Library`` name or the ``SHARED_LIBRARY_NAME`` if set. When
linking to a library with a ``SONAME``, the resulting library or program will
have a dependency on the library with the name corresponding to the ``SONAME``
instead of ``LIBRARY_NAME``. This only impacts ELF systems.
instead of the ``Library`` name. This only impacts ELF systems.
a/moz.build:
LIBRARY_NAME = 'mylib'
Library('mylib')
b/moz.build:
LIBRARY_NAME = 'otherlib'
Library('otherlib')
SONAME = 'foo'
c/moz.build:
PROGRAM = 'myprog'
Program('myprog')
USE_LIBS += [
'mylib',
'otherlib',

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

@ -4,7 +4,7 @@
# 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/.
LIBRARY_NAME = 'stlport_static'
Library('stlport_static')
FORCE_STATIC_LIB = True

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

@ -4,6 +4,105 @@
# 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/.
@template
def StdCppCompat():
'''Template for libstdc++ compatibility for target binaries.'''
if CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION']:
USE_LIBS += ['stdc++compat']
@template
def Program(name):
'''Template for program executables.'''
PROGRAM = name
StdCppCompat()
@template
def SimplePrograms(names, ext='.cpp'):
'''Template for simple program executables.
Those have a single source with the same base name as the executable.
'''
SIMPLE_PROGRAMS += names
SOURCES += ['%s%s' % (name, ext) for name in names]
StdCppCompat()
@template
def CppUnitTests(names, ext='.cpp'):
'''Template for C++ unit tests.
Those have a single source with the same base name as the executable.
'''
CPP_UNIT_TESTS += names
SOURCES += ['%s%s' % (name, ext) for name in names]
StdCppCompat()
@template
def Library(name):
'''Template for libraries.'''
LIBRARY_NAME = name
@template
def SharedLibrary(name):
'''Template for shared libraries.'''
Library(name)
FORCE_SHARED_LIB = True
StdCppCompat()
@template
def Framework(name):
'''Template for OSX Frameworks.'''
SharedLibrary(name)
IS_FRAMEWORK = True
@template
def HostStdCppCompat():
'''Template for libstdc++ compatibility for host binaries.'''
if CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']:
HOST_USE_LIBS += ['host_stdc++compat']
@template
def HostProgram(name):
'''Template for build tools executables.'''
HOST_PROGRAM = name
HostStdCppCompat()
@template
def HostSimplePrograms(names, ext='.cpp'):
'''Template for simple build tools executables.
Those have a single source with the same base name as the executable.
'''
HOST_SIMPLE_PROGRAMS += names
HOST_SOURCES += ['%s%s' % (name.replace('host_', ''), ext)
for name in names]
HostStdCppCompat()
@template
def HostLibrary(name):
'''Template for build tools libraries.'''
HOST_LIBRARY_NAME = name
@template
def GeckoBinary():
'''Template for binaries using Gecko.
@ -24,7 +123,7 @@ def XPCOMBinaryComponent(name):
name is the name of the component.
'''
LIBRARY_NAME = name
SharedLibrary(name)
GeckoBinary()

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

@ -19,7 +19,7 @@ HOST_SOURCES += [
'elfhack.cpp',
]
HOST_PROGRAM = 'elfhack'
HostProgram('elfhack')
DEFINES['ELFHACK_BUILD'] = True

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

@ -5,11 +5,11 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
if CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION']:
LIBRARY_NAME = 'stdc++compat'
Library('stdc++compat')
SOURCES += ['stdc++compat.cpp']
if CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']:
HOST_LIBRARY_NAME = 'host_stdc++compat'
HostLibrary('host_stdc++compat')
HOST_SOURCES += [
'stdc++compat.cpp',
]

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

@ -8,9 +8,7 @@ SOURCES += [
'crashinjectdll.cpp',
]
LIBRARY_NAME = 'crashinjectdll'
FORCE_SHARED_LIB = True
SharedLibrary('crashinjectdll')
DEFFILE = SRCDIR + '/crashinjectdll.def'

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

@ -10,7 +10,7 @@ if CONFIG['_MSC_VER'] and CONFIG['OS_TEST'] != 'x86_64':
TEST_DIRS += ['crashinjectdll']
if CONFIG['ENABLE_TESTS']:
PROGRAM = 'crashinject'
Program('crashinject')
SOURCES += [
'crashinject.cpp',
]

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

@ -8,9 +8,7 @@ SOURCES += [
'vmwarerecordinghelper.cpp',
]
LIBRARY_NAME = 'vmwarerecordinghelper'
FORCE_SHARED_LIB = True
SharedLibrary('vmwarerecordinghelper')
DEFFILE = '%s/%s.def' % (SRCDIR, LIBRARY_NAME)

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

@ -19,7 +19,7 @@ interface nsILoadContext;
[ptr] native JSContextPtr(JSContext);
[ptr] native JSObjectPtr(JSObject);
[scriptable, uuid(f6e1e37e-14d0-44fa-a9bb-712bfad6c5f7)]
[scriptable, uuid(3b021962-975e-43b5-8a93-9fc2d20346e9)]
interface nsIScriptSecurityManager : nsISupports
{
/**
@ -199,7 +199,13 @@ interface nsIScriptSecurityManager : nsISupports
* channel owner if there is one, and the codebase principal for the
* channel's URI otherwise. aChannel must not be null.
*/
nsIPrincipal getChannelPrincipal(in nsIChannel aChannel);
nsIPrincipal getChannelResultPrincipal(in nsIChannel aChannel);
/**
* Get the codebase principal for the channel's URI.
* aChannel must not be null.
*/
nsIPrincipal getChannelURIPrincipal(in nsIChannel aChannel);
/**
* Check whether a given principal is a system principal. This allows us

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

@ -306,8 +306,8 @@ nsScriptSecurityManager::AppStatusForPrincipal(nsIPrincipal *aPrin)
}
NS_IMETHODIMP
nsScriptSecurityManager::GetChannelPrincipal(nsIChannel* aChannel,
nsIPrincipal** aPrincipal)
nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel,
nsIPrincipal** aPrincipal)
{
NS_PRECONDITION(aChannel, "Must have channel!");
nsCOMPtr<nsISupports> owner;
@ -336,14 +336,21 @@ nsScriptSecurityManager::GetChannelPrincipal(nsIChannel* aChannel,
return NS_OK;
}
}
return GetChannelURIPrincipal(aChannel, aPrincipal);
}
// OK, get the principal from the URI. Make sure this does the same thing
NS_IMETHODIMP
nsScriptSecurityManager::GetChannelURIPrincipal(nsIChannel* aChannel,
nsIPrincipal** aPrincipal)
{
NS_PRECONDITION(aChannel, "Must have channel!");
// Get the principal from the URI. Make sure this does the same thing
// as nsDocument::Reset and XULDocument::StartDocumentLoad.
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(aChannel, loadContext);
@ -1189,7 +1196,7 @@ nsScriptSecurityManager::AsyncOnChannelRedirect(nsIChannel* oldChannel,
nsIAsyncVerifyRedirectCallback *cb)
{
nsCOMPtr<nsIPrincipal> oldPrincipal;
GetChannelPrincipal(oldChannel, getter_AddRefs(oldPrincipal));
GetChannelResultPrincipal(oldChannel, getter_AddRefs(oldPrincipal));
nsCOMPtr<nsIURI> newURI;
newChannel->GetURI(getter_AddRefs(newURI));

2
config/external/ffi/moz.build поставляемый
Просмотреть файл

@ -4,7 +4,7 @@
# 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/.
LIBRARY_NAME = 'ffi'
Library('ffi')
if CONFIG['MOZ_NATIVE_FFI']:
OS_LIBS += CONFIG['MOZ_FFI_LIBS']

2
config/external/freetype2/moz.build поставляемый
Просмотреть файл

@ -4,7 +4,7 @@
# 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/.
LIBRARY_NAME = 'freetype'
Library('freetype')
if CONFIG['MOZ_TREE_FREETYPE']:
USE_LIBS += [

2
config/external/icu/moz.build поставляемый
Просмотреть файл

@ -4,7 +4,7 @@
# 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/.
LIBRARY_NAME = 'icu'
Library('icu')
if CONFIG['MOZ_NATIVE_ICU']:
OS_LIBS += CONFIG['MOZ_ICU_LIBS']

2
config/external/nspr/moz.build поставляемый
Просмотреть файл

@ -4,7 +4,7 @@
# 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/.
LIBRARY_NAME = 'nspr'
Library('nspr')
if CONFIG['MOZ_FOLD_LIBS']:
# When folding libraries, nspr is actually in the nss library.

2
config/external/nss/crmf/moz.build поставляемый
Просмотреть файл

@ -4,7 +4,7 @@
# 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/.
LIBRARY_NAME = 'crmf'
Library('crmf')
if CONFIG['MOZ_NATIVE_NSS']:
OS_LIBS += [l for l in CONFIG['NSS_LIBS'] if l.startswith('-L')]

6
config/external/nss/moz.build поставляемый
Просмотреть файл

@ -4,17 +4,16 @@
# 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/.
LIBRARY_NAME = 'nss'
DIRS += ['crmf']
if CONFIG['MOZ_NATIVE_NSS']:
Library('nss')
OS_LIBS += CONFIG['NSS_LIBS']
elif CONFIG['MOZ_FOLD_LIBS']:
SharedLibrary('nss')
# TODO: The library name can be changed when bug 845217 is fixed.
SHARED_LIBRARY_NAME = 'nss3'
FORCE_SHARED_LIB = True
SDK_LIBRARY = True
# Normally, there should be /something/ to ensure nspr is built
@ -38,6 +37,7 @@ elif CONFIG['MOZ_FOLD_LIBS']:
if CONFIG['OS_ARCH'] == 'Linux' and CONFIG['GCC_USE_GNU_LD']:
LD_VERSION_SCRIPT = 'nss3.def'
else:
Library('nss')
USE_LIBS += [
'/security/nss/lib/nss/nss3',
'/security/nss/lib/smime/smime3',

6
config/external/sqlite/moz.build поставляемый
Просмотреть файл

@ -4,19 +4,19 @@
# 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/.
LIBRARY_NAME = 'sqlite'
if CONFIG['MOZ_NATIVE_SQLITE']:
Library('sqlite')
OS_LIBS += CONFIG['SQLITE_LIBS']
else:
DIRS += ['../../../db/sqlite3/src']
if CONFIG['MOZ_FOLD_LIBS']:
Library('sqlite')
# When folding libraries, sqlite is actually in the nss library.
USE_LIBS += [
'nss',
]
else:
FORCE_SHARED_LIB = True
SharedLibrary('sqlite')
SHARED_LIBRARY_NAME = 'mozsqlite3'
if CONFIG['OS_ARCH'] == 'Linux' and CONFIG['GCC_USE_GNU_LD']:

2
config/external/zlib/moz.build поставляемый
Просмотреть файл

@ -4,7 +4,7 @@
# 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/.
LIBRARY_NAME = 'zlib'
Library('zlib')
if CONFIG['MOZ_NATIVE_ZLIB']:
OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']

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

@ -21,7 +21,7 @@ if CONFIG['HOST_OS_ARCH'] != 'WINNT':
'nsinstall.c',
'pathsub.c',
]
HOST_PROGRAM = 'nsinstall_real'
HostProgram('nsinstall_real')
if CONFIG['GKMEDIAS_SHARED_LIBRARY']:
DEFINES['GKMEDIAS_SHARED_LIBRARY'] = True

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

@ -343,8 +343,8 @@ ImportLoader::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
if (nsContentUtils::IsSystemPrincipal(principal)) {
// We should never import non-system documents and run their scripts with system principal!
nsCOMPtr<nsIPrincipal> channelPrincipal;
nsContentUtils::GetSecurityManager()->GetChannelPrincipal(channel,
getter_AddRefs(channelPrincipal));
nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(channel,
getter_AddRefs(channelPrincipal));
if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
return NS_ERROR_FAILURE;
}

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

@ -5670,7 +5670,7 @@ nsContentUtils::CheckSameOrigin(nsIChannel *aOldChannel, nsIChannel *aNewChannel
nsCOMPtr<nsIPrincipal> oldPrincipal;
nsContentUtils::GetSecurityManager()->
GetChannelPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
GetChannelResultPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
nsCOMPtr<nsIURI> newURI;
aNewChannel->GetURI(getter_AddRefs(newURI));

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

@ -682,13 +682,15 @@ nsCORSListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
if (mHasBeenCrossSite) {
// Once we've been cross-site, cross-origin redirects reset our source
// origin.
// origin. Note that we need to call GetChannelURIPrincipal() because
// we are looking for the principal that is actually being loaded and not
// the principal that initiated the load.
nsCOMPtr<nsIPrincipal> oldChannelPrincipal;
nsContentUtils::GetSecurityManager()->
GetChannelPrincipal(aOldChannel, getter_AddRefs(oldChannelPrincipal));
GetChannelURIPrincipal(aOldChannel, getter_AddRefs(oldChannelPrincipal));
nsCOMPtr<nsIPrincipal> newChannelPrincipal;
nsContentUtils::GetSecurityManager()->
GetChannelPrincipal(aNewChannel, getter_AddRefs(newChannelPrincipal));
GetChannelURIPrincipal(aNewChannel, getter_AddRefs(newChannelPrincipal));
if (!oldChannelPrincipal || !newChannelPrincipal) {
rv = NS_ERROR_OUT_OF_MEMORY;
}

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

@ -2208,15 +2208,15 @@ nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
nsCOMPtr<nsIPrincipal> principal;
if (aChannel) {
// Note: this code is duplicated in XULDocument::StartDocumentLoad and
// nsScriptSecurityManager::GetChannelPrincipal.
// nsScriptSecurityManager::GetChannelResultPrincipal.
// Note: this should match nsDocShell::OnLoadingSite
NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
nsIScriptSecurityManager *securityManager =
nsContentUtils::GetSecurityManager();
if (securityManager) {
securityManager->GetChannelPrincipal(aChannel,
getter_AddRefs(principal));
securityManager->GetChannelResultPrincipal(aChannel,
getter_AddRefs(principal));
}
}

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

@ -1461,7 +1461,7 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
// principal as the origin principal
if (aRequest->mCORSMode == CORS_NONE) {
rv = nsContentUtils::GetSecurityManager()->
GetChannelPrincipal(channel, getter_AddRefs(aRequest->mOriginPrincipal));
GetChannelResultPrincipal(channel, getter_AddRefs(aRequest->mOriginPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
}

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

@ -10,14 +10,12 @@ XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
if CONFIG['OS_ARCH'] != 'Darwin':
XPCSHELL_TESTS_MANIFESTS += ['unit_ipc/xpcshell.ini']
CPP_UNIT_TESTS += [
CppUnitTests([
'TestCSPParser',
'TestGetURL',
'TestNativeXMLHttpRequest',
'TestPlainTextSerializer',
]
SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
])
MOCHITEST_MANIFESTS += [
'chrome/mochitest.ini',

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

@ -94,7 +94,7 @@ ImageListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
nsCOMPtr<nsIPrincipal> channelPrincipal;
if (secMan) {
secMan->GetChannelPrincipal(channel, getter_AddRefs(channelPrincipal));
secMan->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
}
int16_t decision = nsIContentPolicy::ACCEPT;

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

@ -534,7 +534,7 @@ ChannelMediaResource::OnDataAvailable(nsIRequest* aRequest,
CopySegmentClosure closure;
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
if (secMan && mChannel) {
secMan->GetChannelPrincipal(mChannel, getter_AddRefs(closure.mPrincipal));
secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(closure.mPrincipal));
}
closure.mResource = this;
@ -1406,7 +1406,7 @@ already_AddRefed<nsIPrincipal> FileMediaResource::GetCurrentPrincipal()
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
if (!secMan || !mChannel)
return nullptr;
secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal));
secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(principal));
return principal.forget();
}

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

@ -740,7 +740,7 @@ already_AddRefed<nsIPrincipal> RtspMediaResource::GetCurrentPrincipal()
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
if (!secMan || !mChannel)
return nullptr;
secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal));
secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(principal));
return principal.forget();
}

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

@ -4,12 +4,10 @@
# 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/.
CPP_UNIT_TESTS += [
CppUnitTests([
'TestAudioBuffers',
'TestAudioMixer'
]
SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
])
FAIL_ON_WARNINGS = True

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

@ -197,6 +197,7 @@ PlatformCallback(void* decompressionOutputRefCon,
}
if (flags & kVTDecodeInfo_FrameDropped) {
NS_WARNING(" ...frame dropped...");
return;
}
MOZ_ASSERT(CFGetTypeID(image) == CVPixelBufferGetTypeID(),
"VideoToolbox returned an unexpected image type");

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

@ -132,6 +132,27 @@ MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
return mReader->CreateSubDecoder(aType);
}
void
MediaSourceDecoder::AddTrackBuffer(TrackBuffer* aTrackBuffer)
{
MOZ_ASSERT(mReader);
mReader->AddTrackBuffer(aTrackBuffer);
}
void
MediaSourceDecoder::RemoveTrackBuffer(TrackBuffer* aTrackBuffer)
{
MOZ_ASSERT(mReader);
mReader->RemoveTrackBuffer(aTrackBuffer);
}
void
MediaSourceDecoder::OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo)
{
MOZ_ASSERT(mReader);
mReader->OnTrackBufferConfigured(aTrackBuffer, aInfo);
}
void
MediaSourceDecoder::Ended()
{

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

@ -20,6 +20,7 @@ class MediaResource;
class MediaDecoderStateMachine;
class MediaSourceReader;
class SourceBufferDecoder;
class TrackBuffer;
namespace dom {
@ -46,6 +47,9 @@ public:
void DetachMediaSource();
already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType);
void AddTrackBuffer(TrackBuffer* aTrackBuffer);
void RemoveTrackBuffer(TrackBuffer* aTrackBuffer);
void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo);
void Ended();

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

@ -14,6 +14,7 @@
#include "MediaSourceDecoder.h"
#include "MediaSourceUtils.h"
#include "SourceBufferDecoder.h"
#include "TrackBuffer.h"
#ifdef MOZ_FMP4
#include "MP4Decoder.h"
@ -37,6 +38,8 @@ namespace mozilla {
MediaSourceReader::MediaSourceReader(MediaSourceDecoder* aDecoder)
: MediaDecoderReader(aDecoder)
, mLastAudioTime(-1)
, mLastVideoTime(-1)
, mTimeThreshold(-1)
, mDropAudioBeforeThreshold(false)
, mDropVideoBeforeThreshold(false)
@ -49,25 +52,35 @@ MediaSourceReader::MediaSourceReader(MediaSourceDecoder* aDecoder)
bool
MediaSourceReader::IsWaitingMediaResources()
{
return mDecoders.IsEmpty() && mPendingDecoders.IsEmpty();
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
if (!mTrackBuffers[i]->IsReady()) {
return true;
}
}
return mTrackBuffers.IsEmpty();
}
void
MediaSourceReader::RequestAudioData()
{
MSE_DEBUGV("MediaSourceReader(%p)::RequestAudioData", this);
if (!mAudioReader) {
MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called with no audio reader", this);
MOZ_ASSERT(mPendingDecoders.IsEmpty());
GetCallback()->OnDecodeError();
return;
}
SwitchReaders(SWITCH_OPTIONAL);
if (SwitchAudioReader(double(mLastAudioTime) / USECS_PER_S)) {
MSE_DEBUGV("MediaSourceReader(%p)::RequestAudioData switching audio reader", this);
}
mAudioReader->RequestAudioData();
}
void
MediaSourceReader::OnAudioDecoded(AudioData* aSample)
{
MSE_DEBUGV("MediaSourceReader(%p)::OnAudioDecoded mTime=%lld mDuration=%lld d=%d",
this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity);
if (mDropAudioBeforeThreshold) {
if (aSample->mTime < mTimeThreshold) {
MSE_DEBUG("MediaSourceReader(%p)::OnAudioDecoded mTime=%lld < mTimeThreshold=%lld",
@ -86,21 +99,22 @@ MediaSourceReader::OnAudioDecoded(AudioData* aSample)
mAudioIsSeeking = false;
aSample->mDiscontinuity = true;
}
mLastAudioTime = aSample->mTime + aSample->mDuration;
GetCallback()->OnAudioDecoded(aSample);
}
void
MediaSourceReader::OnAudioEOS()
{
MSE_DEBUG("MediaSourceReader(%p)::OnAudioEOS reader=%p (readers=%u)",
this, mAudioReader.get(), mDecoders.Length());
if (SwitchReaders(SWITCH_FORCED)) {
MSE_DEBUG("MediaSourceReader(%p)::OnAudioEOS reader=%p (decoders=%u)",
this, mAudioReader.get(), mAudioTrack->Decoders().Length());
if (SwitchAudioReader(double(mLastAudioTime) / USECS_PER_S)) {
// Success! Resume decoding with next audio decoder.
RequestAudioData();
} else if (IsEnded()) {
// End of stream.
MSE_DEBUG("MediaSourceReader(%p)::OnAudioEOS reader=%p EOS (readers=%u)",
this, mAudioReader.get(), mDecoders.Length());
MSE_DEBUG("MediaSourceReader(%p)::OnAudioEOS reader=%p EOS (decoders=%u)",
this, mAudioReader.get(), mAudioTrack->Decoders().Length());
GetCallback()->OnAudioEOS();
}
}
@ -108,26 +122,35 @@ MediaSourceReader::OnAudioEOS()
void
MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold)
{
MSE_DEBUGV("MediaSourceReader(%p)::RequestVideoData(%d, %lld)",
this, aSkipToNextKeyframe, aTimeThreshold);
if (!mVideoReader) {
MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called with no video reader", this);
MOZ_ASSERT(mPendingDecoders.IsEmpty());
GetCallback()->OnDecodeError();
return;
}
mTimeThreshold = aTimeThreshold;
SwitchReaders(SWITCH_OPTIONAL);
if (aSkipToNextKeyframe) {
mTimeThreshold = aTimeThreshold;
mDropAudioBeforeThreshold = true;
mDropVideoBeforeThreshold = true;
}
if (SwitchVideoReader(double(mLastVideoTime) / USECS_PER_S)) {
MSE_DEBUGV("MediaSourceReader(%p)::RequestVideoData switching video reader", this);
}
mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
}
void
MediaSourceReader::OnVideoDecoded(VideoData* aSample)
{
MSE_DEBUGV("MediaSourceReader(%p)::OnVideoDecoded mTime=%lld mDuration=%lld d=%d",
this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity);
if (mDropVideoBeforeThreshold) {
if (aSample->mTime < mTimeThreshold) {
MSE_DEBUG("MediaSourceReader(%p)::OnVideoDecoded mTime=%lld < mTimeThreshold=%lld",
this, aSample->mTime, mTimeThreshold);
delete aSample;
mVideoReader->RequestVideoData(false, mTimeThreshold);
mVideoReader->RequestVideoData(false, 0);
return;
}
mDropVideoBeforeThreshold = false;
@ -140,7 +163,7 @@ MediaSourceReader::OnVideoDecoded(VideoData* aSample)
mVideoIsSeeking = false;
aSample->mDiscontinuity = true;
}
mLastVideoTime = aSample->mTime + aSample->mDuration;
GetCallback()->OnVideoDecoded(aSample);
}
@ -148,15 +171,15 @@ void
MediaSourceReader::OnVideoEOS()
{
// End of stream. See if we can switch to another video decoder.
MSE_DEBUG("MediaSourceReader(%p)::OnVideoEOS reader=%p (readers=%u)",
this, mVideoReader.get(), mDecoders.Length());
if (SwitchReaders(SWITCH_FORCED)) {
MSE_DEBUG("MediaSourceReader(%p)::OnVideoEOS reader=%p (decoders=%u)",
this, mVideoReader.get(), mVideoTrack->Decoders().Length());
if (SwitchVideoReader(double(mLastVideoTime) / USECS_PER_S)) {
// Success! Resume decoding with next video decoder.
RequestVideoData(false, mTimeThreshold);
RequestVideoData(false, 0);
} else if (IsEnded()) {
// End of stream.
MSE_DEBUG("MediaSourceReader(%p)::OnVideoEOS reader=%p EOS (readers=%u)",
this, mVideoReader.get(), mDecoders.Length());
MSE_DEBUG("MediaSourceReader(%p)::OnVideoEOS reader=%p EOS (decoders=%u)",
this, mVideoReader.get(), mVideoTrack->Decoders().Length());
GetCallback()->OnVideoEOS();
}
}
@ -164,6 +187,7 @@ MediaSourceReader::OnVideoEOS()
void
MediaSourceReader::OnDecodeError()
{
MSE_DEBUG("MediaSourceReader(%p)::OnDecodeError", this);
GetCallback()->OnDecodeError();
}
@ -171,171 +195,105 @@ void
MediaSourceReader::Shutdown()
{
MediaDecoderReader::Shutdown();
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
mDecoders[i]->GetReader()->Shutdown();
for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
mTrackBuffers[i]->Shutdown();
}
mTrackBuffers.Clear();
mAudioTrack = nullptr;
mAudioReader = nullptr;
mVideoTrack = nullptr;
mVideoReader = nullptr;
}
void
MediaSourceReader::BreakCycles()
{
MediaDecoderReader::BreakCycles();
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
mDecoders[i]->GetReader()->BreakCycles();
for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
mTrackBuffers[i]->BreakCycles();
}
mTrackBuffers.Clear();
mAudioTrack = nullptr;
mAudioReader = nullptr;
mVideoTrack = nullptr;
mVideoReader = nullptr;
}
bool
MediaSourceReader::SwitchAudioReader(MediaDecoderReader* aTargetReader)
MediaSourceReader::SwitchAudioReader(double aTarget)
{
if (aTargetReader == mAudioReader) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
// XXX: Can't handle adding an audio track after ReadMetadata yet.
if (!mAudioTrack) {
return false;
}
if (mAudioReader) {
AudioInfo targetInfo = aTargetReader->GetMediaInfo().mAudio;
auto& decoders = mAudioTrack->Decoders();
for (uint32_t i = 0; i < decoders.Length(); ++i) {
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
decoders[i]->GetBuffered(ranges);
MediaDecoderReader* newReader = decoders[i]->GetReader();
MSE_DEBUGV("MediaDecoderReader(%p)::SwitchAudioReader(%f) audioReader=%p reader=%p ranges=%s",
this, aTarget, mAudioReader.get(), newReader, DumpTimeRanges(ranges).get());
AudioInfo targetInfo = newReader->GetMediaInfo().mAudio;
AudioInfo currentInfo = mAudioReader->GetMediaInfo().mAudio;
// TODO: We can't handle switching audio formats yet.
if (currentInfo.mRate != targetInfo.mRate ||
currentInfo.mChannels != targetInfo.mChannels) {
return false;
continue;
}
mAudioReader->SetIdle();
if (ranges->Find(aTarget) != dom::TimeRanges::NoIndex) {
if (newReader->AudioQueue().AtEndOfStream()) {
continue;
}
if (mAudioReader) {
mAudioReader->SetIdle();
}
mAudioReader = newReader;
MSE_DEBUG("MediaDecoderReader(%p)::SwitchAudioReader(%f) switching to audio reader %p",
this, aTarget, mAudioReader.get());
return true;
}
}
mAudioReader = aTargetReader;
mDropAudioBeforeThreshold = true;
MSE_DEBUG("MediaDecoderReader(%p)::SwitchReaders(%p) switching audio reader",
this, mAudioReader.get());
return true;
return false;
}
bool
MediaSourceReader::SwitchVideoReader(MediaDecoderReader* aTargetReader)
MediaSourceReader::SwitchVideoReader(double aTarget)
{
if (aTargetReader == mVideoReader) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
// XXX: Can't handle adding a video track after ReadMetadata yet.
if (!mVideoTrack) {
return false;
}
if (mVideoReader) {
mVideoReader->SetIdle();
}
mVideoReader = aTargetReader;
mDropVideoBeforeThreshold = true;
MSE_DEBUG("MediaDecoderReader(%p)::SwitchVideoReader(%p) switching video reader",
this, mVideoReader.get());
return true;
}
bool
MediaSourceReader::SwitchReaders(SwitchType aType)
{
InitializePendingDecoders();
// This monitor must be held after the call to InitializePendingDecoders
// as that method also obtains the lock, and then attempts to exit it
// to call ReadMetadata on the readers. If we hold it before the call then
// it remains held during the ReadMetadata call causing a deadlock.
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
bool didSwitch = false;
double decodeTarget = double(mTimeThreshold) / USECS_PER_S;
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
SourceBufferDecoder* decoder = mDecoders[i];
const MediaInfo& info = decoder->GetReader()->GetMediaInfo();
auto& decoders = mVideoTrack->Decoders();
for (uint32_t i = 0; i < decoders.Length(); ++i) {
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
decoder->GetBuffered(ranges);
decoders[i]->GetBuffered(ranges);
MSE_DEBUGV("MediaDecoderReader(%p)::SwitchReaders(%d) decoder=%u (%p) discarded=%d"
" reader=%p audioReader=%p videoReader=%p"
" hasAudio=%d hasVideo=%d decodeTarget=%f ranges=%s",
this, aType, i, decoder, decoder->IsDiscarded(),
decoder->GetReader(), mAudioReader.get(), mVideoReader.get(),
info.HasAudio(), info.HasVideo(), decodeTarget,
DumpTimeRanges(ranges).get());
MediaDecoderReader* newReader = decoders[i]->GetReader();
MSE_DEBUGV("MediaDecoderReader(%p)::SwitchVideoReader(%f) videoReader=%p reader=%p ranges=%s",
this, aTarget, mVideoReader.get(), newReader, DumpTimeRanges(ranges).get());
if (decoder->IsDiscarded()) {
continue;
}
if (aType == SWITCH_FORCED || ranges->Find(decodeTarget) != dom::TimeRanges::NoIndex) {
if (info.HasAudio()) {
didSwitch |= SwitchAudioReader(mDecoders[i]->GetReader());
if (ranges->Find(aTarget) != dom::TimeRanges::NoIndex) {
if (newReader->VideoQueue().AtEndOfStream()) {
continue;
}
if (info.HasVideo()) {
didSwitch |= SwitchVideoReader(mDecoders[i]->GetReader());
if (mVideoReader) {
mVideoReader->SetIdle();
}
mVideoReader = newReader;
MSE_DEBUG("MediaDecoderReader(%p)::SwitchVideoReader(%f) switching to video reader %p",
this, aTarget, mVideoReader.get());
return true;
}
}
return didSwitch;
}
class ReleaseDecodersTask : public nsRunnable {
public:
explicit ReleaseDecodersTask(nsTArray<nsRefPtr<SourceBufferDecoder>>& aDecoders)
{
mDecoders.SwapElements(aDecoders);
}
NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
mDecoders.Clear();
return NS_OK;
}
private:
nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
};
void
MediaSourceReader::InitializePendingDecoders()
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
for (uint32_t i = 0; i < mPendingDecoders.Length(); ++i) {
nsRefPtr<SourceBufferDecoder> decoder = mPendingDecoders[i];
MediaDecoderReader* reader = decoder->GetReader();
MSE_DEBUG("MediaSourceReader(%p): Initializing subdecoder %p reader %p",
this, decoder.get(), reader);
MediaInfo mi;
nsAutoPtr<MetadataTags> tags; // TODO: Handle metadata.
nsresult rv;
{
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
}
reader->SetIdle();
if (NS_FAILED(rv)) {
// XXX: Need to signal error back to owning SourceBuffer.
MSE_DEBUG("MediaSourceReader(%p): Reader %p failed to initialize rv=%x", this, reader, rv);
continue;
}
bool active = false;
if (mi.HasVideo() || mi.HasAudio()) {
MSE_DEBUG("MediaSourceReader(%p): Reader %p has video=%d audio=%d",
this, reader, mi.HasVideo(), mi.HasAudio());
if (mi.HasVideo()) {
MSE_DEBUG("MediaSourceReader(%p): Reader %p video resolution=%dx%d",
this, reader, mi.mVideo.mDisplay.width, mi.mVideo.mDisplay.height);
}
if (mi.HasAudio()) {
MSE_DEBUG("MediaSourceReader(%p): Reader %p audio sampleRate=%d channels=%d",
this, reader, mi.mAudio.mRate, mi.mAudio.mChannels);
}
active = true;
}
if (active) {
mDecoders.AppendElement(decoder);
} else {
MSE_DEBUG("MediaSourceReader(%p): Reader %p not activated", this, reader);
}
}
NS_DispatchToMainThread(new ReleaseDecodersTask(mPendingDecoders));
MOZ_ASSERT(mPendingDecoders.IsEmpty());
mDecoder->NotifyWaitingForResourcesStatusChanged();
return false;
}
MediaDecoderReader*
@ -376,22 +334,51 @@ MediaSourceReader::CreateSubDecoder(const nsACString& aType)
reader->SetCallback(callback);
reader->SetTaskQueue(GetTaskQueue());
reader->Init(nullptr);
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MSE_DEBUG("MediaSourceReader(%p)::CreateSubDecoder subdecoder %p subreader %p",
this, decoder.get(), reader.get());
decoder->SetReader(reader);
mPendingDecoders.AppendElement(decoder);
RefPtr<nsIRunnable> task =
NS_NewRunnableMethod(this, &MediaSourceReader::InitializePendingDecoders);
if (NS_FAILED(GetTaskQueue()->Dispatch(task))) {
MSE_DEBUG("MediaSourceReader(%p): Failed to enqueue decoder initialization task", this);
return nullptr;
}
mDecoder->NotifyWaitingForResourcesStatusChanged();
return decoder.forget();
}
namespace {
void
MediaSourceReader::AddTrackBuffer(TrackBuffer* aTrackBuffer)
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MSE_DEBUG("MediaSourceReader(%p)::AddTrackBuffer %p", this, aTrackBuffer);
mTrackBuffers.AppendElement(aTrackBuffer);
}
void
MediaSourceReader::RemoveTrackBuffer(TrackBuffer* aTrackBuffer)
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MSE_DEBUG("MediaSourceReader(%p)::RemoveTrackBuffer %p", this, aTrackBuffer);
mTrackBuffers.RemoveElement(aTrackBuffer);
if (mAudioTrack == aTrackBuffer) {
mAudioTrack = nullptr;
}
if (mVideoTrack == aTrackBuffer) {
mVideoTrack = nullptr;
}
}
void
MediaSourceReader::OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo)
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MOZ_ASSERT(mTrackBuffers.Contains(aTrackBuffer));
if (aInfo.HasAudio() && !mAudioTrack) {
MSE_DEBUG("MediaSourceReader(%p)::OnTrackBufferConfigured %p audio", this, aTrackBuffer);
mAudioTrack = aTrackBuffer;
}
if (aInfo.HasVideo() && !mVideoTrack) {
MSE_DEBUG("MediaSourceReader(%p)::OnTrackBufferConfigured %p video", this, aTrackBuffer);
mVideoTrack = aTrackBuffer;
}
mDecoder->NotifyWaitingForResourcesStatusChanged();
}
class ChangeToHaveMetadata : public nsRunnable {
public:
explicit ChangeToHaveMetadata(AbstractMediaDecoder* aDecoder) :
@ -410,23 +397,18 @@ public:
private:
nsRefPtr<AbstractMediaDecoder> mDecoder;
};
}
bool
MediaSourceReader::DecodersContainTime(double aTime)
MediaSourceReader::TrackBuffersContainTime(double aTime)
{
bool found = false;
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
if (!mDecoders[i]->IsDiscarded()) {
if (!mDecoders[i]->ContainsTime(aTime)) {
// No use to continue searching, one source buffer isn't ready yet
return false;
}
found = true;
}
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (mAudioTrack && !mAudioTrack->ContainsTime(aTime)) {
return false;
}
return found;
if (mVideoTrack && !mVideoTrack->ContainsTime(aTime)) {
return false;
}
return true;
}
nsresult
@ -435,8 +417,18 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
{
MSE_DEBUG("MediaSourceReader(%p)::Seek(aTime=%lld, aStart=%lld, aEnd=%lld, aCurrent=%lld)",
this, aTime, aStartTime, aEndTime, aCurrentTime);
ResetDecode();
for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
mTrackBuffers[i]->ResetDecode();
}
// Decoding discontinuity upon seek, reset last times to seek target.
mLastAudioTime = aTime;
mLastVideoTime = aTime;
double target = static_cast<double>(aTime) / USECS_PER_S;
if (!DecodersContainTime(target)) {
if (!TrackBuffersContainTime(target)) {
MSE_DEBUG("MediaSourceReader(%p)::Seek no active buffer contains target=%f", this, target);
NS_DispatchToMainThread(new ChangeToHaveMetadata(mDecoder));
}
@ -444,28 +436,30 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
// Loop until we have the requested time range in the source buffers.
// This is a workaround for our lack of async functionality in the
// MediaDecoderStateMachine. Bug 979104 implements what we need and
// we'll remove this for an async approach based on that in bug XXXXXXX.
while (!DecodersContainTime(target) && !IsShutdown() && !IsEnded()) {
// we'll remove this for an async approach based on that in bug 1056441.
while (!TrackBuffersContainTime(target) && !IsShutdown() && !IsEnded()) {
MSE_DEBUG("MediaSourceReader(%p)::Seek waiting for target=%f", this, target);
static_cast<MediaSourceDecoder*>(mDecoder)->WaitForData();
SwitchReaders(SWITCH_FORCED);
}
if (IsShutdown()) {
return NS_ERROR_FAILURE;
}
ResetDecode();
if (mAudioReader) {
if (mAudioTrack) {
mAudioIsSeeking = true;
DebugOnly<bool> ok = SwitchAudioReader(target);
MOZ_ASSERT(ok && static_cast<SourceBufferDecoder*>(mAudioReader->GetDecoder())->ContainsTime(target));
nsresult rv = mAudioReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
MSE_DEBUG("MediaSourceReader(%p)::Seek audio reader=%p rv=%x", this, mAudioReader.get(), rv);
if (NS_FAILED(rv)) {
return rv;
}
}
if (mVideoReader) {
if (mVideoTrack) {
mVideoIsSeeking = true;
DebugOnly<bool> ok = SwitchVideoReader(target);
MOZ_ASSERT(ok && static_cast<SourceBufferDecoder*>(mVideoReader->GetDecoder())->ContainsTime(target));
nsresult rv = mVideoReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
MSE_DEBUG("MediaSourceReader(%p)::Seek video reader=%p rv=%x", this, mVideoReader.get(), rv);
if (NS_FAILED(rv)) {
@ -478,39 +472,41 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
nsresult
MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
{
InitializePendingDecoders();
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata tracks=%u", this, mTrackBuffers.Length());
// ReadMetadata is called *before* checking IsWaitingMediaResources.
if (IsWaitingMediaResources()) {
return NS_OK;
}
if (!mAudioTrack && !mVideoTrack) {
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata missing track: mAudioTrack=%p mVideoTrack=%p",
this, mAudioTrack.get(), mVideoTrack.get());
return NS_ERROR_FAILURE;
}
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata decoders=%u", this, mDecoders.Length());
// XXX: Make subdecoder setup async, so that use cases like bug 989888 can
// work. This will require teaching the state machine about dynamic track
// changes (and multiple tracks).
// Shorter term, make this block until we've got at least one video track
// and lie about having an audio track, then resample/remix as necessary
// to match any audio track added later to fit the format we lied about
// now. For now we just configure what we've got and cross our fingers.
int64_t maxDuration = -1;
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
MediaDecoderReader* reader = mDecoders[i]->GetReader();
MediaInfo mi = reader->GetMediaInfo();
if (mAudioTrack) {
MOZ_ASSERT(mAudioTrack->IsReady());
mAudioReader = mAudioTrack->Decoders()[0]->GetReader();
if (mi.HasVideo() && !mInfo.HasVideo()) {
MOZ_ASSERT(!mVideoReader);
mVideoReader = reader;
mInfo.mVideo = mi.mVideo;
maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p maxDuration=%lld",
this, reader, maxDuration);
}
if (mi.HasAudio() && !mInfo.HasAudio()) {
MOZ_ASSERT(!mAudioReader);
mAudioReader = reader;
mInfo.mAudio = mi.mAudio;
maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p maxDuration=%lld",
this, reader, maxDuration);
}
const MediaInfo& info = mAudioReader->GetMediaInfo();
MOZ_ASSERT(info.HasAudio());
mInfo.mAudio = info.mAudio;
maxDuration = std::max(maxDuration, mAudioReader->GetDecoder()->GetMediaDuration());
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p maxDuration=%lld",
this, mAudioReader.get(), maxDuration);
}
if (mVideoTrack) {
MOZ_ASSERT(mVideoTrack->IsReady());
mVideoReader = mVideoTrack->Decoders()[0]->GetReader();
const MediaInfo& info = mVideoReader->GetMediaInfo();
MOZ_ASSERT(info.HasVideo());
mInfo.mVideo = info.mVideo;
maxDuration = std::max(maxDuration, mVideoReader->GetDecoder()->GetMediaDuration());
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p maxDuration=%lld",
this, mVideoReader.get(), maxDuration);
}
if (maxDuration != -1) {

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

@ -19,6 +19,7 @@ namespace mozilla {
class MediaSourceDecoder;
class SourceBufferDecoder;
class TrackBuffer;
namespace dom {
@ -70,22 +71,25 @@ public:
nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) MOZ_OVERRIDE;
nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
int64_t aCurrentTime) MOZ_OVERRIDE;
already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType);
void AddTrackBuffer(TrackBuffer* aTrackBuffer);
void RemoveTrackBuffer(TrackBuffer* aTrackBuffer);
void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo);
void Shutdown();
virtual void BreakCycles();
void InitializePendingDecoders();
bool IsShutdown()
{
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
return mDecoder->IsShutdown();
}
// Return true if any of the active decoders contain data for the given time
bool DecodersContainTime(double aTime);
// Return true if all of the active tracks contain data for the specified time.
bool TrackBuffersContainTime(double aTime);
// Mark the reader to indicate that EndOfStream has been called on our MediaSource
void Ended();
@ -94,27 +98,24 @@ public:
bool IsEnded();
private:
enum SwitchType {
SWITCH_OPTIONAL,
SWITCH_FORCED
};
bool SwitchReaders(SwitchType aType);
bool SwitchAudioReader(MediaDecoderReader* aTargetReader);
bool SwitchVideoReader(MediaDecoderReader* aTargetReader);
// These are read and written on the decode task queue threads.
int64_t mTimeThreshold;
bool mDropAudioBeforeThreshold;
bool mDropVideoBeforeThreshold;
nsTArray<nsRefPtr<SourceBufferDecoder>> mPendingDecoders;
nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
bool SwitchAudioReader(double aTarget);
bool SwitchVideoReader(double aTarget);
nsRefPtr<MediaDecoderReader> mAudioReader;
nsRefPtr<MediaDecoderReader> mVideoReader;
nsTArray<nsRefPtr<TrackBuffer>> mTrackBuffers;
nsRefPtr<TrackBuffer> mAudioTrack;
nsRefPtr<TrackBuffer> mVideoTrack;
// These are read and written on the decode task queue threads.
int64_t mLastAudioTime;
int64_t mLastVideoTime;
int64_t mTimeThreshold;
bool mDropAudioBeforeThreshold;
bool mDropVideoBeforeThreshold;
bool mEnded;
// For a seek to complete we need to send a sample with

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

@ -6,14 +6,14 @@
#include "SourceBuffer.h"
#include "AsyncEventRunner.h"
#include "DecoderTraits.h"
#include "MediaDecoder.h"
#include "MediaSourceDecoder.h"
#include "MediaSourceUtils.h"
#include "SourceBufferResource.h"
#include "TrackBuffer.h"
#include "VideoUtils.h"
#include "WebMBufferedParser.h"
#include "mozilla/Endian.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/MediaSourceBinding.h"
#include "mozilla/dom/TimeRanges.h"
#include "mp4_demuxer/BufferStream.h"
@ -23,10 +23,6 @@
#include "nsIRunnable.h"
#include "nsThreadUtils.h"
#include "prlog.h"
#include "SourceBufferDecoder.h"
#include "mozilla/Preferences.h"
#include "WebMBufferedParser.h"
struct JSContext;
class JSObject;
@ -335,19 +331,8 @@ SourceBuffer::GetBuffered(ErrorResult& aRv)
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
double highestEndTime = 0;
nsRefPtr<TimeRanges> ranges = new TimeRanges();
// TODO: Need to adjust mDecoders so it only tracks active decoders.
// Once we have an abstraction for track buffers, this needs to report the
// intersection of buffered ranges within those track buffers.
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
nsRefPtr<TimeRanges> r = new TimeRanges();
mDecoders[i]->GetBuffered(r);
if (r->Length() > 0) {
highestEndTime = std::max(highestEndTime, r->GetEndTime());
ranges->Union(r);
}
}
double highestEndTime = mTrackBuffer->Buffered(ranges);
if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
// Set the end time on the last range to highestEndTime by adding a
// new range spanning the current end time to highestEndTime, which
@ -432,7 +417,7 @@ SourceBuffer::Abort(ErrorResult& aRv)
mAppendWindowEnd = PositiveInfinity<double>();
MSE_DEBUG("SourceBuffer(%p)::Abort() Discarding decoder", this);
DiscardDecoder();
mTrackBuffer->DiscardDecoder();
}
void
@ -464,8 +449,10 @@ SourceBuffer::Detach()
{
MOZ_ASSERT(NS_IsMainThread());
MSE_DEBUG("SourceBuffer(%p)::Detach", this);
Ended();
DiscardDecoder();
if (mTrackBuffer) {
mTrackBuffer->Detach();
}
mTrackBuffer = nullptr;
mMediaSource = nullptr;
}
@ -473,36 +460,34 @@ void
SourceBuffer::Ended()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(IsAttached());
MSE_DEBUG("SourceBuffer(%p)::Ended", this);
if (mDecoder) {
mDecoder->GetResource()->Ended();
}
mTrackBuffer->DiscardDecoder();
}
SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
: DOMEventTargetHelper(aMediaSource->GetParentObject())
, mMediaSource(aMediaSource)
, mType(aType)
, mLastParsedTimestamp(UnspecifiedNaN<double>())
, mAppendWindowStart(0)
, mAppendWindowEnd(PositiveInfinity<double>())
, mTimestampOffset(0)
, mAppendMode(SourceBufferAppendMode::Segments)
, mUpdating(false)
, mDecoderInitialized(false)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aMediaSource);
mParser = ContainerParser::CreateForMIMEType(aType);
MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Creating initial decoder, mParser=%p", this, mParser.get());
InitNewDecoder();
mTrackBuffer = new TrackBuffer(aMediaSource->GetDecoder(), aType);
MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mParser=%p mTrackBuffer=%p",
this, mParser.get(), mTrackBuffer.get());
}
SourceBuffer::~SourceBuffer()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mMediaSource);
MSE_DEBUG("SourceBuffer(%p)::~SourceBuffer", this);
DiscardDecoder();
}
MediaSource*
@ -533,37 +518,6 @@ SourceBuffer::QueueAsyncSimpleEvent(const char* aName)
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
bool
SourceBuffer::InitNewDecoder()
{
MOZ_ASSERT(NS_IsMainThread());
MSE_DEBUG("SourceBuffer(%p)::InitNewDecoder", this);
MOZ_ASSERT(!mDecoder);
MediaSourceDecoder* parentDecoder = mMediaSource->GetDecoder();
nsRefPtr<SourceBufferDecoder> decoder = parentDecoder->CreateSubDecoder(mType);
if (!decoder) {
return false;
}
mDecoder = decoder;
mDecoderInitialized = false;
mDecoders.AppendElement(mDecoder);
return true;
}
void
SourceBuffer::DiscardDecoder()
{
MOZ_ASSERT(NS_IsMainThread());
MSE_DEBUG("SourceBuffer(%p)::DiscardDecoder mDecoder=%p", this, mDecoder.get());
if (mDecoder) {
mDecoder->SetDiscarded();
}
mDecoder = nullptr;
mDecoderInitialized = false;
// XXX: Parser reset may be required?
mLastParsedTimestamp = UnspecifiedNaN<double>();
}
void
SourceBuffer::StartUpdating()
{
@ -610,21 +564,14 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
// TODO: Run buffer append algorithm asynchronously (would call StopUpdating()).
if (mParser->IsInitSegmentPresent(aData, aLength)) {
MSE_DEBUG("SourceBuffer(%p)::AppendData: New initialization segment.", this);
if (mDecoderInitialized) {
// Existing decoder has been used, time for a new one.
DiscardDecoder();
}
// If we've got a decoder here, it's not initialized, so we can use it
// rather than creating a new one.
if (!mDecoder && !InitNewDecoder()) {
mTrackBuffer->DiscardDecoder();
if (!mTrackBuffer->NewDecoder()) {
aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
return;
}
MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
mDecoderInitialized = true;
} else if (!mDecoderInitialized) {
MSE_DEBUG("SourceBuffer(%p)::AppendData: Non-init segment appended during initialization.");
} else if (!mTrackBuffer->HasInitSegment()) {
MSE_DEBUG("SourceBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
ErrorResult dummy;
mMediaSource->EndOfStream(decodeError, dummy);
@ -633,37 +580,39 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
}
double start, end;
if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
double lastStart, lastEnd;
mTrackBuffer->LastTimestamp(lastStart, lastEnd);
if (mParser->IsMediaSegmentPresent(aData, aLength) &&
(start < mLastParsedTimestamp || start - mLastParsedTimestamp > 0.1)) {
MSE_DEBUG("SourceBuffer(%p)::AppendData: Data (%f, %f) overlaps %f.",
this, start, end, mLastParsedTimestamp);
(start < lastEnd || start - lastEnd > 0.1)) {
MSE_DEBUG("SourceBuffer(%p)::AppendData: Data last=[%f, %f] overlaps [%f, %f]",
this, lastStart, lastEnd, start, end);
// This data is earlier in the timeline than data we have already
// processed, so we must create a new decoder to handle the decoding.
DiscardDecoder();
mTrackBuffer->DiscardDecoder();
// If we've got a decoder here, it's not initialized, so we can use it
// rather than creating a new one.
if (!InitNewDecoder()) {
if (!mTrackBuffer->NewDecoder()) {
aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
return;
}
MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
mDecoderInitialized = true;
const nsTArray<uint8_t>& initData = mParser->InitData();
mDecoder->NotifyDataArrived(reinterpret_cast<const char*>(initData.Elements()),
initData.Length(),
0);
mDecoder->GetResource()->AppendData(initData.Elements(), initData.Length());
mTrackBuffer->AppendData(initData.Elements(), initData.Length());
mTrackBuffer->SetLastStartTimestamp(start);
}
mLastParsedTimestamp = end;
MSE_DEBUG("SourceBuffer(%p)::AppendData: Segment start=%f end=%f", this, start, end);
mTrackBuffer->SetLastEndTimestamp(end);
MSE_DEBUG("SourceBuffer(%p)::AppendData: Segment last=[%f, %f] [%f, %f]",
this, lastStart, lastEnd, start, end);
}
if (!mTrackBuffer->AppendData(aData, aLength)) {
Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
ErrorResult dummy;
mMediaSource->EndOfStream(decodeError, dummy);
aRv.Throw(NS_ERROR_FAILURE);
return;
}
// XXX: For future reference: NDA call must run on the main thread.
mDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
aLength,
mDecoder->GetResource()->GetLength());
mDecoder->GetResource()->AppendData(aData, aLength);
// Eviction uses a byte threshold. If the buffer is greater than the
// number of bytes then data is evicted. The time range for this
@ -673,7 +622,7 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
// TODO: Make the eviction threshold smaller for audio-only streams.
// TODO: Drive evictions off memory pressure notifications.
const uint32_t evict_threshold = 75 * (1 << 20);
bool evicted = mDecoder->GetResource()->EvictData(evict_threshold);
bool evicted = mTrackBuffer->EvictData(evict_threshold);
if (evicted) {
MSE_DEBUG("SourceBuffer(%p)::AppendData Evict; current buffered start=%f",
this, GetBufferedStart());
@ -714,20 +663,13 @@ SourceBuffer::Evict(double aStart, double aEnd)
{
MOZ_ASSERT(NS_IsMainThread());
MSE_DEBUG("SourceBuffer(%p)::Evict(aStart=%f, aEnd=%f)", this, aStart, aEnd);
if (!mDecoder) {
return;
}
double currentTime = mMediaSource->GetDecoder()->GetCurrentTime();
double evictTime = aEnd;
const double safety_threshold = 5;
if (currentTime + safety_threshold >= evictTime) {
evictTime -= safety_threshold;
}
int64_t endOffset = mDecoder->ConvertToByteOffset(evictTime);
if (endOffset > 0) {
mDecoder->GetResource()->EvictBefore(endOffset);
}
MSE_DEBUG("SourceBuffer(%p)::Evict offset=%lld", this, endOffset);
mTrackBuffer->EvictBefore(evictTime);
}
NS_IMPL_CYCLE_COLLECTION_INHERITED(SourceBuffer, DOMEventTargetHelper,

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

@ -7,14 +7,13 @@
#ifndef mozilla_dom_SourceBuffer_h_
#define mozilla_dom_SourceBuffer_h_
#include "MediaDecoderReader.h"
#include "MediaSource.h"
#include "js/RootingAPI.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/SourceBufferBinding.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/mozalloc.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
@ -31,8 +30,7 @@ namespace mozilla {
class ContainerParser;
class ErrorResult;
class SourceBufferResource;
class SourceBufferDecoder;
class TrackBuffer;
template <typename T> class AsyncEventRunner;
namespace dom {
@ -139,10 +137,7 @@ private:
nsAutoPtr<ContainerParser> mParser;
double mLastParsedTimestamp;
nsRefPtr<SourceBufferDecoder> mDecoder;
nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
nsRefPtr<TrackBuffer> mTrackBuffer;
double mAppendWindowStart;
double mAppendWindowEnd;
@ -151,8 +146,6 @@ private:
SourceBufferAppendMode mAppendMode;
bool mUpdating;
bool mDecoderInitialized;
};
} // namespace dom

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

@ -38,7 +38,6 @@ SourceBufferDecoder::SourceBufferDecoder(MediaResource* aResource,
, mParentDecoder(aParentDecoder)
, mReader(nullptr)
, mMediaDuration(-1)
, mDiscarded(false)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_CTOR(SourceBufferDecoder);
@ -147,6 +146,10 @@ SourceBufferDecoder::OnStateMachineThread() const
bool
SourceBufferDecoder::OnDecodeThread() const
{
// During initialization we run on our TrackBuffer's task queue.
if (mTaskQueue) {
return mTaskQueue->IsCurrentThreadIn();
}
return mParentDecoder->OnDecodeThread();
}

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

@ -8,9 +8,10 @@
#define MOZILLA_SOURCEBUFFERDECODER_H_
#include "AbstractMediaDecoder.h"
#include "MediaDecoderReader.h"
#include "SourceBufferResource.h"
#include "mozilla/Attributes.h"
#include "mozilla/ReentrantMonitor.h"
#include "SourceBufferResource.h"
namespace mozilla {
@ -74,33 +75,30 @@ public:
return mReader;
}
void SetTaskQueue(MediaTaskQueue* aTaskQueue)
{
MOZ_ASSERT((!mTaskQueue && aTaskQueue) || (mTaskQueue && !aTaskQueue));
mTaskQueue = aTaskQueue;
}
// Given a time convert it into an approximate byte offset from the
// cached data. Returns -1 if no such value is computable.
int64_t ConvertToByteOffset(double aTime);
bool IsDiscarded()
{
return mDiscarded;
}
void SetDiscarded()
{
GetResource()->Ended();
mDiscarded = true;
}
// Returns true if the data buffered by this decoder contains the given time.
bool ContainsTime(double aTime);
private:
virtual ~SourceBufferDecoder();
// Our TrackBuffer's task queue, this is only non-null during initialization.
RefPtr<MediaTaskQueue> mTaskQueue;
nsRefPtr<MediaResource> mResource;
AbstractMediaDecoder* mParentDecoder;
nsRefPtr<MediaDecoderReader> mReader;
int64_t mMediaDuration;
bool mDiscarded;
};
} // namespace mozilla

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

@ -0,0 +1,339 @@
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TrackBuffer.h"
#include "MediaSourceDecoder.h"
#include "SharedThreadPool.h"
#include "MediaTaskQueue.h"
#include "SourceBufferDecoder.h"
#include "SourceBufferResource.h"
#include "VideoUtils.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/dom/MediaSourceBinding.h"
#include "mozilla/dom/TimeRanges.h"
#include "nsError.h"
#include "nsIRunnable.h"
#include "nsThreadUtils.h"
#include "prlog.h"
struct JSContext;
class JSObject;
#ifdef PR_LOGGING
extern PRLogModuleInfo* GetMediaSourceLog();
extern PRLogModuleInfo* GetMediaSourceAPILog();
#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
#define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define MSE_DEBUG(...)
#define MSE_DEBUGV(...)
#define MSE_API(...)
#endif
namespace mozilla {
TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType)
: mParentDecoder(aParentDecoder)
, mType(aType)
, mLastStartTimestamp(0)
, mLastEndTimestamp(UnspecifiedNaN<double>())
, mHasInit(false)
, mHasAudio(false)
, mHasVideo(false)
{
MOZ_COUNT_CTOR(TrackBuffer);
mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
aParentDecoder->AddTrackBuffer(this);
}
TrackBuffer::~TrackBuffer()
{
MOZ_COUNT_DTOR(TrackBuffer);
}
class ReleaseDecoderTask : public nsRunnable {
public:
explicit ReleaseDecoderTask(nsRefPtr<SourceBufferDecoder> aDecoder)
{
mDecoders.AppendElement(aDecoder);
}
explicit ReleaseDecoderTask(nsTArray<nsRefPtr<SourceBufferDecoder>>& aDecoders)
{
mDecoders.SwapElements(aDecoders);
}
NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
mDecoders.Clear();
return NS_OK;
}
private:
nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
};
void
TrackBuffer::Shutdown()
{
// Shutdown waits for any pending events, which may require the monitor,
// so we must not hold the monitor during this call.
mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
mTaskQueue->Shutdown();
mTaskQueue = nullptr;
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
DiscardDecoder();
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
mDecoders[i]->GetReader()->Shutdown();
}
NS_DispatchToMainThread(new ReleaseDecoderTask(mDecoders));
MOZ_ASSERT(mDecoders.IsEmpty());
mParentDecoder = nullptr;
}
bool
TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mCurrentDecoder) {
return false;
}
SourceBufferResource* resource = mCurrentDecoder->GetResource();
// XXX: For future reference: NDA call must run on the main thread.
mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
aLength, resource->GetLength());
resource->AppendData(aData, aLength);
return true;
}
bool
TrackBuffer::EvictData(uint32_t aThreshold)
{
MOZ_ASSERT(NS_IsMainThread());
// XXX Call EvictData on mDecoders?
return mCurrentDecoder->GetResource()->EvictData(aThreshold);
}
void
TrackBuffer::EvictBefore(double aTime)
{
MOZ_ASSERT(NS_IsMainThread());
// XXX Call EvictBefore on mDecoders?
int64_t endOffset = mCurrentDecoder->ConvertToByteOffset(aTime);
if (endOffset > 0) {
mCurrentDecoder->GetResource()->EvictBefore(endOffset);
}
MSE_DEBUG("TrackBuffer(%p)::EvictBefore offset=%lld", this, endOffset);
}
double
TrackBuffer::Buffered(dom::TimeRanges* aRanges)
{
MOZ_ASSERT(NS_IsMainThread());
// XXX check default if mDecoders empty?
double highestEndTime = 0;
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
nsRefPtr<dom::TimeRanges> r = new dom::TimeRanges();
mDecoders[i]->GetBuffered(r);
if (r->Length() > 0) {
highestEndTime = std::max(highestEndTime, r->GetEndTime());
aRanges->Union(r);
}
}
return highestEndTime;
}
bool
TrackBuffer::NewDecoder()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mCurrentDecoder && mParentDecoder);
nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType);
if (!decoder) {
return false;
}
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
mCurrentDecoder = decoder;
mLastStartTimestamp = 0;
mLastEndTimestamp = UnspecifiedNaN<double>();
mHasInit = true;
return QueueInitializeDecoder(decoder);
}
bool
TrackBuffer::QueueInitializeDecoder(nsRefPtr<SourceBufferDecoder> aDecoder)
{
RefPtr<nsIRunnable> task =
NS_NewRunnableMethodWithArg<nsRefPtr<SourceBufferDecoder>>(this,
&TrackBuffer::InitializeDecoder,
aDecoder);
aDecoder->SetTaskQueue(mTaskQueue);
if (NS_FAILED(mTaskQueue->Dispatch(task))) {
MSE_DEBUG("MediaSourceReader(%p): Failed to enqueue decoder initialization task", this);
return false;
}
return true;
}
void
TrackBuffer::InitializeDecoder(nsRefPtr<SourceBufferDecoder> aDecoder)
{
// ReadMetadata may block the thread waiting on data, so it must not be
// called with the monitor held.
mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
MediaDecoderReader* reader = aDecoder->GetReader();
MSE_DEBUG("TrackBuffer(%p): Initializing subdecoder %p reader %p",
this, aDecoder.get(), reader);
MediaInfo mi;
nsAutoPtr<MetadataTags> tags; // TODO: Handle metadata.
nsresult rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
reader->SetIdle();
if (NS_FAILED(rv) || (!mi.HasVideo() && !mi.HasAudio())) {
// XXX: Need to signal error back to owning SourceBuffer.
MSE_DEBUG("TrackBuffer(%p): Reader %p failed to initialize rv=%x audio=%d video=%d",
this, reader, rv, mi.HasAudio(), mi.HasVideo());
aDecoder->SetTaskQueue(nullptr);
NS_DispatchToMainThread(new ReleaseDecoderTask(aDecoder));
return;
}
if (mi.HasVideo()) {
MSE_DEBUG("TrackBuffer(%p): Reader %p video resolution=%dx%d",
this, reader, mi.mVideo.mDisplay.width, mi.mVideo.mDisplay.height);
}
if (mi.HasAudio()) {
MSE_DEBUG("TrackBuffer(%p): Reader %p audio sampleRate=%d channels=%d",
this, reader, mi.mAudio.mRate, mi.mAudio.mChannels);
}
MSE_DEBUG("TrackBuffer(%p): Reader %p activated", this, reader);
RegisterDecoder(aDecoder);
}
void
TrackBuffer::RegisterDecoder(nsRefPtr<SourceBufferDecoder> aDecoder)
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
aDecoder->SetTaskQueue(nullptr);
const MediaInfo& info = aDecoder->GetReader()->GetMediaInfo();
// Initialize the track info since this is the first decoder.
if (mDecoders.IsEmpty()) {
mHasAudio = info.HasAudio();
mHasVideo = info.HasVideo();
mParentDecoder->OnTrackBufferConfigured(this, info);
} else if ((info.HasAudio() && !mHasAudio) || (info.HasVideo() && !mHasVideo)) {
MSE_DEBUG("TrackBuffer(%p)::RegisterDecoder with mismatched audio/video tracks", this);
}
mDecoders.AppendElement(aDecoder);
}
void
TrackBuffer::DiscardDecoder()
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
if (mCurrentDecoder) {
mCurrentDecoder->GetResource()->Ended();
}
mCurrentDecoder = nullptr;
}
void
TrackBuffer::Detach()
{
MOZ_ASSERT(NS_IsMainThread());
if (mCurrentDecoder) {
DiscardDecoder();
}
}
bool
TrackBuffer::HasInitSegment()
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
return mHasInit;
}
bool
TrackBuffer::IsReady()
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
MOZ_ASSERT((mHasAudio || mHasVideo) || mDecoders.IsEmpty());
return HasInitSegment() && (mHasAudio || mHasVideo);
}
void
TrackBuffer::LastTimestamp(double& aStart, double& aEnd)
{
MOZ_ASSERT(NS_IsMainThread());
aStart = mLastStartTimestamp;
aEnd = mLastEndTimestamp;
}
void
TrackBuffer::SetLastStartTimestamp(double aStart)
{
MOZ_ASSERT(NS_IsMainThread());
mLastStartTimestamp = aStart;
}
void
TrackBuffer::SetLastEndTimestamp(double aEnd)
{
MOZ_ASSERT(NS_IsMainThread());
mLastEndTimestamp = aEnd;
}
bool
TrackBuffer::ContainsTime(double aTime)
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
nsRefPtr<dom::TimeRanges> r = new dom::TimeRanges();
mDecoders[i]->GetBuffered(r);
if (r->Find(aTime) != dom::TimeRanges::NoIndex) {
return true;
}
}
return false;
}
void
TrackBuffer::BreakCycles()
{
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
mDecoders[i]->GetReader()->BreakCycles();
}
mDecoders.Clear();
mParentDecoder = nullptr;
}
void
TrackBuffer::ResetDecode()
{
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
mDecoders[i]->GetReader()->ResetDecode();
}
}
const nsTArray<nsRefPtr<SourceBufferDecoder>>&
TrackBuffer::Decoders()
{
// XXX assert OnDecodeThread
return mDecoders;
}
} // namespace mozilla

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

@ -0,0 +1,131 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_TRACKBUFFER_H_
#define MOZILLA_TRACKBUFFER_H_
#include "SourceBufferDecoder.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/mozalloc.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nscore.h"
namespace mozilla {
class MediaSourceDecoder;
namespace dom {
class TimeRanges;
} // namespace dom
class TrackBuffer MOZ_FINAL {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackBuffer);
TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType);
void Shutdown();
// Append data to the current decoder. Also responsible for calling
// NotifyDataArrived on the decoder to keep buffered range computation up
// to date. Returns false if the append failed.
bool AppendData(const uint8_t* aData, uint32_t aLength);
bool EvictData(uint32_t aThreshold);
void EvictBefore(double aTime);
// Returns the highest end time of all of the buffered ranges in the
// decoders managed by this TrackBuffer, and returns the union of the
// decoders buffered ranges in aRanges.
double Buffered(dom::TimeRanges* aRanges);
// Create a new decoder, set mCurrentDecoder to the new decoder, and queue
// the decoder for initialization. The decoder is not considered
// initialized until it is added to mDecoders.
bool NewDecoder();
// Mark the current decoder's resource as ended, clear mCurrentDecoder and
// reset mLast{Start,End}Timestamp.
void DiscardDecoder();
void Detach();
// Returns true if an init segment has been appended.
bool HasInitSegment();
// Returns true iff HasInitSegment() and the decoder using that init
// segment has successfully initialized by setting mHas{Audio,Video}..
bool IsReady();
// Query and update mLast{Start,End}Timestamp.
void LastTimestamp(double& aStart, double& aEnd);
void SetLastStartTimestamp(double aStart);
void SetLastEndTimestamp(double aEnd);
// Returns true if any of the decoders managed by this track buffer
// contain aTime in their buffered ranges.
bool ContainsTime(double aTime);
void BreakCycles();
// Call ResetDecode() on each decoder in mDecoders.
void ResetDecode();
// Returns a reference to mDecoders, used by MediaSourceReader to select
// decoders.
// TODO: Refactor to a clenaer interface between TrackBuffer and MediaSourceReader.
const nsTArray<nsRefPtr<SourceBufferDecoder>>& Decoders();
private:
~TrackBuffer();
// Queue execution of InitializeDecoder on mTaskQueue.
bool QueueInitializeDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
// Runs decoder initialization including calling ReadMetadata. Runs as an
// event on the decode thread pool.
void InitializeDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
// Adds a successfully initialized decoder to mDecoders and (if it's the
// first decoder initialized), initializes mHasAudio/mHasVideo. Called
// from the decode thread pool.
void RegisterDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
// A task queue using the shared media thread pool. Used exclusively to
// initialize (i.e. call ReadMetadata on) decoders as they are created via
// NewDecoder.
RefPtr<MediaTaskQueue> mTaskQueue;
// All of the initialized decoders managed by this TrackBuffer. Access
// protected by mParentDecoder's monitor.
nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
// The decoder that the owning SourceBuffer is currently appending data to.
nsRefPtr<SourceBufferDecoder> mCurrentDecoder;
nsRefPtr<MediaSourceDecoder> mParentDecoder;
const nsCString mType;
// The last start and end timestamps added to the TrackBuffer via
// AppendData. Accessed on the main thread only.
double mLastStartTimestamp;
double mLastEndTimestamp;
// Set when the initialization segment is first seen and cached (implied
// by new decoder creation). Protected by mParentDecoder's monitor.
bool mHasInit;
// Set when the first decoder used by this TrackBuffer is initialized.
// Protected by mParentDecoder's monitor.
bool mHasAudio;
bool mHasVideo;
};
} // namespace mozilla
#endif /* MOZILLA_TRACKBUFFER_H_ */

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

@ -25,6 +25,7 @@ UNIFIED_SOURCES += [
'SourceBufferDecoder.cpp',
'SourceBufferList.cpp',
'SourceBufferResource.cpp',
'TrackBuffer.cpp',
]
FAIL_ON_WARNINGS = True

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

@ -4,3 +4,9 @@ support-files = seek.webm seek.webm^headers^
[test_MediaSource.html]
skip-if = buildapp == 'b2g' # b2g( ReferenceError: MediaSource is not defined)
[test_SplitAppend.html]
skip-if = buildapp == 'b2g' # b2g( ReferenceError: MediaSource is not defined)
[test_SplitAppendDelay.html]
skip-if = buildapp == 'b2g' # b2g( ReferenceError: MediaSource is not defined)

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

@ -0,0 +1,83 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test whether we can create an MediaSource interface</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
ok(!window.MediaSource, "MediaSource should be hidden behind a pref");
var accessThrows = false;
try {
new MediaSource();
} catch (e) {
accessThrows = true;
}
ok(accessThrows, "MediaSource should be hidden behind a pref");
SpecialPowers.pushPrefEnv({"set": [[ "media.mediasource.enabled", true ]]},
function () {
SpecialPowers.setBoolPref("media.mediasource.enabled", true);
var ms = new MediaSource();
ok(ms, "Create a MediaSource object");
ok(ms instanceof EventTarget, "MediaSource must be an EventTarget");
is(ms.readyState, "closed", "New MediaSource must be in closed state");
// Force wrapper creation, tests for leaks.
ms.foo = null;
var o = URL.createObjectURL(ms);
ok(o, "Create an objectURL from the MediaSource");
var v = document.createElement("video");
v.preload = "auto";
document.body.appendChild(v);
v.src = o;
ms.addEventListener("sourceopen", function () {
ok(true, "Receive a sourceopen event");
is(ms.readyState, "open", "MediaSource must be in open state after sourceopen");
var sb = ms.addSourceBuffer("video/webm");
ok(sb, "Create a SourceBuffer");
is(ms.sourceBuffers.length, 1, "MediaSource.sourceBuffers is expected length");
is(ms.sourceBuffers[0], sb, "SourceBuffer in list matches our SourceBuffer");
fetch("seek.webm", function (blob) {
var r = new FileReader();
r.addEventListener("load", function (e) {
sb.appendBuffer(new Uint8Array(e.target.result, 0, 318));
sb.appendBuffer(new Uint8Array(e.target.result, 318));
ms.endOfStream();
v.play();
});
r.readAsArrayBuffer(blob);
});
});
ms.addEventListener("sourceended", function () {
ok(true, "Receive a sourceended event");
is(ms.readyState, "ended", "MediaSource must be in ended state after sourceended");
});
v.addEventListener("ended", function () {
is(v.duration, 4, "Video has correct duration");
v.parentNode.removeChild(v);
SimpleTest.finish();
});
});
});
function fetch(src, cb) {
var xhr = new XMLHttpRequest();
xhr.open("GET", src, true);
xhr.responseType = "blob";
xhr.addEventListener("load", function (e) {
if (xhr.status != 200) {
return false;
}
cb(xhr.response);
});
xhr.send();
};
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,85 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test whether we can create an MediaSource interface</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
ok(!window.MediaSource, "MediaSource should be hidden behind a pref");
var accessThrows = false;
try {
new MediaSource();
} catch (e) {
accessThrows = true;
}
ok(accessThrows, "MediaSource should be hidden behind a pref");
SpecialPowers.pushPrefEnv({"set": [[ "media.mediasource.enabled", true ]]},
function () {
SpecialPowers.setBoolPref("media.mediasource.enabled", true);
var ms = new MediaSource();
ok(ms, "Create a MediaSource object");
ok(ms instanceof EventTarget, "MediaSource must be an EventTarget");
is(ms.readyState, "closed", "New MediaSource must be in closed state");
// Force wrapper creation, tests for leaks.
ms.foo = null;
var o = URL.createObjectURL(ms);
ok(o, "Create an objectURL from the MediaSource");
var v = document.createElement("video");
v.preload = "auto";
document.body.appendChild(v);
v.src = o;
ms.addEventListener("sourceopen", function () {
ok(true, "Receive a sourceopen event");
is(ms.readyState, "open", "MediaSource must be in open state after sourceopen");
var sb = ms.addSourceBuffer("video/webm");
ok(sb, "Create a SourceBuffer");
is(ms.sourceBuffers.length, 1, "MediaSource.sourceBuffers is expected length");
is(ms.sourceBuffers[0], sb, "SourceBuffer in list matches our SourceBuffer");
fetch("seek.webm", function (blob) {
var r = new FileReader();
r.addEventListener("load", function (e) {
sb.appendBuffer(new Uint8Array(e.target.result, 0, 318));
window.setTimeout(function () {
sb.appendBuffer(new Uint8Array(e.target.result, 318));
ms.endOfStream();
}, 1000);
v.play();
});
r.readAsArrayBuffer(blob);
});
});
ms.addEventListener("sourceended", function () {
ok(true, "Receive a sourceended event");
is(ms.readyState, "ended", "MediaSource must be in ended state after sourceended");
});
v.addEventListener("ended", function () {
is(v.duration, 4, "Video has correct duration");
v.parentNode.removeChild(v);
SimpleTest.finish();
});
});
});
function fetch(src, cb) {
var xhr = new XMLHttpRequest();
xhr.open("GET", src, true);
xhr.responseType = "blob";
xhr.addEventListener("load", function (e) {
if (xhr.status != 200) {
return false;
}
cb(xhr.response);
});
xhr.send();
};
</script>
</pre>
</body>
</html>

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

@ -4,11 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
CPP_UNIT_TESTS += [
CppUnitTests([
'TestAudioEventTimeline',
]
SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
])
FAIL_ON_WARNINGS = True

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

@ -2004,7 +2004,7 @@ XULDocument::PrepareToLoad(nsISupports* aContainer,
// Get the document's principal
nsCOMPtr<nsIPrincipal> principal;
nsContentUtils::GetSecurityManager()->
GetChannelPrincipal(aChannel, getter_AddRefs(principal));
GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
return PrepareToLoadPrototype(mDocumentURI, aCommand, principal, aResult);
}
@ -4516,7 +4516,7 @@ XULDocument::ParserObserver::OnStartRequest(nsIRequest *request,
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
if (channel && secMan) {
nsCOMPtr<nsIPrincipal> principal;
secMan->GetChannelPrincipal(channel, getter_AddRefs(principal));
secMan->GetChannelResultPrincipal(channel, getter_AddRefs(principal));
// Failure there is ok -- it'll just set a (safe) null principal
mPrototype->SetDocumentPrincipal(principal);

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

@ -6580,7 +6580,7 @@ NS_IMETHODIMP nsDocShell::SetupRefreshURI(nsIChannel * aChannel)
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> principal;
rv = secMan->GetChannelPrincipal(aChannel, getter_AddRefs(principal));
rv = secMan->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
SetupReferrerFromChannel(aChannel);

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

@ -4,11 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
CPP_UNIT_TESTS += [
CppUnitTests([
'TestAudioChannelService',
]
SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
])
if CONFIG['OS_ARCH'] == 'WINNT':
DEFINES['NOMINMAX'] = True

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

@ -12,7 +12,7 @@ DEFINES.update({
# Do NOT export this library. We don't actually want our test code
# being added to libxul or anything.
LIBRARY_NAME = 'dombindings_test_s'
Library('dombindings_test_s')
EXTRA_COMPONENTS += [
'TestInterfaceJS.js',

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

@ -4,11 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
CPP_UNIT_TESTS += [
CppUnitTests([
'TestWebGLElementArrayCache',
]
SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
])
FAIL_ON_WARNINGS = True

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

@ -2,8 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "base/linked_ptr.h"
#include "mozilla/ModuleUtils.h"
#include "nsIClassInfoImpl.h"

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

@ -9,10 +9,9 @@ SOURCES += [
'gmp-fake.cpp'
]
LIBRARY_NAME = "fake"
SharedLibrary("fake")
USE_STATIC_LIBS = True
FORCE_SHARED_LIB = True
NO_VISIBILITY_FLAGS = True
# Don't use STL wrappers; this isn't Gecko code
DISABLE_STL_WRAPPING = True

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

@ -410,8 +410,6 @@ nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult)
CheckClassInitialized();
nsRefPtr<nsNPAPIPlugin> plugin = new nsNPAPIPlugin();
if (!plugin)
return NS_ERROR_OUT_OF_MEMORY;
PluginLibrary* pluginLib = GetNewPluginLibrary(aPluginTag);
if (!pluginLib) {
@ -421,6 +419,7 @@ nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult)
#if defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
if (!pluginLib->HasRequiredFunctions()) {
NS_WARNING("Not all necessary functions exposed by plugin, it will not load.");
delete pluginLib;
return NS_ERROR_FAILURE;
}
#endif
@ -866,7 +865,7 @@ _geturl(NPP npp, const char* relativeURL, const char* target)
(strncmp(relativeURL, "ftp:", 4) != 0)) {
nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *) npp->ndata;
const char *name = nullptr;
nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
host->GetPluginName(inst, &name);
@ -2096,14 +2095,14 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
#ifndef NP_NO_QUICKDRAW
case NPNVsupportsQuickDrawBool: {
*(NPBool*)result = false;
return NPERR_NO_ERROR;
}
#endif
case NPNVsupportsCoreGraphicsBool: {
*(NPBool*)result = true;
return NPERR_NO_ERROR;
}
@ -2173,14 +2172,14 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
InitMatrixInterface(i);
return NPERR_NO_ERROR;
}
case kPathInterfaceV0_ANPGetValue: {
LOG("get path interface");
ANPPathInterfaceV0 *i = (ANPPathInterfaceV0 *) result;
InitPathInterface(i);
return NPERR_NO_ERROR;
}
case kTypefaceInterfaceV0_ANPGetValue: {
LOG("get typeface interface");
ANPTypefaceInterfaceV0 *i = (ANPTypefaceInterfaceV0 *) result;
@ -2236,7 +2235,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
InitSurfaceInterface(i);
return NPERR_NO_ERROR;
}
case kSupportedDrawingModel_ANPGetValue: {
LOG("get supported drawing model");
uint32_t* bits = reinterpret_cast<uint32_t*>(result);

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

@ -6,7 +6,7 @@
FAIL_ON_WARNINGS = True
PROGRAM = 'plugin-hang-ui'
Program('plugin-hang-ui')
UNIFIED_SOURCES += [
'MiniShmChild.cpp',

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

@ -4,7 +4,7 @@
# 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/.
LIBRARY_NAME = 'plugin_child_interpose'
SharedLibrary('plugin_child_interpose')
UNIFIED_SOURCES += [ "%s.mm" % (LIBRARY_NAME) ]
@ -12,6 +12,4 @@ UNIFIED_SOURCES += [
'plugin_child_quirks.mm',
]
FORCE_SHARED_LIB = True
OS_LIBS += ['-framework Carbon']

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

@ -4,7 +4,7 @@
# 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/.
LIBRARY_NAME = 'nptestjava'
SharedLibrary('nptestjava')
relative_path = '..'
include('../testplugin.mozbuild')

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

@ -6,7 +6,7 @@
DIRS += ['secondplugin', 'javaplugin']
LIBRARY_NAME = 'nptest'
SharedLibrary('nptest')
FAIL_ON_WARNINGS = not CONFIG['_MSC_VER']

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

@ -4,7 +4,7 @@
# 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/.
LIBRARY_NAME = 'npsecondtest'
SharedLibrary('npsecondtest')
relative_path = '..'
include('../testplugin.mozbuild')

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

@ -40,8 +40,6 @@ elif toolkit == 'windows':
'msimg32',
]
FORCE_SHARED_LIB = True
# must link statically with the CRT; nptest isn't Gecko code
USE_STATIC_LIBS = True

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

@ -502,7 +502,7 @@ private:
NS_ASSERTION(ssm, "Should never be null!");
nsCOMPtr<nsIPrincipal> channelPrincipal;
rv = ssm->GetChannelPrincipal(channel, getter_AddRefs(channelPrincipal));
rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
// See if this is a resource URI. Since JSMs usually come from resource://

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

@ -4,11 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
CPP_UNIT_TESTS += [
CppUnitTests([
'TestTXMgr',
]
SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
])
FAIL_ON_WARNINGS = True

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

@ -4,7 +4,7 @@
# 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/.
PROGRAM = 'winEmbed'
Program('winEmbed')
SOURCES += [
'WebBrowserChrome.cpp',

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

@ -2921,13 +2921,20 @@ FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
: mStdDeviation(0)
{}
static float
ClampStdDeviation(float aStdDeviation)
{
// Cap software blur radius for performance reasons.
return std::min(std::max(0.0f, aStdDeviation), 100.0f);
}
void
FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex,
float aStdDeviation)
{
switch (aIndex) {
case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
mStdDeviation = std::max(0.0f, aStdDeviation);
mStdDeviation = ClampStdDeviation(aStdDeviation);
break;
default:
MOZ_CRASH();
@ -2951,7 +2958,7 @@ FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
{
switch (aIndex) {
case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
mStdDeviation = std::max(0.0f, aStdDeviation);
mStdDeviation = ClampStdDeviation(aStdDeviation);
break;
default:
MOZ_CRASH();

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

@ -54,8 +54,7 @@ DISABLE_STL_WRAPPING = True
LOCAL_INCLUDES += [ '../../include', '../../src' ]
USE_LIBS += [ 'libGLESv2' ]
LIBRARY_NAME = 'libEGL'
FORCE_SHARED_LIB = True
SharedLibrary('libEGL')
RCFILE = SRCDIR + '/libEGL.rc'
DEFFILE = SRCDIR + '/libEGL.def'

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

@ -204,8 +204,7 @@ else:
'\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
]
LIBRARY_NAME = 'libGLESv2'
FORCE_SHARED_LIB = True
SharedLibrary('libGLESv2')
RCFILE = SRCDIR + '/libGLESv2.rc'
DEFFILE = SRCDIR + '/libGLESv2.def'

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

@ -38,7 +38,7 @@ GLBlitTextureImageHelper::BlitTextureImage(TextureImage *aSrc, const nsIntRect&
NS_ASSERTION(!aSrc->InUpdate(), "Source texture is in update!");
NS_ASSERTION(!aDst->InUpdate(), "Destination texture is in update!");
if (aSrcRect.IsEmpty() || aDstRect.IsEmpty())
if (!aSrc || !aDst || aSrcRect.IsEmpty() || aDstRect.IsEmpty())
return;
int savedFb = 0;

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

@ -60,10 +60,6 @@ namespace layers {
* time of a layer-tree transaction. These metrics are especially
* useful for shadow layers, because the metrics values are updated
* atomically with new pixels.
*
* Note that the FrameMetrics struct is sometimes stored in shared
* memory and shared across processes, so it should be a "Plain Old
* Data (POD)" type with no members that use dynamic memory.
*/
struct FrameMetrics {
friend struct IPC::ParamTraits<mozilla::layers::FrameMetrics>;
@ -101,7 +97,6 @@ public:
, mViewport(0, 0, 0, 0)
, mBackgroundColor(0, 0, 0, 0)
{
mContentDescription[0] = '\0';
}
// Default copy ctor and operator= are fine
@ -128,8 +123,7 @@ public:
mScrollOffset == aOther.mScrollOffset &&
mHasScrollgrab == aOther.mHasScrollgrab &&
mUpdateScrollOffset == aOther.mUpdateScrollOffset &&
mBackgroundColor == aOther.mBackgroundColor &&
!strcmp(mContentDescription, aOther.mContentDescription);
mBackgroundColor == aOther.mBackgroundColor;
}
bool operator!=(const FrameMetrics& aOther) const
{
@ -247,6 +241,16 @@ public:
mScrollGeneration = aOther.mScrollGeneration;
}
// Make a copy of this FrameMetrics object which does not have any pointers
// to heap-allocated memory (i.e. is Plain Old Data, or 'POD'), and is
// therefore safe to be placed into shared memory.
FrameMetrics MakePODObject() const
{
FrameMetrics copy = *this;
copy.mContentDescription.Truncate();
return copy;
}
// ---------------------------------------------------------------------------
// The following metrics are all in widget space/device pixels.
//
@ -484,16 +488,14 @@ public:
mBackgroundColor = aBackgroundColor;
}
nsCString GetContentDescription() const
const nsCString& GetContentDescription() const
{
return nsCString(mContentDescription);
return mContentDescription;
}
void SetContentDescription(const nsCString& aContentDescription)
{
strncpy(mContentDescription, aContentDescription.get(),
sizeof(mContentDescription));
mContentDescription[sizeof(mContentDescription) - 1] = 0;
mContentDescription = aContentDescription;
}
private:
@ -569,9 +571,9 @@ private:
gfxRGBA mBackgroundColor;
// A description of the content element corresponding to this frame.
// This is empty unless this is a scrollable ContainerLayer and the
// This is empty unless this is a scrollable layer and the
// apz.printtree pref is turned on.
char mContentDescription[20];
nsCString mContentDescription;
};
/**

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

@ -207,6 +207,18 @@ ComputeTouchSensitiveRegion(GeckoContentController* aController,
return unobscured;
}
void
APZCTreeManager::PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
const AsyncPanZoomController* apzc)
{
const FrameMetrics& metrics = aLayer.Metrics();
mApzcTreeLog << "APZC " << apzc->GetGuid() << "\tcb=" << metrics.mCompositionBounds
<< "\tsr=" << metrics.mScrollableRect
<< (aLayer.IsScrollInfoLayer() ? "\tscrollinfo" : "")
<< (apzc->HasScrollgrab() ? "\tscrollgrab" : "") << "\t"
<< metrics.GetContentDescription().get();
}
AsyncPanZoomController*
APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
const FrameMetrics& aMetrics,
@ -239,6 +251,7 @@ APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
auto insertResult = aState.mApzcMap.insert(std::make_pair(guid, static_cast<AsyncPanZoomController*>(nullptr)));
if (!insertResult.second) {
apzc = insertResult.first->second;
PrintAPZCInfo(aLayer, apzc);
}
APZCTM_LOG("Found APZC %p for layer %p with identifiers %lld %lld\n", apzc, aLayer.GetLayer(), guid.mLayersId, guid.mScrollId);
@ -303,12 +316,7 @@ APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
APZCTM_LOG("Setting region %s as visible region for APZC %p\n",
Stringify(unobscured).c_str(), apzc);
mApzcTreeLog << "APZC " << guid
<< "\tcb=" << aMetrics.mCompositionBounds
<< "\tsr=" << aMetrics.mScrollableRect
<< (aLayer.GetVisibleRegion().IsEmpty() ? "\tscrollinfo" : "")
<< (apzc->HasScrollgrab() ? "\tscrollgrab" : "")
<< "\t" << aMetrics.GetContentDescription().get();
PrintAPZCInfo(aLayer, apzc);
// Bind the APZC instance into the tree of APZCs
if (aNextSibling) {

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

@ -407,6 +407,8 @@ private:
AsyncPanZoomController* aNextSibling,
const nsIntRegion& aObscured);
void PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
const AsyncPanZoomController* apzc);
private:
/* Whenever walking or mutating the tree rooted at mRootApzc, mTreeLock must be held.
* This lock does not need to be held while manipulating a single APZC instance in

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

@ -2868,7 +2868,7 @@ void AsyncPanZoomController::UpdateSharedCompositorFrameMetrics()
if (frame && mSharedLock && gfxPrefs::UseProgressiveTilePainting()) {
mSharedLock->Lock();
*frame = mFrameMetrics;
*frame = mFrameMetrics.MakePODObject();
mSharedLock->Unlock();
}
}

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

@ -257,42 +257,6 @@ RenderLayers(ContainerT* aContainer,
{
Compositor* compositor = aManager->GetCompositor();
float opacity = aContainer->GetEffectiveOpacity();
// If this is a scrollable container layer, and it's overscrolled, the layer's
// contents are transformed in a way that would leave blank regions in the
// composited area. If the layer has a background color, fill these areas
// with the background color by drawing a rectangle of the background color
// over the entire composited area before drawing the container contents.
// Make sure not to do this on a "scrollinfo" layer because it's just a
// placeholder for APZ purposes.
if (aContainer->HasScrollableFrameMetrics() && !aContainer->IsScrollInfoLayer()) {
bool overscrolled = false;
gfxRGBA color;
for (uint32_t i = 0; i < aContainer->GetFrameMetricsCount(); i++) {
AsyncPanZoomController* apzc = aContainer->GetAsyncPanZoomController(i);
if (apzc && apzc->IsOverscrolled()) {
overscrolled = true;
color = aContainer->GetFrameMetrics(i).GetBackgroundColor();
break;
}
}
if (overscrolled) {
// If the background is completely transparent, there's no point in
// drawing anything for it. Hopefully the layers behind, if any, will
// provide suitable content for the overscroll effect.
if (color.a != 0.0) {
EffectChain effectChain(aContainer);
effectChain.mPrimaryEffect = new EffectSolidColor(ToColor(color));
gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
compositor->DrawQuad(
RenderTargetPixel::ToUnknown(
compositor->ClipRectInLayersCoordinates(aContainer, aClipRect)),
clipRect, effectChain, opacity, Matrix4x4());
}
}
}
for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
LayerComposite* layerToRender = preparedData.mLayer;

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

@ -934,6 +934,93 @@ TEST_F(APZCBasicTester, FlingIntoOverscroll) {
EXPECT_TRUE(recoveredFromOverscroll);
}
TEST_F(APZCBasicTester, OverScrollPanning) {
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
// Pan sufficiently to hit overscroll behavior
int time = 0;
int touchStart = 500;
int touchEnd = 10;
ApzcPan(apzc, time, touchStart, touchEnd);
EXPECT_TRUE(apzc->IsOverscrolled());
// Note that in the calls to SampleContentTransformForFrame below, the time
// increment used is sufficiently large for the animation to have completed. However,
// any single call to SampleContentTransformForFrame will not finish an animation
// *and* also proceed through the following animation, if there is one.
// Therefore the minimum number of calls to go from an overscroll-inducing pan
// to a reset state is 3; these are documented further below.
ScreenPoint pointOut;
ViewTransform viewTransformOut;
// This sample will run to the end of the non-overscrolling fling animation
// and will schedule the overscrolling fling animation.
apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(10000), &viewTransformOut, pointOut);
EXPECT_EQ(ScreenPoint(0, 90), pointOut);
EXPECT_TRUE(apzc->IsOverscrolled());
// This sample will run to the end of the overscrolling fling animation and
// will schedule the snapback animation.
apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(20000), &viewTransformOut, pointOut);
EXPECT_EQ(ScreenPoint(0, 90), pointOut);
EXPECT_TRUE(apzc->IsOverscrolled());
// This sample will run to the end of the snapback animation and reset the state.
apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(30000), &viewTransformOut, pointOut);
EXPECT_EQ(ScreenPoint(0, 90), pointOut);
EXPECT_FALSE(apzc->IsOverscrolled());
apzc->AssertStateIsReset();
}
TEST_F(APZCBasicTester, OverScrollAbort) {
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
// Pan sufficiently to hit overscroll behavior
int time = 0;
int touchStart = 500;
int touchEnd = 10;
ApzcPan(apzc, time, touchStart, touchEnd);
EXPECT_TRUE(apzc->IsOverscrolled());
ScreenPoint pointOut;
ViewTransform viewTransformOut;
// This sample call will run to the end of the non-overscrolling fling animation
// and will schedule the overscrolling fling animation (see comment in OverScrollPanning
// above for more explanation).
apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(10000), &viewTransformOut, pointOut);
EXPECT_TRUE(apzc->IsOverscrolled());
// At this point, we have an active overscrolling fling animation.
// Check that cancelling the animation clears the overscroll.
apzc->CancelAnimation();
EXPECT_FALSE(apzc->IsOverscrolled());
apzc->AssertStateIsReset();
}
TEST_F(APZCBasicTester, OverScrollPanningAbort) {
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
// Pan sufficiently to hit overscroll behaviour. Keep the finger down so
// the pan does not end.
int time = 0;
int touchStart = 500;
int touchEnd = 10;
ApzcPan(apzc, time, touchStart, touchEnd,
true); // keep finger down
EXPECT_TRUE(apzc->IsOverscrolled());
// Check that calling CancelAnimation() while the user is still panning
// (and thus no fling or snap-back animation has had a chance to start)
// clears the overscroll.
apzc->CancelAnimation();
EXPECT_FALSE(apzc->IsOverscrolled());
apzc->AssertStateIsReset();
}
class APZCFlingStopTester : public APZCGestureDetectorTester {
protected:
// Start a fling, and then tap while the fling is ongoing. When

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