Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-03-30 15:01:41 +02:00
Родитель d14aafd13e c437de0aa6
Коммит 0c079d0b04
138 изменённых файлов: 2579 добавлений и 449 удалений

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

@ -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.&nbsp; The nsIHTMLReflow interface
inherits from the nsIReflow interface and adds methods related to word
breaking and whitespace querying.&nbsp; 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.&nbsp; 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.&nbsp;
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);

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