зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to b2g-inbound
This commit is contained in:
Коммит
0c079d0b04
|
@ -323,7 +323,7 @@ pref("media.video-queue.default-size", 3);
|
|||
|
||||
// optimize images' memory usage
|
||||
pref("image.decode-only-on-draw.enabled", true);
|
||||
pref("image.mem.allow_locking_in_content_processes", false); /* don't allow image locking */
|
||||
pref("image.mem.allow_locking_in_content_processes", true);
|
||||
// Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
|
||||
// Almost everything that was factored into 'max_decoded_image_kb' is now stored
|
||||
// in the surface cache. 1/8 of main memory is 32MB on a 256MB device, which is
|
||||
|
|
|
@ -199,6 +199,11 @@ Sanitizer.prototype = {
|
|||
cookieMgr.removeAll();
|
||||
}
|
||||
|
||||
// Clear deviceIds. Done asynchronously (returns before complete).
|
||||
let mediaMgr = Components.classes["@mozilla.org/mediaManagerService;1"]
|
||||
.getService(Ci.nsIMediaManagerService);
|
||||
mediaMgr.sanitizeDeviceIds(this.range && this.range[0]);
|
||||
|
||||
// Clear plugin data.
|
||||
const phInterface = Ci.nsIPluginHost;
|
||||
const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
|
||||
|
|
|
@ -145,7 +145,7 @@ loop.roomViews = (function(mozL10n) {
|
|||
React.createElement("div", {className: "btn-group call-action-group"},
|
||||
React.createElement("button", {className: "btn btn-info btn-email",
|
||||
onClick: this.handleEmailButtonClick},
|
||||
mozL10n.get("share_button2")
|
||||
mozL10n.get("email_link_button")
|
||||
),
|
||||
React.createElement("button", {className: "btn btn-info btn-copy",
|
||||
onClick: this.handleCopyButtonClick},
|
||||
|
|
|
@ -145,7 +145,7 @@ loop.roomViews = (function(mozL10n) {
|
|||
<div className="btn-group call-action-group">
|
||||
<button className="btn btn-info btn-email"
|
||||
onClick={this.handleEmailButtonClick}>
|
||||
{mozL10n.get("share_button2")}
|
||||
{mozL10n.get("email_link_button")}
|
||||
</button>
|
||||
<button className="btn btn-info btn-copy"
|
||||
onClick={this.handleCopyButtonClick}>
|
||||
|
|
|
@ -2848,7 +2848,6 @@ let E10SUINotification = {
|
|||
|
||||
if (!Services.appinfo.inSafeMode &&
|
||||
!Services.appinfo.accessibilityEnabled &&
|
||||
!Services.appinfo.keyboardMayHaveIME &&
|
||||
isHardwareAccelerated &&
|
||||
e10sPromptShownCount < 5) {
|
||||
Services.tm.mainThread.dispatch(() => {
|
||||
|
|
|
@ -33,7 +33,7 @@ function CloseUI() {
|
|||
}
|
||||
|
||||
function OnAppManagerUpdate(event, what) {
|
||||
if (what == "connection" || what == "list-tabs-response") {
|
||||
if (what == "connection" || what == "runtime-global-actors") {
|
||||
BuildUI();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ function CloseUI() {
|
|||
}
|
||||
|
||||
function OnAppManagerUpdate(event, what) {
|
||||
if (what == "connection" || what == "list-tabs-response") {
|
||||
if (what == "connection" || what == "runtime-global-actors") {
|
||||
BuildUI();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ let Monitor = {
|
|||
*/
|
||||
onAppManagerUpdate: function(event, what, details) {
|
||||
switch (what) {
|
||||
case 'list-tabs-response':
|
||||
case 'runtime-global-actors':
|
||||
Monitor.connectToRuntime();
|
||||
break;
|
||||
case 'connection':
|
||||
|
|
|
@ -25,7 +25,7 @@ function CloseUI() {
|
|||
}
|
||||
|
||||
function OnAppManagerUpdate(event, what) {
|
||||
if (what == "connection" || what == "list-tabs-response") {
|
||||
if (what == "connection" || what == "runtime-global-actors") {
|
||||
BuildUI();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,14 +24,14 @@ window.addEventListener("unload", function onUnload() {
|
|||
AppManager.off("app-manager-update", onAppManagerUpdate);
|
||||
});
|
||||
|
||||
function onAppManagerUpdate(event, what) {
|
||||
function onAppManagerUpdate(event, what, details) {
|
||||
switch (what) {
|
||||
case "list-tabs-response":
|
||||
case "runtime-apps-found":
|
||||
case "runtime-global-actors":
|
||||
case "runtime-targets":
|
||||
case "project-validated":
|
||||
case "project-removed":
|
||||
case "project":
|
||||
projectList.update();
|
||||
projectList.update(details);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ function CloseUI() {
|
|||
}
|
||||
|
||||
function OnAppManagerUpdate(event, what) {
|
||||
if (what == "connection" || what == "list-tabs-response") {
|
||||
if (what == "connection" || what == "runtime-global-actors") {
|
||||
BuildUI();
|
||||
CheckLockState();
|
||||
}
|
||||
|
|
|
@ -156,8 +156,9 @@ let UI = {
|
|||
|
||||
appManagerUpdate: function(event, what, details) {
|
||||
// Got a message from app-manager.js
|
||||
// See AppManager.update() for descriptions of what these events mean.
|
||||
switch (what) {
|
||||
case "runtimelist":
|
||||
case "runtime-list":
|
||||
this.updateRuntimeList();
|
||||
this.autoConnectRuntime();
|
||||
break;
|
||||
|
@ -183,16 +184,16 @@ let UI = {
|
|||
projectList.update();
|
||||
});
|
||||
return;
|
||||
case "project-is-not-running":
|
||||
case "project-is-running":
|
||||
case "list-tabs-response":
|
||||
case "project-stopped":
|
||||
case "project-started":
|
||||
case "runtime-global-actors":
|
||||
this.updateCommands();
|
||||
projectList.update();
|
||||
break;
|
||||
case "runtime-details":
|
||||
this.updateRuntimeButton();
|
||||
break;
|
||||
case "runtime-changed":
|
||||
case "runtime":
|
||||
this.updateRuntimeButton();
|
||||
this.saveLastConnectedRuntime();
|
||||
break;
|
||||
|
@ -203,13 +204,14 @@ let UI = {
|
|||
this.updateProjectEditorHeader();
|
||||
projectList.update();
|
||||
break;
|
||||
case "runtime-targets":
|
||||
case "project-removed":
|
||||
projectList.update();
|
||||
projectList.update(details);
|
||||
break;
|
||||
case "install-progress":
|
||||
this.updateProgress(Math.round(100 * details.bytesSent / details.totalBytes));
|
||||
break;
|
||||
case "runtime-apps-found":
|
||||
case "runtime-targets":
|
||||
this.autoSelectProject();
|
||||
projectList.update();
|
||||
break;
|
||||
|
@ -1076,6 +1078,18 @@ let Cmds = {
|
|||
|
||||
showProjectPanel: function() {
|
||||
ProjectPanel.toggle(projectList.sidebarsEnabled, true);
|
||||
|
||||
// There are currently no available events to listen for when an unselected
|
||||
// tab navigates. Since we show every tab's location in the project menu,
|
||||
// we re-list all the tabs each time the menu is displayed.
|
||||
// TODO: An event-based solution will be needed for the sidebar UI.
|
||||
if (!projectList.sidebarsEnabled && AppManager.connected) {
|
||||
return AppManager.listTabs().then(() => {
|
||||
projectList.updateTabs();
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
return promise.resolve();
|
||||
},
|
||||
|
||||
showRuntimePanel: function() {
|
||||
|
|
|
@ -48,8 +48,10 @@ let AppManager = exports.AppManager = {
|
|||
this.connection.on(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
|
||||
|
||||
this.tabStore = new TabStore(this.connection);
|
||||
this.onTabList = this.onTabList.bind(this);
|
||||
this.onTabNavigate = this.onTabNavigate.bind(this);
|
||||
this.onTabClosed = this.onTabClosed.bind(this);
|
||||
this.tabStore.on("tab-list", this.onTabList);
|
||||
this.tabStore.on("navigate", this.onTabNavigate);
|
||||
this.tabStore.on("closed", this.onTabClosed);
|
||||
|
||||
|
@ -75,6 +77,7 @@ let AppManager = exports.AppManager = {
|
|||
RuntimeScanners.off("runtime-list-updated", this._rebuildRuntimeList);
|
||||
RuntimeScanners.disable();
|
||||
this.runtimeList = null;
|
||||
this.tabStore.off("tab-list", this.onTabList);
|
||||
this.tabStore.off("navigate", this.onTabNavigate);
|
||||
this.tabStore.off("closed", this.onTabClosed);
|
||||
this.tabStore.destroy();
|
||||
|
@ -85,6 +88,52 @@ let AppManager = exports.AppManager = {
|
|||
this.connection = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* This module emits various events when state changes occur. The basic event
|
||||
* naming scheme is that event "X" means "X has changed" or "X is available".
|
||||
* Some names are more detailed to clarify their precise meaning.
|
||||
*
|
||||
* The events this module may emit include:
|
||||
* before-project:
|
||||
* The selected project is about to change. The event includes a special
|
||||
* |cancel| callback that will abort the project change if desired.
|
||||
* connection:
|
||||
* The connection status has changed (connected, disconnected, etc.)
|
||||
* install-progress:
|
||||
* A project being installed to a runtime has made further progress. This
|
||||
* event contains additional details about exactly how far the process is
|
||||
* when such information is available.
|
||||
* project:
|
||||
* The selected project has changed.
|
||||
* project-started:
|
||||
* The selected project started running on the connected runtime.
|
||||
* project-stopped:
|
||||
* The selected project stopped running on the connected runtime.
|
||||
* project-removed:
|
||||
* The selected project was removed from the project list.
|
||||
* project-validated:
|
||||
* The selected project just completed validation. As part of validation,
|
||||
* many pieces of metadata about the project are refreshed, including its
|
||||
* name, manifest details, etc.
|
||||
* runtime:
|
||||
* The selected runtime has changed.
|
||||
* runtime-global-actors:
|
||||
* The list of global actors for the entire runtime (but not actors for a
|
||||
* specific tab or app) are now available, so we can test for features
|
||||
* like preferences and settings.
|
||||
* runtime-details:
|
||||
* The selected runtime's details have changed, such as its user-visible
|
||||
* name.
|
||||
* runtime-list:
|
||||
* The list of available runtimes has changed, or any of the user-visible
|
||||
* details (like names) for the non-selected runtimes has changed.
|
||||
* runtime-targets:
|
||||
* The list of remote runtime targets available from the currently
|
||||
* connected runtime (such as tabs or apps) has changed, or any of the
|
||||
* user-visible details (like names) for the non-selected runtime targets
|
||||
* has changed. This event includes |type| in the details, to distguish
|
||||
* "apps" and "tabs".
|
||||
*/
|
||||
update: function(what, details) {
|
||||
// Anything we want to forward to the UI
|
||||
this.emit("app-manager-update", what, details);
|
||||
|
@ -132,16 +181,16 @@ let AppManager = exports.AppManager = {
|
|||
// first.
|
||||
this._appsFront = front;
|
||||
this._listTabsResponse = response;
|
||||
this.update("list-tabs-response");
|
||||
this.update("runtime-global-actors");
|
||||
})
|
||||
.then(() => {
|
||||
this.checkIfProjectIsRunning();
|
||||
this.update("runtime-apps-found");
|
||||
this.update("runtime-targets", { type: "apps" });
|
||||
front.fetchIcons();
|
||||
});
|
||||
} else {
|
||||
this._listTabsResponse = response;
|
||||
this.update("list-tabs-response");
|
||||
this.update("runtime-global-actors");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -150,7 +199,8 @@ let AppManager = exports.AppManager = {
|
|||
},
|
||||
|
||||
get connected() {
|
||||
return this.connection.status == Connection.Status.CONNECTED;
|
||||
return this.connection &&
|
||||
this.connection.status == Connection.Status.CONNECTED;
|
||||
},
|
||||
|
||||
get apps() {
|
||||
|
@ -178,9 +228,9 @@ let AppManager = exports.AppManager = {
|
|||
checkIfProjectIsRunning: function() {
|
||||
if (this.selectedProject) {
|
||||
if (this.isProjectRunning()) {
|
||||
this.update("project-is-running");
|
||||
this.update("project-started");
|
||||
} else {
|
||||
this.update("project-is-not-running");
|
||||
this.update("project-stopped");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -189,8 +239,13 @@ let AppManager = exports.AppManager = {
|
|||
return this.tabStore.listTabs();
|
||||
},
|
||||
|
||||
onTabList: function() {
|
||||
this.update("runtime-targets", { type: "tabs" });
|
||||
},
|
||||
|
||||
// TODO: Merge this into TabProject as part of project-agnostic work
|
||||
onTabNavigate: function() {
|
||||
this.update("runtime-targets", { type: "tabs" });
|
||||
if (this.selectedProject.type !== "tab") {
|
||||
return;
|
||||
}
|
||||
|
@ -373,7 +428,7 @@ let AppManager = exports.AppManager = {
|
|||
this.selectedProject.type == "tab")) {
|
||||
this.selectedProject = null;
|
||||
}
|
||||
this.update("runtime-changed");
|
||||
this.update("runtime");
|
||||
},
|
||||
|
||||
get selectedRuntime() {
|
||||
|
@ -580,7 +635,7 @@ let AppManager = exports.AppManager = {
|
|||
if (!app.running) {
|
||||
let deferred = promise.defer();
|
||||
self.on("app-manager-update", function onUpdate(event, what) {
|
||||
if (what == "project-is-running") {
|
||||
if (what == "project-started") {
|
||||
self.off("app-manager-update", onUpdate);
|
||||
deferred.resolve();
|
||||
}
|
||||
|
@ -720,7 +775,7 @@ let AppManager = exports.AppManager = {
|
|||
}
|
||||
|
||||
this.update("runtime-details");
|
||||
this.update("runtimelist");
|
||||
this.update("runtime-list");
|
||||
},
|
||||
|
||||
/* MANIFEST UTILS */
|
||||
|
|
|
@ -117,22 +117,27 @@ ProjectList.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_buildProjectPanelTabs: function() {
|
||||
let tabs = AppManager.tabStore.tabs;
|
||||
updateTabs: function() {
|
||||
let tabsHeaderNode = this._doc.querySelector("#panel-header-tabs");
|
||||
|
||||
if (AppManager.connected && tabs.length > 0) {
|
||||
tabsHeaderNode.removeAttribute("hidden");
|
||||
} else {
|
||||
tabsHeaderNode.setAttribute("hidden", "true");
|
||||
}
|
||||
|
||||
let tabsNode = this._doc.querySelector("#project-panel-tabs");
|
||||
|
||||
while (tabsNode.hasChildNodes()) {
|
||||
tabsNode.firstChild.remove();
|
||||
}
|
||||
|
||||
if (!AppManager.connected) {
|
||||
tabsHeaderNode.setAttribute("hidden", "true");
|
||||
return;
|
||||
}
|
||||
|
||||
let tabs = AppManager.tabStore.tabs;
|
||||
|
||||
if (tabs.length > 0) {
|
||||
tabsHeaderNode.removeAttribute("hidden");
|
||||
} else {
|
||||
tabsHeaderNode.setAttribute("hidden", "true");
|
||||
}
|
||||
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
let tab = tabs[i];
|
||||
let URL = this._parentWindow.URL;
|
||||
|
@ -173,54 +178,12 @@ ProjectList.prototype = {
|
|||
};
|
||||
}, true);
|
||||
}
|
||||
|
||||
return promise.resolve();
|
||||
},
|
||||
|
||||
update: function() {
|
||||
let deferred = promise.defer();
|
||||
updateApps: function() {
|
||||
let doc = this._doc;
|
||||
let panelVboxNode = doc.querySelector("#project-panel > #project-panel-box");
|
||||
let anchorNode = doc.querySelector("#project-panel-button > .panel-button-anchor");
|
||||
let projectsNode = doc.querySelector("#project-panel-projects");
|
||||
|
||||
while (projectsNode.hasChildNodes()) {
|
||||
projectsNode.firstChild.remove();
|
||||
}
|
||||
|
||||
AppProjects.load().then(() => {
|
||||
let projects = AppProjects.store.object.projects;
|
||||
for (let i = 0; i < projects.length; i++) {
|
||||
let project = projects[i];
|
||||
let panelItemNode = doc.createElement(this._panelNodeEl);
|
||||
panelItemNode.className = "panel-item";
|
||||
projectsNode.appendChild(panelItemNode);
|
||||
this._renderProjectItem({
|
||||
panel: panelItemNode,
|
||||
name: project.name || AppManager.DEFAULT_PROJECT_NAME,
|
||||
icon: project.icon|| AppManager.DEFAULT_PROJECT_ICON
|
||||
});
|
||||
if (!project.name || !project.icon) {
|
||||
// The result of the validation process (storing names, icons, …) is not stored in
|
||||
// the IndexedDB database when App Manager v1 is used.
|
||||
// We need to run the validation again and update the name and icon of the app.
|
||||
AppManager.validateAndUpdateProject(project).then(() => {
|
||||
this._renderProjectItem({
|
||||
panel: panelItemNode,
|
||||
name: project.name,
|
||||
icon: project.icon
|
||||
});
|
||||
});
|
||||
}
|
||||
panelItemNode.addEventListener("click", () => {
|
||||
if (!this._sidebarsEnabled) {
|
||||
this._UI.hidePanels();
|
||||
}
|
||||
AppManager.selectedProject = project;
|
||||
}, true);
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
}, deferred.reject);
|
||||
|
||||
let runtimeappsHeaderNode = doc.querySelector("#panel-header-runtimeapps");
|
||||
let sortedApps = [];
|
||||
for (let [manifestURL, app] of AppManager.apps) {
|
||||
|
@ -285,14 +248,77 @@ ProjectList.prototype = {
|
|||
}, true);
|
||||
}
|
||||
|
||||
return promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger an update of the project and remote runtime list.
|
||||
* @param options object (optional)
|
||||
* An |options| object containing a type of |apps| or |tabs| will limit
|
||||
* what is updated to only those sections.
|
||||
*/
|
||||
update: function(options) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
if (options && options.type === "apps") {
|
||||
return this.updateApps();
|
||||
} else if (options && options.type === "tabs") {
|
||||
return this.updateTabs();
|
||||
}
|
||||
|
||||
let doc = this._doc;
|
||||
let projectsNode = doc.querySelector("#project-panel-projects");
|
||||
|
||||
while (projectsNode.hasChildNodes()) {
|
||||
projectsNode.firstChild.remove();
|
||||
}
|
||||
|
||||
AppProjects.load().then(() => {
|
||||
let projects = AppProjects.store.object.projects;
|
||||
for (let i = 0; i < projects.length; i++) {
|
||||
let project = projects[i];
|
||||
let panelItemNode = doc.createElement(this._panelNodeEl);
|
||||
panelItemNode.className = "panel-item";
|
||||
projectsNode.appendChild(panelItemNode);
|
||||
this._renderProjectItem({
|
||||
panel: panelItemNode,
|
||||
name: project.name || AppManager.DEFAULT_PROJECT_NAME,
|
||||
icon: project.icon || AppManager.DEFAULT_PROJECT_ICON
|
||||
});
|
||||
if (!project.name || !project.icon) {
|
||||
// The result of the validation process (storing names, icons, …) is not stored in
|
||||
// the IndexedDB database when App Manager v1 is used.
|
||||
// We need to run the validation again and update the name and icon of the app.
|
||||
AppManager.validateAndUpdateProject(project).then(() => {
|
||||
this._renderProjectItem({
|
||||
panel: panelItemNode,
|
||||
name: project.name,
|
||||
icon: project.icon
|
||||
});
|
||||
});
|
||||
}
|
||||
panelItemNode.addEventListener("click", () => {
|
||||
if (!this._sidebarsEnabled) {
|
||||
this._UI.hidePanels();
|
||||
}
|
||||
AppManager.selectedProject = project;
|
||||
}, true);
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
}, deferred.reject);
|
||||
|
||||
// List remote apps and the main process, if they exist
|
||||
this.updateApps();
|
||||
|
||||
// Build the tab list right now, so it's fast...
|
||||
this._buildProjectPanelTabs();
|
||||
this.updateTabs();
|
||||
|
||||
// But re-list them and rebuild, in case any tabs navigated since the last
|
||||
// time they were listed.
|
||||
if (AppManager.connected) {
|
||||
AppManager.listTabs().then(() => {
|
||||
this._buildProjectPanelTabs();
|
||||
this.updateTabs();
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@ 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 promise = require("promise");
|
||||
const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
const { devtools } =
|
||||
Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
|
@ -82,7 +81,8 @@ TabStore.prototype = {
|
|||
},
|
||||
|
||||
_onTabListChanged: function() {
|
||||
this.listTabs();
|
||||
this.listTabs().then(() => this.emit("tab-list"))
|
||||
.catch(console.error);
|
||||
},
|
||||
|
||||
_onTabNavigated: function(e, { from, title, url }) {
|
||||
|
@ -105,9 +105,13 @@ TabStore.prototype = {
|
|||
deferred.reject(response.error);
|
||||
return;
|
||||
}
|
||||
let tabsChanged = JSON.stringify(this.tabs) !== JSON.stringify(response.tabs);
|
||||
this.response = response;
|
||||
this.tabs = response.tabs;
|
||||
this._checkSelectedTab();
|
||||
if (tabsChanged) {
|
||||
this.emit("tab-list");
|
||||
}
|
||||
deferred.resolve(response);
|
||||
});
|
||||
return deferred.promise;
|
||||
|
|
|
@ -32,9 +32,17 @@ function test() {
|
|||
is(project.location, TEST_URI, "Location is correct");
|
||||
is(project.name, "example.com: Test Tab", "Name is correct");
|
||||
|
||||
yield closeWebIDE(win);
|
||||
DebuggerServer.destroy();
|
||||
// Ensure tab list changes are noticed
|
||||
let tabsNode = win.document.querySelector("#project-panel-tabs");
|
||||
is(tabsNode.querySelectorAll(".panel-item").length, 2, "2 tabs available");
|
||||
yield removeTab(tab);
|
||||
yield waitForUpdate(win, "runtime-targets");
|
||||
is(tabsNode.querySelectorAll(".panel-item").length, 1, "1 tab available");
|
||||
|
||||
yield win.Cmds.disconnectRuntime();
|
||||
yield closeWebIDE(win);
|
||||
|
||||
DebuggerServer.destroy();
|
||||
}).then(finish, handleError);
|
||||
}
|
||||
|
||||
|
@ -49,9 +57,8 @@ function connectToLocal(win) {
|
|||
|
||||
function selectTabProject(win) {
|
||||
return Task.spawn(function() {
|
||||
yield win.AppManager.listTabs();
|
||||
win.projectList.update();
|
||||
yield nextTick();
|
||||
yield win.Cmds.showProjectPanel();
|
||||
yield waitForUpdate(win, "runtime-targets");
|
||||
let tabsNode = win.document.querySelector("#project-panel-tabs");
|
||||
let tabNode = tabsNode.querySelectorAll(".panel-item")[1];
|
||||
tabNode.click();
|
||||
|
|
|
@ -195,16 +195,9 @@ function connectToLocalRuntime(aWindow) {
|
|||
let items = panelNode.querySelectorAll(".runtime-panel-item-other");
|
||||
is(items.length, 2, "Found 2 custom runtime buttons");
|
||||
|
||||
let deferred = promise.defer();
|
||||
aWindow.AppManager.on("app-manager-update", function onUpdate(e,w) {
|
||||
if (w == "list-tabs-response") {
|
||||
aWindow.AppManager.off("app-manager-update", onUpdate);
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
let updated = waitForUpdate(aWindow, "runtime-global-actors");
|
||||
items[1].click();
|
||||
return deferred.promise;
|
||||
return updated;
|
||||
}
|
||||
|
||||
function handleError(aError) {
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
win.AppManager.update("runtimelist");
|
||||
win.AppManager.update("runtime-list");
|
||||
|
||||
let packagedAppLocation = getTestFilePath("app");
|
||||
|
||||
|
@ -86,7 +86,7 @@
|
|||
|
||||
is(Object.keys(DebuggerServer._connections).length, 1, "Connected");
|
||||
|
||||
yield waitForUpdate(win, "runtime-apps-found");
|
||||
yield waitForUpdate(win, "runtime-targets");
|
||||
|
||||
ok(isPlayActive(), "play button is enabled 1");
|
||||
ok(!isStopActive(), "stop button is disabled 1");
|
||||
|
@ -115,7 +115,7 @@
|
|||
|
||||
winIframe.document.querySelectorAll(".runtime-panel-item-other")[1].click();
|
||||
|
||||
yield waitForUpdate(win, "runtime-apps-found");
|
||||
yield waitForUpdate(win, "runtime-targets");
|
||||
|
||||
is(Object.keys(DebuggerServer._connections).length, 1, "Locally connected");
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
}
|
||||
};
|
||||
win.AppManager.runtimeList.usb.push(fakeRuntime);
|
||||
win.AppManager.update("runtimelist");
|
||||
win.AppManager.update("runtime-list");
|
||||
|
||||
let panelNode = win.document.querySelector("#runtime-panel");
|
||||
let items = panelNode.querySelectorAll(".runtime-panel-item-usb");
|
||||
|
@ -69,9 +69,9 @@
|
|||
win = yield openWebIDE();
|
||||
|
||||
win.AppManager.runtimeList.usb.push(fakeRuntime);
|
||||
win.AppManager.update("runtimelist");
|
||||
win.AppManager.update("runtime-list");
|
||||
|
||||
yield waitForUpdate(win, "runtime-apps-found");
|
||||
yield waitForUpdate(win, "runtime-targets");
|
||||
|
||||
is(Object.keys(DebuggerServer._connections).length, 1, "Automatically reconnected");
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
let prefIframe = win.document.querySelector("#deck-panel-devicepreferences");
|
||||
|
||||
win.AppManager.update("runtimelist");
|
||||
win.AppManager.update("runtime-list");
|
||||
|
||||
yield connectToLocalRuntime(win);
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
let settingIframe = win.document.querySelector("#deck-panel-devicesettings");
|
||||
|
||||
win.AppManager.update("runtimelist");
|
||||
win.AppManager.update("runtime-list");
|
||||
|
||||
yield connectToLocalRuntime(win);
|
||||
|
||||
|
|
|
@ -31,14 +31,13 @@
|
|||
Task.spawn(function* () {
|
||||
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
|
||||
win = yield openWebIDE();
|
||||
win.AppManager.update("runtimelist");
|
||||
win.AppManager.update("runtime-list");
|
||||
|
||||
yield connectToLocal(win);
|
||||
|
||||
yield waitForUpdate(win, "runtime-apps-found");
|
||||
|
||||
// Select main process
|
||||
yield win.Cmds.showProjectPanel();
|
||||
yield waitForUpdate(win, "runtime-targets");
|
||||
SimpleTest.executeSoon(() => {
|
||||
win.document.querySelectorAll("#project-panel-runtimeapps .panel-item")[0].click();
|
||||
});
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
win.AppManager.update("runtimelist");
|
||||
win.AppManager.update("runtime-list");
|
||||
|
||||
let packagedAppLocation = getTestFilePath("app");
|
||||
|
||||
|
@ -109,7 +109,7 @@
|
|||
|
||||
is(Object.keys(DebuggerServer._connections).length, 1, "Connected");
|
||||
|
||||
yield waitForUpdate(win, "runtime-apps-found");
|
||||
yield waitForUpdate(win, "runtime-global-actors");
|
||||
|
||||
ok(isPlayActive(), "play button is enabled 1");
|
||||
ok(!isStopActive(), "stop button is disabled 1");
|
||||
|
@ -138,7 +138,7 @@
|
|||
|
||||
win.document.querySelectorAll(".runtime-panel-item-other")[1].click();
|
||||
|
||||
yield waitForUpdate(win, "runtime-apps-found");
|
||||
yield waitForUpdate(win, "runtime-targets");
|
||||
|
||||
is(Object.keys(DebuggerServer._connections).length, 1, "Locally connected");
|
||||
|
||||
|
@ -146,6 +146,7 @@
|
|||
|
||||
// Select main process
|
||||
yield win.Cmds.showProjectPanel();
|
||||
yield waitForUpdate(win, "runtime-targets");
|
||||
SimpleTest.executeSoon(() => {
|
||||
win.document.querySelectorAll("#project-panel-runtimeapps .panel-item")[0].click();
|
||||
});
|
||||
|
|
|
@ -106,7 +106,7 @@
|
|||
|
||||
win.AppManager.runtimeList.other = [remote, local, other];
|
||||
|
||||
win.AppManager.update("runtimelist");
|
||||
win.AppManager.update("runtime-list");
|
||||
}
|
||||
|
||||
function addTestApp(win) {
|
||||
|
|
|
@ -26,8 +26,7 @@ display_name_available_status=Available
|
|||
|
||||
# Error bars
|
||||
## LOCALIZATION NOTE(unable_retrieve_url,session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
|
||||
## These may be displayed at the top of the panel here:
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#error
|
||||
## These may be displayed at the top of the panel.
|
||||
unable_retrieve_url=Sorry, we were unable to retrieve a call URL.
|
||||
session_expired_error_description=Session expired. All URLs you have previously created and shared will no longer work.
|
||||
could_not_authenticate=Could Not Authenticate
|
||||
|
@ -41,15 +40,32 @@ problem_accessing_account=There Was A Problem Accessing Your Account
|
|||
|
||||
## LOCALIZATION NOTE(retry_button): Displayed when there is an error to retry
|
||||
## the appropriate action.
|
||||
## See https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#error for location
|
||||
retry_button=Retry
|
||||
|
||||
share_email_subject5={{clientShortname2}} — Join the conversation
|
||||
## LOCALIZATION NOTE (share_email_subject_context): This is the alternate email
|
||||
## subject when a conversation is shared with a context attached.
|
||||
## {{clientShortName2}} will be replaced with the respective string in this file.
|
||||
## {{title}} will be replaced with the title of an HTML document.
|
||||
share_email_subject_context={{clientShortname2}} conversation: {{title}}
|
||||
## LOCALIZATION NOTE (share_email_body4): In this item, don't translate the
|
||||
## part between {{..}} and leave the \n\n part alone
|
||||
share_email_body5=Hello!\n\nJoin me for a video conversation on {{clientShortname2}}.\n\nIt's the easiest way to connect by video with anyone anywhere. With {{clientSuperShortname}}, you don't have to download or install anything. Just click or paste this link into your {{brandShortname}}, Opera, or Chrome browser to join the conversation:\n\n{{callUrl}}\n\nIf you'd like to learn more about {{clientSuperShortname}} and how you can start your own free video conversations, visit {{learnMoreUrl}}\n\nTalk to you soon!
|
||||
## LOCALIZATION NOTE (share_email_body_context): In this item, don't translate
|
||||
## the part between {{..}} and leave the \n\n part alone.
|
||||
share_email_body_context=Hello!\n\nJoin me for a video conversation on {{clientShortname2}} about:\n{{title}}.\n\nIt's the easiest way to connect by video with anyone anywhere. With {{clientSuperShortname}}, you don't have to download or install anything. Just click or paste this link into your {{brandShortname}}, Opera, or Chrome browser to join the conversation:\n\n{{callUrl}}\n\nIf you'd like to learn more about {{clientSuperShortname}} and how you can start your own free video conversations, visit {{learnMoreUrl}}\n\nTalk to you soon!
|
||||
## LOCALIZATION NOTE (share_tweeet): In this item, don't translate the part
|
||||
## between {{..}}. Please keep the text below 117 characters to make sure it fits
|
||||
## in a tweet.
|
||||
share_tweet=Join me for a video conversation on {{clientShortname2}}!
|
||||
|
||||
share_button2=Email Link
|
||||
share_button3=Share Link
|
||||
share_add_service_button=Add a Service
|
||||
add_to_toolbar_button=Add the Share panel to my toolbar
|
||||
share_panel_header=Share the web with your friends!
|
||||
## LOCALIZATION NOTE (share_panel_body): In this item, don't translate the part
|
||||
## between {{..}}.
|
||||
share_panel_body={{brandShortname}}'s new Share panel allows you to quickly share links or {{clientSuperShortname}} invitations with your favourite social networks.
|
||||
copy_url_button2=Copy Link
|
||||
copied_url_button=Copied!
|
||||
|
||||
|
@ -64,18 +80,15 @@ settings_menu_button_tooltip=Settings
|
|||
# Contact Strings (Panel)
|
||||
|
||||
## LOCALIZATION NOTE(contacts_search_placeholder): This is the placeholder text for
|
||||
## the search field at https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
## the search field.
|
||||
contacts_search_placesholder=Search…
|
||||
|
||||
## LOCALIZATION NOTE (new_contact_button): This is the button to open the
|
||||
## new contact sub-panel.
|
||||
## See https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
## for where this appears on the UI
|
||||
new_contact_button=New Contact
|
||||
## LOCALIZATION NOTE (new_contact_name_placeholder, new_contact_email_placeholder):
|
||||
## These are the placeholders for the fields for entering a new contact
|
||||
## See https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
## and click the 'New Contact' button to see the fields.
|
||||
## These are the placeholders for the fields for entering a new contact. Click
|
||||
## the 'New Contact' button to see the fields.
|
||||
new_contact_name_placeholder=Name
|
||||
new_contact_email_placeholder=Email
|
||||
new_contact_fxos_phone_placeholder=Firefox OS Phone
|
||||
|
@ -84,21 +97,17 @@ new_contact_phone_placeholder2=Phone
|
|||
contacts_blocked_contacts=Blocked Contacts
|
||||
|
||||
## LOCALIZATION NOTE (add_contact_button):
|
||||
## This is the button to actually add the new contact
|
||||
## See https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
## and click the 'New Contact' button to see the fields.
|
||||
## This is the button to actually add the new contact. Click the 'New Contact'
|
||||
## button to see the fields.
|
||||
add_contact_button=Add Contact
|
||||
### LOCALIZATION NOTE (valid_email_text_description): This is displayed when
|
||||
### the user enters an invalid email address, preventing the addition of the
|
||||
### contact.
|
||||
valid_email_text_description=Please enter a valid email address
|
||||
|
||||
## LOCALIZATION NOTE (add_or_import_contact_title): This is the subtitle of the panel
|
||||
## at https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
## LOCALIZATION NOTE (add_or_import_contact_title): This is the subtitle of the
|
||||
## panel.
|
||||
add_or_import_contact_title=Add or Import Contact
|
||||
## LOCALIZATION NOTE (import_contacts_button, importing_contacts_progress_button):
|
||||
## See https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
## for where these appear on the UI
|
||||
import_contacts_button=Import
|
||||
importing_contacts_progress_button=Importing…
|
||||
import_contacts_failure_message=Some contacts could not be imported. Please try again.
|
||||
|
@ -113,15 +122,13 @@ import_contacts_success_message={{total}} contact was successfully imported.;{{t
|
|||
sync_contacts_button=Sync Contacts
|
||||
|
||||
## LOCALIZATION NOTE(import_failed_description simple): Displayed when an import of
|
||||
## contacts fails. This is displayed in the error field here:
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#error
|
||||
## contacts fails. This is displayed in the error field.
|
||||
import_failed_description_simple=Sorry, contact import failed
|
||||
import_failed_description_some=Some contacts could not be imported
|
||||
import_failed_support_button=Help
|
||||
|
||||
## LOCALIZATION NOTE(remove_contact_menu_button): Displayed in the contact list in
|
||||
## a pop-up menu next to the contact's name.
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
remove_contact_menu_button=Remove Contact
|
||||
## LOCALIZATION NOTE(confirm_delete_contact_alert): This is an alert that is displayed
|
||||
## to confirm deletion of a contact.
|
||||
|
@ -133,40 +140,34 @@ confirm_delete_contact_cancel_button=Cancel
|
|||
|
||||
## LOCALIZATION NOTE(block_contact_menu_button): Displayed in the contact list in
|
||||
## a pop-up menu next to the contact's name, used to block a contact from calling
|
||||
## the user. https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
## the user.
|
||||
block_contact_menu_button=Block Contact
|
||||
## LOCALIZATION NOTE(unblock_contact_menu_button): Displayed in the contact list in
|
||||
## a pop-up menu next to the contact's name, used to unblock a contact and allow them
|
||||
## to call the user. https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
## to call the user.
|
||||
unblock_contact_menu_button=Unblock Contact
|
||||
|
||||
## LOCALIZATION NOTE(edit_contact_menu_button): Displayed in the contact list in a
|
||||
## pop-up menu next to the contact's name.
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
edit_contact_menu_button=Edit Contact…
|
||||
## LOCALIZATION NOTE(edit_contact_tile): This is the subtitle of the edit contact
|
||||
## panel. It is displayed when Edit Contact is selected.
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
edit_contact_title=Edit Contact
|
||||
## LOCALIZATION NOTE(edit_contact_name_label, edit_contact_email_label):
|
||||
## These fields are display when the Edit Contact button is selected.
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
edit_contact_name_label=Name
|
||||
edit_contact_email_label=Email
|
||||
## LOCALIZATION NOTE(edit_contact_name_label, edit_contact_email_label):
|
||||
## These button is displayed when the Edit Contact button is selected and is used
|
||||
## to accept the change.
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
edit_contact_done_button=Done
|
||||
|
||||
## LOCALIZATION NOTE(audio_call_menu_button): Displayed in the contact list in a
|
||||
## pop-up menu next to the contact's name.
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
audio_call_menu_button=Audio Conversation
|
||||
|
||||
## LOCALIZATION NOTE(video_call_menu_button): Displayed in the contact list in a
|
||||
## pop-up menu next to the contact's name.
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
video_call_menu_button=Video Conversation
|
||||
|
||||
## LOCALIZATION NOTE(gravatars_promo_message): The {{learn_more}} part will be
|
||||
|
@ -201,7 +202,6 @@ share_windows_button_title=Share other Windows
|
|||
## LOCALIZATION NOTE (call_with_contact_title): The title displayed
|
||||
## when calling a contact. Don't translate the part between {{..}} because
|
||||
## this will be replaced by the contact's name.
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#call-outgoing
|
||||
call_with_contact_title=Conversation with {{contactName}}
|
||||
|
||||
# Outgoing conversation
|
||||
|
@ -213,12 +213,10 @@ initiate_audio_call_button2=Voice conversation
|
|||
initiate_call_cancel_button=Cancel
|
||||
|
||||
## LOCALIZATION NOTE (call_progress_connecting_description): This is displayed
|
||||
## whilst the client is contacting the client at the other end of the connection
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#call-outgoing
|
||||
## whilst the client is contacting the client at the other end of the connection.
|
||||
call_progress_connecting_description=Connecting…
|
||||
## LOCALIZATION NOTE (call_progress_ringing_description): This is displayed
|
||||
## when the other client is actually ringing.
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#call-outgoing
|
||||
call_progress_ringing_description=Ringing…
|
||||
|
||||
peer_ended_conversation2=The person you were calling has ended the conversation.
|
||||
|
@ -235,18 +233,15 @@ generic_failure_title=Something went wrong.
|
|||
generic_failure_with_reason2=You can try again or email a link to be reached at later.
|
||||
generic_failure_no_reason2=Would you like to try again?
|
||||
|
||||
## LOCALIZATION NOTE (contact_offline_title): Title for
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#link-prompt
|
||||
## displayed when the contact is offline.
|
||||
## LOCALIZATION NOTE (contact_offline_title): Title which is displayed when the
|
||||
## contact is offline.
|
||||
contact_offline_title=This person is not online
|
||||
## LOCALIZATION NOTE (call_timeout_notification_text): Title for
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#link-prompt
|
||||
## displayed when the call didn't go through.
|
||||
## LOCALIZATION NOTE (call_timeout_notification_text): Title which is displayed
|
||||
## when the call didn't go through.
|
||||
call_timeout_notification_text=Your call did not go through.
|
||||
|
||||
## LOCALIZATION NOTE (retry_call_button, cancel_button, email_link_button):
|
||||
## These buttons are displayed when a call has failed:
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#link-prompt
|
||||
## These buttons are displayed when a call has failed.
|
||||
retry_call_button=Retry
|
||||
email_link_button=Email Link
|
||||
cancel_button=Cancel
|
||||
|
@ -288,7 +283,6 @@ feedback_back_button=Back
|
|||
feedback_window_will_close_in2=This window will close in {{countdown}} second;This window will close in {{countdown}} seconds
|
||||
## LOCALIZATION_NOTE (feedback_rejoin_button): Displayed on the feedback form after
|
||||
## a signed-in to signed-in user call.
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#feedback
|
||||
feedback_rejoin_button=Rejoin
|
||||
## LOCALIZATION NOTE (feedback_report_user_button): Used to report a user in the case of
|
||||
## an abusive user.
|
||||
|
@ -334,3 +328,15 @@ infobar_menuitem_dontshowagain_accesskey=D
|
|||
|
||||
# Context in conversation strings
|
||||
context_offer_label=Let's talk about this page
|
||||
# LOCALIZATION NOTE (context_inroom_label): this string is followed by the
|
||||
# title/URL of the website you are having a conversation about, displayed on a
|
||||
# separate line. If this structure doesn't work for your locale, you might want
|
||||
# to consider this as a stand-alone title. See example screenshot:
|
||||
# https://bug1115342.bugzilla.mozilla.org/attachment.cgi?id=8563677
|
||||
context_inroom_label=Let's talk about:
|
||||
## LOCALIZATION_NOTE (context_edit_activate_label): {{title}} will be replaced
|
||||
## by the title of the active tab, also known as the title of an HTML document.
|
||||
## The quotes around the title are intentional.
|
||||
context_edit_activate_label=Talk about "{{title}}"
|
||||
context_edit_name_placeholder=Conversation Name
|
||||
context_edit_comments_placeholder=Comments
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/MediaDeviceInfo.h"
|
||||
#include "mozilla/dom/MediaStreamBinding.h"
|
||||
#include "mozilla/MediaManager.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
MediaDeviceInfo::MediaDeviceInfo(const nsAString& aDeviceId,
|
||||
MediaDeviceKind aKind,
|
||||
const nsAString& aLabel,
|
||||
const nsAString& aGroupId)
|
||||
: mKind(aKind)
|
||||
, mDeviceId(aDeviceId)
|
||||
, mLabel(aLabel)
|
||||
, mGroupId(aGroupId) {}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(MediaDeviceInfo)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaDeviceInfo)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaDeviceInfo)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaDeviceInfo)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
JSObject*
|
||||
MediaDeviceInfo::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return MediaDeviceInfoBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsISupports* MediaDeviceInfo::GetParentObject()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MediaDeviceInfo::GetDeviceId(nsString& retval)
|
||||
{
|
||||
retval = mDeviceId;
|
||||
}
|
||||
|
||||
MediaDeviceKind
|
||||
MediaDeviceInfo::Kind()
|
||||
{
|
||||
return mKind;
|
||||
}
|
||||
|
||||
void MediaDeviceInfo::GetGroupId(nsString& retval)
|
||||
{
|
||||
retval = mGroupId;
|
||||
}
|
||||
|
||||
void MediaDeviceInfo::GetLabel(nsString& retval)
|
||||
{
|
||||
retval = mLabel;
|
||||
}
|
||||
|
||||
MediaDeviceKind Kind();
|
||||
void GetLabel(nsString& retval);
|
||||
void GetGroupId(nsString& retval);
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,60 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_MediaDeviceInfo_h
|
||||
#define mozilla_dom_MediaDeviceInfo_h
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "MediaDeviceInfoBinding.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
struct MediaStreamConstraints;
|
||||
|
||||
#define MOZILLA_DOM_MEDIADEVICEINFO_IMPLEMENTATION_IID \
|
||||
{0x25091870, 0x84d6, 0x4acf, {0xaf, 0x97, 0x6e, 0xd5, 0x5b, 0xe0, 0x47, 0xb2}}
|
||||
|
||||
class MediaDeviceInfo final : public nsISupports, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
explicit MediaDeviceInfo(const nsAString& aDeviceId,
|
||||
MediaDeviceKind aKind,
|
||||
const nsAString& aLabel,
|
||||
const nsAString& aGroupId = nsString());
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaDeviceInfo)
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_MEDIADEVICEINFO_IMPLEMENTATION_IID)
|
||||
|
||||
JSObject*
|
||||
WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsISupports* GetParentObject();
|
||||
|
||||
void GetDeviceId(nsString& retval);
|
||||
MediaDeviceKind Kind();
|
||||
void GetLabel(nsString& retval);
|
||||
void GetGroupId(nsString& retval);
|
||||
|
||||
private:
|
||||
MediaDeviceKind mKind;
|
||||
nsString mDeviceId;
|
||||
nsString mLabel;
|
||||
nsString mGroupId;
|
||||
|
||||
virtual ~MediaDeviceInfo() {}
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(MediaDeviceInfo,
|
||||
MOZILLA_DOM_MEDIADEVICEINFO_IMPLEMENTATION_IID)
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_MediaDeviceInfo_h
|
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/MediaManager.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -37,6 +38,109 @@ private:
|
|||
nsRefPtr<Promise> mPromise;
|
||||
};
|
||||
|
||||
class MediaDevices::EnumDevResolver : public nsIGetUserMediaDevicesSuccessCallback
|
||||
{
|
||||
static bool HasAPersistentPermission(uint64_t aWindowId)
|
||||
{
|
||||
nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
|
||||
(nsGlobalWindow::GetInnerWindowWithId(aWindowId));
|
||||
if (NS_WARN_IF(!window)) {
|
||||
return false;
|
||||
}
|
||||
// Check if this site has persistent permissions.
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIPermissionManager> mgr =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false; // no permission manager no permissions!
|
||||
}
|
||||
|
||||
uint32_t audio = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
{
|
||||
auto* principal = window->GetExtantDoc()->NodePrincipal();
|
||||
rv = mgr->TestExactPermissionFromPrincipal(principal, "microphone", &audio);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
rv = mgr->TestExactPermissionFromPrincipal(principal, "camera", &video);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return audio == nsIPermissionManager::ALLOW_ACTION ||
|
||||
video == nsIPermissionManager::ALLOW_ACTION;
|
||||
}
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
EnumDevResolver(Promise* aPromise, uint64_t aWindowId)
|
||||
: mPromise(aPromise), mWindowId(aWindowId) {}
|
||||
|
||||
NS_IMETHOD
|
||||
OnSuccess(nsIVariant* aDevices) override
|
||||
{
|
||||
// Cribbed from MediaPermissionGonk.cpp
|
||||
nsIID elementIID;
|
||||
uint16_t elementType;
|
||||
|
||||
// Create array for nsIMediaDevice
|
||||
nsTArray<nsCOMPtr<nsIMediaDevice>> devices;
|
||||
// Contain the fumes
|
||||
{
|
||||
void* rawArray;
|
||||
uint32_t arrayLen;
|
||||
nsresult rv;
|
||||
rv = aDevices->GetAsArray(&elementType, &elementIID, &arrayLen, &rawArray);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (elementType != nsIDataType::VTYPE_INTERFACE) {
|
||||
NS_Free(rawArray);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsISupports **supportsArray = reinterpret_cast<nsISupports **>(rawArray);
|
||||
for (uint32_t i = 0; i < arrayLen; ++i) {
|
||||
nsCOMPtr<nsIMediaDevice> device(do_QueryInterface(supportsArray[i]));
|
||||
devices.AppendElement(device);
|
||||
NS_IF_RELEASE(supportsArray[i]); // explicitly decrease refcount for rawptr
|
||||
}
|
||||
NS_Free(rawArray); // explicitly free memory from nsIVariant::GetAsArray
|
||||
}
|
||||
nsTArray<nsRefPtr<MediaDeviceInfo>> infos;
|
||||
for (auto& device : devices) {
|
||||
nsString type;
|
||||
device->GetType(type);
|
||||
bool isVideo = type.EqualsLiteral("video");
|
||||
bool isAudio = type.EqualsLiteral("audio");
|
||||
if (isVideo || isAudio) {
|
||||
MediaDeviceKind kind = isVideo ?
|
||||
MediaDeviceKind::Videoinput : MediaDeviceKind::Audioinput;
|
||||
nsString id;
|
||||
nsString name;
|
||||
device->GetId(id);
|
||||
// Include name only if page currently has a gUM stream active or
|
||||
// persistent permissions (audio or video) have been granted
|
||||
if (MediaManager::Get()->IsWindowActivelyCapturing(mWindowId) ||
|
||||
HasAPersistentPermission(mWindowId) ||
|
||||
Preferences::GetBool("media.navigator.permission.disabled", false)) {
|
||||
device->GetName(name);
|
||||
}
|
||||
nsRefPtr<MediaDeviceInfo> info = new MediaDeviceInfo(id, kind, name);
|
||||
infos.AppendElement(info);
|
||||
}
|
||||
}
|
||||
mPromise->MaybeResolve(infos);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~EnumDevResolver() {}
|
||||
nsRefPtr<Promise> mPromise;
|
||||
uint64_t mWindowId;
|
||||
};
|
||||
|
||||
class MediaDevices::GumRejecter : public nsIDOMGetUserMediaErrorCallback
|
||||
{
|
||||
public:
|
||||
|
@ -61,17 +165,17 @@ private:
|
|||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(MediaDevices::GumResolver, nsIDOMGetUserMediaSuccessCallback)
|
||||
NS_IMPL_ISUPPORTS(MediaDevices::EnumDevResolver, nsIGetUserMediaDevicesSuccessCallback)
|
||||
NS_IMPL_ISUPPORTS(MediaDevices::GumRejecter, nsIDOMGetUserMediaErrorCallback)
|
||||
|
||||
already_AddRefed<Promise>
|
||||
MediaDevices::GetUserMedia(const MediaStreamConstraints& aConstraints,
|
||||
ErrorResult &aRv)
|
||||
{
|
||||
ErrorResult rv;
|
||||
nsPIDOMWindow* window = GetOwner();
|
||||
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
|
||||
nsRefPtr<Promise> p = Promise::Create(go, aRv);
|
||||
NS_ENSURE_TRUE(!rv.Failed(), nullptr);
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
nsRefPtr<GumResolver> resolver = new GumResolver(p);
|
||||
nsRefPtr<GumRejecter> rejecter = new GumRejecter(p);
|
||||
|
@ -81,6 +185,21 @@ MediaDevices::GetUserMedia(const MediaStreamConstraints& aConstraints,
|
|||
return p.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
MediaDevices::EnumerateDevices(ErrorResult &aRv)
|
||||
{
|
||||
nsPIDOMWindow* window = GetOwner();
|
||||
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
|
||||
nsRefPtr<Promise> p = Promise::Create(go, aRv);
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
nsRefPtr<EnumDevResolver> resolver = new EnumDevResolver(p, window->WindowID());
|
||||
nsRefPtr<GumRejecter> rejecter = new GumRejecter(p);
|
||||
|
||||
aRv = MediaManager::Get()->EnumerateDevices(window, resolver, rejecter);
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(MediaDevices, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(MediaDevices, DOMEventTargetHelper)
|
||||
NS_INTERFACE_MAP_BEGIN(MediaDevices)
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MediaDevices_h__
|
||||
#define MediaDevices_h__
|
||||
#ifndef mozilla_dom_MediaDevices_h
|
||||
#define mozilla_dom_MediaDevices_h
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
@ -35,8 +35,12 @@ public:
|
|||
already_AddRefed<Promise>
|
||||
GetUserMedia(const MediaStreamConstraints& aConstraints, ErrorResult &aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
EnumerateDevices(ErrorResult &aRv);
|
||||
|
||||
private:
|
||||
class GumResolver;
|
||||
class EnumDevResolver;
|
||||
class GumRejecter;
|
||||
|
||||
virtual ~MediaDevices() {}
|
||||
|
@ -48,4 +52,4 @@ NS_DEFINE_STATIC_IID_ACCESSOR(MediaDevices,
|
|||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MediaDevices_h__
|
||||
#endif // mozilla_dom_MediaDevices_h
|
||||
|
|
|
@ -25,6 +25,13 @@
|
|||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIIDNService.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPrincipal.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsICryptoHMAC.h"
|
||||
#include "nsIKeyModule.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsILineInputStream.h"
|
||||
#include "mozilla/Types.h"
|
||||
#include "mozilla/PeerIdentity.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
|
@ -33,8 +40,11 @@
|
|||
#include "mozilla/dom/MediaStreamTrackBinding.h"
|
||||
#include "mozilla/dom/GetUserMediaRequestBinding.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/media/MediaChild.h"
|
||||
#include "MediaTrackConstraints.h"
|
||||
|
||||
#include "VideoUtils.h"
|
||||
#include "Latency.h"
|
||||
|
||||
// For PR_snprintf
|
||||
|
@ -42,6 +52,10 @@
|
|||
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nspr.h"
|
||||
#include "nss.h"
|
||||
#include "pk11pub.h"
|
||||
|
||||
/* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
|
||||
#include "MediaEngineDefault.h"
|
||||
|
@ -194,45 +208,61 @@ HostHasPermission(nsIURI &docURI)
|
|||
return false;
|
||||
}
|
||||
|
||||
ErrorCallbackRunnable::ErrorCallbackRunnable(
|
||||
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess,
|
||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
|
||||
MediaMgrError& aError,
|
||||
uint64_t aWindowID)
|
||||
: mError(&aError)
|
||||
, mWindowID(aWindowID)
|
||||
, mManager(MediaManager::GetInstance())
|
||||
/**
|
||||
* Send an error back to content.
|
||||
* Do this only on the main thread. The onSuccess callback is also passed here
|
||||
* so it can be released correctly.
|
||||
*/
|
||||
template<class SuccessCallbackType>
|
||||
class ErrorCallbackRunnable : public nsRunnable
|
||||
{
|
||||
mOnSuccess.swap(aOnSuccess);
|
||||
mOnFailure.swap(aOnFailure);
|
||||
}
|
||||
public:
|
||||
ErrorCallbackRunnable(
|
||||
nsCOMPtr<SuccessCallbackType>& aOnSuccess,
|
||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
|
||||
MediaMgrError& aError,
|
||||
uint64_t aWindowID)
|
||||
: mError(&aError)
|
||||
, mWindowID(aWindowID)
|
||||
, mManager(MediaManager::GetInstance())
|
||||
{
|
||||
mOnSuccess.swap(aOnSuccess);
|
||||
mOnFailure.swap(aOnFailure);
|
||||
}
|
||||
|
||||
ErrorCallbackRunnable::~ErrorCallbackRunnable()
|
||||
{
|
||||
MOZ_ASSERT(!mOnSuccess && !mOnFailure);
|
||||
}
|
||||
NS_IMETHODIMP
|
||||
Run()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
||||
NS_IMETHODIMP
|
||||
ErrorCallbackRunnable::Run()
|
||||
{
|
||||
// Only run if the window is still active.
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
nsCOMPtr<SuccessCallbackType> onSuccess = mOnSuccess.forget();
|
||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
|
||||
|
||||
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess = mOnSuccess.forget();
|
||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
|
||||
|
||||
if (!(mManager->IsWindowStillActive(mWindowID))) {
|
||||
// Only run if the window is still active.
|
||||
if (!(mManager->IsWindowStillActive(mWindowID))) {
|
||||
return NS_OK;
|
||||
}
|
||||
// This is safe since we're on main-thread, and the windowlist can only
|
||||
// be invalidated from the main-thread (see OnNavigation)
|
||||
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
|
||||
if (window) {
|
||||
nsRefPtr<MediaStreamError> error = new MediaStreamError(window, *mError);
|
||||
onFailure->OnError(error);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
// This is safe since we're on main-thread, and the windowlist can only
|
||||
// be invalidated from the main-thread (see OnNavigation)
|
||||
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
|
||||
if (window) {
|
||||
nsRefPtr<MediaStreamError> error = new MediaStreamError(window, *mError);
|
||||
onFailure->OnError(error);
|
||||
private:
|
||||
~ErrorCallbackRunnable()
|
||||
{
|
||||
MOZ_ASSERT(!mOnSuccess && !mOnFailure);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<SuccessCallbackType> mOnSuccess;
|
||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
|
||||
nsRefPtr<MediaMgrError> mError;
|
||||
uint64_t mWindowID;
|
||||
nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoke the "onSuccess" callback in content. The callback will take a
|
||||
|
@ -302,6 +332,50 @@ public:
|
|||
mOnFailure.swap(aOnFailure);
|
||||
}
|
||||
|
||||
nsresult
|
||||
AnonymizeId(nsAString& aId, const nsACString& aOriginKey)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIKeyObjectFactory> factory =
|
||||
do_GetService("@mozilla.org/security/keyobjectfactory;1", &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsCString rawKey;
|
||||
rv = Base64Decode(aOriginKey, rawKey);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsIKeyObject> key;
|
||||
rv = factory->KeyFromString(nsIKeyObject::HMAC, rawKey, getter_AddRefs(key));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICryptoHMAC> hasher =
|
||||
do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = hasher->Init(nsICryptoHMAC::SHA256, key);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
NS_ConvertUTF16toUTF8 id(aId);
|
||||
rv = hasher->Update(reinterpret_cast<const uint8_t*> (id.get()), id.Length());
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsCString mac;
|
||||
rv = hasher->Finish(true, mac);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
aId = NS_ConvertUTF8toUTF16(mac);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
|
@ -315,7 +389,7 @@ public:
|
|||
nsCOMPtr<nsIWritableVariant> devices =
|
||||
do_CreateInstance("@mozilla.org/variant;1");
|
||||
|
||||
int32_t len = mDevices->Length();
|
||||
size_t len = mDevices->Length();
|
||||
if (len == 0) {
|
||||
// XXX
|
||||
// We should in the future return an empty array, and dynamically add
|
||||
|
@ -331,8 +405,14 @@ public:
|
|||
}
|
||||
|
||||
nsTArray<nsIMediaDevice*> tmp(len);
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
tmp.AppendElement(mDevices->ElementAt(i));
|
||||
for (auto& device : *mDevices) {
|
||||
if (!mOriginKey.IsEmpty()) {
|
||||
nsString id;
|
||||
device->GetId(id);
|
||||
AnonymizeId(id, mOriginKey);
|
||||
device->SetId(id);
|
||||
}
|
||||
tmp.AppendElement(device);
|
||||
}
|
||||
|
||||
devices->SetAsArray(nsIDataType::VTYPE_INTERFACE,
|
||||
|
@ -346,6 +426,7 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCString mOriginKey;
|
||||
private:
|
||||
nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> mOnSuccess;
|
||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
|
||||
|
@ -516,6 +597,12 @@ MediaDevice::GetId(nsAString& aID)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDevice::SetId(const nsAString& aID)
|
||||
{
|
||||
mID.Assign(aID);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MediaDevice::GetFacingMode(nsAString& aFacingMode)
|
||||
{
|
||||
|
@ -726,6 +813,26 @@ public:
|
|||
uint32_t mPlayoutDelay;
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
MediaOperationTask::ReturnCallbackError(nsresult rv, const char* errorLog)
|
||||
{
|
||||
MM_LOG(("%s , rv=%d", errorLog, rv));
|
||||
NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(),
|
||||
mOnTracksAvailableCallback.forget()));
|
||||
nsString log;
|
||||
|
||||
log.AssignASCII(errorLog);
|
||||
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess;
|
||||
nsRefPtr<MediaMgrError> error = new MediaMgrError(
|
||||
NS_LITERAL_STRING("InternalError"), log);
|
||||
NS_DispatchToMainThread(
|
||||
new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>(onSuccess,
|
||||
mOnFailure,
|
||||
*error,
|
||||
mWindowID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MediaStream, attaches a listener and fires off a success callback
|
||||
* to the DOM with the stream. We also pass in the error callback so it can
|
||||
|
@ -1091,8 +1198,11 @@ public:
|
|||
Fail(const nsAString& aName,
|
||||
const nsAString& aMessage = EmptyString()) {
|
||||
nsRefPtr<MediaMgrError> error = new MediaMgrError(aName, aMessage);
|
||||
nsRefPtr<ErrorCallbackRunnable> runnable =
|
||||
new ErrorCallbackRunnable(mOnSuccess, mOnFailure, *error, mWindowID);
|
||||
nsRefPtr<ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>> runnable =
|
||||
new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>(mOnSuccess,
|
||||
mOnFailure,
|
||||
*error,
|
||||
mWindowID);
|
||||
// These should be empty now
|
||||
MOZ_ASSERT(!mOnSuccess);
|
||||
MOZ_ASSERT(!mOnFailure);
|
||||
|
@ -1105,7 +1215,7 @@ public:
|
|||
void
|
||||
Run()
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mOnSuccess);
|
||||
MOZ_ASSERT(mOnFailure);
|
||||
|
||||
|
@ -1309,11 +1419,30 @@ private:
|
|||
};
|
||||
#endif
|
||||
|
||||
class SanitizeDeviceIdsTask : public Task
|
||||
{
|
||||
public:
|
||||
explicit SanitizeDeviceIdsTask(int64_t aSinceWhen)
|
||||
: mSinceWhen(aSinceWhen) {}
|
||||
|
||||
void // NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
nsRefPtr<media::ChildPledge<bool>> p =
|
||||
mozilla::media::SanitizeOriginKeys(mSinceWhen); // we fire and forget
|
||||
}
|
||||
private:
|
||||
int64_t mSinceWhen;
|
||||
};
|
||||
|
||||
/**
|
||||
* Similar to GetUserMediaTask, but used for the chrome-only
|
||||
* GetUserMediaDevices function. Enumerates a list of audio & video devices,
|
||||
* wraps them up in nsIMediaDevice objects and returns it to the success
|
||||
* callback.
|
||||
*
|
||||
* All code in this class runs on the MediaManager thread.
|
||||
*/
|
||||
class GetUserMediaDevicesTask : public Task
|
||||
{
|
||||
|
@ -1323,7 +1452,8 @@ public:
|
|||
already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aOnSuccess,
|
||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aOnFailure,
|
||||
uint64_t aWindowId, nsACString& aAudioLoopbackDev,
|
||||
nsACString& aVideoLoopbackDev, bool aUseFakeDevices)
|
||||
nsACString& aVideoLoopbackDev, bool aPrivileged, const nsACString& aOrigin,
|
||||
bool aInPrivateBrowsing, bool aUseFakeDevices)
|
||||
: mConstraints(aConstraints)
|
||||
, mOnSuccess(aOnSuccess)
|
||||
, mOnFailure(aOnFailure)
|
||||
|
@ -1331,12 +1461,15 @@ public:
|
|||
, mWindowId(aWindowId)
|
||||
, mLoopbackAudioDevice(aAudioLoopbackDev)
|
||||
, mLoopbackVideoDevice(aVideoLoopbackDev)
|
||||
, mPrivileged(aPrivileged)
|
||||
, mOrigin(aOrigin)
|
||||
, mInPrivateBrowsing(aInPrivateBrowsing)
|
||||
, mUseFakeDevices(aUseFakeDevices) {}
|
||||
|
||||
void // NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
nsRefPtr<MediaEngine> backend;
|
||||
if (mConstraints.mFake || mUseFakeDevices)
|
||||
|
@ -1346,28 +1479,48 @@ public:
|
|||
|
||||
typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
|
||||
|
||||
ScopedDeletePtr<SourceSet> final(new SourceSet);
|
||||
ScopedDeletePtr<SourceSet> result(new SourceSet);
|
||||
if (IsOn(mConstraints.mVideo)) {
|
||||
nsTArray<nsRefPtr<VideoDevice>> s;
|
||||
nsTArray<nsRefPtr<VideoDevice>> sources;
|
||||
GetSources(backend, GetInvariant(mConstraints.mVideo),
|
||||
&MediaEngine::EnumerateVideoDevices, s, mLoopbackVideoDevice.get());
|
||||
for (uint32_t i = 0; i < s.Length(); i++) {
|
||||
final->AppendElement(s[i]);
|
||||
&MediaEngine::EnumerateVideoDevices, sources,
|
||||
mLoopbackVideoDevice.get());
|
||||
for (auto& source : sources) {
|
||||
result->AppendElement(source);
|
||||
}
|
||||
}
|
||||
if (IsOn(mConstraints.mAudio)) {
|
||||
nsTArray<nsRefPtr<AudioDevice>> s;
|
||||
nsTArray<nsRefPtr<AudioDevice>> sources;
|
||||
GetSources(backend, GetInvariant(mConstraints.mAudio),
|
||||
&MediaEngine::EnumerateAudioDevices, s, mLoopbackAudioDevice.get());
|
||||
for (uint32_t i = 0; i < s.Length(); i++) {
|
||||
final->AppendElement(s[i]);
|
||||
&MediaEngine::EnumerateAudioDevices, sources,
|
||||
mLoopbackAudioDevice.get());
|
||||
for (auto& source : sources) {
|
||||
result->AppendElement(source);
|
||||
}
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mWindowId,
|
||||
mOnSuccess, mOnFailure,
|
||||
final.forget()));
|
||||
// DeviceSuccessCallbackRunnable should have taken these.
|
||||
// In the case of failure with this newly allocated runnable, we
|
||||
// intentionally leak the runnable, because we've pawned mOnSuccess and
|
||||
// mOnFailure onto it which are main thread objects unsafe to release here.
|
||||
DeviceSuccessCallbackRunnable* runnable =
|
||||
new DeviceSuccessCallbackRunnable(mWindowId, mOnSuccess, mOnFailure,
|
||||
result.forget());
|
||||
if (mPrivileged) {
|
||||
NS_DispatchToMainThread(runnable);
|
||||
} else {
|
||||
// Get persistent origin-unique uuid to anonymize deviceIds back on main.
|
||||
//
|
||||
// GetOriginKey is an async API that returns a pledge (as promise-like
|
||||
// pattern). We use .Then() to pass in a lambda to run back on this
|
||||
// thread once GetOriginKey resolves asynchronously . The "runnable"
|
||||
// pointer is "captured" (passed by value) into the lambda.
|
||||
nsRefPtr<media::ChildPledge<nsCString>> p =
|
||||
media::GetOriginKey(mOrigin, mInPrivateBrowsing);
|
||||
p->Then([runnable](nsCString result) mutable {
|
||||
runnable->mOriginKey = result;
|
||||
NS_DispatchToMainThread(runnable);
|
||||
});
|
||||
}
|
||||
// One of the Runnables have taken these.
|
||||
MOZ_ASSERT(!mOnSuccess && !mOnFailure);
|
||||
}
|
||||
|
||||
|
@ -1383,6 +1536,9 @@ private:
|
|||
// automated media tests only.
|
||||
nsCString mLoopbackAudioDevice;
|
||||
nsCString mLoopbackVideoDevice;
|
||||
bool mPrivileged;
|
||||
nsCString mOrigin;
|
||||
bool mInPrivateBrowsing;
|
||||
bool mUseFakeDevices;
|
||||
};
|
||||
|
||||
|
@ -1411,6 +1567,16 @@ NS_IMPL_ISUPPORTS(MediaManager, nsIMediaManagerService, nsIObserver)
|
|||
|
||||
/* static */ StaticRefPtr<MediaManager> MediaManager::sSingleton;
|
||||
|
||||
#ifdef DEBUG
|
||||
/* static */ bool
|
||||
MediaManager::IsInMediaThread()
|
||||
{
|
||||
return sSingleton?
|
||||
(sSingleton->mMediaThread->thread_id() == PlatformThread::CurrentId()) :
|
||||
false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
|
||||
// thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
|
||||
// from MediaManager thread.
|
||||
|
@ -1577,16 +1743,8 @@ MediaManager::GetUserMedia(
|
|||
#endif //MOZ_B2G
|
||||
}
|
||||
|
||||
// Store the WindowID in a hash table and mark as active. The entry is removed
|
||||
// when this window is closed or navigated away from.
|
||||
uint64_t windowID = aWindow->WindowID();
|
||||
// This is safe since we're on main-thread, and the windowlist can only
|
||||
// be invalidated from the main-thread (see OnNavigation)
|
||||
StreamListeners* listeners = GetActiveWindows()->Get(windowID);
|
||||
if (!listeners) {
|
||||
listeners = new StreamListeners;
|
||||
GetActiveWindows()->Put(windowID, listeners);
|
||||
}
|
||||
StreamListeners* listeners = AddWindowID(windowID);
|
||||
|
||||
// Create a disabled listener to act as a placeholder
|
||||
GetUserMediaCallbackMediaStreamListener* listener =
|
||||
|
@ -1801,7 +1959,8 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
|
|||
const MediaStreamConstraints& aConstraints,
|
||||
nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
|
||||
nsIDOMGetUserMediaErrorCallback* aOnFailure,
|
||||
uint64_t aInnerWindowID)
|
||||
uint64_t aInnerWindowID,
|
||||
bool aPrivileged)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
||||
|
@ -1819,15 +1978,40 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
|
|||
bool useFakeStreams =
|
||||
Preferences::GetBool("media.navigator.streams.fake", false);
|
||||
|
||||
nsCString origin;
|
||||
nsPrincipal::GetOriginForURI(aWindow->GetDocumentURI(),
|
||||
getter_Copies(origin));
|
||||
bool inPrivateBrowsing;
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc = aWindow->GetDoc();
|
||||
nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
|
||||
inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
|
||||
}
|
||||
MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
|
||||
new GetUserMediaDevicesTask(
|
||||
aConstraints, onSuccess.forget(), onFailure.forget(),
|
||||
(aInnerWindowID ? aInnerWindowID : aWindow->WindowID()),
|
||||
loopbackAudioDevice, loopbackVideoDevice, useFakeStreams));
|
||||
loopbackAudioDevice, loopbackVideoDevice, aPrivileged, origin,
|
||||
inPrivateBrowsing, useFakeStreams));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaManager::EnumerateDevices(nsPIDOMWindow* aWindow,
|
||||
nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
|
||||
nsIDOMGetUserMediaErrorCallback* aOnFailure)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
||||
MediaStreamConstraints c;
|
||||
c.mVideo.SetAsBoolean() = true;
|
||||
c.mAudio.SetAsBoolean() = true;
|
||||
|
||||
AddWindowID(aWindow->WindowID());
|
||||
return GetUserMediaDevices(aWindow, c, aOnSuccess, aOnFailure, 0, false);
|
||||
}
|
||||
|
||||
MediaEngine*
|
||||
MediaManager::GetBackend(uint64_t aWindowId)
|
||||
{
|
||||
|
@ -1896,6 +2080,22 @@ MediaManager::OnNavigation(uint64_t aWindowID)
|
|||
}
|
||||
}
|
||||
|
||||
StreamListeners*
|
||||
MediaManager::AddWindowID(uint64_t aWindowId)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
// Store the WindowID in a hash table and mark as active. The entry is removed
|
||||
// when this window is closed or navigated away from.
|
||||
// This is safe since we're on main-thread, and the windowlist can only
|
||||
// be invalidated from the main-thread (see OnNavigation)
|
||||
StreamListeners* listeners = GetActiveWindows()->Get(aWindowId);
|
||||
if (!listeners) {
|
||||
listeners = new StreamListeners;
|
||||
GetActiveWindows()->Put(aWindowId, listeners);
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
|
||||
void
|
||||
MediaManager::RemoveWindowID(uint64_t aWindowId)
|
||||
{
|
||||
|
@ -2009,8 +2209,34 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
prefs->RemoveObserver("media.navigator.video.default_minfps", this);
|
||||
}
|
||||
|
||||
// Close off any remaining active windows.
|
||||
// Because mMediaThread is not an nsThread, we must dispatch to it so it can
|
||||
// clean up BackgroundChild. Continue stopping thread once this is done.
|
||||
|
||||
class ShutdownTask : public Task
|
||||
{
|
||||
public:
|
||||
explicit ShutdownTask(nsRunnable* aReply) : mReply(aReply) {}
|
||||
private:
|
||||
virtual void
|
||||
Run()
|
||||
{
|
||||
MOZ_ASSERT(MediaManager::IsInMediaThread());
|
||||
mozilla::ipc::BackgroundChild::CloseForCurrentThread();
|
||||
NS_DispatchToMainThread(mReply);
|
||||
}
|
||||
nsRefPtr<nsRunnable> mReply;
|
||||
};
|
||||
|
||||
// Post ShutdownTask to execute on mMediaThread and pass in a lambda
|
||||
// callback to be executed back on this thread once it is done.
|
||||
//
|
||||
// The lambda callback "captures" the 'this' pointer for member access.
|
||||
// This is safe since this is guaranteed to be here since sSingleton isn't
|
||||
// cleared until the lambda function clears it.
|
||||
|
||||
MediaManager::GetMessageLoop()->PostTask(FROM_HERE, new ShutdownTask(
|
||||
media::CallbackRunnable::New([this]() mutable {
|
||||
// Close off any remaining active windows.
|
||||
MutexAutoLock lock(mMutex);
|
||||
GetActiveWindows()->Clear();
|
||||
mActiveCallbacks.Clear();
|
||||
|
@ -2022,8 +2248,8 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
mMediaThread->Stop();
|
||||
}
|
||||
mBackend = nullptr;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
})));
|
||||
return NS_OK;
|
||||
|
||||
} else if (!strcmp(aTopic, "getUserMedia:response:allow")) {
|
||||
|
@ -2259,6 +2485,17 @@ MediaManager::MediaCaptureWindowState(nsIDOMWindow* aWindow, bool* aVideo,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MediaManager::SanitizeDeviceIds(int64_t aSinceWhen)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
LOG(("%s: sinceWhen = %llu", __FUNCTION__, aSinceWhen));
|
||||
|
||||
MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
|
||||
new SanitizeDeviceIdsTask(aSinceWhen));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
StopScreensharingCallback(MediaManager *aThis,
|
||||
uint64_t aWindowID,
|
||||
|
@ -2345,6 +2582,24 @@ MediaManager::StopMediaStreams()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MediaManager::IsWindowActivelyCapturing(uint64_t aWindowId)
|
||||
{
|
||||
nsCOMPtr<nsISupportsArray> array;
|
||||
GetActiveMediaCaptureWindows(getter_AddRefs(array));
|
||||
uint32_t len;
|
||||
array->Count(&len);
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
nsCOMPtr<nsISupports> window;
|
||||
array->GetElementAt(i, getter_AddRefs(window));
|
||||
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(window));
|
||||
if (win && win->WindowID() == aWindowId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
GetUserMediaCallbackMediaStreamListener::AudioConfig(bool aEchoOn,
|
||||
uint32_t aEcho,
|
||||
|
|
|
@ -300,29 +300,6 @@ typedef enum {
|
|||
class MediaManager;
|
||||
class GetUserMediaTask;
|
||||
|
||||
/**
|
||||
* Send an error back to content.
|
||||
* Do this only on the main thread. The onSuccess callback is also passed here
|
||||
* so it can be released correctly.
|
||||
*/
|
||||
class ErrorCallbackRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ErrorCallbackRunnable(
|
||||
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess,
|
||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
|
||||
MediaMgrError& aError, uint64_t aWindowID);
|
||||
NS_IMETHOD Run();
|
||||
private:
|
||||
~ErrorCallbackRunnable();
|
||||
|
||||
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
|
||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
|
||||
nsRefPtr<MediaMgrError> mError;
|
||||
uint64_t mWindowID;
|
||||
nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
|
||||
};
|
||||
|
||||
class ReleaseMediaOperationResource : public nsRunnable
|
||||
{
|
||||
public:
|
||||
|
@ -369,20 +346,7 @@ public:
|
|||
}
|
||||
|
||||
void
|
||||
ReturnCallbackError(nsresult rv, const char* errorLog)
|
||||
{
|
||||
MM_LOG(("%s , rv=%d", errorLog, rv));
|
||||
NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(),
|
||||
mOnTracksAvailableCallback.forget()));
|
||||
nsString log;
|
||||
|
||||
log.AssignASCII(errorLog);
|
||||
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess;
|
||||
nsRefPtr<MediaMgrError> error = new MediaMgrError(
|
||||
NS_LITERAL_STRING("InternalError"), log);
|
||||
NS_DispatchToMainThread(new ErrorCallbackRunnable(onSuccess, mOnFailure,
|
||||
*error, mWindowID));
|
||||
}
|
||||
ReturnCallbackError(nsresult rv, const char* errorLog);
|
||||
|
||||
void
|
||||
Run()
|
||||
|
@ -503,6 +467,7 @@ public:
|
|||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIMEDIADEVICE
|
||||
|
||||
void SetId(const nsAString& aID);
|
||||
protected:
|
||||
virtual ~MediaDevice() {}
|
||||
explicit MediaDevice(MediaEngineSource* aSource);
|
||||
|
@ -556,6 +521,9 @@ public:
|
|||
static MediaManager* Get();
|
||||
static MediaManager* GetIfExists();
|
||||
static MessageLoop* GetMessageLoop();
|
||||
#ifdef DEBUG
|
||||
static bool IsInMediaThread();
|
||||
#endif
|
||||
|
||||
static bool Exists()
|
||||
{
|
||||
|
@ -595,12 +563,21 @@ public:
|
|||
const dom::MediaStreamConstraints& aConstraints,
|
||||
nsIGetUserMediaDevicesSuccessCallback* onSuccess,
|
||||
nsIDOMGetUserMediaErrorCallback* onError,
|
||||
uint64_t aInnerWindowID = 0);
|
||||
uint64_t aInnerWindowID = 0,
|
||||
bool aPrivileged = true);
|
||||
|
||||
nsresult EnumerateDevices(nsPIDOMWindow* aWindow,
|
||||
nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
|
||||
nsIDOMGetUserMediaErrorCallback* aOnFailure);
|
||||
|
||||
nsresult EnumerateDevices(nsPIDOMWindow* aWindow, dom::Promise& aPromise);
|
||||
void OnNavigation(uint64_t aWindowID);
|
||||
bool IsWindowActivelyCapturing(uint64_t aWindowId);
|
||||
|
||||
MediaEnginePrefs mPrefs;
|
||||
|
||||
private:
|
||||
StreamListeners* AddWindowID(uint64_t aWindowId);
|
||||
WindowTable *GetActiveWindows() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
|
||||
return &mActiveWindows;
|
||||
|
@ -628,6 +605,7 @@ private:
|
|||
WindowTable mActiveWindows;
|
||||
nsClassHashtable<nsStringHashKey, GetUserMediaTask> mActiveCallbacks;
|
||||
nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds;
|
||||
|
||||
// Always exists
|
||||
nsAutoPtr<base::Thread> mMediaThread;
|
||||
|
||||
|
|
|
@ -261,18 +261,16 @@ ExtractH264CodecDetails(const nsAString& aCodec,
|
|||
}
|
||||
|
||||
nsresult
|
||||
GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength)
|
||||
GenerateRandomName(nsCString& aOutSalt, uint32_t aLength)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIRandomGenerator> rg =
|
||||
do_GetService("@mozilla.org/security/random-generator;1", &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// For each three bytes of random data we will get four bytes of
|
||||
// ASCII. Request a bit more to be safe and truncate to the length
|
||||
// we want at the end.
|
||||
// For each three bytes of random data we will get four bytes of ASCII.
|
||||
const uint32_t requiredBytesLength =
|
||||
static_cast<uint32_t>((aLength + 1) / 4 * 3);
|
||||
static_cast<uint32_t>((aLength + 3) / 4 * 3);
|
||||
|
||||
uint8_t* buffer;
|
||||
rv = rg->GenerateRandomBytes(requiredBytesLength, &buffer);
|
||||
|
@ -286,14 +284,19 @@ GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength)
|
|||
buffer = nullptr;
|
||||
if (NS_FAILED (rv)) return rv;
|
||||
|
||||
temp.Truncate(aLength);
|
||||
aOutSalt = temp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength)
|
||||
{
|
||||
nsresult rv = GenerateRandomName(aOutSalt, aLength);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// Base64 characters are alphanumeric (a-zA-Z0-9) and '+' and '/', so we need
|
||||
// to replace illegal characters -- notably '/'
|
||||
temp.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
|
||||
|
||||
aOutSalt = temp;
|
||||
|
||||
aOutSalt.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -261,7 +261,11 @@ ExtractH264CodecDetails(const nsAString& aCodecs,
|
|||
int16_t& aLevel);
|
||||
|
||||
// Use a cryptographic quality PRNG to generate raw random bytes
|
||||
// and convert that to a base64 string suitable for use as a file or URL
|
||||
// and convert that to a base64 string.
|
||||
nsresult
|
||||
GenerateRandomName(nsCString& aOutSalt, uint32_t aLength);
|
||||
|
||||
// This version returns a string suitable for use as a file or URL
|
||||
// path. This is based on code from nsExternalAppHandler::SetUpTempFile.
|
||||
nsresult
|
||||
GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength);
|
||||
|
|
|
@ -140,7 +140,7 @@ nsresult
|
|||
GMPAudioDecoderParent::Close()
|
||||
{
|
||||
LOGD(("%s: %p", __FUNCTION__, this));
|
||||
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
|
||||
MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
|
||||
|
||||
// Consumer is done with us; we can shut down. No more callbacks should
|
||||
// be made to mCallback. Note: do this before Shutdown()!
|
||||
|
@ -160,7 +160,7 @@ nsresult
|
|||
GMPAudioDecoderParent::Shutdown()
|
||||
{
|
||||
LOGD(("%s: %p", __FUNCTION__, this));
|
||||
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
|
||||
MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
|
||||
|
||||
if (mShuttingDown) {
|
||||
return NS_OK;
|
||||
|
|
|
@ -146,6 +146,7 @@ EXPORTS.mozilla.dom += [
|
|||
'AudioTrack.h',
|
||||
'AudioTrackList.h',
|
||||
'GetUserMediaRequest.h',
|
||||
'MediaDeviceInfo.h',
|
||||
'MediaDevices.h',
|
||||
'MediaStreamError.h',
|
||||
'MediaStreamTrack.h',
|
||||
|
@ -183,6 +184,7 @@ UNIFIED_SOURCES += [
|
|||
'MediaDecoder.cpp',
|
||||
'MediaDecoderReader.cpp',
|
||||
'MediaDecoderStateMachine.cpp',
|
||||
'MediaDeviceInfo.cpp',
|
||||
'MediaDevices.cpp',
|
||||
'MediaManager.cpp',
|
||||
'MediaRecorder.cpp',
|
||||
|
|
|
@ -12,7 +12,7 @@ interface nsIDOMWindow;
|
|||
#define MEDIAMANAGERSERVICE_CONTRACTID "@mozilla.org/mediaManagerService;1"
|
||||
%}
|
||||
|
||||
[scriptable, builtinclass, uuid(9b10661f-77b3-47f7-a8de-ee58daaff5d2)]
|
||||
[scriptable, builtinclass, uuid(24b23e01-33fd-401f-ba25-6e52658750b0)]
|
||||
interface nsIMediaManagerService : nsISupports
|
||||
{
|
||||
/* return a array of inner windows that have active captures */
|
||||
|
@ -22,4 +22,8 @@ interface nsIMediaManagerService : nsISupports
|
|||
void mediaCaptureWindowState(in nsIDOMWindow aWindow, out boolean aVideo, out boolean aAudio,
|
||||
[optional] out boolean aScreenShare, [optional] out boolean aWindowShare,
|
||||
[optional] out boolean aAppShare, [optional] out boolean aBrowserShare);
|
||||
|
||||
/* Clear per-orgin list of persistent DeviceIds stored for enumerateDevices
|
||||
sinceTime is milliseconds since 1 January 1970 00:00:00 UTC. 0 = clear all */
|
||||
void sanitizeDeviceIds(in long long sinceWhen);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* 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 "MediaChild.h"
|
||||
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "mozilla/MediaManager.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#undef LOG
|
||||
#if defined(PR_LOGGING)
|
||||
PRLogModuleInfo *gMediaChildLog;
|
||||
#define LOG(args) PR_LOG(gMediaChildLog, PR_LOG_DEBUG, args)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#endif
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace media {
|
||||
|
||||
static Child* sChild;
|
||||
|
||||
template<typename ValueType> void
|
||||
ChildPledge<ValueType>::ActorCreated(PBackgroundChild* aActor)
|
||||
{
|
||||
if (!sChild) {
|
||||
// Create PMedia by sending a message to the parent
|
||||
sChild = static_cast<Child*>(aActor->SendPMediaConstructor());
|
||||
}
|
||||
Run(sChild);
|
||||
}
|
||||
|
||||
template<typename ValueType> void
|
||||
ChildPledge<ValueType>::ActorFailed()
|
||||
{
|
||||
Pledge<ValueType>::Reject(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
template<typename ValueType> NS_IMPL_ADDREF(ChildPledge<ValueType>)
|
||||
template<typename ValueType> NS_IMPL_RELEASE(ChildPledge<ValueType>)
|
||||
template<typename ValueType> NS_INTERFACE_MAP_BEGIN(ChildPledge<ValueType>)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
already_AddRefed<ChildPledge<nsCString>>
|
||||
GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing)
|
||||
{
|
||||
class Pledge : public ChildPledge<nsCString>
|
||||
{
|
||||
public:
|
||||
explicit Pledge(const nsCString& aOrigin, bool aPrivateBrowsing)
|
||||
: mOrigin(aOrigin), mPrivateBrowsing(aPrivateBrowsing) {}
|
||||
private:
|
||||
~Pledge() {}
|
||||
void Run(PMediaChild* aChild)
|
||||
{
|
||||
Child* child = static_cast<Child*>(aChild);
|
||||
|
||||
uint32_t id = child->AddRequestPledge(*this);
|
||||
child->SendGetOriginKey(id, mOrigin, mPrivateBrowsing);
|
||||
}
|
||||
const nsCString mOrigin;
|
||||
const bool mPrivateBrowsing;
|
||||
};
|
||||
|
||||
nsRefPtr<ChildPledge<nsCString>> p = new Pledge(aOrigin, aPrivateBrowsing);
|
||||
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> cb = do_QueryObject(p);
|
||||
bool ok = ipc::BackgroundChild::GetOrCreateForCurrentThread(cb);
|
||||
MOZ_RELEASE_ASSERT(ok);
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<ChildPledge<bool>>
|
||||
SanitizeOriginKeys(const uint64_t& aSinceWhen)
|
||||
{
|
||||
class Pledge : public ChildPledge<bool>
|
||||
{
|
||||
public:
|
||||
explicit Pledge(const uint64_t& aSinceWhen) : mSinceWhen(aSinceWhen) {}
|
||||
private:
|
||||
void Run(PMediaChild* aMedia)
|
||||
{
|
||||
aMedia->SendSanitizeOriginKeys(mSinceWhen);
|
||||
mValue = true;
|
||||
LOG(("SanitizeOriginKeys since %llu", mSinceWhen));
|
||||
Resolve();
|
||||
}
|
||||
const uint64_t mSinceWhen;
|
||||
};
|
||||
|
||||
nsRefPtr<ChildPledge<bool>> p = new Pledge(aSinceWhen);
|
||||
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> cb = do_QueryObject(p);
|
||||
bool ok = ipc::BackgroundChild::GetOrCreateForCurrentThread(cb);
|
||||
MOZ_RELEASE_ASSERT(ok);
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
Child::Child()
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!gMediaChildLog) {
|
||||
gMediaChildLog = PR_NewLogModule("MediaChild");
|
||||
}
|
||||
#endif
|
||||
LOG(("media::Child: %p", this));
|
||||
MOZ_COUNT_CTOR(Child);
|
||||
}
|
||||
|
||||
Child::~Child()
|
||||
{
|
||||
LOG(("~media::Child: %p", this));
|
||||
sChild = nullptr;
|
||||
MOZ_COUNT_DTOR(Child);
|
||||
}
|
||||
|
||||
uint32_t Child::sRequestCounter = 0;
|
||||
|
||||
uint32_t
|
||||
Child::AddRequestPledge(ChildPledge<nsCString>& aPledge)
|
||||
{
|
||||
uint32_t id = ++sRequestCounter;
|
||||
nsRefPtr<ChildPledge<nsCString>> ptr(&aPledge);
|
||||
mRequestPledges.AppendElement(PledgeEntry(id, ptr));
|
||||
return id;
|
||||
}
|
||||
|
||||
already_AddRefed<ChildPledge<nsCString>>
|
||||
Child::RemoveRequestPledge(uint32_t aRequestId)
|
||||
{
|
||||
for (PledgeEntry& entry : mRequestPledges) {
|
||||
if (entry.first == aRequestId) {
|
||||
nsRefPtr<ChildPledge<nsCString>> ref;
|
||||
ref.swap(entry.second);
|
||||
mRequestPledges.RemoveElement(entry);
|
||||
return ref.forget();
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT_UNREACHABLE("Received response with no matching media::ChildPledge!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
Child::RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey)
|
||||
{
|
||||
nsRefPtr<ChildPledge<nsCString>> pledge = RemoveRequestPledge(aRequestId);
|
||||
if (pledge) {
|
||||
pledge->Resolve(aKey);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PMediaChild*
|
||||
AllocPMediaChild()
|
||||
{
|
||||
Child* obj = new Child();
|
||||
obj->AddRef();
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool
|
||||
DeallocPMediaChild(media::PMediaChild *aActor)
|
||||
{
|
||||
static_cast<Child*>(aActor)->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* 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_MediaChild_h
|
||||
#define mozilla_MediaChild_h
|
||||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/media/PMediaChild.h"
|
||||
#include "mozilla/media/PMediaParent.h"
|
||||
#include "nsIIPCBackgroundChildCreateCallback.h"
|
||||
#include "MediaUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace media {
|
||||
|
||||
// media::Child implements proxying to the chrome process for some media-related
|
||||
// functions, for the moment just:
|
||||
//
|
||||
// GetOriginKey() - get a cookie-like persisted unique key for a given origin.
|
||||
// SanitizeOriginKeys() - reset persisted unique keys.
|
||||
|
||||
// GetOriginKey and SanitizeOriginKeys are asynchronous APIs that return pledges
|
||||
// (promise-like objects) with the future value. Use pledge.Then(func) to access.
|
||||
|
||||
class Child;
|
||||
|
||||
template<typename ValueType>
|
||||
class ChildPledge : public Pledge<ValueType>,
|
||||
public nsIIPCBackgroundChildCreateCallback
|
||||
{
|
||||
friend Child;
|
||||
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
|
||||
NS_DECL_ISUPPORTS
|
||||
public:
|
||||
explicit ChildPledge() {};
|
||||
protected:
|
||||
virtual ~ChildPledge() {}
|
||||
virtual void Run(PMediaChild* aMedia) = 0;
|
||||
};
|
||||
|
||||
already_AddRefed<ChildPledge<nsCString>>
|
||||
GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing);
|
||||
|
||||
already_AddRefed<ChildPledge<bool>>
|
||||
SanitizeOriginKeys(const uint64_t& aSinceWhen);
|
||||
|
||||
class Child : public PMediaChild
|
||||
{
|
||||
NS_INLINE_DECL_REFCOUNTING(Child)
|
||||
public:
|
||||
Child();
|
||||
|
||||
bool RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey);
|
||||
|
||||
uint32_t AddRequestPledge(ChildPledge<nsCString>& aPledge);
|
||||
already_AddRefed<ChildPledge<nsCString>> RemoveRequestPledge(uint32_t aRequestId);
|
||||
private:
|
||||
virtual ~Child();
|
||||
|
||||
typedef std::pair<uint32_t,nsRefPtr<ChildPledge<nsCString>>> PledgeEntry;
|
||||
static uint32_t sRequestCounter;
|
||||
nsTArray<PledgeEntry> mRequestPledges;
|
||||
};
|
||||
|
||||
PMediaChild* AllocPMediaChild();
|
||||
bool DeallocPMediaChild(PMediaChild *aActor);
|
||||
|
||||
} // namespace media
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_MediaChild_h
|
|
@ -0,0 +1,456 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* 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 "MediaParent.h"
|
||||
|
||||
#include "mozilla/Base64.h"
|
||||
#include <mozilla/StaticMutex.h>
|
||||
|
||||
#include "MediaUtils.h"
|
||||
#include "MediaEngine.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsILineInputStream.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#undef LOG
|
||||
#if defined(PR_LOGGING)
|
||||
PRLogModuleInfo *gMediaParentLog;
|
||||
#define LOG(args) PR_LOG(gMediaParentLog, PR_LOG_DEBUG, args)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#endif
|
||||
|
||||
// A file in the profile dir is used to persist mOriginKeys used to anonymize
|
||||
// deviceIds to be unique per origin, to avoid them being supercookies.
|
||||
|
||||
#define ORIGINKEYS_FILE "enumerate_devices.txt"
|
||||
#define ORIGINKEYS_VERSION "1"
|
||||
|
||||
namespace mozilla {
|
||||
namespace media {
|
||||
|
||||
static StaticMutex gMutex;
|
||||
static ParentSingleton* sParentSingleton = nullptr;
|
||||
|
||||
class ParentSingleton : public nsISupports
|
||||
{
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
class OriginKey
|
||||
{
|
||||
public:
|
||||
static const size_t DecodedLength = 18;
|
||||
|
||||
OriginKey(const nsACString& aKey, int64_t aSecondsStamp)
|
||||
: mKey(aKey)
|
||||
, mSecondsStamp(aSecondsStamp) {}
|
||||
|
||||
nsCString mKey; // Base64 encoded.
|
||||
int64_t mSecondsStamp;
|
||||
};
|
||||
|
||||
class OriginKeysTable
|
||||
{
|
||||
public:
|
||||
OriginKeysTable() {}
|
||||
|
||||
nsresult
|
||||
GetOriginKey(const nsACString& aOrigin, nsCString& result)
|
||||
{
|
||||
OriginKey* key;
|
||||
if (!mKeys.Get(aOrigin, &key)) {
|
||||
nsCString salt; // Make a new one
|
||||
nsresult rv = GenerateRandomName(salt, key->DecodedLength * 4 / 3);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
key = new OriginKey(salt, PR_Now() / PR_USEC_PER_SEC);
|
||||
mKeys.Put(aOrigin, key);
|
||||
}
|
||||
result = key->mKey;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
HashCleaner(const nsACString& aOrigin, nsAutoPtr<OriginKey>& aOriginKey,
|
||||
void *aUserArg)
|
||||
{
|
||||
OriginKey* since = static_cast<OriginKey*>(aUserArg);
|
||||
|
||||
LOG((((aOriginKey->mSecondsStamp >= since->mSecondsStamp)?
|
||||
"%s: REMOVE %lld >= %lld" :
|
||||
"%s: KEEP %lld < %lld"),
|
||||
__FUNCTION__, aOriginKey->mSecondsStamp, since->mSecondsStamp));
|
||||
|
||||
return (aOriginKey->mSecondsStamp >= since->mSecondsStamp)?
|
||||
PL_DHASH_REMOVE : PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void Clear(int64_t aSinceWhen)
|
||||
{
|
||||
// Avoid int64_t* <-> void* casting offset
|
||||
OriginKey since(nsCString(), aSinceWhen / PR_USEC_PER_SEC);
|
||||
mKeys.Enumerate(HashCleaner, &since);
|
||||
}
|
||||
|
||||
protected:
|
||||
nsClassHashtable<nsCStringHashKey, OriginKey> mKeys;
|
||||
};
|
||||
|
||||
class OriginKeysLoader : public OriginKeysTable
|
||||
{
|
||||
public:
|
||||
OriginKeysLoader()
|
||||
{
|
||||
Load();
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetOriginKey(const nsACString& aOrigin, nsCString& result)
|
||||
{
|
||||
auto before = mKeys.Count();
|
||||
OriginKeysTable::GetOriginKey(aOrigin, result);
|
||||
if (mKeys.Count() != before) {
|
||||
Save();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIFile>
|
||||
GetFile()
|
||||
{
|
||||
if (!mProfileDir) {
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
||||
getter_AddRefs(mProfileDir));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIFile> file;
|
||||
nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
file->Append(NS_LITERAL_STRING(ORIGINKEYS_FILE));
|
||||
return file.forget();
|
||||
}
|
||||
|
||||
// Format of file is key secondsstamp origin (first line is version #):
|
||||
//
|
||||
// 1
|
||||
// rOMAAbFujNwKyIpj4RJ3Wt5Q 1424733961 http://fiddle.jshell.net
|
||||
// rOMAAbFujNwKyIpj4RJ3Wt5Q 1424734841 http://mozilla.github.io
|
||||
// etc.
|
||||
|
||||
nsresult Read()
|
||||
{
|
||||
nsCOMPtr<nsIFile> file = GetFile();
|
||||
if (NS_WARN_IF(!file)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
bool exists;
|
||||
nsresult rv = file->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
if (!exists) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsILineInputStream> i = do_QueryInterface(stream);
|
||||
MOZ_ASSERT(i);
|
||||
|
||||
nsCString line;
|
||||
bool hasMoreLines;
|
||||
rv = i->ReadLine(line, &hasMoreLines);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
if (!line.EqualsLiteral(ORIGINKEYS_VERSION)) {
|
||||
// If version on disk is newer than we can understand then ignore it.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
while (hasMoreLines) {
|
||||
rv = i->ReadLine(line, &hasMoreLines);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
// Read key secondsstamp origin.
|
||||
// Ignore any lines that don't fit format in the comment above exactly.
|
||||
int32_t f = line.FindChar(' ');
|
||||
if (f < 0) {
|
||||
continue;
|
||||
}
|
||||
const nsACString& key = Substring(line, 0, f);
|
||||
const nsACString& s = Substring(line, f+1);
|
||||
f = s.FindChar(' ');
|
||||
if (f < 0) {
|
||||
continue;
|
||||
}
|
||||
int64_t secondsstamp = nsCString(Substring(s, 0, f)).ToInteger64(&rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
const nsACString& origin = Substring(s, f+1);
|
||||
|
||||
// Validate key
|
||||
if (key.Length() != OriginKey::DecodedLength) {
|
||||
continue;
|
||||
}
|
||||
nsCString dummy;
|
||||
rv = Base64Decode(key, dummy);
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
mKeys.Put(origin, new OriginKey(key, secondsstamp));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
HashWriter(const nsACString& aOrigin, OriginKey* aOriginKey, void *aUserArg)
|
||||
{
|
||||
auto* stream = static_cast<nsIOutputStream *>(aUserArg);
|
||||
|
||||
nsCString buffer;
|
||||
buffer.Append(aOriginKey->mKey);
|
||||
buffer.Append(' ');
|
||||
buffer.AppendInt(aOriginKey->mSecondsStamp);
|
||||
buffer.Append(' ');
|
||||
buffer.Append(aOrigin);
|
||||
buffer.Append('\n');
|
||||
|
||||
uint32_t count;
|
||||
nsresult rv = stream->Write(buffer.Data(), buffer.Length(), &count);
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || count != buffer.Length()) {
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Write()
|
||||
{
|
||||
nsCOMPtr<nsIFile> file = GetFile();
|
||||
if (NS_WARN_IF(!file)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOutputStream> stream;
|
||||
nsresult rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
nsAutoCString buffer;
|
||||
buffer.AppendLiteral(ORIGINKEYS_VERSION);
|
||||
buffer.Append('\n');
|
||||
|
||||
uint32_t count;
|
||||
rv = stream->Write(buffer.Data(), buffer.Length(), &count);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
if (count != buffer.Length()) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
mKeys.EnumerateRead(HashWriter, stream.get());
|
||||
|
||||
nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
|
||||
MOZ_ASSERT(safeStream);
|
||||
|
||||
rv = safeStream->Finish();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Load()
|
||||
{
|
||||
nsresult rv = Read();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Delete();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult Save()
|
||||
{
|
||||
nsresult rv = Write();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
NS_WARNING("Failed to write data for EnumerateDevices id-persistence.");
|
||||
Delete();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void Clear(int64_t aSinceWhen)
|
||||
{
|
||||
OriginKeysTable::Clear(aSinceWhen);
|
||||
Delete();
|
||||
Save();
|
||||
}
|
||||
|
||||
nsresult Delete()
|
||||
{
|
||||
nsCOMPtr<nsIFile> file = GetFile();
|
||||
if (NS_WARN_IF(!file)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
nsresult rv = file->Remove(false);
|
||||
if (rv == NS_ERROR_FILE_NOT_FOUND) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIFile> mProfileDir;
|
||||
};
|
||||
|
||||
private:
|
||||
virtual ~ParentSingleton()
|
||||
{
|
||||
sParentSingleton = nullptr;
|
||||
LOG((__FUNCTION__));
|
||||
}
|
||||
|
||||
public:
|
||||
static ParentSingleton* Get()
|
||||
{
|
||||
// Protect creation of singleton and access from multiple Background threads.
|
||||
//
|
||||
// Multiple Background threads happen because sanitize.js calls us from the
|
||||
// chrome process and gets a thread separate from the one servicing ipc from
|
||||
// the content process.
|
||||
|
||||
StaticMutexAutoLock lock(gMutex);
|
||||
if (!sParentSingleton) {
|
||||
sParentSingleton = new ParentSingleton();
|
||||
}
|
||||
return sParentSingleton;
|
||||
}
|
||||
|
||||
OriginKeysLoader mOriginKeys;
|
||||
OriginKeysTable mPrivateBrowsingOriginKeys;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS0(ParentSingleton)
|
||||
|
||||
bool
|
||||
Parent::RecvGetOriginKey(const uint32_t& aRequestId,
|
||||
const nsCString& aOrigin,
|
||||
const bool& aPrivateBrowsing)
|
||||
{
|
||||
// Hand over to stream-transport thread.
|
||||
|
||||
nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
||||
MOZ_ASSERT(sts);
|
||||
nsRefPtr<ParentSingleton> singleton(mSingleton);
|
||||
|
||||
nsRefPtr<PledgeRunnable<nsCString>> p = PledgeRunnable<nsCString>::New(
|
||||
[singleton, aOrigin, aPrivateBrowsing](nsCString& aResult) {
|
||||
if (aPrivateBrowsing) {
|
||||
singleton->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, aResult);
|
||||
} else {
|
||||
singleton->mOriginKeys.GetOriginKey(aOrigin, aResult);
|
||||
}
|
||||
return NS_OK;
|
||||
});
|
||||
nsresult rv = sts->Dispatch(p, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
nsRefPtr<media::Parent> keepAlive(this);
|
||||
p->Then([this, keepAlive, aRequestId](const nsCString& aKey) mutable {
|
||||
if (!mDestroyed) {
|
||||
unused << SendGetOriginKeyResponse(aRequestId, aKey);
|
||||
}
|
||||
return NS_OK;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Parent::RecvSanitizeOriginKeys(const uint64_t& aSinceWhen)
|
||||
{
|
||||
// Hand over to stream-transport thread.
|
||||
|
||||
nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
||||
MOZ_ASSERT(sts);
|
||||
nsRefPtr<ParentSingleton> singleton(mSingleton);
|
||||
|
||||
nsRefPtr<PledgeRunnable<bool>> p = PledgeRunnable<bool>::New(
|
||||
[singleton, aSinceWhen](bool) {
|
||||
singleton->mPrivateBrowsingOriginKeys.Clear(aSinceWhen);
|
||||
singleton->mOriginKeys.Clear(aSinceWhen);
|
||||
return NS_OK;
|
||||
});
|
||||
nsresult rv = sts->Dispatch(p, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Parent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
// No more IPC from here
|
||||
mDestroyed = true;
|
||||
LOG((__FUNCTION__));
|
||||
}
|
||||
|
||||
Parent::Parent()
|
||||
: mSingleton(ParentSingleton::Get())
|
||||
, mDestroyed(false)
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!gMediaParentLog)
|
||||
gMediaParentLog = PR_NewLogModule("MediaParent");
|
||||
#endif
|
||||
LOG(("media::Parent: %p", this));
|
||||
|
||||
MOZ_COUNT_CTOR(Parent);
|
||||
}
|
||||
|
||||
Parent::~Parent()
|
||||
{
|
||||
LOG(("~media::Parent: %p", this));
|
||||
|
||||
MOZ_COUNT_DTOR(Parent);
|
||||
}
|
||||
|
||||
PMediaParent*
|
||||
AllocPMediaParent()
|
||||
{
|
||||
Parent* obj = new Parent();
|
||||
obj->AddRef();
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool
|
||||
DeallocPMediaParent(media::PMediaParent *aActor)
|
||||
{
|
||||
static_cast<Parent*>(aActor)->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* 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_MediaParent_h
|
||||
#define mozilla_MediaParent_h
|
||||
|
||||
#include "MediaChild.h"
|
||||
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/media/PMediaParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace media {
|
||||
|
||||
// media::Parent implements the chrome-process side of ipc for media::Child APIs
|
||||
|
||||
class ParentSingleton;
|
||||
|
||||
class Parent : public PMediaParent
|
||||
{
|
||||
NS_INLINE_DECL_REFCOUNTING(Parent)
|
||||
public:
|
||||
virtual bool RecvGetOriginKey(const uint32_t& aRequestId,
|
||||
const nsCString& aOrigin,
|
||||
const bool& aPrivateBrowsing) override;
|
||||
virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) override;
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
Parent();
|
||||
private:
|
||||
virtual ~Parent();
|
||||
|
||||
nsRefPtr<ParentSingleton> mSingleton;
|
||||
bool mDestroyed;
|
||||
};
|
||||
|
||||
PMediaParent* AllocPMediaParent();
|
||||
bool DeallocPMediaParent(PMediaParent *aActor);
|
||||
|
||||
} // namespace media
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_MediaParent_h
|
|
@ -0,0 +1,13 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* 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 "MediaUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace media {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* 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_MediaUtils_h
|
||||
#define mozilla_MediaUtils_h
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace media {
|
||||
|
||||
// A media::Pledge is a promise-like pattern for c++ that takes lambda functions.
|
||||
//
|
||||
// Asynchronous APIs that proxy to the chrome process and back, may return a
|
||||
// pledge to allow callers to use pledge.Then() to specify a lambda function to
|
||||
// invoke with the result once the asynchronous API resolves later back on the
|
||||
// same thread.
|
||||
//
|
||||
// An advantage of this pattern is that lambdas allow "capturing" of local
|
||||
// variables, much like closures in JavaScript.
|
||||
|
||||
template<typename ValueType>
|
||||
class Pledge
|
||||
{
|
||||
// TODO: Remove workaround once mozilla allows std::function from <functional>
|
||||
class FunctorsBase
|
||||
{
|
||||
public:
|
||||
FunctorsBase() {}
|
||||
virtual void Succeed(const ValueType& result) = 0;
|
||||
virtual void Fail(nsresult rv) = 0;
|
||||
virtual ~FunctorsBase() {};
|
||||
};
|
||||
|
||||
public:
|
||||
explicit Pledge() : mDone(false), mResult(NS_OK) {}
|
||||
|
||||
template<typename OnSuccessType>
|
||||
void Then(OnSuccessType aOnSuccess)
|
||||
{
|
||||
Then(aOnSuccess, [](nsresult){});
|
||||
}
|
||||
|
||||
template<typename OnSuccessType, typename OnFailureType>
|
||||
void Then(OnSuccessType aOnSuccess, OnFailureType aOnFailure)
|
||||
{
|
||||
class F : public FunctorsBase
|
||||
{
|
||||
public:
|
||||
F(OnSuccessType& aOnSuccess, OnFailureType& aOnFailure)
|
||||
: mOnSuccess(aOnSuccess), mOnFailure(aOnFailure) {}
|
||||
|
||||
void Succeed(const ValueType& result)
|
||||
{
|
||||
mOnSuccess(result);
|
||||
}
|
||||
void Fail(nsresult rv)
|
||||
{
|
||||
mOnFailure(rv);
|
||||
};
|
||||
|
||||
OnSuccessType mOnSuccess;
|
||||
OnFailureType mOnFailure;
|
||||
};
|
||||
mFunctors = new F(aOnSuccess, aOnFailure);
|
||||
|
||||
if (mDone) {
|
||||
if (mResult == NS_OK) {
|
||||
mFunctors->Succeed(mValue);
|
||||
} else {
|
||||
mFunctors->Fail(mResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void Resolve(const ValueType& aValue)
|
||||
{
|
||||
mValue = aValue;
|
||||
Resolve();
|
||||
}
|
||||
|
||||
void Resolve()
|
||||
{
|
||||
if (!mDone) {
|
||||
mDone = true;
|
||||
MOZ_ASSERT(mResult == NS_OK);
|
||||
if (mFunctors) {
|
||||
mFunctors->Succeed(mValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Reject(nsresult rv)
|
||||
{
|
||||
if (!mDone) {
|
||||
mDone = true;
|
||||
mResult = rv;
|
||||
if (mFunctors) {
|
||||
mFunctors->Fail(mResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ValueType mValue;
|
||||
protected:
|
||||
bool mDone;
|
||||
nsresult mResult;
|
||||
private:
|
||||
nsAutoPtr<FunctorsBase> mFunctors;
|
||||
};
|
||||
|
||||
// General purpose runnable that also acts as a Pledge for the resulting value.
|
||||
// Use PledgeRunnable<>::New() factory function to use with lambdas.
|
||||
|
||||
template<typename ValueType>
|
||||
class PledgeRunnable : public Pledge<ValueType>, public nsRunnable
|
||||
{
|
||||
public:
|
||||
template<typename OnRunType>
|
||||
static PledgeRunnable<ValueType>*
|
||||
New(OnRunType aOnRun)
|
||||
{
|
||||
class P : public PledgeRunnable<ValueType>
|
||||
{
|
||||
public:
|
||||
explicit P(OnRunType& aOnRun)
|
||||
: mOriginThread(NS_GetCurrentThread())
|
||||
, mOnRun(aOnRun)
|
||||
, mHasRun(false) {}
|
||||
private:
|
||||
virtual ~P() {}
|
||||
NS_IMETHODIMP
|
||||
Run()
|
||||
{
|
||||
if (!mHasRun) {
|
||||
P::mResult = mOnRun(P::mValue);
|
||||
mHasRun = true;
|
||||
return mOriginThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
bool on;
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(mOriginThread->IsOnCurrentThread(&on)));
|
||||
MOZ_RELEASE_ASSERT(on);
|
||||
|
||||
if (NS_SUCCEEDED(P::mResult)) {
|
||||
P::Resolve();
|
||||
} else {
|
||||
P::Reject(P::mResult);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
nsCOMPtr<nsIThread> mOriginThread;
|
||||
OnRunType mOnRun;
|
||||
bool mHasRun;
|
||||
};
|
||||
|
||||
return new P(aOnRun);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~PledgeRunnable() {}
|
||||
};
|
||||
|
||||
// General purpose runnable with an eye toward lambdas
|
||||
|
||||
namespace CallbackRunnable
|
||||
{
|
||||
template<typename OnRunType>
|
||||
class Impl : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit Impl(OnRunType& aOnRun) : mOnRun(aOnRun) {}
|
||||
private:
|
||||
NS_IMETHODIMP
|
||||
Run()
|
||||
{
|
||||
return mOnRun();
|
||||
}
|
||||
OnRunType mOnRun;
|
||||
};
|
||||
|
||||
template<typename OnRunType>
|
||||
Impl<OnRunType>*
|
||||
New(OnRunType aOnRun)
|
||||
{
|
||||
return new Impl<OnRunType>(aOnRun);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // mozilla_MediaUtils_h
|
|
@ -0,0 +1,35 @@
|
|||
/* 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 protocol PContent;
|
||||
include protocol PBackground;
|
||||
|
||||
namespace mozilla {
|
||||
namespace media {
|
||||
|
||||
protocol PMedia
|
||||
{
|
||||
manager PBackground;
|
||||
|
||||
parent:
|
||||
/**
|
||||
* Requests a persistent unique secret key for each origin.
|
||||
* Has no expiry, but is cleared by age along with cookies.
|
||||
* This is needed by mediaDevices.enumerateDevices() to produce persistent
|
||||
* deviceIds that wont work cross-origin.
|
||||
*/
|
||||
GetOriginKey(uint32_t aRequestId, nsCString aOrigin, bool aPrivateBrowsing);
|
||||
|
||||
/**
|
||||
* On clear cookies. Fire and forget.
|
||||
*/
|
||||
SanitizeOriginKeys(uint64_t aSinceWhen);
|
||||
|
||||
child:
|
||||
GetOriginKeyResponse(uint32_t aRequestId, nsCString key);
|
||||
__delete__();
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
} // namespace mozilla
|
|
@ -37,6 +37,22 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
|||
]
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.media += ['MediaChild.h',
|
||||
'MediaParent.h',
|
||||
'MediaUtils.h'
|
||||
]
|
||||
UNIFIED_SOURCES += ['MediaChild.cpp',
|
||||
'MediaParent.cpp',
|
||||
'MediaUtils.cpp'
|
||||
]
|
||||
IPDL_SOURCES += [
|
||||
'PMedia.ipdl',
|
||||
]
|
||||
# /dom/base needed for nsGlobalWindow.h in MediaChild.cpp
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
|
|
@ -644,6 +644,8 @@ var interfaceNamesInGlobalScope =
|
|||
"LocalMediaStream",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Location",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"MediaDeviceInfo",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"MediaDevices",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* http://dev.w3.org/2011/webrtc/editor/getusermedia.html
|
||||
*/
|
||||
|
||||
enum MediaDeviceKind {
|
||||
"audioinput",
|
||||
"audiooutput",
|
||||
"videoinput"
|
||||
};
|
||||
|
||||
[Func="Navigator::HasUserMediaSupport"]
|
||||
interface MediaDeviceInfo {
|
||||
readonly attribute DOMString deviceId;
|
||||
readonly attribute MediaDeviceKind kind;
|
||||
readonly attribute DOMString label;
|
||||
readonly attribute DOMString groupId;
|
||||
};
|
|
@ -12,12 +12,12 @@
|
|||
|
||||
[Func="Navigator::HasUserMediaSupport"]
|
||||
interface MediaDevices : EventTarget {
|
||||
// attribute EventHandler ondevicechange;
|
||||
//
|
||||
// void enumerateDevices (MediaDeviceInfoCallback resultCallback);
|
||||
//
|
||||
// attribute EventHandler ondevicechange;
|
||||
// static Dictionary getSupportedConstraints (DOMString kind);
|
||||
|
||||
[Throws, Func="Navigator::HasUserMediaSupport"]
|
||||
[Throws]
|
||||
Promise<sequence<MediaDeviceInfo>> enumerateDevices();
|
||||
|
||||
[Throws]
|
||||
Promise<MediaStream> getUserMedia(optional MediaStreamConstraints constraints);
|
||||
};
|
||||
|
|
|
@ -262,6 +262,7 @@ WEBIDL_FILES = [
|
|||
'ListBoxObject.webidl',
|
||||
'LocalMediaStream.webidl',
|
||||
'Location.webidl',
|
||||
'MediaDeviceInfo.webidl',
|
||||
'MediaDevices.webidl',
|
||||
'MediaElementAudioSourceNode.webidl',
|
||||
'MediaError.webidl',
|
||||
|
|
|
@ -40,7 +40,6 @@ protected:
|
|||
|
||||
nsCOMPtr<nsIURI> mUrl;
|
||||
nsCOMPtr<nsIURI> mOriginalURI;
|
||||
int64_t mContentLength;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
|
|
|
@ -460,8 +460,8 @@ nsIconChannel::
|
|||
NS_IMETHODIMP
|
||||
nsIconChannel::GetContentLength(int64_t* aContentLength)
|
||||
{
|
||||
*aContentLength = mContentLength;
|
||||
return NS_OK;
|
||||
*aContentLength = 0;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -708,8 +708,8 @@ nsIconChannel::
|
|||
NS_IMETHODIMP
|
||||
nsIconChannel::GetContentLength(int64_t* aContentLength)
|
||||
{
|
||||
*aContentLength = mContentLength;
|
||||
return NS_OK;
|
||||
*aContentLength = 0;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -43,7 +43,6 @@ public:
|
|||
protected:
|
||||
nsCOMPtr<nsIURI> mUrl;
|
||||
nsCOMPtr<nsIURI> mOriginalURI;
|
||||
int64_t mContentLength;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
|
|
|
@ -121,8 +121,13 @@ SourceBuffer::Compact()
|
|||
// consumer will ever have to wait.
|
||||
mWaitingConsumers.Compact();
|
||||
|
||||
// If we have no more than one chunk, then we can't compact further.
|
||||
if (mChunks.Length() < 2) {
|
||||
// If we have no chunks, then there's nothing to compact.
|
||||
if (mChunks.Length() < 1) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we have one chunk, then we can compact if it has excess capacity.
|
||||
if (mChunks.Length() == 1 && mChunks[0].Length() == mChunks[0].Capacity()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -662,6 +662,13 @@ imgRequest::GetMultipart() const
|
|||
return mIsMultiPartChannel;
|
||||
}
|
||||
|
||||
bool
|
||||
imgRequest::HadInsecureRedirect() const
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mHadInsecureRedirect;
|
||||
}
|
||||
|
||||
/** nsIRequestObserver methods **/
|
||||
|
||||
/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
|
||||
|
@ -1184,6 +1191,7 @@ imgRequest::OnRedirectVerifyCallback(nsresult result)
|
|||
NS_FAILED(mCurrentURI->SchemeIs("chrome", &isChrome)) ||
|
||||
NS_FAILED(NS_URIChainHasFlags(mCurrentURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal)) ||
|
||||
(!isHttps && !isChrome && !schemeLocal)) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
mHadInsecureRedirect = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ public:
|
|||
|
||||
// Returns whether we went through an insecure (non-HTTPS) redirect at some
|
||||
// point during loading. This does not consider the current URI.
|
||||
bool HadInsecureRedirect() const { return mHadInsecureRedirect; }
|
||||
bool HadInsecureRedirect() const;
|
||||
|
||||
// The CORS mode for which we loaded this image.
|
||||
int32_t GetCORSMode() const { return mCORSMode; }
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "ActorsChild.h" // IndexedDB
|
||||
#include "BroadcastChannelChild.h"
|
||||
#include "FileDescriptorSetChild.h"
|
||||
#include "mozilla/media/MediaChild.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/dom/PBlobChild.h"
|
||||
#include "mozilla/dom/cache/ActorUtils.h"
|
||||
|
@ -281,6 +282,18 @@ BackgroundChildImpl::DeallocPCacheStreamControlChild(PCacheStreamControlChild* a
|
|||
return true;
|
||||
}
|
||||
|
||||
media::PMediaChild*
|
||||
BackgroundChildImpl::AllocPMediaChild()
|
||||
{
|
||||
return media::AllocPMediaChild();
|
||||
}
|
||||
|
||||
bool
|
||||
BackgroundChildImpl::DeallocPMediaChild(media::PMediaChild *aActor)
|
||||
{
|
||||
return media::DeallocPMediaChild(aActor);
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -71,6 +71,12 @@ protected:
|
|||
virtual bool
|
||||
DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) override;
|
||||
|
||||
virtual PMediaChild*
|
||||
AllocPMediaChild() override;
|
||||
|
||||
virtual bool
|
||||
DeallocPMediaChild(PMediaChild* aActor) override;
|
||||
|
||||
virtual PVsyncChild*
|
||||
AllocPVsyncChild() override;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "BroadcastChannelParent.h"
|
||||
#include "FileDescriptorSetParent.h"
|
||||
#include "mozilla/media/MediaParent.h"
|
||||
#include "mozilla/AppProcessChecker.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
|
@ -360,6 +361,18 @@ BackgroundParentImpl::DeallocPBroadcastChannelParent(
|
|||
return true;
|
||||
}
|
||||
|
||||
media::PMediaParent*
|
||||
BackgroundParentImpl::AllocPMediaParent()
|
||||
{
|
||||
return media::AllocPMediaParent();
|
||||
}
|
||||
|
||||
bool
|
||||
BackgroundParentImpl::DeallocPMediaParent(media::PMediaParent *aActor)
|
||||
{
|
||||
return media::DeallocPMediaParent(aActor);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class RegisterServiceWorkerCallback final : public nsRunnable
|
||||
|
|
|
@ -84,6 +84,12 @@ protected:
|
|||
virtual bool
|
||||
DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
|
||||
|
||||
virtual PMediaParent*
|
||||
AllocPMediaParent() override;
|
||||
|
||||
virtual bool
|
||||
DeallocPMediaParent(PMediaParent* aActor) override;
|
||||
|
||||
virtual bool
|
||||
RecvRegisterServiceWorker(const ServiceWorkerRegistrationData& aData)
|
||||
override;
|
||||
|
|
|
@ -11,6 +11,7 @@ include protocol PCacheStorage;
|
|||
include protocol PCacheStreamControl;
|
||||
include protocol PFileDescriptorSet;
|
||||
include protocol PVsync;
|
||||
include protocol PMedia;
|
||||
|
||||
include DOMTypes;
|
||||
include PBackgroundSharedTypes;
|
||||
|
@ -34,6 +35,7 @@ sync protocol PBackground
|
|||
manages PCacheStreamControl;
|
||||
manages PFileDescriptorSet;
|
||||
manages PVsync;
|
||||
manages PMedia;
|
||||
|
||||
parent:
|
||||
// Only called at startup during mochitests to check the basic infrastructure.
|
||||
|
@ -42,6 +44,7 @@ parent:
|
|||
PBackgroundIDBFactory(LoggingInfo loggingInfo);
|
||||
|
||||
PVsync();
|
||||
PMedia();
|
||||
|
||||
PBroadcastChannel(PrincipalInfo pInfo, nsString origin, nsString channel);
|
||||
|
||||
|
|
|
@ -9088,8 +9088,6 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible)
|
|||
mCurrentReflowRoot = target;
|
||||
#endif
|
||||
|
||||
target->WillReflow(mPresContext);
|
||||
|
||||
// If the target frame is the root of the frame hierarchy, then
|
||||
// use all the available space. If it's simply a `reflow root',
|
||||
// then use the target frame's size as the available space.
|
||||
|
|
|
@ -46,9 +46,9 @@ associated with the frame, painting the frame, and handling events that
|
|||
are passed in from the widget hierarchy. The nsIHTMLReflow interface
|
||||
inherits from the nsIReflow interface and adds methods related to word
|
||||
breaking and whitespace querying. The nsIReflow interface defines
|
||||
the Reflow() method that initiates the reflow process along with the WillReflow()
|
||||
and DidReflow() methods that get called before and after the reflow process
|
||||
respectively. nsReflowState and nsReflowMetrics are parameters to
|
||||
the Reflow() method that initiates the reflow process along with the
|
||||
DidReflow() method that get calledafter the reflow process.
|
||||
nsReflowState and nsReflowMetrics are parameters to
|
||||
the templatized nsIReflow interface: the former is used to hold state during
|
||||
reflow of a frame and the latter is used to return the frame's desired
|
||||
size and alignment to the parent frame during the reflow process.
|
||||
|
|
|
@ -775,6 +775,7 @@ nsComboboxControlFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
// Constraints we try to satisfy:
|
||||
|
||||
// 1) Default width of button is the vertical scrollbar size
|
||||
|
|
|
@ -379,6 +379,7 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
|
||||
|
|
|
@ -180,10 +180,11 @@ nsHTMLButtonControlFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
|
|||
|
||||
void
|
||||
nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsHTMLButtonControlFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
|
||||
|
|
|
@ -126,10 +126,10 @@ nsImageControlFrame::GetType() const
|
|||
}
|
||||
|
||||
void
|
||||
nsImageControlFrame::Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
nsImageControlFrame::Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
DO_GLOBAL_REFLOW_COUNT("nsImageControlFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
|
|
|
@ -364,6 +364,7 @@ nsListControlFrame::Reflow(nsPresContext* aPresContext,
|
|||
return;
|
||||
}
|
||||
|
||||
MarkInReflow();
|
||||
/*
|
||||
* Due to the fact that our intrinsic height depends on the heights of our
|
||||
* kids, we end up having to do two-pass reflow, in general -- the first pass
|
||||
|
@ -457,8 +458,6 @@ nsListControlFrame::Reflow(nsPresContext* aPresContext,
|
|||
computedHeight = state.ApplyMinMaxHeight(computedHeight);
|
||||
state.SetComputedHeight(computedHeight);
|
||||
|
||||
nsHTMLScrollFrame::WillReflow(aPresContext);
|
||||
|
||||
// XXXbz to make the ascent really correct, we should add our
|
||||
// mComputedPadding.top to it (and subtract it from descent). Need that
|
||||
// because nsGfxScrollFrame just adds in the border....
|
||||
|
@ -577,7 +576,6 @@ nsListControlFrame::ReflowAsDropdown(nsPresContext* aPresContext,
|
|||
|
||||
mLastDropdownComputedHeight = state.ComputedHeight();
|
||||
|
||||
nsHTMLScrollFrame::WillReflow(aPresContext);
|
||||
nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
|
||||
}
|
||||
|
||||
|
|
|
@ -95,10 +95,11 @@ NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
|
|||
|
||||
void
|
||||
nsMeterFrame::Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsMeterFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
|
||||
|
|
|
@ -106,6 +106,7 @@ nsNumberControlFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsNumberControlFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
|
||||
|
|
|
@ -100,10 +100,11 @@ nsProgressFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
|
||||
void
|
||||
nsProgressFrame::Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsProgressFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
|
||||
|
|
|
@ -279,6 +279,7 @@ nsRangeFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsRangeFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
|
||||
|
|
|
@ -489,6 +489,7 @@ nsTextControlFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
|
||||
|
|
|
@ -397,9 +397,6 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat
|
|||
aContainingBlock.width,
|
||||
aContainingBlock.height);
|
||||
|
||||
// Send the WillReflow() notification and position the frame
|
||||
aKidFrame->WillReflow(aPresContext);
|
||||
|
||||
// Get the border values
|
||||
const nsMargin& border = aReflowState.mStyleBorder->GetComputedBorder();
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@ BRFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("BRFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
|
||||
WritingMode wm = aReflowState.GetWritingMode();
|
||||
|
|
|
@ -1012,6 +1012,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
|
||||
#ifdef DEBUG
|
||||
|
@ -7003,7 +7004,6 @@ nsBlockFrame::ReflowBullet(nsIFrame* aBulletFrame,
|
|||
nsHTMLReflowState reflowState(aState.mPresContext, rs,
|
||||
aBulletFrame, availSize);
|
||||
nsReflowStatus status;
|
||||
aBulletFrame->WillReflow(aState.mPresContext);
|
||||
aBulletFrame->Reflow(aState.mPresContext, aMetrics, reflowState, status);
|
||||
|
||||
// Get the float available space using our saved state from before we
|
||||
|
|
|
@ -282,9 +282,6 @@ nsBlockReflowContext::ReflowBlock(const LogicalRect& aSpace,
|
|||
mOuterReflowState.mBlockDelta + mBCoord - aLine->BStart();
|
||||
}
|
||||
|
||||
// Let frame know that we are reflowing it
|
||||
mFrame->WillReflow(mPresContext);
|
||||
|
||||
#ifdef DEBUG
|
||||
mMetrics.ISize(mWritingMode) = nscoord(0xdeadbeef);
|
||||
mMetrics.BSize(mWritingMode) = nscoord(0xdeadbeef);
|
||||
|
|
|
@ -623,6 +623,7 @@ nsBulletFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsBulletFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
|
||||
|
||||
|
|
|
@ -604,6 +604,7 @@ nsCanvasFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow");
|
||||
|
|
|
@ -1014,6 +1014,7 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
// Don't support interruption in columns
|
||||
nsPresContext::InterruptPreventer noInterrupts(aPresContext);
|
||||
|
||||
|
|
|
@ -934,11 +934,6 @@ nsContainerFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext,
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the WillReflow() function, positions the frame and its view (if
|
||||
* requested), and then calls Reflow(). If the reflow succeeds and the child
|
||||
* frame is complete, deletes any next-in-flows using DeleteNextInFlowChild()
|
||||
*/
|
||||
void
|
||||
nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
|
||||
nsPresContext* aPresContext,
|
||||
|
@ -957,10 +952,7 @@ nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
|
|||
"FinishReflowChild with unconstrained container width!");
|
||||
}
|
||||
|
||||
// Send the WillReflow() notification, and position the child frame
|
||||
// and its view if requested
|
||||
aKidFrame->WillReflow(aPresContext);
|
||||
|
||||
// Position the child frame and its view if requested.
|
||||
if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
|
||||
aKidFrame->SetPosition(aWM, aPos, aContainerWidth);
|
||||
}
|
||||
|
@ -1002,10 +994,7 @@ nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
|
|||
{
|
||||
NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state");
|
||||
|
||||
// Send the WillReflow() notification, and position the child frame
|
||||
// and its view if requested
|
||||
aKidFrame->WillReflow(aPresContext);
|
||||
|
||||
// Position the child frame and its view if requested.
|
||||
if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
|
||||
aKidFrame->SetPosition(nsPoint(aX, aY));
|
||||
}
|
||||
|
|
|
@ -231,9 +231,9 @@ public:
|
|||
bool aShrinkWrap) override;
|
||||
|
||||
/**
|
||||
* Invokes the WillReflow() function, positions the frame and its view (if
|
||||
* requested), and then calls Reflow(). If the reflow succeeds and the child
|
||||
* frame is complete, deletes any next-in-flows using DeleteNextInFlowChild()
|
||||
* Positions aChildFrame and its view (if requested), and then calls Reflow().
|
||||
* If the reflow status after reflowing the child is FULLY_COMPLETE then any
|
||||
* next-in-flows are deleted using DeleteNextInFlowChild().
|
||||
*
|
||||
* Flags:
|
||||
* NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
|
||||
|
@ -241,7 +241,7 @@ public:
|
|||
* NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this
|
||||
* case. Also implies NS_FRAME_NO_MOVE_VIEW
|
||||
*/
|
||||
void ReflowChild(nsIFrame* aKidFrame,
|
||||
void ReflowChild(nsIFrame* aChildFrame,
|
||||
nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
|
|
|
@ -164,6 +164,7 @@ nsFirstLetterFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aReflowStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus);
|
||||
|
||||
|
@ -205,7 +206,6 @@ nsFirstLetterFrame::Reflow(nsPresContext* aPresContext,
|
|||
ll.SetInFirstLetter(true);
|
||||
ll.SetFirstLetterStyleOK(true);
|
||||
|
||||
kid->WillReflow(aPresContext);
|
||||
kid->Reflow(aPresContext, kidMetrics, rs, aReflowStatus);
|
||||
|
||||
ll.EndLineReflow();
|
||||
|
|
|
@ -3429,6 +3429,7 @@ nsFlexContainerFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsFlexContainerFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
|
||||
|
|
|
@ -4410,20 +4410,6 @@ nsFrame::ShrinkWidthToFit(nsRenderingContext *aRenderingContext,
|
|||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
nsFrame::WillReflow(nsPresContext* aPresContext)
|
||||
{
|
||||
#ifdef DEBUG_dbaron_off
|
||||
// bug 81268
|
||||
NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
|
||||
"nsFrame::WillReflow: frame is already in reflow");
|
||||
#endif
|
||||
|
||||
NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
|
||||
("WillReflow: oldState=%x", mState));
|
||||
mState |= NS_FRAME_IN_REFLOW;
|
||||
}
|
||||
|
||||
void
|
||||
nsFrame::DidReflow(nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState* aReflowState,
|
||||
|
@ -4524,6 +4510,7 @@ nsFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsFrame");
|
||||
aDesiredSize.ClearSize();
|
||||
aStatus = NS_FRAME_COMPLETE;
|
||||
|
@ -8644,7 +8631,6 @@ nsFrame::BoxReflow(nsBoxLayoutState& aState,
|
|||
#endif
|
||||
|
||||
// place the child and reflow
|
||||
WillReflow(aPresContext);
|
||||
|
||||
Reflow(aPresContext, aDesiredSize, reflowState, status);
|
||||
|
||||
|
@ -8961,7 +8947,7 @@ GetTagName(nsFrame* aFrame, nsIContent* aContent, int aResultSize,
|
|||
void
|
||||
nsFrame::Trace(const char* aMethod, bool aEnter)
|
||||
{
|
||||
if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
|
||||
if (NS_FRAME_LOG_TEST(GetLogModuleInfo(), NS_FRAME_TRACE_CALLS)) {
|
||||
char tagbuf[40];
|
||||
GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
|
||||
PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
|
||||
|
@ -8971,7 +8957,7 @@ nsFrame::Trace(const char* aMethod, bool aEnter)
|
|||
void
|
||||
nsFrame::Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus)
|
||||
{
|
||||
if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
|
||||
if (NS_FRAME_LOG_TEST(GetLogModuleInfo(), NS_FRAME_TRACE_CALLS)) {
|
||||
char tagbuf[40];
|
||||
GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
|
||||
PR_LogPrint("%s: %s %s, status=%scomplete%s",
|
||||
|
@ -8984,7 +8970,7 @@ nsFrame::Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus)
|
|||
void
|
||||
nsFrame::TraceMsg(const char* aFormatString, ...)
|
||||
{
|
||||
if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
|
||||
if (NS_FRAME_LOG_TEST(GetLogModuleInfo(), NS_FRAME_TRACE_CALLS)) {
|
||||
// Format arguments into a buffer
|
||||
char argbuf[200];
|
||||
va_list ap;
|
||||
|
|
|
@ -302,7 +302,6 @@ public:
|
|||
nscoord ShrinkWidthToFit(nsRenderingContext *aRenderingContext,
|
||||
nscoord aWidthInCB);
|
||||
|
||||
virtual void WillReflow(nsPresContext* aPresContext) override;
|
||||
/**
|
||||
* Calculates the size of this frame after reflowing (calling Reflow on, and
|
||||
* updating the size and position of) its children, as necessary. The
|
||||
|
|
|
@ -855,6 +855,7 @@ nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
nsIPresShell *shell = aPresContext->PresShell();
|
||||
|
|
|
@ -802,6 +802,7 @@ nsHTMLScrollFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
|
||||
|
|
|
@ -1180,6 +1180,7 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
|
||||
|
|
|
@ -251,6 +251,7 @@ nsHTMLCanvasFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
|
||||
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
||||
|
|
|
@ -1766,17 +1766,6 @@ public:
|
|||
nscoord* aX,
|
||||
nscoord* aXMost);
|
||||
|
||||
/**
|
||||
* Pre-reflow hook. Before a frame is reflowed this method will be called.
|
||||
* This call will always be invoked at least once before a subsequent Reflow
|
||||
* and DidReflow call. It may be called more than once, In general you will
|
||||
* receive on WillReflow notification before each Reflow request.
|
||||
*
|
||||
* XXX Is this really the semantics we want? Because we have the NS_FRAME_IN_REFLOW
|
||||
* bit we can ensure we don't call it more than once...
|
||||
*/
|
||||
virtual void WillReflow(nsPresContext* aPresContext) = 0;
|
||||
|
||||
/**
|
||||
* The frame is given an available size and asked for its desired
|
||||
* size. This is the frame's opportunity to reflow its children.
|
||||
|
@ -3021,6 +3010,14 @@ private:
|
|||
}
|
||||
|
||||
protected:
|
||||
void MarkInReflow() {
|
||||
#ifdef DEBUG_dbaron_off
|
||||
// bug 81268
|
||||
NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW), "frame is already in reflow");
|
||||
#endif
|
||||
mState |= NS_FRAME_IN_REFLOW;
|
||||
}
|
||||
|
||||
nsFrameState mState;
|
||||
|
||||
// When there is an overflow area only slightly larger than mRect,
|
||||
|
|
|
@ -858,6 +858,7 @@ nsImageFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
|
||||
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
||||
|
|
|
@ -333,6 +333,7 @@ nsInlineFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsInlineFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
|
||||
if (nullptr == aReflowState.mLineLayout) {
|
||||
|
@ -1064,6 +1065,7 @@ nsFirstLineFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
if (nullptr == aReflowState.mLineLayout) {
|
||||
return; // XXX does this happen? why?
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ nsLeafFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsLeafFrame");
|
||||
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
||||
("enter nsLeafFrame::Reflow: aMaxSize=%d,%d",
|
||||
|
|
|
@ -931,10 +931,8 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
|||
mLineBox->DisableResizeReflowOptimization();
|
||||
}
|
||||
|
||||
// Let frame know that are reflowing it. Note that we don't bother
|
||||
// positioning the frame yet, because we're probably going to end up
|
||||
// moving it when we do the block-direction alignment
|
||||
aFrame->WillReflow(mPresContext);
|
||||
// Note that we don't bother positioning the frame yet, because we're probably
|
||||
// going to end up moving it when we do the block-direction alignment.
|
||||
|
||||
// Adjust spacemanager coordinate system for the frame.
|
||||
nsHTMLReflowMetrics metrics(lineWM);
|
||||
|
|
|
@ -26,6 +26,7 @@ nsPageContentFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsPageContentFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
aStatus = NS_FRAME_COMPLETE; // initialize out parameter
|
||||
|
|
|
@ -55,6 +55,7 @@ nsPageFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsPageFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
aStatus = NS_FRAME_COMPLETE; // initialize out parameter
|
||||
|
|
|
@ -141,6 +141,7 @@ nsPlaceholderFrame::Reflow(nsPresContext* aPresContext,
|
|||
}
|
||||
#endif
|
||||
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
aDesiredSize.ClearSize();
|
||||
|
|
|
@ -495,6 +495,7 @@ nsPluginFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsPluginFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
|
||||
|
||||
|
|
|
@ -426,6 +426,7 @@ nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
aStatus = NS_FRAME_COMPLETE;
|
||||
|
|
|
@ -134,6 +134,7 @@ nsRubyFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
DO_GLOBAL_REFLOW_COUNT("nsRubyFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче