mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound.
This commit is contained in:
commit
2a773817b4
@ -925,7 +925,7 @@ function getSelectionRange(element) {
|
||||
// Get the selection range of contenteditable elements
|
||||
let win = element.ownerDocument.defaultView;
|
||||
let sel = win.getSelection();
|
||||
if (sel) {
|
||||
if (sel && sel.rangeCount > 0) {
|
||||
start = getContentEditableSelectionStart(element, sel);
|
||||
end = start + getContentEditableSelectionLength(element, sel);
|
||||
} else {
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "290fc4628fbf4de6c75fd2ea796b1065a6e50043",
|
||||
"revision": "a4a7f42eac6eb7813b097c716b264d675fceeae8",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -55,4 +55,6 @@ MOZ_PAY=1
|
||||
MOZ_TOOLKIT_SEARCH=
|
||||
MOZ_PLACES=
|
||||
MOZ_B2G=1
|
||||
|
||||
#MOZ_NUWA_PROCESS=1
|
||||
MOZ_FOLD_LIBS=1
|
||||
|
@ -123,6 +123,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "SessionHistory",
|
||||
"resource:///modules/sessionstore/SessionHistory.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "_SessionFile",
|
||||
"resource:///modules/sessionstore/_SessionFile.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TabStateCache",
|
||||
"resource:///modules/sessionstore/TabStateCache.jsm");
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
|
||||
@ -4179,126 +4181,6 @@ function TabData(obj = null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A cache for tabs data.
|
||||
*
|
||||
* This cache implements a weak map from tabs (as XUL elements)
|
||||
* to tab data (as instances of TabData).
|
||||
*
|
||||
* Note that we should never cache private data, as:
|
||||
* - that data is used very seldom by SessionStore;
|
||||
* - caching private data in addition to public data is memory consuming.
|
||||
*/
|
||||
let TabStateCache = {
|
||||
_data: new WeakMap(),
|
||||
|
||||
/**
|
||||
* Tells whether an entry is in the cache.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
* @return {bool} Whether there's a cached entry for the given tab.
|
||||
*/
|
||||
has: function (aTab) {
|
||||
let key = this._normalizeToBrowser(aTab);
|
||||
return this._data.has(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add or replace an entry in the cache.
|
||||
*
|
||||
* @param {XULElement} aTab The key, which may be either a tab
|
||||
* or the corresponding browser. The binding will disappear
|
||||
* if the tab/browser is destroyed.
|
||||
* @param {TabData} aValue The data associated to |aTab|.
|
||||
*/
|
||||
set: function(aTab, aValue) {
|
||||
let key = this._normalizeToBrowser(aTab);
|
||||
if (!(aValue instanceof TabData)) {
|
||||
throw new TypeError("Attempting to cache a non TabData");
|
||||
}
|
||||
this._data.set(key, aValue);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the tab data associated with a tab.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
*
|
||||
* @return {TabData|undefined} The data if available, |undefined|
|
||||
* otherwise.
|
||||
*/
|
||||
get: function(aKey) {
|
||||
let key = this._normalizeToBrowser(aKey);
|
||||
let result = this._data.get(key);
|
||||
TabStateCacheTelemetry.recordAccess(!!result);
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete the tab data associated with a tab.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
*
|
||||
* Noop of there is no tab data associated with the tab.
|
||||
*/
|
||||
delete: function(aKey) {
|
||||
let key = this._normalizeToBrowser(aKey);
|
||||
this._data.delete(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete all tab data.
|
||||
*/
|
||||
clear: function() {
|
||||
TabStateCacheTelemetry.recordClear();
|
||||
this._data.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update in place a piece of data.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
* If the tab/browser is not present, do nothing.
|
||||
* @param {string} aField The field to update.
|
||||
* @param {*} aValue The new value to place in the field.
|
||||
*/
|
||||
updateField: function(aKey, aField, aValue) {
|
||||
let key = this._normalizeToBrowser(aKey);
|
||||
let data = this._data.get(key);
|
||||
if (data) {
|
||||
data[aField] = aValue;
|
||||
}
|
||||
TabStateCacheTelemetry.recordAccess(!!data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a given field from a cached tab state.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
* If the tab/browser is not present, do nothing.
|
||||
* @param {string} aField The field to remove.
|
||||
*/
|
||||
removeField: function(aKey, aField) {
|
||||
let key = this._normalizeToBrowser(aKey);
|
||||
let data = this._data.get(key);
|
||||
if (data && aField in data) {
|
||||
delete data[aField];
|
||||
}
|
||||
TabStateCacheTelemetry.recordAccess(!!data);
|
||||
},
|
||||
|
||||
_normalizeToBrowser: function(aKey) {
|
||||
let nodeName = aKey.localName;
|
||||
if (nodeName == "tab") {
|
||||
return aKey.linkedBrowser;
|
||||
}
|
||||
if (nodeName == "browser") {
|
||||
return aKey;
|
||||
}
|
||||
throw new TypeError("Key is neither a tab nor a browser: " + nodeName);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Module that contains tab state collection methods.
|
||||
*/
|
||||
@ -4347,7 +4229,7 @@ let TabState = {
|
||||
let options = {omitSessionHistory: true,
|
||||
omitSessionStorage: true,
|
||||
omitDocShellCapabilities: true};
|
||||
let tabData = new TabData(this._collectBaseTabData(tab, options));
|
||||
let tabData = this._collectBaseTabData(tab, options);
|
||||
|
||||
// Apply collected data.
|
||||
tabData.entries = history.entries;
|
||||
@ -4401,7 +4283,7 @@ let TabState = {
|
||||
return TabStateCache.get(tab);
|
||||
}
|
||||
|
||||
let tabData = new TabData(this._collectBaseTabData(tab));
|
||||
let tabData = this._collectBaseTabData(tab);
|
||||
if (this._updateTextAndScrollDataForTab(tab, tabData)) {
|
||||
TabStateCache.set(tab, tabData);
|
||||
}
|
||||
@ -4715,68 +4597,4 @@ let TabState = {
|
||||
}
|
||||
};
|
||||
|
||||
let TabStateCacheTelemetry = {
|
||||
// Total number of hits during the session
|
||||
_hits: 0,
|
||||
// Total number of misses during the session
|
||||
_misses: 0,
|
||||
// Total number of clears during the session
|
||||
_clears: 0,
|
||||
// |true| once we have been initialized
|
||||
_initialized: false,
|
||||
|
||||
/**
|
||||
* Record a cache access.
|
||||
*
|
||||
* @param {boolean} isHit If |true|, the access was a hit, otherwise
|
||||
* a miss.
|
||||
*/
|
||||
recordAccess: function(isHit) {
|
||||
this._init();
|
||||
if (isHit) {
|
||||
++this._hits;
|
||||
} else {
|
||||
++this._misses;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Record a cache clear
|
||||
*/
|
||||
recordClear: function() {
|
||||
this._init();
|
||||
++this._clears;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the telemetry.
|
||||
*/
|
||||
_init: function() {
|
||||
if (this._initialized) {
|
||||
// Avoid double initialization
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
Services.obs.addObserver(this, "profile-before-change", false);
|
||||
},
|
||||
|
||||
observe: function() {
|
||||
Services.obs.removeObserver(this, "profile-before-change");
|
||||
|
||||
// Record hit/miss rate
|
||||
let accesses = this._hits + this._misses;
|
||||
if (accesses == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fillHistogram("HIT_RATE", this._hits, accesses);
|
||||
this._fillHistogram("CLEAR_RATIO", this._clears, accesses);
|
||||
},
|
||||
|
||||
_fillHistogram: function(suffix, positive, total) {
|
||||
let PREFIX = "FX_SESSION_RESTORE_TABSTATECACHE_";
|
||||
let histo = Services.telemetry.getHistogramById(PREFIX + suffix);
|
||||
let rate = Math.floor( ( positive * 100 ) / total );
|
||||
histo.add(rate);
|
||||
}
|
||||
};
|
||||
|
292
browser/components/sessionstore/src/TabStateCache.jsm
Normal file
292
browser/components/sessionstore/src/TabStateCache.jsm
Normal file
@ -0,0 +1,292 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["TabStateCache"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
|
||||
|
||||
/**
|
||||
* A cache for tabs data.
|
||||
*
|
||||
* This cache implements a weak map from tabs (as XUL elements)
|
||||
* to tab data (as objects).
|
||||
*
|
||||
* Note that we should never cache private data, as:
|
||||
* - that data is used very seldom by SessionStore;
|
||||
* - caching private data in addition to public data is memory consuming.
|
||||
*/
|
||||
this.TabStateCache = Object.freeze({
|
||||
/**
|
||||
* Tells whether an entry is in the cache.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
* @return {bool} Whether there's a cached entry for the given tab.
|
||||
*/
|
||||
has: function (aTab) {
|
||||
return TabStateCacheInternal.has(aTab);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add or replace an entry in the cache.
|
||||
*
|
||||
* @param {XULElement} aTab The key, which may be either a tab
|
||||
* or the corresponding browser. The binding will disappear
|
||||
* if the tab/browser is destroyed.
|
||||
* @param {*} aValue The data associated to |aTab|.
|
||||
*/
|
||||
set: function(aTab, aValue) {
|
||||
return TabStateCacheInternal.set(aTab, aValue);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the tab data associated with a tab.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
*
|
||||
* @return {*|undefined} The data if available, |undefined|
|
||||
* otherwise.
|
||||
*/
|
||||
get: function(aKey) {
|
||||
return TabStateCacheInternal.get(aKey);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete the tab data associated with a tab.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
*
|
||||
* Noop of there is no tab data associated with the tab.
|
||||
*/
|
||||
delete: function(aKey) {
|
||||
return TabStateCacheInternal.delete(aKey);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete all tab data.
|
||||
*/
|
||||
clear: function() {
|
||||
return TabStateCacheInternal.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update in place a piece of data.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
* If the tab/browser is not present, do nothing.
|
||||
* @param {string} aField The field to update.
|
||||
* @param {*} aValue The new value to place in the field.
|
||||
*/
|
||||
updateField: function(aKey, aField, aValue) {
|
||||
return TabStateCacheInternal.updateField(aKey, aField, aValue);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a given field from a cached tab state.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
* If the tab/browser is not present, do nothing.
|
||||
* @param {string} aField The field to remove.
|
||||
*/
|
||||
removeField: function(aKey, aField) {
|
||||
return TabStateCacheInternal.removeField(aKey, aField);
|
||||
},
|
||||
|
||||
/**
|
||||
* Total number of cache hits during the session.
|
||||
*/
|
||||
get hits() {
|
||||
return TabStateCacheTelemetry.hits;
|
||||
},
|
||||
|
||||
/**
|
||||
* Total number of cache misses during the session.
|
||||
*/
|
||||
get misses() {
|
||||
return TabStateCacheTelemetry.misses;
|
||||
},
|
||||
|
||||
/**
|
||||
* Total number of cache clears during the session.
|
||||
*/
|
||||
get clears() {
|
||||
return TabStateCacheTelemetry.clears;
|
||||
},
|
||||
});
|
||||
|
||||
let TabStateCacheInternal = {
|
||||
_data: new WeakMap(),
|
||||
|
||||
/**
|
||||
* Tells whether an entry is in the cache.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
* @return {bool} Whether there's a cached entry for the given tab.
|
||||
*/
|
||||
has: function (aTab) {
|
||||
let key = this._normalizeToBrowser(aTab);
|
||||
return this._data.has(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add or replace an entry in the cache.
|
||||
*
|
||||
* @param {XULElement} aTab The key, which may be either a tab
|
||||
* or the corresponding browser. The binding will disappear
|
||||
* if the tab/browser is destroyed.
|
||||
* @param {*} aValue The data associated to |aTab|.
|
||||
*/
|
||||
set: function(aTab, aValue) {
|
||||
let key = this._normalizeToBrowser(aTab);
|
||||
this._data.set(key, aValue);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the tab data associated with a tab.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
*
|
||||
* @return {*|undefined} The data if available, |undefined|
|
||||
* otherwise.
|
||||
*/
|
||||
get: function(aKey) {
|
||||
let key = this._normalizeToBrowser(aKey);
|
||||
let result = this._data.get(key);
|
||||
TabStateCacheTelemetry.recordAccess(!!result);
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete the tab data associated with a tab.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
*
|
||||
* Noop of there is no tab data associated with the tab.
|
||||
*/
|
||||
delete: function(aKey) {
|
||||
let key = this._normalizeToBrowser(aKey);
|
||||
this._data.delete(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete all tab data.
|
||||
*/
|
||||
clear: function() {
|
||||
TabStateCacheTelemetry.recordClear();
|
||||
this._data.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update in place a piece of data.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
* If the tab/browser is not present, do nothing.
|
||||
* @param {string} aField The field to update.
|
||||
* @param {*} aValue The new value to place in the field.
|
||||
*/
|
||||
updateField: function(aKey, aField, aValue) {
|
||||
let key = this._normalizeToBrowser(aKey);
|
||||
let data = this._data.get(key);
|
||||
if (data) {
|
||||
data[aField] = aValue;
|
||||
}
|
||||
TabStateCacheTelemetry.recordAccess(!!data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a given field from a cached tab state.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
* If the tab/browser is not present, do nothing.
|
||||
* @param {string} aField The field to remove.
|
||||
*/
|
||||
removeField: function(aKey, aField) {
|
||||
let key = this._normalizeToBrowser(aKey);
|
||||
let data = this._data.get(key);
|
||||
if (data && aField in data) {
|
||||
delete data[aField];
|
||||
}
|
||||
TabStateCacheTelemetry.recordAccess(!!data);
|
||||
},
|
||||
|
||||
_normalizeToBrowser: function(aKey) {
|
||||
let nodeName = aKey.localName;
|
||||
if (nodeName == "tab") {
|
||||
return aKey.linkedBrowser;
|
||||
}
|
||||
if (nodeName == "browser") {
|
||||
return aKey;
|
||||
}
|
||||
throw new TypeError("Key is neither a tab nor a browser: " + nodeName);
|
||||
}
|
||||
};
|
||||
|
||||
let TabStateCacheTelemetry = {
|
||||
// Total number of hits during the session
|
||||
hits: 0,
|
||||
// Total number of misses during the session
|
||||
misses: 0,
|
||||
// Total number of clears during the session
|
||||
clears: 0,
|
||||
// |true| once we have been initialized
|
||||
_initialized: false,
|
||||
|
||||
/**
|
||||
* Record a cache access.
|
||||
*
|
||||
* @param {boolean} isHit If |true|, the access was a hit, otherwise
|
||||
* a miss.
|
||||
*/
|
||||
recordAccess: function(isHit) {
|
||||
this._init();
|
||||
if (isHit) {
|
||||
++this.hits;
|
||||
} else {
|
||||
++this.misses;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Record a cache clear
|
||||
*/
|
||||
recordClear: function() {
|
||||
this._init();
|
||||
++this.clears;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the telemetry.
|
||||
*/
|
||||
_init: function() {
|
||||
if (this._initialized) {
|
||||
// Avoid double initialization
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
Services.obs.addObserver(this, "profile-before-change", false);
|
||||
},
|
||||
|
||||
observe: function() {
|
||||
Services.obs.removeObserver(this, "profile-before-change");
|
||||
|
||||
// Record hit/miss rate
|
||||
let accesses = this.hits + this.misses;
|
||||
if (accesses == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fillHistogram("HIT_RATE", this.hits, accesses);
|
||||
this._fillHistogram("CLEAR_RATIO", this.clears, accesses);
|
||||
},
|
||||
|
||||
_fillHistogram: function(suffix, positive, total) {
|
||||
let PREFIX = "FX_SESSION_RESTORE_TABSTATECACHE_";
|
||||
let histo = Services.telemetry.getHistogramById(PREFIX + suffix);
|
||||
let rate = Math.floor( ( positive * 100 ) / total );
|
||||
histo.add(rate);
|
||||
}
|
||||
};
|
@ -22,6 +22,7 @@ EXTRA_JS_MODULES = [
|
||||
'SessionMigration.jsm',
|
||||
'SessionStorage.jsm',
|
||||
'SessionWorker.js',
|
||||
'TabStateCache.jsm',
|
||||
'XPathGenerator.jsm',
|
||||
'_SessionFile.jsm',
|
||||
]
|
||||
|
@ -20,6 +20,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_input_sample.html \
|
||||
browser_pageshow.js \
|
||||
browser_sessionStorage.js \
|
||||
browser_tabStateCache.js \
|
||||
browser_upgrade_backup.js \
|
||||
browser_windowRestore_perwindowpb.js \
|
||||
browser_248970_b_perwindowpb.js \
|
||||
|
@ -7,26 +7,6 @@ Cu.import("resource://gre/modules/Task.jsm", Scope);
|
||||
Cu.import("resource://gre/modules/Promise.jsm", Scope);
|
||||
let {Task, Promise} = Scope;
|
||||
|
||||
function promiseBrowserLoaded(aBrowser) {
|
||||
let deferred = Promise.defer();
|
||||
whenBrowserLoaded(aBrowser, () => deferred.resolve());
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function forceWriteState() {
|
||||
let deferred = Promise.defer();
|
||||
const PREF = "browser.sessionstore.interval";
|
||||
const TOPIC = "sessionstore-state-write";
|
||||
|
||||
Services.obs.addObserver(function observe() {
|
||||
Services.obs.removeObserver(observe, TOPIC);
|
||||
Services.prefs.clearUserPref(PREF);
|
||||
deferred.resolve();
|
||||
}, TOPIC, false);
|
||||
|
||||
Services.prefs.setIntPref(PREF, 0);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function waitForStorageChange(aTab) {
|
||||
let deferred = Promise.defer();
|
||||
@ -57,7 +37,7 @@ function test() {
|
||||
// Flush loading and next save, call getBrowserState()
|
||||
// a few times to ensure that everything is cached.
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
yield forceWriteState();
|
||||
yield forceSaveState();
|
||||
info("Calling getBrowserState() to populate cache");
|
||||
ss.getBrowserState();
|
||||
|
||||
@ -66,7 +46,7 @@ function test() {
|
||||
win.sessionStorage[SESSION_STORAGE_KEY] = SESSION_STORAGE_VALUE;
|
||||
let storageChanged = yield storageChangedPromise;
|
||||
ok(storageChanged, "Changing sessionStorage triggered the right message");
|
||||
yield forceWriteState();
|
||||
yield forceSaveState();
|
||||
|
||||
let state = ss.getBrowserState();
|
||||
ok(state.indexOf(SESSION_STORAGE_KEY) != -1, "Key appears in state");
|
||||
@ -78,7 +58,7 @@ function test() {
|
||||
win.localStorage[LOCAL_STORAGE_KEY] = LOCAL_STORAGE_VALUE;
|
||||
storageChanged = yield storageChangedPromise;
|
||||
ok(!storageChanged, "Changing localStorage did not trigger a message");
|
||||
yield forceWriteState();
|
||||
yield forceSaveState();
|
||||
|
||||
state = ss.getBrowserState();
|
||||
ok(state.indexOf(LOCAL_STORAGE_KEY) == -1, "Key does not appear in state");
|
||||
|
141
browser/components/sessionstore/test/browser_tabStateCache.js
Normal file
141
browser/components/sessionstore/test/browser_tabStateCache.js
Normal file
@ -0,0 +1,141 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
let wrapper = {};
|
||||
Cu.import("resource:///modules/sessionstore/TabStateCache.jsm", wrapper);
|
||||
let {TabStateCache} = wrapper;
|
||||
|
||||
// The number of tabs that are present in the browser but that we're not dealing
|
||||
// with. This should be one (for an empty about:blank), but let's not make this
|
||||
// a magic number.
|
||||
let numberOfUntrackedTabs;
|
||||
|
||||
// Arbitrary URL prefix, used to generate the URL of pages we visit
|
||||
const URL_PREFIX = "http://example.org:80/";
|
||||
|
||||
/**
|
||||
* Check tab state cache telemetry statistics before and after an operation.
|
||||
*
|
||||
* @param {function} f The operation being measured. If it returns a promise,
|
||||
* we wait until the promise is resolved before proceeding.
|
||||
* @return {promise}
|
||||
*/
|
||||
function getTelemetryDelta(f) {
|
||||
return Task.spawn(function() {
|
||||
let KEYS = ["hits", "misses", "clears"];
|
||||
let old = {};
|
||||
for (let key of KEYS) {
|
||||
old[key] = TabStateCache[key];
|
||||
}
|
||||
yield f();
|
||||
let result = {};
|
||||
for (let key of KEYS) {
|
||||
result[key] = TabStateCache[key] - old[key];
|
||||
}
|
||||
ok(result.hits >= 0, "Sanity check: hits have not decreased");
|
||||
ok(result.misses >= 0, "Sanity check: misses have not decreased");
|
||||
ok(result.clears >= 0, "Sanity check: clears have not decreased");
|
||||
throw new Task.Result(result);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function init() {
|
||||
// Start with an empty cache
|
||||
closeAllButPrimaryWindow();
|
||||
TabStateCache.clear();
|
||||
numberOfUntrackedTabs = gBrowser.tabs.length;
|
||||
info("Starting with " + numberOfUntrackedTabs + " tabs");
|
||||
});
|
||||
|
||||
add_task(function add_remove() {
|
||||
info("Adding the first tab");
|
||||
// Initialize one tab, save to initialize cache
|
||||
let tab1 = gBrowser.addTab(URL_PREFIX + "?tab1");
|
||||
yield promiseBrowserLoaded(tab1.linkedBrowser);
|
||||
yield getTelemetryDelta(forceSaveState);
|
||||
|
||||
// Save/collect again a few times, ensure that we always hit
|
||||
info("Save/collect a few times with one tab");
|
||||
for (let collector of [forceSaveState, ss.getBrowserState]) {
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
let PREFIX = "Trivial test " + i + " using " + collector.name + ": ";
|
||||
let delta = yield getTelemetryDelta(collector);
|
||||
is(delta.hits, numberOfUntrackedTabs + 1, PREFIX + " has at least one hit " + delta.hits);
|
||||
is(delta.misses, 0, PREFIX + " has no miss");
|
||||
is(delta.clears, 0, PREFIX + " has no clear");
|
||||
}
|
||||
}
|
||||
|
||||
// Add a second tab, ensure that we have both hits and misses
|
||||
info("Adding the second tab");
|
||||
let tab2 = gBrowser.addTab(URL_PREFIX + "?tab2");
|
||||
yield promiseBrowserLoaded(tab2.linkedBrowser);
|
||||
|
||||
let PREFIX = "Adding second tab: ";
|
||||
ok(!TabStateCache.has(tab2), PREFIX + " starts out of the cache");
|
||||
let delta = yield getTelemetryDelta(forceSaveState);
|
||||
is(delta.hits, numberOfUntrackedTabs + 2, PREFIX + " we hit all tabs, thanks to prefetching");
|
||||
is(delta.misses, 0, PREFIX + " we missed no tabs, thanks to prefetching");
|
||||
is(delta.clears, 0, PREFIX + " has no clear");
|
||||
|
||||
// Save/collect again a few times, ensure that we always hit
|
||||
info("Save/collect a few times with two tabs");
|
||||
for (let collector of [forceSaveState, ss.getBrowserState]) {
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
let PREFIX = "With two tabs " + i + " using " + collector.name + ": ";
|
||||
let delta = yield getTelemetryDelta(collector);
|
||||
is(delta.hits, numberOfUntrackedTabs + 2, PREFIX + " both tabs hit");
|
||||
is(delta.misses, 0, PREFIX + " has no miss");
|
||||
is(delta.clears, 0, PREFIX + " has no clear");
|
||||
}
|
||||
}
|
||||
|
||||
info("Removing second tab");
|
||||
gBrowser.removeTab(tab2);
|
||||
PREFIX = "Removing second tab: ";
|
||||
delta = yield getTelemetryDelta(forceSaveState);
|
||||
is(delta.hits, numberOfUntrackedTabs + 1, PREFIX + " we hit for one tab");
|
||||
is(delta.misses, 0, PREFIX + " has no miss");
|
||||
is(delta.clears, 0, PREFIX + " has no clear");
|
||||
|
||||
info("Removing first tab");
|
||||
gBrowser.removeTab(tab1);
|
||||
});
|
||||
|
||||
add_task(function browsing() {
|
||||
info("Opening first browsing tab");
|
||||
let tab1 = gBrowser.addTab(URL_PREFIX + "?do_not_move_from_here");
|
||||
let browser1 = tab1.linkedBrowser;
|
||||
yield promiseBrowserLoaded(browser1);
|
||||
yield forceSaveState();
|
||||
|
||||
info("Opening second browsing tab");
|
||||
let tab2 = gBrowser.addTab(URL_PREFIX + "?start_browsing_from_here");
|
||||
let browser2 = tab2.linkedBrowser;
|
||||
yield promiseBrowserLoaded(browser2);
|
||||
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
let url = URL_PREFIX + "?browsing" + i; // Arbitrary url, easy to recognize
|
||||
let PREFIX = "Browsing to " + url;
|
||||
info(PREFIX);
|
||||
let delta = yield getTelemetryDelta(function() {
|
||||
return Task.spawn(function() {
|
||||
// Move to new URI then save session
|
||||
let promise = promiseBrowserLoaded(browser2);
|
||||
browser2.loadURI(url);
|
||||
yield promise;
|
||||
ok(!TabStateCache.has(browser2), PREFIX + " starts out of the cache");
|
||||
yield forceSaveState();
|
||||
});
|
||||
});
|
||||
is(delta.hits, numberOfUntrackedTabs + 2, PREFIX + " has all hits, thanks to prefetching");
|
||||
is(delta.misses, 0, PREFIX + " has no miss, thanks to prefetching");
|
||||
is(delta.clears, 0, PREFIX + " has no clear");
|
||||
}
|
||||
gBrowser.removeTab(tab2);
|
||||
gBrowser.removeTab(tab1);
|
||||
});
|
||||
|
||||
|
@ -240,6 +240,34 @@ function waitForSaveState(aCallback) {
|
||||
Services.prefs.getIntPref("browser.sessionstore.interval");
|
||||
return waitForTopic("sessionstore-state-write", timeout, aCallback);
|
||||
}
|
||||
function promiseSaveState() {
|
||||
let deferred = Promise.defer();
|
||||
waitForSaveState(isSuccessful => {
|
||||
if (isSuccessful) {
|
||||
deferred.resolve();
|
||||
} else {
|
||||
deferred.reject(new Error("timeout"));
|
||||
}});
|
||||
return deferred.promise;
|
||||
}
|
||||
function forceSaveState() {
|
||||
let promise = promiseSaveState();
|
||||
const PREF = "browser.sessionstore.interval";
|
||||
// Set interval to an arbitrary non-0 duration
|
||||
// to ensure that setting it to 0 will notify observers
|
||||
Services.prefs.setIntPref(PREF, 1000);
|
||||
Services.prefs.setIntPref(PREF, 0);
|
||||
return promise.then(
|
||||
function onSuccess(x) {
|
||||
Services.prefs.clearUserPref(PREF);
|
||||
return x;
|
||||
},
|
||||
function onError(x) {
|
||||
Services.prefs.clearUserPref(PREF);
|
||||
throw x;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function whenBrowserLoaded(aBrowser, aCallback = next) {
|
||||
aBrowser.addEventListener("load", function onLoad() {
|
||||
@ -247,6 +275,22 @@ function whenBrowserLoaded(aBrowser, aCallback = next) {
|
||||
executeSoon(aCallback);
|
||||
}, true);
|
||||
}
|
||||
function promiseBrowserLoaded(aBrowser) {
|
||||
let deferred = Promise.defer();
|
||||
whenBrowserLoaded(aBrowser, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
function whenBrowserUnloaded(aBrowser, aContainer, aCallback = next) {
|
||||
aBrowser.addEventListener("unload", function onUnload() {
|
||||
aBrowser.removeEventListener("unload", onUnload, true);
|
||||
executeSoon(aCallback);
|
||||
}, true);
|
||||
}
|
||||
function promiseBrowserUnloaded(aBrowser, aContainer) {
|
||||
let deferred = Promise.defer();
|
||||
whenBrowserUnloaded(aBrowser, aContainer, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function whenWindowLoaded(aWindow, aCallback = next) {
|
||||
aWindow.addEventListener("load", function windowLoadListener() {
|
||||
|
@ -7087,6 +7087,9 @@ if test "$OS_TARGET" = Android; then
|
||||
WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=fork,--wrap=pthread_atfork,--wrap=raise"
|
||||
WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=memccpy,--wrap=memchr,--wrap=memrchr,--wrap=memcmp,--wrap=memcpy,--wrap=memmove,--wrap=memset,--wrap=memmem,--wrap=memswap,--wrap=index,--wrap=strchr,--wrap=strrchr,--wrap=strlen,--wrap=strcmp,--wrap=strcpy,--wrap=strcat,--wrap=strcasecmp,--wrap=strncasecmp,--wrap=strstr,--wrap=strcasestr,--wrap=strtok,--wrap=strtok_r,--wrap=strerror,--wrap=strerror_r,--wrap=strnlen,--wrap=strncat,--wrap=strncmp,--wrap=strncpy,--wrap=strlcat,--wrap=strlcpy,--wrap=strcspn,--wrap=strpbrk,--wrap=strsep,--wrap=strspn,--wrap=strcoll,--wrap=strxfrm"
|
||||
fi
|
||||
if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
|
||||
WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=__pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2"
|
||||
fi
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
@ -8593,6 +8596,12 @@ AC_SUBST(MOZ_BZ2_LIBS)
|
||||
AC_SUBST(MOZ_PNG_CFLAGS)
|
||||
AC_SUBST(MOZ_PNG_LIBS)
|
||||
|
||||
if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
|
||||
export MOZ_NUWA_PROCESS
|
||||
AC_DEFINE(MOZ_NUWA_PROCESS)
|
||||
fi
|
||||
AC_SUBST(MOZ_NUWA_PROCESS)
|
||||
|
||||
AC_SUBST(NSPR_CFLAGS)
|
||||
AC_SUBST(NSPR_LIBS)
|
||||
AC_SUBST(MOZ_NATIVE_NSPR)
|
||||
|
@ -1783,6 +1783,16 @@ bool Navigator::HasPushNotificationsSupport(JSContext* /* unused */,
|
||||
return win && Preferences::GetBool("services.push.enabled", false) && CheckPermission(win, "push");
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool Navigator::HasInputMethodSupport(JSContext* /* unused */,
|
||||
JSObject* aGlobal)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
|
||||
return Preferences::GetBool("dom.mozInputMethod.testing", false) ||
|
||||
(Preferences::GetBool("dom.mozInputMethod.enabled", false) &&
|
||||
win && CheckPermission(win, "keyboard"));
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<nsPIDOMWindow>
|
||||
Navigator::GetWindowFromGlobal(JSObject* aGlobal)
|
||||
|
@ -300,6 +300,8 @@ public:
|
||||
static bool HasPushNotificationsSupport(JSContext* /* unused */,
|
||||
JSObject* aGlobal);
|
||||
|
||||
static bool HasInputMethodSupport(JSContext* /* unused */, JSObject* aGlobal);
|
||||
|
||||
nsPIDOMWindow* GetParentObject() const
|
||||
{
|
||||
return GetWindow();
|
||||
|
@ -17,6 +17,9 @@ static const ScreenOrientation eScreenOrientation_PortraitPrimary = 1u << 0;
|
||||
static const ScreenOrientation eScreenOrientation_PortraitSecondary = 1u << 1;
|
||||
static const ScreenOrientation eScreenOrientation_LandscapePrimary = 1u << 2;
|
||||
static const ScreenOrientation eScreenOrientation_LandscapeSecondary = 1u << 3;
|
||||
//eScreenOrientation_Default will use the natural orientation for the deivce,
|
||||
//it could be PortraitPrimary or LandscapePrimary depends on display resolution
|
||||
static const ScreenOrientation eScreenOrientation_Default = 1u << 4;
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -331,6 +331,8 @@ nsScreen::MozLockOrientation(const Sequence<nsString>& aOrientations,
|
||||
orientation |= eScreenOrientation_LandscapePrimary;
|
||||
} else if (item.EqualsLiteral("landscape-secondary")) {
|
||||
orientation |= eScreenOrientation_LandscapeSecondary;
|
||||
} else if (item.EqualsLiteral("default")) {
|
||||
orientation |= eScreenOrientation_Default;
|
||||
} else {
|
||||
// If we don't recognize the token, we should just return 'false'
|
||||
// without throwing.
|
||||
|
@ -112,6 +112,24 @@ IndexedDBParent::CheckWritePermission(const nsAString& aDatabaseName)
|
||||
return CheckPermissionInternal(aDatabaseName, permission);
|
||||
}
|
||||
|
||||
mozilla::ipc::IProtocol*
|
||||
IndexedDBParent::CloneProtocol(Channel* aChannel,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx)
|
||||
{
|
||||
MOZ_ASSERT(mManagerContent != nullptr);
|
||||
MOZ_ASSERT(mManagerTab == nullptr);
|
||||
MOZ_ASSERT(!mDisconnected);
|
||||
MOZ_ASSERT(IndexedDatabaseManager::Get());
|
||||
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
|
||||
|
||||
ContentParent* contentParent = aCtx->GetContentParent();
|
||||
nsAutoPtr<PIndexedDBParent> actor(contentParent->AllocPIndexedDBParent());
|
||||
if (!actor || !contentParent->RecvPIndexedDBConstructor(actor)) {
|
||||
return nullptr;
|
||||
}
|
||||
return actor.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
IndexedDBParent::CheckPermissionInternal(const nsAString& aDatabaseName,
|
||||
const nsDependentCString& aPermission)
|
||||
|
@ -194,6 +194,10 @@ public:
|
||||
bool
|
||||
CheckWritePermission(const nsAString& aDatabaseName);
|
||||
|
||||
mozilla::ipc::IProtocol*
|
||||
CloneProtocol(Channel* aChannel,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
bool
|
||||
CheckPermissionInternal(const nsAString& aDatabaseName,
|
||||
|
@ -199,8 +199,6 @@ MozKeyboard.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
const TESTING_ENABLED_PREF = "dom.mozInputMethod.testing";
|
||||
|
||||
/*
|
||||
* A WeakMap to map input method iframe window to its active status.
|
||||
*/
|
||||
@ -317,24 +315,6 @@ MozInputMethod.prototype = {
|
||||
}),
|
||||
|
||||
init: function mozInputMethodInit(win) {
|
||||
// Check if we're in testing mode.
|
||||
let isTesting = false;
|
||||
try {
|
||||
isTesting = Services.prefs.getBoolPref(TESTING_ENABLED_PREF);
|
||||
} catch (e) {}
|
||||
|
||||
// Don't bypass the permission check if not in testing mode.
|
||||
if (!isTesting) {
|
||||
let principal = win.document.nodePrincipal;
|
||||
let perm = Services.perms
|
||||
.testExactPermissionFromPrincipal(principal, "keyboard");
|
||||
if (perm != Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
dump("No permission to use the keyboard API for " +
|
||||
principal.origin + "\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._window = win;
|
||||
this._mgmt = new MozInputMethodManager(win);
|
||||
this.innerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
|
@ -169,7 +169,7 @@ public:
|
||||
typedef typename BlobTraits<ActorFlavor>::BaseType BaseType;
|
||||
typedef RemoteBlob<ActorFlavor> RemoteBlobType;
|
||||
typedef mozilla::ipc::IProtocolManager<
|
||||
mozilla::ipc::MessageListener>::ActorDestroyReason
|
||||
mozilla::ipc::IProtocol>::ActorDestroyReason
|
||||
ActorDestroyReason;
|
||||
|
||||
protected:
|
||||
|
@ -90,6 +90,11 @@
|
||||
#include "nsIAccessibilityService.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include <setjmp.h>
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/dom/indexedDB/PIndexedDBChild.h"
|
||||
#include "mozilla/dom/mobilemessage/SmsChild.h"
|
||||
#include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
|
||||
@ -131,6 +136,19 @@ using namespace mozilla::jsipc;
|
||||
using namespace mozilla::system;
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
static bool sNuwaForking = false;
|
||||
|
||||
// The size of the reserved stack (in unsigned ints). It's used to reserve space
|
||||
// to push sigsetjmp() in NuwaCheckpointCurrentThread() to higher in the stack
|
||||
// so that after it returns and do other work we don't garble the stack we want
|
||||
// to preserve in NuwaCheckpointCurrentThread().
|
||||
#define RESERVED_INT_STACK 128
|
||||
|
||||
// A sentinel value for checking whether RESERVED_INT_STACK is large enough.
|
||||
#define STACK_SENTINEL_VALUE 0xdeadbeef
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@ -310,6 +328,10 @@ ContentChild::Init(MessageLoop* aIOLoop,
|
||||
XRE_InstallX11ErrorHandler();
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
SetTransport(aChannel);
|
||||
#endif
|
||||
|
||||
NS_ASSERTION(!sSingleton, "only one ContentChild per child");
|
||||
|
||||
Open(aChannel, aParentHandle, aIOLoop);
|
||||
@ -324,6 +346,12 @@ ContentChild::Init(MessageLoop* aIOLoop,
|
||||
|
||||
GetCPOWManager();
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
SetProcessName(NS_LITERAL_STRING("(Nuwa)"));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (mIsForApp && !mIsForBrowser) {
|
||||
SetProcessName(NS_LITERAL_STRING("(Preallocated app)"));
|
||||
} else {
|
||||
@ -503,14 +531,14 @@ ContentChild::RecvDumpGCAndCCLogsToFile(const nsString& aIdentifier,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PCompositorChild*
|
||||
ContentChild::AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
|
||||
base::ProcessId aOtherProcess)
|
||||
{
|
||||
return CompositorChild::Create(aTransport, aOtherProcess);
|
||||
}
|
||||
|
||||
bool
|
||||
PImageBridgeChild*
|
||||
ContentChild::AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport,
|
||||
base::ProcessId aOtherProcess)
|
||||
{
|
||||
@ -1198,6 +1226,12 @@ ContentChild::RecvScreenSizeChanged(const gfxIntSize& size)
|
||||
bool
|
||||
ContentChild::RecvFlushMemory(const nsString& reason)
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
// Don't flush memory in the nuwa process: the GC thread could be frozen.
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
nsCOMPtr<nsIObserverService> os =
|
||||
mozilla::services::GetObserverService();
|
||||
if (os)
|
||||
@ -1332,6 +1366,13 @@ ContentChild::RecvNotifyProcessPriorityChanged(
|
||||
bool
|
||||
ContentChild::RecvMinimizeMemoryUsage()
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
// Don't minimize memory in the nuwa process: it will perform GC, but the
|
||||
// GC thread could be frozen.
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
nsCOMPtr<nsIMemoryReporterManager> mgr =
|
||||
do_GetService("@mozilla.org/memory-reporter-manager;1");
|
||||
NS_ENSURE_TRUE(mgr, true);
|
||||
@ -1406,5 +1447,180 @@ ContentChild::RecvUnregisterSheet(const URIParams& aURI, const uint32_t& aType)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
class CallNuwaSpawn : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
NuwaSpawn();
|
||||
if (IsNuwaProcess()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// In the new process.
|
||||
ContentChild* child = ContentChild::GetSingleton();
|
||||
child->SetProcessName(NS_LITERAL_STRING("(Preallocated app)"));
|
||||
mozilla::ipc::Transport* transport = child->GetTransport();
|
||||
int fd = transport->GetFileDescriptor();
|
||||
transport->ResetFileDescriptor(fd);
|
||||
|
||||
IToplevelProtocol* toplevel = child->GetFirstOpenedActors();
|
||||
while (toplevel != nullptr) {
|
||||
transport = toplevel->GetTransport();
|
||||
fd = transport->GetFileDescriptor();
|
||||
transport->ResetFileDescriptor(fd);
|
||||
|
||||
toplevel = toplevel->getNext();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
DoNuwaFork()
|
||||
{
|
||||
NS_ASSERTION(NuwaSpawnPrepare != nullptr,
|
||||
"NuwaSpawnPrepare() is not available!");
|
||||
NuwaSpawnPrepare(); // NuwaSpawn will be blocked.
|
||||
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> callSpawn(new CallNuwaSpawn());
|
||||
NS_DispatchToMainThread(callSpawn);
|
||||
}
|
||||
|
||||
// IOThread should be blocked here for waiting NuwaSpawn().
|
||||
NS_ASSERTION(NuwaSpawnWait != nullptr,
|
||||
"NuwaSpawnWait() is not available!");
|
||||
NuwaSpawnWait(); // Now! NuwaSpawn can go.
|
||||
// Here, we can make sure the spawning was finished.
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should keep IO thread in a stable state and freeze it
|
||||
* until the spawning is finished.
|
||||
*/
|
||||
static void
|
||||
RunNuwaFork()
|
||||
{
|
||||
if (NuwaCheckpointCurrentThread()) {
|
||||
DoNuwaFork();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
ContentChild::RecvNuwaFork()
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (sNuwaForking) { // No reentry.
|
||||
return true;
|
||||
}
|
||||
sNuwaForking = true;
|
||||
|
||||
MessageLoop* ioloop = XRE_GetIOMessageLoop();
|
||||
ioloop->PostTask(FROM_HERE, NewRunnableFunction(RunNuwaFork));
|
||||
return true;
|
||||
#else
|
||||
return false; // Makes the underlying IPC channel abort.
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
extern "C" {
|
||||
|
||||
#if defined(MOZ_NUWA_PROCESS)
|
||||
NS_EXPORT void
|
||||
GetProtoFdInfos(NuwaProtoFdInfo* aInfoList,
|
||||
size_t aInfoListSize,
|
||||
size_t* aInfoSize)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
mozilla::dom::ContentChild* content =
|
||||
mozilla::dom::ContentChild::GetSingleton();
|
||||
aInfoList[i].protoId = content->GetProtocolId();
|
||||
aInfoList[i].originFd =
|
||||
content->GetTransport()->GetFileDescriptor();
|
||||
i++;
|
||||
|
||||
for (IToplevelProtocol* actor = content->GetFirstOpenedActors();
|
||||
actor != nullptr;
|
||||
actor = actor->getNext()) {
|
||||
if (i >= aInfoListSize) {
|
||||
NS_RUNTIMEABORT("Too many top level protocols!");
|
||||
}
|
||||
|
||||
aInfoList[i].protoId = actor->GetProtocolId();
|
||||
aInfoList[i].originFd =
|
||||
actor->GetTransport()->GetFileDescriptor();
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i > NUWA_TOPLEVEL_MAX) {
|
||||
NS_RUNTIMEABORT("Too many top level protocols!");
|
||||
}
|
||||
*aInfoSize = i;
|
||||
}
|
||||
|
||||
class RunAddNewIPCProcess : public nsRunnable
|
||||
{
|
||||
public:
|
||||
RunAddNewIPCProcess(pid_t aPid,
|
||||
nsTArray<mozilla::ipc::ProtocolFdMapping>& aMaps)
|
||||
: mPid(aPid)
|
||||
{
|
||||
mMaps.SwapElements(aMaps);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
mozilla::dom::ContentChild::GetSingleton()->
|
||||
SendAddNewProcess(mPid, mMaps);
|
||||
|
||||
MOZ_ASSERT(sNuwaForking);
|
||||
sNuwaForking = false;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
pid_t mPid;
|
||||
nsTArray<mozilla::ipc::ProtocolFdMapping> mMaps;
|
||||
};
|
||||
|
||||
/**
|
||||
* AddNewIPCProcess() is called by Nuwa process to tell the parent
|
||||
* process that a new process is created.
|
||||
*
|
||||
* In the newly created process, ResetContentChildTransport() is called to
|
||||
* reset fd for the IPC Channel and the session.
|
||||
*/
|
||||
NS_EXPORT void
|
||||
AddNewIPCProcess(pid_t aPid, NuwaProtoFdInfo* aInfoList, size_t aInfoListSize)
|
||||
{
|
||||
nsTArray<mozilla::ipc::ProtocolFdMapping> maps;
|
||||
|
||||
for (size_t i = 0; i < aInfoListSize; i++) {
|
||||
int _fd = aInfoList[i].newFds[NUWA_NEWFD_PARENT];
|
||||
mozilla::ipc::FileDescriptor fd(_fd);
|
||||
mozilla::ipc::ProtocolFdMapping map(aInfoList[i].protoId, fd);
|
||||
maps.AppendElement(map);
|
||||
}
|
||||
|
||||
nsRefPtr<RunAddNewIPCProcess> runner = new RunAddNewIPCProcess(aPid, maps);
|
||||
NS_DispatchToMainThread(runner);
|
||||
}
|
||||
|
||||
NS_EXPORT void
|
||||
OnNuwaProcessReady()
|
||||
{
|
||||
mozilla::dom::ContentChild* content =
|
||||
mozilla::dom::ContentChild::GetSingleton();
|
||||
content->SendNuwaReady();
|
||||
}
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
}
|
||||
|
@ -77,10 +77,10 @@ public:
|
||||
void SetProcessName(const nsAString& aName);
|
||||
const void GetProcessName(nsAString& aName);
|
||||
|
||||
bool
|
||||
PCompositorChild*
|
||||
AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
|
||||
base::ProcessId aOtherProcess) MOZ_OVERRIDE;
|
||||
bool
|
||||
PImageBridgeChild*
|
||||
AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport,
|
||||
base::ProcessId aOtherProcess) MOZ_OVERRIDE;
|
||||
|
||||
@ -216,6 +216,8 @@ public:
|
||||
const bool& aIsMediaPresent,
|
||||
const bool& aIsSharing);
|
||||
|
||||
virtual bool RecvNuwaFork() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvNotifyProcessPriorityChanged(const hal::ProcessPriority& aPriority);
|
||||
virtual bool RecvMinimizeMemoryUsage();
|
||||
virtual bool RecvCancelMinimizeMemoryUsage();
|
||||
|
@ -120,6 +120,10 @@ using namespace mozilla::system;
|
||||
#include "BluetoothService.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
#include "JavaScriptParent.h"
|
||||
|
||||
#ifdef MOZ_B2G_FM
|
||||
@ -135,6 +139,8 @@ using namespace mozilla::system;
|
||||
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
|
||||
static const char* sClipboardTextFlavors[] = { kUnicodeMime };
|
||||
|
||||
#define NUWA_FORK_WAIT_DURATION_MS 2000 // 2 seconds.
|
||||
|
||||
using base::ChildPrivileges;
|
||||
using base::KillProcess;
|
||||
using namespace mozilla::dom::bluetooth;
|
||||
@ -318,15 +324,146 @@ static bool sCanLaunchSubprocesses;
|
||||
// The first content child has ID 1, so the chrome process can have ID 0.
|
||||
static uint64_t gContentChildID = 1;
|
||||
|
||||
|
||||
// sNuwaProcess points to the Nuwa process which is used for forking new
|
||||
// processes later.
|
||||
static StaticRefPtr<ContentParent> sNuwaProcess;
|
||||
// Nuwa process is ready for creating new process.
|
||||
static bool sNuwaReady = false;
|
||||
// The array containing the preallocated processes. 4 as the inline storage size
|
||||
// should be enough so we don't need to grow the nsAutoTArray.
|
||||
static StaticAutoPtr<nsAutoTArray<nsRefPtr<ContentParent>, 4> > sSpareProcesses;
|
||||
static StaticAutoPtr<nsTArray<CancelableTask*> > sNuwaForkWaitTasks;
|
||||
|
||||
// We want the prelaunched process to know that it's for apps, but not
|
||||
// actually for any app in particular. Use a magic manifest URL.
|
||||
// Can't be a static constant.
|
||||
#define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}")
|
||||
|
||||
void
|
||||
ContentParent::RunNuwaProcess()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sNuwaProcess) {
|
||||
NS_RUNTIMEABORT("sNuwaProcess is created twice.");
|
||||
}
|
||||
|
||||
sNuwaProcess =
|
||||
new ContentParent(/* aApp = */ nullptr,
|
||||
/* aIsForBrowser = */ false,
|
||||
/* aIsForPreallocated = */ true,
|
||||
// Final privileges are set when we
|
||||
// transform into our app.
|
||||
base::PRIVILEGES_INHERIT,
|
||||
PROCESS_PRIORITY_BACKGROUND,
|
||||
/* aIsNuwaProcess = */ true);
|
||||
sNuwaProcess->Init();
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
// initialization off the critical path of app startup.
|
||||
static CancelableTask* sPreallocateAppProcessTask;
|
||||
// This number is fairly arbitrary ... the intention is to put off
|
||||
// launching another app process until the last one has finished
|
||||
// loading its content, to reduce CPU/memory/IO contention.
|
||||
static int sPreallocateDelayMs = 1000;
|
||||
|
||||
static void
|
||||
DelayedNuwaFork()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
sPreallocateAppProcessTask = nullptr;
|
||||
|
||||
if (!sNuwaReady) {
|
||||
if (!sNuwaProcess) {
|
||||
ContentParent::RunNuwaProcess();
|
||||
}
|
||||
// else sNuwaProcess is starting. It will SendNuwaFork() when ready.
|
||||
} else if (sSpareProcesses->IsEmpty()) {
|
||||
sNuwaProcess->SendNuwaFork();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ScheduleDelayedNuwaFork()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sPreallocateAppProcessTask) {
|
||||
// Make sure there is only one request running.
|
||||
return;
|
||||
}
|
||||
|
||||
sPreallocateAppProcessTask = NewRunnableFunction(DelayedNuwaFork);
|
||||
MessageLoop::current()->
|
||||
PostDelayedTask(FROM_HERE,
|
||||
sPreallocateAppProcessTask,
|
||||
sPreallocateDelayMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a spare ContentParent from sSpareProcesses list.
|
||||
*/
|
||||
static already_AddRefed<ContentParent>
|
||||
GetSpareProcess()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sSpareProcesses->IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<ContentParent> process = sSpareProcesses->LastElement();
|
||||
sSpareProcesses->RemoveElementAt(sSpareProcesses->Length() - 1);
|
||||
|
||||
if (sSpareProcesses->IsEmpty() && sNuwaReady) {
|
||||
NS_ASSERTION(sNuwaProcess != nullptr,
|
||||
"Nuwa process is not present!");
|
||||
ScheduleDelayedNuwaFork();
|
||||
}
|
||||
|
||||
return process.forget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish a ContentParent to spare process list.
|
||||
*/
|
||||
static void
|
||||
PublishSpareProcess(ContentParent* aContent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!sNuwaForkWaitTasks->IsEmpty()) {
|
||||
sNuwaForkWaitTasks->ElementAt(0)->Cancel();
|
||||
sNuwaForkWaitTasks->RemoveElementAt(0);
|
||||
}
|
||||
|
||||
sSpareProcesses->AppendElement(aContent);
|
||||
}
|
||||
|
||||
static void
|
||||
MaybeForgetSpare(ContentParent* aContent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sSpareProcesses->RemoveElement(aContent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aContent == sNuwaProcess) {
|
||||
sNuwaProcess = nullptr;
|
||||
sNuwaReady = false;
|
||||
ScheduleDelayedNuwaFork();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// PreallocateAppProcess is called by the PreallocatedProcessManager.
|
||||
// ContentParent then takes this process back within
|
||||
// MaybeTakePreallocatedAppProcess.
|
||||
|
||||
/*static*/ already_AddRefed<ContentParent>
|
||||
ContentParent::PreallocateAppProcess()
|
||||
{
|
||||
@ -347,7 +484,11 @@ ContentParent::MaybeTakePreallocatedAppProcess(const nsAString& aAppManifestURL,
|
||||
ChildPrivileges aPrivs,
|
||||
ProcessPriority aInitialPriority)
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
nsRefPtr<ContentParent> process = GetSpareProcess();
|
||||
#else
|
||||
nsRefPtr<ContentParent> process = PreallocatedProcessManager::Take();
|
||||
#endif
|
||||
if (!process) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -375,8 +516,17 @@ ContentParent::StartUp()
|
||||
|
||||
sCanLaunchSubprocesses = true;
|
||||
|
||||
sSpareProcesses = new nsAutoTArray<nsRefPtr<ContentParent>, 4>();
|
||||
ClearOnShutdown(&sSpareProcesses);
|
||||
|
||||
sNuwaForkWaitTasks = new nsTArray<CancelableTask*>();
|
||||
ClearOnShutdown(&sNuwaForkWaitTasks);
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
ScheduleDelayedNuwaFork();
|
||||
#else
|
||||
// Try to preallocate a process that we can transform into an app later.
|
||||
PreallocatedProcessManager::AllocateAfterDelay();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
@ -627,7 +777,7 @@ ContentParent::GetAll(nsTArray<ContentParent*>& aArray)
|
||||
}
|
||||
|
||||
for (ContentParent* cp = sContentParents->getFirst(); cp;
|
||||
cp = cp->getNext()) {
|
||||
cp = cp->LinkedListElement<ContentParent>::getNext()) {
|
||||
if (cp->mIsAlive) {
|
||||
aArray.AppendElement(cp);
|
||||
}
|
||||
@ -644,7 +794,7 @@ ContentParent::GetAllEvenIfDead(nsTArray<ContentParent*>& aArray)
|
||||
}
|
||||
|
||||
for (ContentParent* cp = sContentParents->getFirst(); cp;
|
||||
cp = cp->getNext()) {
|
||||
cp = cp->LinkedListElement<ContentParent>::getNext()) {
|
||||
aArray.AppendElement(cp);
|
||||
}
|
||||
}
|
||||
@ -931,6 +1081,28 @@ ContentParent::MarkAsDead()
|
||||
mIsAlive = false;
|
||||
}
|
||||
|
||||
void
|
||||
ContentParent::OnNuwaForkTimeout()
|
||||
{
|
||||
if (!sNuwaForkWaitTasks->IsEmpty()) {
|
||||
sNuwaForkWaitTasks->RemoveElementAt(0);
|
||||
}
|
||||
|
||||
// We haven't RecvAddNewProcess() after SendNuwaFork(). Maybe the main
|
||||
// thread of the Nuwa process is in deadlock.
|
||||
MOZ_ASSERT(false, "Can't fork from the nuwa process.");
|
||||
}
|
||||
|
||||
void
|
||||
ContentParent::OnChannelError()
|
||||
{
|
||||
nsRefPtr<ContentParent> content(this);
|
||||
PContentParent::OnChannelError();
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
MaybeForgetSpare(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ContentParent::OnChannelConnected(int32_t pid)
|
||||
{
|
||||
@ -1197,24 +1369,32 @@ ContentParent::GetTestShellSingleton()
|
||||
return static_cast<TestShellParent*>(ManagedPTestShellParent()[0]);
|
||||
}
|
||||
|
||||
void
|
||||
ContentParent::InitializeMembers()
|
||||
{
|
||||
mSubprocess = nullptr;
|
||||
mChildID = gContentChildID++;
|
||||
mGeolocationWatchID = -1;
|
||||
mForceKillTask = nullptr;
|
||||
mNumDestroyingTabs = 0;
|
||||
mIsAlive = true;
|
||||
mSendPermissionUpdates = false;
|
||||
mCalledClose = false;
|
||||
mCalledCloseWithError = false;
|
||||
mCalledKillHard = false;
|
||||
}
|
||||
|
||||
ContentParent::ContentParent(mozIApplication* aApp,
|
||||
bool aIsForBrowser,
|
||||
bool aIsForPreallocated,
|
||||
ChildPrivileges aOSPrivileges,
|
||||
ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */)
|
||||
: mSubprocess(nullptr)
|
||||
, mOSPrivileges(aOSPrivileges)
|
||||
, mChildID(gContentChildID++)
|
||||
, mGeolocationWatchID(-1)
|
||||
, mForceKillTask(nullptr)
|
||||
, mNumDestroyingTabs(0)
|
||||
, mIsAlive(true)
|
||||
, mSendPermissionUpdates(false)
|
||||
ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */,
|
||||
bool aIsNuwaProcess /* = false */)
|
||||
: mOSPrivileges(aOSPrivileges)
|
||||
, mIsForBrowser(aIsForBrowser)
|
||||
, mCalledClose(false)
|
||||
, mCalledCloseWithError(false)
|
||||
, mCalledKillHard(false)
|
||||
{
|
||||
InitializeMembers(); // Perform common initialization.
|
||||
|
||||
// No more than one of !!aApp, aIsForBrowser, and aIsForPreallocated should
|
||||
// be true.
|
||||
MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1);
|
||||
@ -1223,7 +1403,9 @@ ContentParent::ContentParent(mozIApplication* aApp,
|
||||
if (!sContentParents) {
|
||||
sContentParents = new LinkedList<ContentParent>();
|
||||
}
|
||||
sContentParents->insertBack(this);
|
||||
if (!aIsNuwaProcess) {
|
||||
sContentParents->insertBack(this);
|
||||
}
|
||||
|
||||
if (aApp) {
|
||||
aApp->GetManifestURL(mAppManifestURL);
|
||||
@ -1240,7 +1422,13 @@ ContentParent::ContentParent(mozIApplication* aApp,
|
||||
mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content,
|
||||
aOSPrivileges);
|
||||
|
||||
mSubprocess->LaunchAndWaitForProcessHandle();
|
||||
IToplevelProtocol::SetTransport(mSubprocess->GetChannel());
|
||||
|
||||
std::vector<std::string> extraArgs;
|
||||
if (aIsNuwaProcess) {
|
||||
extraArgs.push_back("-nuwa");
|
||||
}
|
||||
mSubprocess->LaunchAndWaitForProcessHandle(extraArgs);
|
||||
|
||||
Open(mSubprocess->GetChannel(), mSubprocess->GetChildProcessHandle());
|
||||
|
||||
@ -1316,6 +1504,78 @@ ContentParent::ContentParent(mozIApplication* aApp,
|
||||
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
static const FileDescriptor*
|
||||
FindFdProtocolFdMapping(const nsTArray<ProtocolFdMapping>& aFds,
|
||||
ProtocolId aProtoId)
|
||||
{
|
||||
for (unsigned int i = 0; i < aFds.Length(); i++) {
|
||||
if (aFds[i].protocolId() == aProtoId) {
|
||||
return &aFds[i].fd();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is used for new content process cloned from a template.
|
||||
*
|
||||
* For Nuwa.
|
||||
*/
|
||||
ContentParent::ContentParent(ContentParent* aTemplate,
|
||||
const nsAString& aAppManifestURL,
|
||||
base::ProcessHandle aPid,
|
||||
const nsTArray<ProtocolFdMapping>& aFds,
|
||||
ChildPrivileges aOSPrivileges)
|
||||
: mOSPrivileges(aOSPrivileges)
|
||||
, mAppManifestURL(aAppManifestURL)
|
||||
, mIsForBrowser(false)
|
||||
{
|
||||
InitializeMembers(); // Perform common initialization.
|
||||
|
||||
sContentParents->insertBack(this);
|
||||
|
||||
// From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the
|
||||
// PID along with the warning.
|
||||
nsDebugImpl::SetMultiprocessMode("Parent");
|
||||
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
const FileDescriptor* fd = FindFdProtocolFdMapping(aFds, GetProtocolId());
|
||||
|
||||
NS_ASSERTION(fd != nullptr, "IPC Channel for PContent is necessary!");
|
||||
mSubprocess = new GeckoExistingProcessHost(GeckoProcessType_Content,
|
||||
aPid,
|
||||
*fd,
|
||||
aOSPrivileges);
|
||||
|
||||
mSubprocess->LaunchAndWaitForProcessHandle();
|
||||
|
||||
// Clone actors routed by aTemplate for this instance.
|
||||
IToplevelProtocol::SetTransport(mSubprocess->GetChannel());
|
||||
ProtocolCloneContext cloneContext;
|
||||
cloneContext.SetContentParent(this);
|
||||
CloneManagees(aTemplate, &cloneContext);
|
||||
CloneOpenedToplevels(aTemplate, aFds, aPid, &cloneContext);
|
||||
|
||||
Open(mSubprocess->GetChannel(),
|
||||
mSubprocess->GetChildProcessHandle());
|
||||
|
||||
// Set the subprocess's priority (bg if we're a preallocated process, fg
|
||||
// otherwise). We do this first because we're likely /lowering/ its CPU and
|
||||
// memory priority, which it has inherited from this process.
|
||||
ProcessPriority priority;
|
||||
if (IsPreallocated()) {
|
||||
priority = PROCESS_PRIORITY_BACKGROUND;
|
||||
} else {
|
||||
priority = PROCESS_PRIORITY_FOREGROUND;
|
||||
}
|
||||
|
||||
ProcessPriorityManager::SetProcessPriority(this, priority);
|
||||
mMessageManager = nsFrameMessageManager::NewProcessMessageManager(this);
|
||||
}
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
ContentParent::~ContentParent()
|
||||
{
|
||||
if (mForceKillTask) {
|
||||
@ -1568,12 +1828,19 @@ ContentParent::RecvGetShowPasswordSetting(bool* showPassword)
|
||||
bool
|
||||
ContentParent::RecvFirstIdle()
|
||||
{
|
||||
// When the ContentChild goes idle, it sends us a FirstIdle message which we
|
||||
// use as an indicator that it's a good time to prelaunch another process.
|
||||
// If we prelaunch any sooner than this, then we'll be competing with the
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (sSpareProcesses->IsEmpty() && sNuwaReady) {
|
||||
ScheduleDelayedNuwaFork();
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
// When the ContentChild goes idle, it sends us a FirstIdle message
|
||||
// which we use as a good time to prelaunch another process. If we
|
||||
// prelaunch any sooner than this, then we'll be competing with the
|
||||
// child process and slowing it down.
|
||||
PreallocatedProcessManager::AllocateAfterDelay();
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1669,6 +1936,56 @@ ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::SendNuwaFork()
|
||||
{
|
||||
if (this != sNuwaProcess) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CancelableTask* nuwaForkTimeoutTask = NewRunnableMethod(
|
||||
this, &ContentParent::OnNuwaForkTimeout);
|
||||
sNuwaForkWaitTasks->AppendElement(nuwaForkTimeoutTask);
|
||||
|
||||
MessageLoop::current()->
|
||||
PostDelayedTask(FROM_HERE,
|
||||
nuwaForkTimeoutTask,
|
||||
NUWA_FORK_WAIT_DURATION_MS);
|
||||
|
||||
return PContentParent::SendNuwaFork();
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvNuwaReady()
|
||||
{
|
||||
NS_ASSERTION(!sNuwaReady, "Multiple Nuwa processes created!");
|
||||
ProcessPriorityManager::SetProcessPriority(sNuwaProcess,
|
||||
hal::PROCESS_PRIORITY_FOREGROUND);
|
||||
sNuwaReady = true;
|
||||
SendNuwaFork();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvAddNewProcess(const uint32_t& aPid,
|
||||
const InfallibleTArray<ProtocolFdMapping>& aFds)
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
nsRefPtr<ContentParent> content;
|
||||
content = new ContentParent(this,
|
||||
MAGIC_PREALLOCATED_APP_MANIFEST_URL,
|
||||
aPid,
|
||||
aFds,
|
||||
base::PRIVILEGES_DEFAULT);
|
||||
content->Init();
|
||||
PublishSpareProcess(content);
|
||||
return true;
|
||||
#else
|
||||
NS_ERROR("ContentParent::RecvAddNewProcess() not implemented!");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS3(ContentParent,
|
||||
nsIObserver,
|
||||
nsIThreadObserver,
|
||||
@ -1778,14 +2095,14 @@ ContentParent::Observe(nsISupports* aSubject,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
PCompositorParent*
|
||||
ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
|
||||
base::ProcessId aOtherProcess)
|
||||
{
|
||||
return CompositorParent::Create(aTransport, aOtherProcess);
|
||||
}
|
||||
|
||||
bool
|
||||
PImageBridgeParent*
|
||||
ContentParent::AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
|
||||
base::ProcessId aOtherProcess)
|
||||
{
|
||||
|
@ -39,6 +39,7 @@ class TestShellParent;
|
||||
|
||||
namespace jsipc {
|
||||
class JavaScriptParent;
|
||||
class PJavaScriptParent;
|
||||
}
|
||||
|
||||
namespace layers {
|
||||
@ -91,6 +92,8 @@ public:
|
||||
*/
|
||||
static already_AddRefed<ContentParent> PreallocateAppProcess();
|
||||
|
||||
static void RunNuwaProcess();
|
||||
|
||||
/**
|
||||
* Get or create a content process for the given TabContext. aFrameElement
|
||||
* should be the frame/iframe element with which this process will
|
||||
@ -171,9 +174,48 @@ public:
|
||||
*/
|
||||
void FriendlyName(nsAString& aName);
|
||||
|
||||
virtual void OnChannelError() MOZ_OVERRIDE;
|
||||
|
||||
virtual PIndexedDBParent* AllocPIndexedDBParent() MOZ_OVERRIDE;
|
||||
virtual bool
|
||||
RecvPIndexedDBConstructor(PIndexedDBParent* aActor) MOZ_OVERRIDE;
|
||||
|
||||
virtual PCrashReporterParent*
|
||||
AllocPCrashReporterParent(const NativeThreadId& tid,
|
||||
const uint32_t& processType) MOZ_OVERRIDE;
|
||||
virtual bool
|
||||
RecvPCrashReporterConstructor(PCrashReporterParent* actor,
|
||||
const NativeThreadId& tid,
|
||||
const uint32_t& processType) MOZ_OVERRIDE;
|
||||
|
||||
virtual PNeckoParent* AllocPNeckoParent() MOZ_OVERRIDE;
|
||||
virtual bool RecvPNeckoConstructor(PNeckoParent* aActor) MOZ_OVERRIDE {
|
||||
return PContentParent::RecvPNeckoConstructor(aActor);
|
||||
}
|
||||
|
||||
virtual PHalParent* AllocPHalParent() MOZ_OVERRIDE;
|
||||
virtual bool RecvPHalConstructor(PHalParent* aActor) MOZ_OVERRIDE {
|
||||
return PContentParent::RecvPHalConstructor(aActor);
|
||||
}
|
||||
|
||||
virtual PStorageParent* AllocPStorageParent() MOZ_OVERRIDE;
|
||||
virtual bool RecvPStorageConstructor(PStorageParent* aActor) MOZ_OVERRIDE {
|
||||
return PContentParent::RecvPStorageConstructor(aActor);
|
||||
}
|
||||
|
||||
virtual PJavaScriptParent*
|
||||
AllocPJavaScriptParent() MOZ_OVERRIDE;
|
||||
virtual bool
|
||||
RecvPJavaScriptConstructor(PJavaScriptParent* aActor) MOZ_OVERRIDE {
|
||||
return PContentParent::RecvPJavaScriptConstructor(aActor);
|
||||
}
|
||||
|
||||
virtual bool SendNuwaFork();
|
||||
|
||||
protected:
|
||||
void OnChannelConnected(int32_t pid) MOZ_OVERRIDE;
|
||||
virtual void ActorDestroy(ActorDestroyReason why);
|
||||
void OnNuwaForkTimeout();
|
||||
|
||||
bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
|
||||
|
||||
@ -207,7 +249,19 @@ private:
|
||||
bool aIsForBrowser,
|
||||
bool aIsForPreallocated,
|
||||
ChildPrivileges aOSPrivileges = base::PRIVILEGES_DEFAULT,
|
||||
hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND);
|
||||
hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND,
|
||||
bool aIsNuwaProcess = false);
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
ContentParent(ContentParent* aTemplate,
|
||||
const nsAString& aAppManifestURL,
|
||||
base::ProcessHandle aPid,
|
||||
const nsTArray<ProtocolFdMapping>& aFds,
|
||||
ChildPrivileges aOSPrivileges = base::PRIVILEGES_DEFAULT);
|
||||
#endif
|
||||
|
||||
// The common initialization for the constructors.
|
||||
void InitializeMembers();
|
||||
|
||||
virtual ~ContentParent();
|
||||
|
||||
@ -250,10 +304,10 @@ private:
|
||||
*/
|
||||
void ShutDownProcess(bool aCloseWithError);
|
||||
|
||||
bool
|
||||
PCompositorParent*
|
||||
AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
|
||||
base::ProcessId aOtherProcess) MOZ_OVERRIDE;
|
||||
bool
|
||||
PImageBridgeParent*
|
||||
AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
|
||||
base::ProcessId aOtherProcess) MOZ_OVERRIDE;
|
||||
|
||||
@ -262,7 +316,6 @@ private:
|
||||
bool* aIsForBrowser) MOZ_OVERRIDE;
|
||||
virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline) MOZ_OVERRIDE;
|
||||
|
||||
virtual mozilla::jsipc::PJavaScriptParent* AllocPJavaScriptParent();
|
||||
virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*);
|
||||
|
||||
virtual PBrowserParent* AllocPBrowserParent(const IPCTabContext& aContext,
|
||||
@ -275,33 +328,21 @@ private:
|
||||
virtual PBlobParent* AllocPBlobParent(const BlobConstructorParams& aParams);
|
||||
virtual bool DeallocPBlobParent(PBlobParent*);
|
||||
|
||||
virtual PCrashReporterParent* AllocPCrashReporterParent(const NativeThreadId& tid,
|
||||
const uint32_t& processType);
|
||||
virtual bool DeallocPCrashReporterParent(PCrashReporterParent* crashreporter);
|
||||
virtual bool RecvPCrashReporterConstructor(PCrashReporterParent* actor,
|
||||
const NativeThreadId& tid,
|
||||
const uint32_t& processType);
|
||||
|
||||
virtual bool RecvGetRandomValues(const uint32_t& length,
|
||||
InfallibleTArray<uint8_t>* randomValues);
|
||||
|
||||
virtual PHalParent* AllocPHalParent() MOZ_OVERRIDE;
|
||||
virtual bool DeallocPHalParent(PHalParent*) MOZ_OVERRIDE;
|
||||
|
||||
virtual PIndexedDBParent* AllocPIndexedDBParent();
|
||||
|
||||
virtual bool DeallocPIndexedDBParent(PIndexedDBParent* aActor);
|
||||
|
||||
virtual bool
|
||||
RecvPIndexedDBConstructor(PIndexedDBParent* aActor);
|
||||
|
||||
virtual PMemoryReportRequestParent* AllocPMemoryReportRequestParent();
|
||||
virtual bool DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor);
|
||||
|
||||
virtual PTestShellParent* AllocPTestShellParent();
|
||||
virtual bool DeallocPTestShellParent(PTestShellParent* shell);
|
||||
|
||||
virtual PNeckoParent* AllocPNeckoParent();
|
||||
virtual bool DeallocPNeckoParent(PNeckoParent* necko);
|
||||
|
||||
virtual PExternalHelperAppParent* AllocPExternalHelperAppParent(
|
||||
@ -320,7 +361,6 @@ private:
|
||||
virtual PTelephonyParent* AllocPTelephonyParent();
|
||||
virtual bool DeallocPTelephonyParent(PTelephonyParent*);
|
||||
|
||||
virtual PStorageParent* AllocPStorageParent();
|
||||
virtual bool DeallocPStorageParent(PStorageParent* aActor);
|
||||
|
||||
virtual PBluetoothParent* AllocPBluetoothParent();
|
||||
@ -431,6 +471,11 @@ private:
|
||||
|
||||
virtual bool RecvSystemMessageHandled() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvNuwaReady() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvAddNewProcess(const uint32_t& aPid,
|
||||
const InfallibleTArray<ProtocolFdMapping>& aFds) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvCreateFakeVolume(const nsString& fsName, const nsString& mountPoint) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvSetFakeVolumeState(const nsString& fsName, const int32_t& fsState) MOZ_OVERRIDE;
|
||||
|
@ -37,5 +37,5 @@ ContentProcess::CleanUp()
|
||||
mXREEmbed.Stop();
|
||||
}
|
||||
|
||||
} // namespace tabs
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -4,10 +4,14 @@
|
||||
* 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 "CrashReporterParent.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
#include "nsExceptionHandler.h"
|
||||
#endif
|
||||
|
||||
using namespace base;
|
||||
|
||||
namespace mozilla {
|
||||
@ -29,6 +33,34 @@ CrashReporterParent::RecvAppendAppNotes(const nsCString& data)
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::ipc::IProtocol*
|
||||
CrashReporterParent::CloneProtocol(Channel* aChannel,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx)
|
||||
{
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
ContentParent* contentParent = aCtx->GetContentParent();
|
||||
CrashReporter::ThreadId childThreadId = contentParent->Pid();
|
||||
GeckoProcessType childProcessType =
|
||||
contentParent->Process()->GetProcessType();
|
||||
|
||||
nsAutoPtr<PCrashReporterParent> actor(
|
||||
contentParent->AllocPCrashReporterParent(childThreadId,
|
||||
childProcessType)
|
||||
);
|
||||
if (!actor ||
|
||||
!contentParent->RecvPCrashReporterConstructor(actor,
|
||||
childThreadId,
|
||||
childThreadId)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return actor.forget();
|
||||
#else
|
||||
MOZ_CRASH("Not Implemented");
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
CrashReporterParent::CrashReporterParent()
|
||||
:
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
|
@ -80,6 +80,9 @@ public:
|
||||
}
|
||||
virtual bool
|
||||
RecvAppendAppNotes(const nsCString& data);
|
||||
virtual mozilla::ipc::IProtocol*
|
||||
CloneProtocol(Channel* aChannel,
|
||||
mozilla::ipc::ProtocolCloneContext *aCtx) MOZ_OVERRIDE;
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
AnnotationTable mNotes;
|
||||
|
@ -28,6 +28,7 @@ include JavaScriptTypes;
|
||||
include InputStreamParams;
|
||||
include PTabContext;
|
||||
include URIParams;
|
||||
include ProtocolTypes;
|
||||
|
||||
include "mozilla/chrome/RegistryMessageUtils.h";
|
||||
include "mozilla/dom/PermissionMessageUtils.h";
|
||||
@ -310,6 +311,9 @@ child:
|
||||
int32_t mountGeneration, bool isMediaPresent,
|
||||
bool isSharing);
|
||||
|
||||
// Ask the Nuwa process to create a new child process.
|
||||
NuwaFork();
|
||||
|
||||
NotifyProcessPriorityChanged(ProcessPriority priority);
|
||||
MinimizeMemoryUsage();
|
||||
CancelMinimizeMemoryUsage();
|
||||
@ -463,6 +467,10 @@ parent:
|
||||
// Notify the parent that the child has finished handling a system message.
|
||||
async SystemMessageHandled();
|
||||
|
||||
NuwaReady();
|
||||
|
||||
sync AddNewProcess(uint32_t pid, ProtocolFdMapping[] aFds);
|
||||
|
||||
// called by the child (test code only) to propagate volume changes to the parent
|
||||
async CreateFakeVolume(nsString fsName, nsString mountPoint);
|
||||
async SetFakeVolumeState(nsString fsName, int32_t fsState);
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "DOMStorageManager.h"
|
||||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsIDiskSpaceWatcher.h"
|
||||
#include "nsThreadUtils.h"
|
||||
@ -364,6 +365,18 @@ DOMStorageDBParent::~DOMStorageDBParent()
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::ipc::IProtocol*
|
||||
DOMStorageDBParent::CloneProtocol(Channel* aChannel,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx)
|
||||
{
|
||||
ContentParent* contentParent = aCtx->GetContentParent();
|
||||
nsAutoPtr<PStorageParent> actor(contentParent->AllocPStorageParent());
|
||||
if (!actor || !contentParent->RecvPStorageConstructor(actor)) {
|
||||
return nullptr;
|
||||
}
|
||||
return actor.forget();
|
||||
}
|
||||
|
||||
DOMStorageDBParent::CacheParentBridge*
|
||||
DOMStorageDBParent::NewCache(const nsACString& aScope)
|
||||
{
|
||||
|
@ -112,6 +112,10 @@ public:
|
||||
DOMStorageDBParent();
|
||||
virtual ~DOMStorageDBParent();
|
||||
|
||||
virtual mozilla::ipc::IProtocol*
|
||||
CloneProtocol(Channel* aChannel,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
|
||||
|
||||
NS_IMETHOD_(nsrefcnt) AddRef(void);
|
||||
NS_IMETHOD_(nsrefcnt) Release(void);
|
||||
|
||||
|
@ -75,6 +75,38 @@ struct Paths {
|
||||
nsString tmpDir;
|
||||
nsString profileDir;
|
||||
nsString localProfileDir;
|
||||
/**
|
||||
* The user's home directory
|
||||
*/
|
||||
nsString homeDir;
|
||||
/**
|
||||
* The user's desktop directory, if there is one. Otherwise this is
|
||||
* the same as homeDir.
|
||||
*/
|
||||
nsString desktopDir;
|
||||
|
||||
#if defined(XP_WIN)
|
||||
/**
|
||||
* The user's application data directory.
|
||||
*/
|
||||
nsString winAppDataDir;
|
||||
/**
|
||||
* The programs subdirectory in the user's start menu directory.
|
||||
*/
|
||||
nsString winStartMenuProgsDir;
|
||||
#endif // defined(XP_WIN)
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
/**
|
||||
* The user's Library directory.
|
||||
*/
|
||||
nsString macUserLibDir;
|
||||
/**
|
||||
* The Application directory, that stores applications installed in the
|
||||
* system.
|
||||
*/
|
||||
nsString macLocalApplicationsDir;
|
||||
#endif // defined(XP_MACOSX)
|
||||
|
||||
Paths()
|
||||
{
|
||||
@ -82,6 +114,18 @@ struct Paths {
|
||||
tmpDir.SetIsVoid(true);
|
||||
profileDir.SetIsVoid(true);
|
||||
localProfileDir.SetIsVoid(true);
|
||||
homeDir.SetIsVoid(true);
|
||||
desktopDir.SetIsVoid(true);
|
||||
|
||||
#if defined(XP_WIN)
|
||||
winAppDataDir.SetIsVoid(true);
|
||||
winStartMenuProgsDir.SetIsVoid(true);
|
||||
#endif // defined(XP_WIN)
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
macUserLibDir.SetIsVoid(true);
|
||||
macLocalApplicationsDir.SetIsVoid(true);
|
||||
#endif // defined(XP_MACOSX)
|
||||
}
|
||||
};
|
||||
|
||||
@ -209,6 +253,18 @@ nsresult InitOSFileConstants()
|
||||
// some platforms or in non-Firefox embeddings of Gecko).
|
||||
|
||||
GetPathToSpecialDir(NS_OS_TEMP_DIR, paths->tmpDir);
|
||||
GetPathToSpecialDir(NS_OS_HOME_DIR, paths->homeDir);
|
||||
GetPathToSpecialDir(NS_OS_DESKTOP_DIR, paths->desktopDir);
|
||||
|
||||
#if defined(XP_WIN)
|
||||
GetPathToSpecialDir(NS_WIN_APPDATA_DIR, paths->winAppDataDir);
|
||||
GetPathToSpecialDir(NS_WIN_PROGRAMS_DIR, paths->winStartMenuProgsDir);
|
||||
#endif // defined(XP_WIN)
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
GetPathToSpecialDir(NS_MAC_USER_LIB_DIR, paths->macUserLibDir);
|
||||
GetPathToSpecialDir(NS_OSX_LOCAL_APPLICATIONS_DIR, paths->macLocalApplicationsDir);
|
||||
#endif // defined(XP_MACOSX)
|
||||
|
||||
gPaths = paths.forget();
|
||||
return NS_OK;
|
||||
@ -769,6 +825,34 @@ bool DefineOSFileConstants(JSContext *cx, JS::Handle<JSObject*> global)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetStringProperty(cx, objPath, "homeDir", gPaths->homeDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetStringProperty(cx, objPath, "desktopDir", gPaths->desktopDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(XP_WIN)
|
||||
if (!SetStringProperty(cx, objPath, "winAppDataDir", gPaths->winAppDataDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetStringProperty(cx, objPath, "winStartMenuProgsDir", gPaths->winStartMenuProgsDir)) {
|
||||
return false;
|
||||
}
|
||||
#endif // defined(XP_WIN)
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
if (!SetStringProperty(cx, objPath, "macUserLibDir", gPaths->macUserLibDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetStringProperty(cx, objPath, "macLocalApplicationsDir", gPaths->macLocalApplicationsDir)) {
|
||||
return false;
|
||||
}
|
||||
#endif // defined(XP_MACOSX)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -448,12 +448,15 @@ this.netHelpers = {
|
||||
|
||||
/**
|
||||
* Convert string representation of an IP address to the integer
|
||||
* representation.
|
||||
* representation (network byte order).
|
||||
*
|
||||
* @param string
|
||||
* String containing the IP address.
|
||||
*/
|
||||
stringToIP: function stringToIP(string) {
|
||||
if (!string) {
|
||||
return null;
|
||||
}
|
||||
let ip = 0;
|
||||
let start, end = -1;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
|
@ -324,7 +324,6 @@ var interfaceNamesInGlobalScope =
|
||||
{name: "MozEmergencyCbModeEvent", b2g: true},
|
||||
{name: "MozIccManager", b2g: true},
|
||||
{name: "MozInputContext", b2g: true},
|
||||
{name: "MozInputMethod", b2g: true},
|
||||
{name: "MozInputMethodManager", b2g: true},
|
||||
"MozMmsEvent",
|
||||
"MozMmsMessage",
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
[JSImplementation="@mozilla.org/b2g-inputmethod;1",
|
||||
NavigatorProperty="mozInputMethod",
|
||||
Pref="dom.mozInputMethod.enabled"]
|
||||
Func="Navigator::HasInputMethodSupport"]
|
||||
interface MozInputMethod : EventTarget {
|
||||
// Input Method Manager contain a few global methods expose to apps
|
||||
readonly attribute MozInputMethodManager mgmt;
|
||||
|
@ -45,6 +45,10 @@
|
||||
#include "Worker.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
@ -893,6 +897,14 @@ public:
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
|
||||
"NuwaMarkCurrentThread is undefined!");
|
||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
||||
NuwaFreezeCurrentThread();
|
||||
}
|
||||
#endif
|
||||
WorkerPrivate* workerPrivate = mWorkerPrivate;
|
||||
mWorkerPrivate = nullptr;
|
||||
|
||||
|
@ -51,7 +51,7 @@ CompositorChild::Destroy()
|
||||
SendStop();
|
||||
}
|
||||
|
||||
/*static*/ bool
|
||||
/*static*/ PCompositorChild*
|
||||
CompositorChild::Create(Transport* aTransport, ProcessId aOtherProcess)
|
||||
{
|
||||
// There's only one compositor per child process.
|
||||
@ -62,15 +62,14 @@ CompositorChild::Create(Transport* aTransport, ProcessId aOtherProcess)
|
||||
if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
|
||||
// We can't go on without a compositor.
|
||||
NS_RUNTIMEABORT("Couldn't OpenProcessHandle() to parent process.");
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
if (!child->Open(aTransport, handle, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
|
||||
NS_RUNTIMEABORT("Couldn't Open() Compositor channel.");
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
// We release this ref in ActorDestroy().
|
||||
sCompositor = child.forget().get();
|
||||
return true;
|
||||
return sCompositor = child.forget().get();
|
||||
}
|
||||
|
||||
/*static*/ PCompositorChild*
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
* or Bridge() request from our parent process. The Transport is to
|
||||
* the compositor's context.
|
||||
*/
|
||||
static bool
|
||||
static PCompositorChild*
|
||||
Create(Transport* aTransport, ProcessId aOtherProcess);
|
||||
|
||||
static PCompositorChild* Get();
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "mozilla/layers/CompositorD3D9.h"
|
||||
#endif
|
||||
#include "GeckoProfiler.h"
|
||||
#include "mozilla/ipc/ProtocolTypes.h"
|
||||
|
||||
using namespace base;
|
||||
using namespace mozilla;
|
||||
@ -869,6 +870,12 @@ public:
|
||||
{}
|
||||
virtual ~CrossProcessCompositorParent();
|
||||
|
||||
// IToplevelProtocol::CloneToplevel()
|
||||
virtual IToplevelProtocol*
|
||||
CloneToplevel(const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& aFds,
|
||||
base::ProcessHandle aPeerProcess,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
|
||||
|
||||
// FIXME/bug 774388: work out what shutdown protocol we need.
|
||||
@ -913,7 +920,7 @@ OpenCompositor(CrossProcessCompositorParent* aCompositor,
|
||||
MOZ_ASSERT(ok);
|
||||
}
|
||||
|
||||
/*static*/ bool
|
||||
/*static*/ PCompositorParent*
|
||||
CompositorParent::Create(Transport* aTransport, ProcessId aOtherProcess)
|
||||
{
|
||||
nsRefPtr<CrossProcessCompositorParent> cpcp =
|
||||
@ -921,14 +928,34 @@ CompositorParent::Create(Transport* aTransport, ProcessId aOtherProcess)
|
||||
ProcessHandle handle;
|
||||
if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
|
||||
// XXX need to kill |aOtherProcess|, it's boned
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
cpcp->mSelfRef = cpcp;
|
||||
CompositorLoop()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(OpenCompositor, cpcp.get(),
|
||||
aTransport, handle, XRE_GetIOMessageLoop()));
|
||||
return true;
|
||||
// The return value is just compared to null for success checking,
|
||||
// we're not sharing a ref.
|
||||
return cpcp.get();
|
||||
}
|
||||
|
||||
IToplevelProtocol*
|
||||
CompositorParent::CloneToplevel(const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& aFds,
|
||||
base::ProcessHandle aPeerProcess,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx)
|
||||
{
|
||||
for (unsigned int i = 0; i < aFds.Length(); i++) {
|
||||
if (aFds[i].protocolId() == (unsigned)GetProtocolId()) {
|
||||
Transport* transport = OpenDescriptor(aFds[i].fd(),
|
||||
Transport::MODE_SERVER);
|
||||
PCompositorParent* compositor = Create(transport, base::GetProcId(aPeerProcess));
|
||||
compositor->CloneManagees(this, aCtx);
|
||||
compositor->IToplevelProtocol::SetTransport(transport);
|
||||
return compositor;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1030,5 +1057,24 @@ CrossProcessCompositorParent::~CrossProcessCompositorParent()
|
||||
new DeleteTask<Transport>(mTransport));
|
||||
}
|
||||
|
||||
IToplevelProtocol*
|
||||
CrossProcessCompositorParent::CloneToplevel(const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& aFds,
|
||||
base::ProcessHandle aPeerProcess,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx)
|
||||
{
|
||||
for (unsigned int i = 0; i < aFds.Length(); i++) {
|
||||
if (aFds[i].protocolId() == (unsigned)GetProtocolId()) {
|
||||
Transport* transport = OpenDescriptor(aFds[i].fd(),
|
||||
Transport::MODE_SERVER);
|
||||
PCompositorParent* compositor =
|
||||
CompositorParent::Create(transport, base::GetProcId(aPeerProcess));
|
||||
compositor->CloneManagees(this, aCtx);
|
||||
compositor->IToplevelProtocol::SetTransport(transport);
|
||||
return compositor;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
@ -73,6 +73,12 @@ public:
|
||||
|
||||
virtual ~CompositorParent();
|
||||
|
||||
// IToplevelProtocol::CloneToplevel()
|
||||
virtual IToplevelProtocol*
|
||||
CloneToplevel(const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& aFds,
|
||||
base::ProcessHandle aPeerProcess,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvWillStop() MOZ_OVERRIDE;
|
||||
virtual bool RecvStop() MOZ_OVERRIDE;
|
||||
virtual bool RecvPause() MOZ_OVERRIDE;
|
||||
@ -176,7 +182,7 @@ public:
|
||||
* A new child process has been configured to push transactions
|
||||
* directly to us. Transport is to its thread context.
|
||||
*/
|
||||
static bool
|
||||
static PCompositorParent*
|
||||
Create(Transport* aTransport, ProcessId aOtherProcess);
|
||||
|
||||
/**
|
||||
|
@ -361,6 +361,10 @@ ConnectImageBridgeInChildProcess(Transport* aTransport,
|
||||
ipc::ChildSide);
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
static void ReleaseImageClientNow(ImageClient* aClient)
|
||||
{
|
||||
MOZ_ASSERT(InImageBridgeChildThread());
|
||||
@ -521,7 +525,7 @@ ImageBridgeChild::EndTransaction()
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PImageBridgeChild*
|
||||
ImageBridgeChild::StartUpInChildProcess(Transport* aTransport,
|
||||
ProcessId aOtherProcess)
|
||||
{
|
||||
@ -529,21 +533,31 @@ ImageBridgeChild::StartUpInChildProcess(Transport* aTransport,
|
||||
|
||||
ProcessHandle processHandle;
|
||||
if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sImageBridgeChildThread = new Thread("ImageBridgeChild");
|
||||
if (!sImageBridgeChildThread->Start()) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
sImageBridgeChildThread
|
||||
->message_loop()->PostTask(FROM_HERE,
|
||||
NewRunnableFunction(NuwaMarkCurrentThread,
|
||||
(void (*)(void *))nullptr,
|
||||
(void *)nullptr));
|
||||
}
|
||||
#endif
|
||||
|
||||
sImageBridgeChildSingleton = new ImageBridgeChild();
|
||||
sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(ConnectImageBridgeInChildProcess,
|
||||
aTransport, processHandle));
|
||||
|
||||
return true;
|
||||
return sImageBridgeChildSingleton;
|
||||
}
|
||||
|
||||
void ImageBridgeChild::ShutDown()
|
||||
|
@ -114,7 +114,7 @@ public:
|
||||
*/
|
||||
static void StartUp();
|
||||
|
||||
static bool
|
||||
static PImageBridgeChild*
|
||||
StartUpInChildProcess(Transport* aTransport, ProcessId aOtherProcess);
|
||||
|
||||
/**
|
||||
|
@ -102,12 +102,12 @@ ConnectImageBridgeInParentProcess(ImageBridgeParent* aBridge,
|
||||
aBridge->Open(aTransport, aOtherProcess, XRE_GetIOMessageLoop(), ipc::ParentSide);
|
||||
}
|
||||
|
||||
/*static*/ bool
|
||||
/*static*/ PImageBridgeParent*
|
||||
ImageBridgeParent::Create(Transport* aTransport, ProcessId aOtherProcess)
|
||||
{
|
||||
ProcessHandle processHandle;
|
||||
if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MessageLoop* loop = CompositorParent::CompositorLoop();
|
||||
@ -116,7 +116,7 @@ ImageBridgeParent::Create(Transport* aTransport, ProcessId aOtherProcess)
|
||||
loop->PostTask(FROM_HERE,
|
||||
NewRunnableFunction(ConnectImageBridgeInParentProcess,
|
||||
bridge.get(), aTransport, processHandle));
|
||||
return true;
|
||||
return bridge.get();
|
||||
}
|
||||
|
||||
bool ImageBridgeParent::RecvStop()
|
||||
@ -185,5 +185,23 @@ ImageBridgeParent::DeferredDestroy()
|
||||
// |this| was just destroyed, hands off
|
||||
}
|
||||
|
||||
IToplevelProtocol*
|
||||
ImageBridgeParent::CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
|
||||
base::ProcessHandle aPeerProcess,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx)
|
||||
{
|
||||
for (unsigned int i = 0; i < aFds.Length(); i++) {
|
||||
if (aFds[i].protocolId() == (int)GetProtocolId()) {
|
||||
Transport* transport = OpenDescriptor(aFds[i].fd(),
|
||||
Transport::MODE_SERVER);
|
||||
PImageBridgeParent* bridge = Create(transport, base::GetProcId(aPeerProcess));
|
||||
bridge->CloneManagees(this, aCtx);
|
||||
bridge->IToplevelProtocol::SetTransport(transport);
|
||||
return bridge;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // layers
|
||||
} // mozilla
|
||||
|
@ -44,7 +44,7 @@ public:
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
|
||||
|
||||
static bool
|
||||
static PImageBridgeParent*
|
||||
Create(Transport* aTransport, ProcessId aOtherProcess);
|
||||
|
||||
virtual PGrallocBufferParent*
|
||||
@ -88,6 +88,12 @@ public:
|
||||
PImageBridgeParent::DeallocShmem(aShmem);
|
||||
}
|
||||
|
||||
// Overriden from IToplevelProtocol
|
||||
IToplevelProtocol*
|
||||
CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
|
||||
base::ProcessHandle aPeerProcess,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
void DeferredDestroy();
|
||||
|
||||
|
@ -7,6 +7,7 @@ include LayersSurfaces;
|
||||
include LayersMessages;
|
||||
include protocol PGrallocBuffer;
|
||||
include protocol PCompositable;
|
||||
include ProtocolTypes;
|
||||
|
||||
include "mozilla/layers/CompositorTypes.h";
|
||||
include "mozilla/GfxMessageUtils.h";
|
||||
|
@ -194,6 +194,7 @@ LockScreenOrientation(const ScreenOrientation& aOrientation)
|
||||
case eScreenOrientation_LandscapePrimary:
|
||||
case eScreenOrientation_LandscapeSecondary:
|
||||
case eScreenOrientation_LandscapePrimary | eScreenOrientation_LandscapeSecondary:
|
||||
case eScreenOrientation_Default:
|
||||
bridge->LockScreenOrientation(aOrientation);
|
||||
return true;
|
||||
default:
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "Hal.h"
|
||||
#include "mozilla/AppProcessChecker.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/hal_sandbox/PHalChild.h"
|
||||
#include "mozilla/hal_sandbox/PHalParent.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
@ -815,6 +816,18 @@ public:
|
||||
hal::FactoryReset();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual mozilla::ipc::IProtocol*
|
||||
CloneProtocol(Channel* aChannel,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE
|
||||
{
|
||||
ContentParent* contentParent = aCtx->GetContentParent();
|
||||
nsAutoPtr<PHalParent> actor(contentParent->AllocPHalParent());
|
||||
if (!actor || !contentParent->RecvPHalConstructor(actor)) {
|
||||
return nullptr;
|
||||
}
|
||||
return actor.forget();
|
||||
}
|
||||
};
|
||||
|
||||
class HalChild : public PHalChild {
|
||||
|
@ -43,6 +43,10 @@
|
||||
#include "gfx2DGlue.h"
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::image;
|
||||
using namespace mozilla::layers;
|
||||
@ -3055,6 +3059,37 @@ RasterImage::DecodePool::GetEventTarget()
|
||||
return target.forget();
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
|
||||
class RIDThreadPoolListener : public nsIThreadPoolListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITHREADPOOLLISTENER
|
||||
|
||||
RIDThreadPoolListener() {}
|
||||
~RIDThreadPoolListener() {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(RIDThreadPoolListener, nsIThreadPoolListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
RIDThreadPoolListener::OnThreadCreated()
|
||||
{
|
||||
if (IsNuwaProcess()) {
|
||||
NuwaMarkCurrentThread((void (*)(void *))nullptr, nullptr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RIDThreadPoolListener::OnThreadShuttingDown()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
RasterImage::DecodePool::DecodePool()
|
||||
: mThreadPoolMutex("Thread Pool")
|
||||
{
|
||||
@ -3072,6 +3107,12 @@ RasterImage::DecodePool::DecodePool()
|
||||
mThreadPool->SetThreadLimit(limit);
|
||||
mThreadPool->SetIdleThreadLimit(limit);
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
mThreadPool->SetListener(new RIDThreadPoolListener());
|
||||
}
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
|
||||
if (obsSvc) {
|
||||
obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
|
||||
|
@ -41,15 +41,14 @@
|
||||
|
||||
#endif
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// This creates a ThreadPool for binder ipc. A ThreadPool is necessary to
|
||||
// receive binder calls, though not necessary to send binder calls.
|
||||
// ProcessState::Self() also needs to be called once on the main thread to
|
||||
// register the main thread with the binder driver.
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include <binder/ProcessState.h>
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
static void
|
||||
InitializeBinder(void *aDummy) {
|
||||
// Change thread priority to 0 only during calling ProcessState::self().
|
||||
// The priority is registered to binder driver and used for default Binder
|
||||
// Thread's priority.
|
||||
@ -60,6 +59,38 @@ main(int argc, char* argv[])
|
||||
LOGE_IF(err, "setpriority failed. Current process needs root permission.");
|
||||
android::ProcessState::self()->startThreadPool();
|
||||
setpriority(PRIO_PROCESS, 0, curPrio);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
bool isNuwa = false;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-nuwa") == 0) {
|
||||
PrepareNuwaProcess();
|
||||
isNuwa = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// This creates a ThreadPool for binder ipc. A ThreadPool is necessary to
|
||||
// receive binder calls, though not necessary to send binder calls.
|
||||
// ProcessState::Self() also needs to be called once on the main thread to
|
||||
// register the main thread with the binder driver.
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (!isNuwa) {
|
||||
InitializeBinder(nullptr);
|
||||
} else {
|
||||
NuwaAddFinalConstructor(&InitializeBinder, nullptr);
|
||||
}
|
||||
#else
|
||||
InitializeBinder(nullptr);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(XP_WIN) && defined(DEBUG_bent)
|
||||
|
@ -49,7 +49,11 @@ PlatformThreadId PlatformThread::CurrentId() {
|
||||
mach_port_deallocate(mach_task_self(), port);
|
||||
return port;
|
||||
#elif defined(OS_LINUX)
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
return (intptr_t) (pthread_self());
|
||||
#else
|
||||
return syscall(__NR_gettid);
|
||||
#endif
|
||||
#elif defined(OS_OPENBSD) || defined(__GLIBC__)
|
||||
return (intptr_t) (pthread_self());
|
||||
#elif defined(OS_NETBSD)
|
||||
|
@ -248,6 +248,9 @@ bool DidProcessCrash(bool* child_exited, ProcessHandle handle) {
|
||||
int status;
|
||||
const int result = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
|
||||
if (result == -1) {
|
||||
// The dead process originally spawned from Nuwa might be taken as not
|
||||
// crashed because the above waitpid() call returns -1 and ECHILD. The
|
||||
// caller shouldn't behave incorrectly because of this false negative.
|
||||
LOG(ERROR) << "waitpid failed pid:" << handle << " errno:" << errno;
|
||||
if (child_exited)
|
||||
*child_exited = false;
|
||||
|
@ -106,6 +106,12 @@ class Thread : PlatformThread::Delegate {
|
||||
// The thread ID.
|
||||
PlatformThreadId thread_id() const { return thread_id_; }
|
||||
|
||||
// Reset thread ID as current thread.
|
||||
PlatformThreadId reset_thread_id() {
|
||||
thread_id_ = PlatformThread::CurrentId();
|
||||
return thread_id_;
|
||||
}
|
||||
|
||||
// Returns true if the thread has been started, and not yet stopped.
|
||||
// When a thread is running, the thread_id_ is non-zero.
|
||||
bool IsRunning() const { return thread_id_ != 0; }
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "base/waitable_event.h"
|
||||
#include "mozilla/ipc/ProcessChild.h"
|
||||
#include "mozilla/ipc/BrowserProcessSubThread.h"
|
||||
#include "mozilla/ipc/Transport.h"
|
||||
typedef mozilla::ipc::BrowserProcessSubThread ChromeThread;
|
||||
#include "chrome/common/ipc_logging.h"
|
||||
#include "chrome/common/notification_service.h"
|
||||
@ -19,6 +20,7 @@ typedef mozilla::ipc::BrowserProcessSubThread ChromeThread;
|
||||
#include "chrome/common/process_watcher.h"
|
||||
#include "chrome/common/result_codes.h"
|
||||
|
||||
using mozilla::ipc::FileDescriptor;
|
||||
|
||||
namespace {
|
||||
typedef std::list<ChildProcessHost*> ChildProcessList;
|
||||
@ -84,6 +86,22 @@ bool ChildProcessHost::CreateChannel() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChildProcessHost::CreateChannel(FileDescriptor& aFileDescriptor) {
|
||||
if (channel_.get()) {
|
||||
channel_->Close();
|
||||
}
|
||||
channel_.reset(mozilla::ipc::OpenDescriptor(
|
||||
aFileDescriptor, IPC::Channel::MODE_SERVER));
|
||||
channel_->set_listener(&listener_);
|
||||
if (!channel_->Connect()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
opening_channel_ = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ChildProcessHost::SetHandle(base::ProcessHandle process) {
|
||||
#if defined(OS_WIN)
|
||||
process_event_.reset(new base::WaitableEvent(process));
|
||||
|
@ -15,6 +15,12 @@
|
||||
#include "chrome/common/child_process_info.h"
|
||||
#include "chrome/common/ipc_channel.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
class FileDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationType;
|
||||
|
||||
// Plugins/workers and other child processes that live on the IO thread should
|
||||
@ -59,6 +65,8 @@ class ChildProcessHost :
|
||||
// Creates the IPC channel. Returns true iff it succeeded.
|
||||
bool CreateChannel();
|
||||
|
||||
bool CreateChannel(mozilla::ipc::FileDescriptor& aFileDescriptor);
|
||||
|
||||
// Once the subclass gets a handle to the process, it needs to tell
|
||||
// ChildProcessHost using this function.
|
||||
void SetHandle(base::ProcessHandle handle);
|
||||
|
@ -26,14 +26,35 @@ ChildThread::ChildThread(Thread::Options options)
|
||||
ChildThread::~ChildThread() {
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
bool ChildThread::Run() {
|
||||
return StartWithOptions(options_);
|
||||
bool r = StartWithOptions(options_);
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
NS_ASSERTION(NuwaMarkCurrentThread, "NuwaMarkCurrentThread is not defined!");
|
||||
if (IsNuwaProcess()) {
|
||||
message_loop()->PostTask(FROM_HERE,
|
||||
NewRunnableFunction(&ChildThread::MarkThread));
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
void ChildThread::OnChannelError() {
|
||||
owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
void ChildThread::MarkThread() {
|
||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
||||
if (!NuwaCheckpointCurrentThread()) {
|
||||
NS_RUNTIMEABORT("Should not be here!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ChildThread::Send(IPC::Message* msg) {
|
||||
if (!channel_.get()) {
|
||||
delete msg;
|
||||
|
@ -61,6 +61,10 @@ class ChildThread : public IPC::Channel::Listener,
|
||||
virtual void OnMessageReceived(const IPC::Message& msg);
|
||||
virtual void OnChannelError();
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
static void MarkThread();
|
||||
#endif
|
||||
|
||||
// The message loop used to run tasks on the thread that started this thread.
|
||||
MessageLoop* owner_loop_;
|
||||
|
||||
|
@ -117,8 +117,11 @@ class Channel : public Message::Sender {
|
||||
// socketpair() in which case this method returns -1 for both parameters.
|
||||
void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
|
||||
|
||||
// Return the server side of the socketpair.
|
||||
int GetServerFileDescriptor() const;
|
||||
// Return the file descriptor for communication with the peer.
|
||||
int GetFileDescriptor() const;
|
||||
|
||||
// Reset the file descriptor for communication with the peer.
|
||||
void ResetFileDescriptor(int fd);
|
||||
|
||||
// Close the client side of the socketpair.
|
||||
void CloseClientFileDescriptor();
|
||||
|
@ -356,6 +356,15 @@ bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id,
|
||||
return EnqueueHelloMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the file descriptor for communication with the peer.
|
||||
*/
|
||||
void Channel::ChannelImpl::ResetFileDescriptor(int fd) {
|
||||
NS_ASSERTION(fd > 0 && fd == pipe_, "Invalid file descriptor");
|
||||
|
||||
EnqueueHelloMessage();
|
||||
}
|
||||
|
||||
bool Channel::ChannelImpl::EnqueueHelloMessage() {
|
||||
scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE,
|
||||
HELLO_MESSAGE_TYPE,
|
||||
@ -971,8 +980,12 @@ void Channel::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const {
|
||||
return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd);
|
||||
}
|
||||
|
||||
int Channel::GetServerFileDescriptor() const {
|
||||
return channel_impl_->GetServerFileDescriptor();
|
||||
void Channel::ResetFileDescriptor(int fd) {
|
||||
channel_impl_->ResetFileDescriptor(fd);
|
||||
}
|
||||
|
||||
int Channel::GetFileDescriptor() const {
|
||||
return channel_impl_->GetFileDescriptor();
|
||||
}
|
||||
|
||||
void Channel::CloseClientFileDescriptor() {
|
||||
|
@ -36,9 +36,11 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
|
||||
}
|
||||
bool Send(Message* message);
|
||||
void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
|
||||
int GetServerFileDescriptor() const {
|
||||
DCHECK(mode_ == MODE_SERVER);
|
||||
return pipe_;
|
||||
|
||||
void ResetFileDescriptor(int fd);
|
||||
|
||||
int GetFileDescriptor() const {
|
||||
return pipe_;
|
||||
}
|
||||
void CloseClientFileDescriptor();
|
||||
|
||||
|
@ -34,6 +34,10 @@
|
||||
#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
|
||||
#endif
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
using mozilla::MonitorAutoLock;
|
||||
using mozilla::ipc::GeckoChildProcessHost;
|
||||
|
||||
@ -89,11 +93,6 @@ GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
|
||||
#endif
|
||||
{
|
||||
MOZ_COUNT_CTOR(GeckoChildProcessHost);
|
||||
|
||||
MessageLoop* ioLoop = XRE_GetIOMessageLoop();
|
||||
ioLoop->PostTask(FROM_HERE,
|
||||
NewRunnableMethod(this,
|
||||
&GeckoChildProcessHost::InitializeChannel));
|
||||
}
|
||||
|
||||
GeckoChildProcessHost::~GeckoChildProcessHost()
|
||||
@ -287,7 +286,7 @@ GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTime
|
||||
|
||||
ioLoop->PostTask(FROM_HERE,
|
||||
NewRunnableMethod(this,
|
||||
&GeckoChildProcessHost::PerformAsyncLaunch,
|
||||
&GeckoChildProcessHost::RunPerformAsyncLaunch,
|
||||
aExtraOpts, arch));
|
||||
// NB: this uses a different mechanism than the chromium parent
|
||||
// class.
|
||||
@ -322,7 +321,7 @@ GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts)
|
||||
MessageLoop* ioLoop = XRE_GetIOMessageLoop();
|
||||
ioLoop->PostTask(FROM_HERE,
|
||||
NewRunnableMethod(this,
|
||||
&GeckoChildProcessHost::PerformAsyncLaunch,
|
||||
&GeckoChildProcessHost::RunPerformAsyncLaunch,
|
||||
aExtraOpts, base::GetCurrentProcessArchitecture()));
|
||||
|
||||
// This may look like the sync launch wait, but we only delay as
|
||||
@ -343,7 +342,7 @@ GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts)
|
||||
MessageLoop* ioLoop = XRE_GetIOMessageLoop();
|
||||
ioLoop->PostTask(FROM_HERE,
|
||||
NewRunnableMethod(this,
|
||||
&GeckoChildProcessHost::PerformAsyncLaunch,
|
||||
&GeckoChildProcessHost::RunPerformAsyncLaunch,
|
||||
aExtraOpts, base::GetCurrentProcessArchitecture()));
|
||||
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
@ -425,6 +424,14 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts, b
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool
|
||||
GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector<std::string> aExtraOpts,
|
||||
base::ProcessArchitecture aArch)
|
||||
{
|
||||
InitializeChannel();
|
||||
return PerformAsyncLaunch(aExtraOpts, aArch);
|
||||
}
|
||||
|
||||
void
|
||||
#if defined(XP_WIN)
|
||||
AddAppDirToCommandLine(CommandLine& aCmdLine)
|
||||
@ -833,3 +840,52 @@ GeckoChildProcessHost::OnWaitableEventSignaled(base::WaitableEvent *event)
|
||||
}
|
||||
ChildProcessHost::OnWaitableEventSignaled(event);
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
|
||||
using mozilla::ipc::GeckoExistingProcessHost;
|
||||
using mozilla::ipc::FileDescriptor;
|
||||
|
||||
GeckoExistingProcessHost::
|
||||
GeckoExistingProcessHost(GeckoProcessType aProcessType,
|
||||
base::ProcessHandle aProcess,
|
||||
const FileDescriptor& aFileDescriptor,
|
||||
ChildPrivileges aPrivileges)
|
||||
: GeckoChildProcessHost(aProcessType, aPrivileges)
|
||||
, mExistingProcessHandle(aProcess)
|
||||
, mExistingFileDescriptor(aFileDescriptor)
|
||||
{
|
||||
NS_ASSERTION(aFileDescriptor.IsValid(),
|
||||
"Expected file descriptor to be valid");
|
||||
}
|
||||
|
||||
GeckoExistingProcessHost::~GeckoExistingProcessHost()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
GeckoExistingProcessHost::PerformAsyncLaunch(StringVector aExtraOpts,
|
||||
base::ProcessArchitecture aArch)
|
||||
{
|
||||
SetHandle(mExistingProcessHandle);
|
||||
|
||||
OpenPrivilegedHandle(base::GetProcId(mExistingProcessHandle));
|
||||
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
mProcessState = PROCESS_CREATED;
|
||||
lock.Notify();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
GeckoExistingProcessHost::InitializeChannel()
|
||||
{
|
||||
CreateChannel(mExistingFileDescriptor);
|
||||
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
mProcessState = CHANNEL_INITIALIZED;
|
||||
lock.Notify();
|
||||
}
|
||||
|
||||
#endif /* MOZ_NUWA_PROCESS */
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "base/waitable_event.h"
|
||||
#include "chrome/common/child_process_host.h"
|
||||
|
||||
#include "mozilla/ipc/FileDescriptor.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
#include "nsXULAppAPI.h" // for GeckoProcessType
|
||||
@ -66,15 +67,15 @@ public:
|
||||
int32_t timeoutMs=0,
|
||||
base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture());
|
||||
|
||||
bool PerformAsyncLaunch(StringVector aExtraOpts=StringVector(),
|
||||
base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture());
|
||||
virtual bool PerformAsyncLaunch(StringVector aExtraOpts=StringVector(),
|
||||
base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture());
|
||||
|
||||
virtual void OnChannelConnected(int32_t peer_pid);
|
||||
virtual void OnMessageReceived(const IPC::Message& aMsg);
|
||||
virtual void OnChannelError();
|
||||
virtual void GetQueuedMessages(std::queue<IPC::Message>& queue);
|
||||
|
||||
void InitializeChannel();
|
||||
virtual void InitializeChannel();
|
||||
|
||||
virtual bool CanShutdown() { return true; }
|
||||
|
||||
@ -92,6 +93,10 @@ public:
|
||||
return mChildProcessHandle;
|
||||
}
|
||||
|
||||
GeckoProcessType GetProcessType() {
|
||||
return mProcessType;
|
||||
}
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
task_t GetChildTask() {
|
||||
return mChildTask;
|
||||
@ -146,6 +151,8 @@ protected:
|
||||
task_t mChildTask;
|
||||
#endif
|
||||
|
||||
void OpenPrivilegedHandle(base::ProcessId aPid);
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(GeckoChildProcessHost);
|
||||
|
||||
@ -153,7 +160,8 @@ private:
|
||||
bool PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts,
|
||||
base::ProcessArchitecture arch);
|
||||
|
||||
void OpenPrivilegedHandle(base::ProcessId aPid);
|
||||
bool RunPerformAsyncLaunch(StringVector aExtraOpts=StringVector(),
|
||||
base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture());
|
||||
|
||||
// In between launching the subprocess and handing off its IPC
|
||||
// channel, there's a small window of time in which *we* might still
|
||||
@ -165,6 +173,28 @@ private:
|
||||
std::queue<IPC::Message> mQueue;
|
||||
};
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
class GeckoExistingProcessHost MOZ_FINAL : public GeckoChildProcessHost
|
||||
{
|
||||
public:
|
||||
GeckoExistingProcessHost(GeckoProcessType aProcessType,
|
||||
base::ProcessHandle aProcess,
|
||||
const FileDescriptor& aFileDescriptor,
|
||||
ChildPrivileges aPrivileges=base::PRIVILEGES_DEFAULT);
|
||||
|
||||
~GeckoExistingProcessHost();
|
||||
|
||||
virtual bool PerformAsyncLaunch(StringVector aExtraOpts=StringVector(),
|
||||
base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture()) MOZ_OVERRIDE;
|
||||
|
||||
virtual void InitializeChannel() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
base::ProcessHandle mExistingProcessHandle;
|
||||
mozilla::ipc::FileDescriptor mExistingFileDescriptor;
|
||||
};
|
||||
#endif /* MOZ_NUWA_PROCESS */
|
||||
|
||||
} /* namespace ipc */
|
||||
} /* namespace mozilla */
|
||||
|
||||
|
@ -18,6 +18,10 @@
|
||||
#include "AndroidBridge.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
using mozilla::ipc::DoWorkRunnable;
|
||||
using mozilla::ipc::MessagePump;
|
||||
using mozilla::ipc::MessagePumpForChildProcess;
|
||||
@ -96,7 +100,11 @@ MessagePump::Run(MessagePump::Delegate* aDelegate)
|
||||
|
||||
did_work |= aDelegate->DoDelayedWork(&delayed_work_time_);
|
||||
|
||||
if (did_work && delayed_work_time_.is_null())
|
||||
if (did_work && delayed_work_time_.is_null()
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
&& (!IsNuwaReady() || !IsNuwaProcess())
|
||||
#endif
|
||||
)
|
||||
mDelayedWorkTimer->Cancel();
|
||||
|
||||
if (!keep_running_)
|
||||
@ -116,7 +124,10 @@ MessagePump::Run(MessagePump::Delegate* aDelegate)
|
||||
NS_ProcessNextEvent(mThread, true);
|
||||
}
|
||||
|
||||
mDelayedWorkTimer->Cancel();
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (!IsNuwaReady() || !IsNuwaProcess())
|
||||
#endif
|
||||
mDelayedWorkTimer->Cancel();
|
||||
|
||||
keep_running_ = true;
|
||||
}
|
||||
@ -148,6 +159,11 @@ MessagePump::ScheduleWorkForNestedLoop()
|
||||
void
|
||||
MessagePump::ScheduleDelayedWork(const base::TimeTicks& aDelayedTime)
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaReady() && IsNuwaProcess())
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (!mDelayedWorkTimer) {
|
||||
mDelayedWorkTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
if (!mDelayedWorkTimer) {
|
||||
|
18
ipc/glue/ProtocolTypes.ipdlh
Normal file
18
ipc/glue/ProtocolTypes.ipdlh
Normal file
@ -0,0 +1,18 @@
|
||||
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 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/. */
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
struct ProtocolFdMapping
|
||||
{
|
||||
uint32_t protocolId;
|
||||
FileDescriptor fd;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,48 @@ using namespace IPC;
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
IToplevelProtocol::~IToplevelProtocol()
|
||||
{
|
||||
mOpenActors.clear();
|
||||
}
|
||||
|
||||
void IToplevelProtocol::AddOpenedActor(IToplevelProtocol* aActor)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
for (const IToplevelProtocol* actor = mOpenActors.getFirst();
|
||||
actor;
|
||||
actor = actor->getNext()) {
|
||||
NS_ASSERTION(actor != aActor,
|
||||
"Open the same protocol for more than one time");
|
||||
}
|
||||
#endif
|
||||
|
||||
mOpenActors.insertBack(aActor);
|
||||
}
|
||||
|
||||
IToplevelProtocol*
|
||||
IToplevelProtocol::CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
|
||||
base::ProcessHandle aPeerProcess,
|
||||
ProtocolCloneContext* aCtx)
|
||||
{
|
||||
NS_NOTREACHED("Clone() for this protocol actor is not implemented");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
IToplevelProtocol::CloneOpenedToplevels(IToplevelProtocol* aTemplate,
|
||||
const InfallibleTArray<ProtocolFdMapping>& aFds,
|
||||
base::ProcessHandle aPeerProcess,
|
||||
ProtocolCloneContext* aCtx)
|
||||
{
|
||||
for (IToplevelProtocol* actor = aTemplate->GetFirstOpenedActors();
|
||||
actor;
|
||||
actor = actor->getNext()) {
|
||||
IToplevelProtocol* newactor = actor->CloneToplevel(aFds, aPeerProcess, aCtx);
|
||||
AddOpenedActor(newactor);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelOpened : public IPC::Message
|
||||
{
|
||||
public:
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "mozilla/ipc/FileDescriptor.h"
|
||||
#include "mozilla/ipc/Shmem.h"
|
||||
#include "mozilla/ipc/Transport.h"
|
||||
#include "mozilla/ipc/MessageLink.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
|
||||
// WARNING: this takes into account the private, special-message-type
|
||||
// enum in ipc_channel.h. They need to be kept in sync.
|
||||
@ -40,9 +42,18 @@ enum {
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class ContentParent;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
class NeckoParent;
|
||||
}
|
||||
|
||||
namespace ipc {
|
||||
|
||||
class MessageChannel;
|
||||
class ProtocolFdMapping;
|
||||
class ProtocolCloneContext;
|
||||
|
||||
// Used to pass references to protocol actors across the wire.
|
||||
// Actors created on the parent-side have a positive ID, and actors
|
||||
@ -70,6 +81,35 @@ struct Trigger
|
||||
int32_t mMsg;
|
||||
};
|
||||
|
||||
class ProtocolCloneContext
|
||||
{
|
||||
typedef mozilla::dom::ContentParent ContentParent;
|
||||
typedef mozilla::net::NeckoParent NeckoParent;
|
||||
|
||||
ContentParent* mContentParent;
|
||||
NeckoParent* mNeckoParent;
|
||||
|
||||
public:
|
||||
ProtocolCloneContext()
|
||||
: mContentParent(nullptr)
|
||||
, mNeckoParent(nullptr)
|
||||
{}
|
||||
|
||||
void SetContentParent(ContentParent* aContentParent)
|
||||
{
|
||||
mContentParent = aContentParent;
|
||||
}
|
||||
|
||||
ContentParent* GetContentParent() { return mContentParent; }
|
||||
|
||||
void SetNeckoParent(NeckoParent* aNeckoParent)
|
||||
{
|
||||
mNeckoParent = aNeckoParent;
|
||||
}
|
||||
|
||||
NeckoParent* GetNeckoParent() { return mNeckoParent; }
|
||||
};
|
||||
|
||||
template<class ListenerT>
|
||||
class /*NS_INTERFACE_CLASS*/ IProtocolManager
|
||||
{
|
||||
@ -100,6 +140,90 @@ public:
|
||||
// XXX odd ducks, acknowledged
|
||||
virtual ProcessHandle OtherProcess() const = 0;
|
||||
virtual MessageChannel* GetIPCChannel() = 0;
|
||||
|
||||
// The implementation of function is generated by code generator.
|
||||
virtual void CloneManagees(ListenerT* aSource,
|
||||
ProtocolCloneContext* aCtx) = 0;
|
||||
};
|
||||
|
||||
typedef IPCMessageStart ProtocolId;
|
||||
|
||||
/**
|
||||
* All RPC protocols should implement this interface.
|
||||
*/
|
||||
class IProtocol : protected MessageListener
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This function is used to clone this protocol actor.
|
||||
*
|
||||
* see IProtocol::CloneProtocol()
|
||||
*/
|
||||
virtual IProtocol*
|
||||
CloneProtocol(MessageChannel* aChannel,
|
||||
ProtocolCloneContext* aCtx) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* All top-level protocols should inherit this class.
|
||||
*
|
||||
* IToplevelProtocol tracks all top-level protocol actors created from
|
||||
* this protocol actor.
|
||||
*/
|
||||
class IToplevelProtocol : public LinkedListElement<IToplevelProtocol>
|
||||
{
|
||||
protected:
|
||||
IToplevelProtocol(ProtocolId aProtoId)
|
||||
: mProtocolId(aProtoId)
|
||||
, mTrans(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~IToplevelProtocol();
|
||||
|
||||
/**
|
||||
* Add an actor to the list of actors that have been opened by this
|
||||
* protocol.
|
||||
*/
|
||||
void AddOpenedActor(IToplevelProtocol* aActor);
|
||||
|
||||
public:
|
||||
void SetTransport(Transport* aTrans)
|
||||
{
|
||||
mTrans = aTrans;
|
||||
}
|
||||
|
||||
Transport* GetTransport() const { return mTrans; }
|
||||
|
||||
ProtocolId GetProtocolId() const { return mProtocolId; }
|
||||
|
||||
/**
|
||||
* Return first of actors of top level protocols opened by this one.
|
||||
*/
|
||||
IToplevelProtocol* GetFirstOpenedActors()
|
||||
{
|
||||
return mOpenActors.getFirst();
|
||||
}
|
||||
const IToplevelProtocol* GetFirstOpenedActors() const
|
||||
{
|
||||
return mOpenActors.getFirst();
|
||||
}
|
||||
|
||||
virtual IToplevelProtocol*
|
||||
CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
|
||||
base::ProcessHandle aPeerProcess,
|
||||
ProtocolCloneContext* aCtx);
|
||||
|
||||
void CloneOpenedToplevels(IToplevelProtocol* aTemplate,
|
||||
const InfallibleTArray<ProtocolFdMapping>& aFds,
|
||||
base::ProcessHandle aPeerProcess,
|
||||
ProtocolCloneContext* aCtx);
|
||||
|
||||
private:
|
||||
LinkedList<IToplevelProtocol> mOpenActors; // All protocol actors opened by this.
|
||||
|
||||
ProtocolId mProtocolId;
|
||||
Transport* mTrans;
|
||||
};
|
||||
|
||||
|
||||
@ -120,8 +244,6 @@ MOZ_NEVER_INLINE void
|
||||
FatalError(const char* aProtocolName, const char* aMsg,
|
||||
base::ProcessHandle aHandle, bool aIsParent);
|
||||
|
||||
typedef IPCMessageStart ProtocolId;
|
||||
|
||||
struct PrivateIPDLInterface {};
|
||||
|
||||
bool
|
||||
|
@ -20,6 +20,7 @@
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
class FileDescriptor;
|
||||
|
||||
typedef IPC::Channel Transport;
|
||||
|
||||
@ -29,6 +30,9 @@ bool CreateTransport(base::ProcessHandle aProcOne, base::ProcessHandle aProcTwo,
|
||||
Transport* OpenDescriptor(const TransportDescriptor& aTd,
|
||||
Transport::Mode aMode);
|
||||
|
||||
Transport* OpenDescriptor(const FileDescriptor& aFd,
|
||||
Transport::Mode aMode);
|
||||
|
||||
void CloseDescriptor(const TransportDescriptor& aTd);
|
||||
|
||||
} // namespace ipc
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "chrome/common/child_process_info.h"
|
||||
|
||||
#include "mozilla/ipc/Transport.h"
|
||||
#include "mozilla/ipc/FileDescriptor.h"
|
||||
|
||||
using namespace base;
|
||||
using namespace std;
|
||||
@ -29,7 +30,7 @@ CreateTransport(ProcessHandle /*unused*/, ProcessHandle /*unused*/,
|
||||
wstring id = ChildProcessInfo::GenerateRandomChannelID(aOne);
|
||||
// Use MODE_SERVER to force creation of the socketpair
|
||||
Transport t(id, Transport::MODE_SERVER, nullptr);
|
||||
int fd1 = t.GetServerFileDescriptor();
|
||||
int fd1 = t.GetFileDescriptor();
|
||||
int fd2, dontcare;
|
||||
t.GetClientFileDescriptorMapping(&fd2, &dontcare);
|
||||
if (fd1 < 0 || fd2 < 0) {
|
||||
@ -44,8 +45,8 @@ CreateTransport(ProcessHandle /*unused*/, ProcessHandle /*unused*/,
|
||||
return false;
|
||||
}
|
||||
|
||||
aOne->mFd = FileDescriptor(fd1, true/*close after sending*/);
|
||||
aTwo->mFd = FileDescriptor(fd2, true/*close after sending*/);
|
||||
aOne->mFd = base::FileDescriptor(fd1, true/*close after sending*/);
|
||||
aTwo->mFd = base::FileDescriptor(fd2, true/*close after sending*/);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -55,6 +56,12 @@ OpenDescriptor(const TransportDescriptor& aTd, Transport::Mode aMode)
|
||||
return new Transport(aTd.mFd.fd, aMode, nullptr);
|
||||
}
|
||||
|
||||
Transport*
|
||||
OpenDescriptor(const FileDescriptor& aFd, Transport::Mode aMode)
|
||||
{
|
||||
return new Transport(aFd.PlatformHandle(), aMode, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
CloseDescriptor(const TransportDescriptor& aTd)
|
||||
{
|
||||
|
@ -58,6 +58,13 @@ OpenDescriptor(const TransportDescriptor& aTd, Transport::Mode aMode)
|
||||
return new Transport(aTd.mPipeName, aTd.mServerPipe, aMode, nullptr);
|
||||
}
|
||||
|
||||
Transport*
|
||||
OpenDescriptor(const FileDescriptor& aFd, Transport::Mode aMode)
|
||||
{
|
||||
NS_NOTREACHED("Not implemented!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
CloseDescriptor(const TransportDescriptor& aTd)
|
||||
{
|
||||
|
@ -96,6 +96,7 @@ CPP_SOURCES += [
|
||||
|
||||
IPDL_SOURCES = [
|
||||
'InputStreamParams.ipdlh',
|
||||
'ProtocolTypes.ipdlh',
|
||||
'URIParams.ipdlh',
|
||||
]
|
||||
|
||||
|
@ -1027,10 +1027,17 @@ class Protocol(ipdl.ast.Protocol):
|
||||
def fqListenerName(self):
|
||||
return 'mozilla::ipc::MessageListener'
|
||||
|
||||
def fqBaseClass(self):
|
||||
return 'mozilla::ipc::IProtocol'
|
||||
|
||||
def managerInterfaceType(self, ptr=0):
|
||||
return Type('mozilla::ipc::IProtocolManager',
|
||||
ptr=ptr,
|
||||
T=Type(self.fqListenerName()))
|
||||
T=Type(self.fqBaseClass()))
|
||||
|
||||
def openedProtocolInterfaceType(self, ptr=0):
|
||||
return Type('mozilla::ipc::IToplevelProtocol',
|
||||
ptr=ptr)
|
||||
|
||||
def _ipdlmgrtype(self):
|
||||
assert 1 == len(self.decl.type.managers)
|
||||
@ -1099,6 +1106,12 @@ class Protocol(ipdl.ast.Protocol):
|
||||
fn = ExprSelect(actorThis, '->', fn.name)
|
||||
return ExprCall(fn)
|
||||
|
||||
def cloneManagees(self):
|
||||
return ExprVar('CloneManagees')
|
||||
|
||||
def cloneProtocol(self):
|
||||
return ExprVar('CloneProtocol')
|
||||
|
||||
def processingErrorVar(self):
|
||||
assert self.decl.type.isToplevel()
|
||||
return ExprVar('ProcessingError')
|
||||
@ -1172,7 +1185,9 @@ class Protocol(ipdl.ast.Protocol):
|
||||
assert not self.decl.type.isToplevel()
|
||||
return ExprVar('mId')
|
||||
|
||||
def stateVar(self):
|
||||
def stateVar(self, actorThis=None):
|
||||
if actorThis is not None:
|
||||
return ExprSelect(actorThis, '->', 'mState')
|
||||
return ExprVar('mState')
|
||||
|
||||
def fqStateType(self):
|
||||
@ -2452,13 +2467,16 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
|
||||
def standardTypedefs(self):
|
||||
return [
|
||||
Typedef(Type(self.protocol.fqBaseClass()), 'ProtocolBase'),
|
||||
Typedef(Type('IPC::Message'), 'Message'),
|
||||
Typedef(Type(self.protocol.channelName()), 'Channel'),
|
||||
Typedef(Type(self.protocol.fqListenerName()), 'ChannelListener'),
|
||||
Typedef(Type('base::ProcessHandle'), 'ProcessHandle'),
|
||||
Typedef(Type('mozilla::ipc::MessageChannel'), 'MessageChannel'),
|
||||
Typedef(Type('mozilla::ipc::SharedMemory'), 'SharedMemory'),
|
||||
Typedef(Type('mozilla::ipc::Trigger'), 'Trigger')
|
||||
Typedef(Type('mozilla::ipc::Trigger'), 'Trigger'),
|
||||
Typedef(Type('mozilla::ipc::ProtocolCloneContext'),
|
||||
'ProtocolCloneContext')
|
||||
]
|
||||
|
||||
|
||||
@ -2604,6 +2622,10 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
CppDirective('include', '"'+ p.channelHeaderFile() +'"'),
|
||||
Whitespace.NL ])
|
||||
|
||||
optinherits = []
|
||||
if ptype.isToplevel():
|
||||
optinherits.append(Inherit(p.openedProtocolInterfaceType(),
|
||||
viz='public'))
|
||||
if ptype.isToplevel() and self.side is 'parent':
|
||||
self.hdrfile.addthings([
|
||||
_makeForwardDeclForQClass('nsIFile', []),
|
||||
@ -2612,8 +2634,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
|
||||
self.cls = Class(
|
||||
self.clsname,
|
||||
inherits=[ Inherit(Type(p.fqListenerName()), viz='protected'),
|
||||
Inherit(p.managerInterfaceType(), viz='protected') ],
|
||||
inherits=[ Inherit(Type(p.fqBaseClass()), viz='public'),
|
||||
Inherit(p.managerInterfaceType(), viz='protected') ] +
|
||||
optinherits,
|
||||
abstract=True)
|
||||
|
||||
bridgeActorsCreated = ProcessGraph.bridgeEndpointsOf(ptype, self.side)
|
||||
@ -2715,7 +2738,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
_allocMethod(actor.ptype, actor.side).name,
|
||||
params=[ Decl(Type('Transport', ptr=1), 'transport'),
|
||||
Decl(Type('ProcessId'), 'otherProcess') ],
|
||||
ret=Type.BOOL,
|
||||
ret=actortype,
|
||||
virtual=1, pure=1)))
|
||||
|
||||
# optional ActorDestroy() method; default is no-op
|
||||
@ -2778,6 +2801,10 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
ExprMemberInit(p.stateVar(),
|
||||
[ p.startState() ])
|
||||
]
|
||||
if ptype.isToplevel():
|
||||
ctor.memberinits = [ExprMemberInit(
|
||||
p.openedProtocolInterfaceType(),
|
||||
[ _protocolId(ptype) ])] + ctor.memberinits
|
||||
else:
|
||||
ctor.memberinits = [
|
||||
ExprMemberInit(p.idVar(), [ ExprLiteral.ZERO ]),
|
||||
@ -3370,7 +3397,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
self.cls.addstmt(StmtDecl(Decl(p.channelType(), 'mChannel')))
|
||||
if ptype.isToplevel():
|
||||
self.cls.addstmts([
|
||||
StmtDecl(Decl(Type('IDMap', T=Type('ChannelListener')),
|
||||
StmtDecl(Decl(Type('IDMap', T=Type('ProtocolBase')),
|
||||
p.actorMapVar().name)),
|
||||
StmtDecl(Decl(_actorIdType(), p.lastActorIdVar().name)),
|
||||
StmtDecl(Decl(Type('ProcessHandle'),
|
||||
@ -3406,22 +3433,28 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
sizevar = ExprVar('aSize')
|
||||
typevar = ExprVar('aType')
|
||||
unsafevar = ExprVar('aUnsafe')
|
||||
listenertype = Type('ChannelListener', ptr=1)
|
||||
protocolbase = Type('ProtocolBase', ptr=1)
|
||||
sourcevar = ExprVar('aSource')
|
||||
ivar = ExprVar('i')
|
||||
kidsvar = ExprVar('kids')
|
||||
ithkid = ExprIndex(kidsvar, ivar)
|
||||
clonecontexttype = Type('ProtocolCloneContext', ptr=1)
|
||||
clonecontextvar = ExprVar('aCtx')
|
||||
|
||||
register = MethodDefn(MethodDecl(
|
||||
p.registerMethod().name,
|
||||
params=[ Decl(listenertype, routedvar.name) ],
|
||||
params=[ Decl(protocolbase, routedvar.name) ],
|
||||
ret=_actorIdType(), virtual=1))
|
||||
registerid = MethodDefn(MethodDecl(
|
||||
p.registerIDMethod().name,
|
||||
params=[ Decl(listenertype, routedvar.name),
|
||||
params=[ Decl(protocolbase, routedvar.name),
|
||||
Decl(_actorIdType(), idvar.name) ],
|
||||
ret=_actorIdType(),
|
||||
virtual=1))
|
||||
lookup = MethodDefn(MethodDecl(
|
||||
p.lookupIDMethod().name,
|
||||
params=[ Decl(_actorIdType(), idvar.name) ],
|
||||
ret=listenertype, virtual=1))
|
||||
ret=protocolbase, virtual=1))
|
||||
unregister = MethodDefn(MethodDecl(
|
||||
p.unregisterMethod().name,
|
||||
params=[ Decl(_actorIdType(), idvar.name) ],
|
||||
@ -3467,6 +3500,19 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
ret=Type('MessageChannel', ptr=1),
|
||||
virtual=1))
|
||||
|
||||
clonemanagees = MethodDefn(MethodDecl(
|
||||
p.cloneManagees().name,
|
||||
params=[ Decl(protocolbase, sourcevar.name),
|
||||
Decl(clonecontexttype, clonecontextvar.name) ],
|
||||
virtual=1))
|
||||
|
||||
cloneprotocol = MethodDefn(MethodDecl(
|
||||
p.cloneProtocol().name,
|
||||
params=[ Decl(Type('Channel', ptr=True), 'aChannel'),
|
||||
Decl(clonecontexttype, clonecontextvar.name) ],
|
||||
ret=Type(p.fqBaseClass(), ptr=1),
|
||||
virtual=1))
|
||||
|
||||
if p.decl.type.isToplevel():
|
||||
tmpvar = ExprVar('tmp')
|
||||
|
||||
@ -3683,13 +3729,82 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
p.callOtherProcess(p.managerVar())))
|
||||
getchannel.addstmt(StmtReturn(p.channelVar()))
|
||||
|
||||
cloneprotocol.addstmts([
|
||||
_runtimeAbort('Clone() for ' +
|
||||
p.name +
|
||||
' has not yet been implemented'),
|
||||
StmtReturn(ExprLiteral.NULL)
|
||||
])
|
||||
|
||||
othervar = ExprVar('other')
|
||||
managertype = Type(_actorName(p.name, self.side), ptr=1)
|
||||
otherstmt = StmtDecl(Decl(managertype,
|
||||
othervar.name),
|
||||
init=ExprCast(sourcevar,
|
||||
managertype,
|
||||
static=1))
|
||||
clonemanagees.addstmt(otherstmt)
|
||||
actorvar = ExprVar('actor')
|
||||
for managee in p.managesStmts:
|
||||
block = StmtBlock()
|
||||
manageeipdltype = managee.decl.type
|
||||
actortype = ipdl.type.ActorType(manageeipdltype)
|
||||
manageecxxtype = _cxxBareType(actortype, self.side)
|
||||
manageearray = p.managedVar(manageeipdltype, self.side)
|
||||
abortstmt = StmtIf(ExprBinary(actorvar, '==', ExprLiteral.NULL))
|
||||
abortstmt.addifstmts([
|
||||
_runtimeAbort('can not clone an ' + actortype.name() + ' actor'),
|
||||
StmtReturn()])
|
||||
forstmt = StmtFor(
|
||||
init=Param(Type.UINT32, ivar.name, ExprLiteral.ZERO),
|
||||
cond=ExprBinary(ivar, '<', _callCxxArrayLength(kidsvar)),
|
||||
update=ExprPrefixUnop(ivar, '++'))
|
||||
forstmt.addstmts([
|
||||
StmtExpr(ExprAssn(
|
||||
actorvar,
|
||||
ExprCast(
|
||||
ExprCall(
|
||||
ExprSelect(ithkid,
|
||||
'->',
|
||||
p.cloneProtocol().name),
|
||||
args=[ p.channelForSubactor(),
|
||||
clonecontextvar ]),
|
||||
manageecxxtype,
|
||||
static=1))),
|
||||
abortstmt,
|
||||
StmtExpr(ExprAssn(_actorId(actorvar), _actorId(ithkid))),
|
||||
StmtExpr(ExprAssn(_actorManager(actorvar), ExprVar.THIS)),
|
||||
StmtExpr(ExprAssn(
|
||||
_actorChannel(actorvar),
|
||||
p.channelForSubactor())),
|
||||
StmtExpr(ExprAssn(_actorState(actorvar), _actorState(ithkid))),
|
||||
StmtExpr(_callCxxArrayInsertSorted(manageearray, actorvar)),
|
||||
StmtExpr(ExprCall(p.registerIDMethod(),
|
||||
args=[actorvar, _actorId(actorvar)])),
|
||||
StmtExpr(ExprCall(
|
||||
ExprSelect(actorvar,
|
||||
'->',
|
||||
p.cloneManagees().name),
|
||||
args=[ ithkid, clonecontextvar ]))
|
||||
])
|
||||
block.addstmts([
|
||||
StmtDecl(Decl(_cxxArrayType(manageecxxtype, ref=0),
|
||||
kidsvar.name)),
|
||||
StmtExpr(ExprAssn(kidsvar,
|
||||
ExprSelect(othervar,
|
||||
'->',
|
||||
manageearray.name))),
|
||||
StmtDecl(Decl(manageecxxtype, actorvar.name)),
|
||||
forstmt])
|
||||
clonemanagees.addstmt(block)
|
||||
|
||||
# all protocols share the "same" RemoveManagee() implementation
|
||||
pvar = ExprVar('aProtocolId')
|
||||
listenervar = ExprVar('aListener')
|
||||
removemanagee = MethodDefn(MethodDecl(
|
||||
p.removeManageeMethod().name,
|
||||
params=[ Decl(_protocolIdType(), pvar.name),
|
||||
Decl(listenertype, listenervar.name) ],
|
||||
Decl(protocolbase, listenervar.name) ],
|
||||
virtual=1))
|
||||
|
||||
if not len(p.managesStmts):
|
||||
@ -3735,6 +3850,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
destroyshmem,
|
||||
otherprocess,
|
||||
getchannel,
|
||||
clonemanagees,
|
||||
cloneprotocol,
|
||||
Whitespace.NL ]
|
||||
|
||||
def makeShmemIface(self):
|
||||
@ -3968,15 +4085,30 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
args=[ tdvar, modevar ]))))
|
||||
iffailopen.addifstmt(StmtReturn(_Result.ValuError))
|
||||
|
||||
iffailalloc = StmtIf(ExprNot(ExprCall(
|
||||
_allocMethod(actor.ptype, actor.side),
|
||||
args=[ tvar, pidvar ])))
|
||||
pvar = ExprVar('p')
|
||||
iffailalloc = StmtIf(ExprNot(ExprAssn(
|
||||
pvar,
|
||||
ExprCall(
|
||||
_allocMethod(actor.ptype, actor.side),
|
||||
args=[ tvar, pidvar ]))))
|
||||
iffailalloc.addifstmt(StmtReturn(_Result.ProcessingError))
|
||||
|
||||
settrans = StmtExpr(ExprCall(
|
||||
ExprSelect(pvar, '->', 'IToplevelProtocol::SetTransport'),
|
||||
args=[tvar]))
|
||||
|
||||
addopened = StmtExpr(ExprCall(
|
||||
ExprVar('IToplevelProtocol::AddOpenedActor'),
|
||||
args=[pvar]))
|
||||
|
||||
case.addstmts([
|
||||
StmtDecl(Decl(Type('Transport', ptr=1), tvar.name)),
|
||||
StmtDecl(Decl(Type(_actorName(actor.ptype.name(), self.side),
|
||||
ptr=1), pvar.name)),
|
||||
iffailopen,
|
||||
iffailalloc,
|
||||
settrans,
|
||||
addopened,
|
||||
StmtBreak()
|
||||
])
|
||||
label = _messageStartName(actor.ptype)
|
||||
|
@ -25,7 +25,7 @@ TestBridgeMainParent::Main()
|
||||
fail("sending Start");
|
||||
}
|
||||
|
||||
bool
|
||||
PTestBridgeMainSubParent*
|
||||
TestBridgeMainParent::AllocPTestBridgeMainSubParent(Transport* transport,
|
||||
ProcessId otherProcess)
|
||||
{
|
||||
@ -38,8 +38,7 @@ TestBridgeMainParent::AllocPTestBridgeMainSubParent(Transport* transport,
|
||||
if (!a->Open(transport, h, XRE_GetIOMessageLoop(), ipc::ParentSide)) {
|
||||
return nullptr;
|
||||
}
|
||||
a.forget();
|
||||
return true;
|
||||
return a.forget();
|
||||
}
|
||||
|
||||
void
|
||||
@ -177,25 +176,24 @@ TestBridgeSubChild::RecvPing()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PTestBridgeMainSubChild*
|
||||
TestBridgeSubChild::AllocPTestBridgeMainSubChild(Transport* transport,
|
||||
ProcessId otherProcess)
|
||||
{
|
||||
ProcessHandle h;
|
||||
if (!base::OpenProcessHandle(otherProcess, &h)) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoPtr<TestBridgeMainSubChild> a(new TestBridgeMainSubChild(transport));
|
||||
if (!a->Open(transport, h, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!a->SendHello())
|
||||
fail("sending Hello");
|
||||
|
||||
a.forget();
|
||||
return true;
|
||||
return a.forget();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -31,7 +31,7 @@ public:
|
||||
void Main();
|
||||
|
||||
protected:
|
||||
virtual bool
|
||||
virtual PTestBridgeMainSubParent*
|
||||
AllocPTestBridgeMainSubParent(Transport* transport,
|
||||
ProcessId otherProcess) MOZ_OVERRIDE;
|
||||
|
||||
@ -105,7 +105,7 @@ public:
|
||||
protected:
|
||||
virtual bool RecvPing() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
virtual PTestBridgeMainSubChild*
|
||||
AllocPTestBridgeMainSubChild(Transport* transport,
|
||||
ProcessId otherProcess) MOZ_OVERRIDE;
|
||||
|
||||
|
@ -64,7 +64,7 @@ OpenParent(TestOpensOpenedParent* aParent,
|
||||
fail("opening Parent");
|
||||
}
|
||||
|
||||
bool
|
||||
PTestOpensOpenedParent*
|
||||
TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport,
|
||||
ProcessId otherProcess)
|
||||
{
|
||||
@ -72,7 +72,7 @@ TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport,
|
||||
|
||||
ProcessHandle h;
|
||||
if (!base::OpenProcessHandle(otherProcess, &h)) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gParentThread = new Thread("ParentThread");
|
||||
@ -84,7 +84,7 @@ TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport,
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(OpenParent, a, transport, h));
|
||||
|
||||
return true;
|
||||
return a;
|
||||
}
|
||||
|
||||
void
|
||||
@ -177,7 +177,7 @@ OpenChild(TestOpensOpenedChild* aChild,
|
||||
fail("sending Hello");
|
||||
}
|
||||
|
||||
bool
|
||||
PTestOpensOpenedChild*
|
||||
TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport,
|
||||
ProcessId otherProcess)
|
||||
{
|
||||
@ -185,7 +185,7 @@ TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport,
|
||||
|
||||
ProcessHandle h;
|
||||
if (!base::OpenProcessHandle(otherProcess, &h)) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gChildThread = new Thread("ChildThread");
|
||||
@ -197,7 +197,7 @@ TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport,
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(OpenChild, a, transport, h));
|
||||
|
||||
return true;
|
||||
return a;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -27,7 +27,7 @@ public:
|
||||
void Main();
|
||||
|
||||
protected:
|
||||
virtual bool
|
||||
virtual PTestOpensOpenedParent*
|
||||
AllocPTestOpensOpenedParent(Transport* transport, ProcessId otherProcess) MOZ_OVERRIDE;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
|
||||
@ -70,7 +70,7 @@ public:
|
||||
protected:
|
||||
virtual bool RecvStart() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
virtual PTestOpensOpenedChild*
|
||||
AllocPTestOpensOpenedChild(Transport* transport, ProcessId otherProcess) MOZ_OVERRIDE;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
|
||||
|
@ -19,6 +19,7 @@ using namespace js;
|
||||
using namespace JS;
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::jsipc;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
JavaScriptParent::JavaScriptParent()
|
||||
: refcount_(1),
|
||||
@ -686,3 +687,14 @@ JavaScriptParent::domInstanceOf(JSObject *obj, int prototypeID, int depth, bool
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::ipc::IProtocol*
|
||||
JavaScriptParent::CloneProtocol(Channel* aChannel, ProtocolCloneContext* aCtx)
|
||||
{
|
||||
ContentParent *contentParent = aCtx->GetContentParent();
|
||||
nsAutoPtr<PJavaScriptParent> actor(contentParent->AllocPJavaScriptParent());
|
||||
if (!actor || !contentParent->RecvPJavaScriptConstructor(actor)) {
|
||||
return nullptr;
|
||||
}
|
||||
return actor.forget();
|
||||
}
|
||||
|
@ -77,6 +77,9 @@ class JavaScriptParent
|
||||
static bool DOMInstanceOf(JSObject *obj, int prototypeID, int depth, bool *bp);
|
||||
bool domInstanceOf(JSObject *obj, int prototypeID, int depth, bool *bp);
|
||||
|
||||
mozilla::ipc::IProtocol*
|
||||
CloneProtocol(Channel* aChannel, ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
JSObject *unwrap(JSContext *cx, ObjectId objId);
|
||||
|
||||
|
@ -4062,6 +4062,10 @@ AC_SUBST(NSPR_CFLAGS)
|
||||
AC_SUBST(NSPR_LIBS)
|
||||
AC_SUBST(MOZ_NATIVE_NSPR)
|
||||
|
||||
if test -n "$MOZ_NUWA_PROCESS"; then
|
||||
AC_DEFINE(MOZ_NUWA_PROCESS)
|
||||
fi
|
||||
|
||||
OS_CFLAGS="$CFLAGS"
|
||||
OS_CXXFLAGS="$CXXFLAGS"
|
||||
OS_CPPFLAGS="$CPPFLAGS"
|
||||
|
@ -2279,11 +2279,26 @@ GCHelperThread::finish()
|
||||
}
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
extern "C" {
|
||||
MFBT_API bool IsNuwaProcess();
|
||||
MFBT_API void NuwaMarkCurrentThread(void (*recreate)(void *), void *arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* static */
|
||||
void
|
||||
GCHelperThread::threadMain(void *arg)
|
||||
{
|
||||
PR_SetCurrentThreadName("JS GC Helper");
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess && IsNuwaProcess()) {
|
||||
JS_ASSERT(NuwaMarkCurrentThread != nullptr);
|
||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
static_cast<GCHelperThread *>(arg)->threadLoop();
|
||||
}
|
||||
|
||||
|
@ -1022,6 +1022,10 @@ class Watchdog
|
||||
bool mShuttingDown;
|
||||
};
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
class WatchdogManager : public nsIObserver
|
||||
{
|
||||
public:
|
||||
@ -1155,6 +1159,15 @@ WatchdogMain(void *arg)
|
||||
{
|
||||
PR_SetCurrentThreadName("JS Watchdog");
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
|
||||
"NuwaMarkCurrentThread is undefined!");
|
||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
||||
NuwaFreezeCurrentThread();
|
||||
}
|
||||
#endif
|
||||
|
||||
Watchdog* self = static_cast<Watchdog*>(arg);
|
||||
WatchdogManager* manager = self->Manager();
|
||||
|
||||
|
@ -1443,21 +1443,27 @@ void AudioDeviceAndroidOpenSLES::RecorderSimpleBufferQueueCallbackHandler(
|
||||
}
|
||||
audio = rec_queue_.front();
|
||||
rec_queue_.pop();
|
||||
rec_voe_audio_queue_.push(audio);
|
||||
rec_timer_.Set(); // wake up record thread to process it
|
||||
// prevent rec_queue being overrun
|
||||
if (rec_voe_audio_queue_.size() < N_REC_QUEUE_BUFFERS) {
|
||||
rec_voe_audio_queue_.push(audio);
|
||||
rec_timer_.Set(); // wake up record thread to process it
|
||||
|
||||
if (rec_voe_ready_queue_.size() <= 0) {
|
||||
// Log Error.
|
||||
rec_error_ = 1;
|
||||
WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_,
|
||||
" Audio Rec thread buffers underrun");
|
||||
// This isn't good, but continuing (as it used to) is even worse.
|
||||
// Worst case we end up with buffers building up at the other end or
|
||||
// starved. To be fixed in full rewrite from upstream.
|
||||
return;
|
||||
if (rec_voe_ready_queue_.size() <= 0) {
|
||||
// Log Error.
|
||||
rec_error_ = 1;
|
||||
WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_,
|
||||
" Audio Rec thread buffers underrun");
|
||||
// This isn't good, but continuing (as it used to) is even worse.
|
||||
// Worst case we end up with buffers building up at the other end or
|
||||
// starved. To be fixed in full rewrite from upstream.
|
||||
return;
|
||||
} else {
|
||||
audio = rec_voe_ready_queue_.front();
|
||||
rec_voe_ready_queue_.pop();
|
||||
}
|
||||
} else {
|
||||
audio = rec_voe_ready_queue_.front();
|
||||
rec_voe_ready_queue_.pop();
|
||||
// the audio frame will be recycled instead of sending to rec thread.
|
||||
rec_timer_.Set(); // try wake up record thread
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -835,18 +835,24 @@ abstract public class GeckoApp
|
||||
void showButtonToast(final String message, final String buttonText,
|
||||
final String buttonIcon, final String buttonId) {
|
||||
BitmapUtils.getDrawable(GeckoApp.this, buttonIcon, new BitmapUtils.BitmapLoader() {
|
||||
public void onBitmapFound(Drawable d) {
|
||||
mToast.show(false, message, buttonText, d, new ButtonToast.ToastListener() {
|
||||
@Override
|
||||
public void onButtonClicked() {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Toast:Click", buttonId));
|
||||
}
|
||||
public void onBitmapFound(final Drawable d) {
|
||||
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void onToastHidden(ButtonToast.ReasonHidden reason) {
|
||||
if (reason == ButtonToast.ReasonHidden.TIMEOUT) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Toast:Hidden", buttonId));
|
||||
}
|
||||
public void run() {
|
||||
mToast.show(false, message, buttonText, d, new ButtonToast.ToastListener() {
|
||||
@Override
|
||||
public void onButtonClicked() {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Toast:Click", buttonId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToastHidden(ButtonToast.ReasonHidden reason) {
|
||||
if (reason == ButtonToast.ReasonHidden.TIMEOUT) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Toast:Hidden", buttonId));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ public class GeckoScreenOrientationListener {
|
||||
static public final short eScreenOrientation_PortraitSecondary = 2; // PR_BIT(1)
|
||||
static public final short eScreenOrientation_LandscapePrimary = 4; // PR_BIT(2)
|
||||
static public final short eScreenOrientation_LandscapeSecondary = 8; // PR_BIT(3)
|
||||
static public final short eScreenOrientation_Default = 16;// PR_BIT(4)
|
||||
|
||||
static private final short DEFAULT_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
|
||||
@ -199,6 +200,9 @@ public class GeckoScreenOrientationListener {
|
||||
case eScreenOrientation_LandscapePrimary | eScreenOrientation_LandscapeSecondary:
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
|
||||
break;
|
||||
case eScreenOrientation_Default:
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
|
||||
break;
|
||||
default:
|
||||
Log.e(LOGTAG, "Unexpected value received! (" + aOrientation + ")");
|
||||
return;
|
||||
|
@ -157,7 +157,6 @@ FENNEC_JAVA_FILES = \
|
||||
TextSelectionHandle.java \
|
||||
ThumbnailHelper.java \
|
||||
TouchEventInterceptor.java \
|
||||
UpdateServiceHelper.java \
|
||||
VideoPlayer.java \
|
||||
WebAppAllocator.java \
|
||||
WebAppImpl.java \
|
||||
@ -253,6 +252,8 @@ FENNEC_JAVA_FILES = \
|
||||
menu/MenuPopup.java \
|
||||
preferences/SearchPreferenceCategory.java \
|
||||
preferences/SearchEnginePreference.java \
|
||||
updater/UpdateServiceHelper.java \
|
||||
updater/UpdateService.java \
|
||||
widget/ActivityChooserModel.java \
|
||||
widget/ButtonToast.java \
|
||||
widget/ArrowPopup.java \
|
||||
@ -267,7 +268,6 @@ FENNEC_JAVA_FILES = \
|
||||
widget/TwoWayView.java \
|
||||
GeckoNetworkManager.java \
|
||||
GeckoScreenOrientationListener.java \
|
||||
UpdateService.java \
|
||||
GeckoUpdateReceiver.java \
|
||||
ReferrerReceiver.java \
|
||||
$(NULL)
|
||||
|
@ -60,7 +60,7 @@ public class UpdateServiceHelper {
|
||||
} else {
|
||||
pkgSpecial = "";
|
||||
}
|
||||
UPDATE_URL = "https://aus2.mozilla.org/update/4/" + AppConstants.MOZ_APP_BASENAME + "/" +
|
||||
UPDATE_URL = "https://aus4.mozilla.org/update/4/" + AppConstants.MOZ_APP_BASENAME + "/" +
|
||||
AppConstants.MOZ_APP_VERSION +
|
||||
"/%BUILDID%/Android_" + AppConstants.MOZ_APP_ABI + pkgSpecial +
|
||||
"/%LOCALE%/" + AppConstants.MOZ_UPDATE_CHANNEL +
|
@ -30,7 +30,8 @@ let WebAppRT = {
|
||||
// Set a future policy version to avoid the telemetry prompt.
|
||||
pref("toolkit.telemetry.prompted", 999),
|
||||
pref("toolkit.telemetry.notifiedOptOut", 999),
|
||||
pref("media.useAudioChannelService", true)
|
||||
pref("media.useAudioChannelService", true),
|
||||
pref("dom.mozTCPSocket.enabled", true),
|
||||
],
|
||||
|
||||
init: function(aStatus, aUrl, aCallback) {
|
||||
|
@ -4154,7 +4154,7 @@ var BrowserEventHandler = {
|
||||
if (this._scrollableElement != null) {
|
||||
// Discard if it's the top-level scrollable, we let Java handle this
|
||||
let doc = BrowserApp.selectedBrowser.contentDocument;
|
||||
if (this._scrollableElement != doc.body && this._scrollableElement != doc.documentElement)
|
||||
if (this._scrollableElement != doc.documentElement)
|
||||
sendMessageToJava({ type: "Panning:Override" });
|
||||
}
|
||||
}
|
||||
@ -4241,7 +4241,6 @@ var BrowserEventHandler = {
|
||||
|
||||
let doc = BrowserApp.selectedBrowser.contentDocument;
|
||||
if (this._scrollableElement == null ||
|
||||
this._scrollableElement == doc.body ||
|
||||
this._scrollableElement == doc.documentElement) {
|
||||
sendMessageToJava({ type: "Panning:CancelOverride" });
|
||||
return;
|
||||
@ -4612,15 +4611,14 @@ var BrowserEventHandler = {
|
||||
let scrollable = false;
|
||||
while (elem) {
|
||||
/* Element is scrollable if its scroll-size exceeds its client size, and:
|
||||
* - It has overflow 'auto' or 'scroll'
|
||||
* - It's a textarea
|
||||
* - It's an HTML/BODY node
|
||||
* - It has overflow 'auto' or 'scroll', or
|
||||
* - It's a textarea or HTML node, or
|
||||
* - It's a select element showing multiple rows
|
||||
*/
|
||||
if (checkElem) {
|
||||
if ((elem.scrollTopMax > 0 || elem.scrollLeftMax > 0) &&
|
||||
(this._hasScrollableOverflow(elem) ||
|
||||
elem.mozMatchesSelector("html, body, textarea")) ||
|
||||
elem.mozMatchesSelector("html, textarea")) ||
|
||||
(elem instanceof HTMLSelectElement && (elem.size > 1 || elem.multiple))) {
|
||||
scrollable = true;
|
||||
break;
|
||||
@ -6539,6 +6537,13 @@ var SearchEngines = {
|
||||
prompted: Services.prefs.getBoolPref(this.PREF_SUGGEST_PROMPTED)
|
||||
}
|
||||
});
|
||||
|
||||
// Send a speculative connection to the default engine.
|
||||
let connector = Services.io.QueryInterface(Ci.nsISpeculativeConnect);
|
||||
let searchURI = Services.search.defaultEngine.getSubmission("dummy").uri;
|
||||
let callbacks = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsILoadContext);
|
||||
connector.speculativeConnect(searchURI, callbacks);
|
||||
},
|
||||
|
||||
_handleSearchEnginesGetAll: function _handleSearchEnginesGetAll(rv) {
|
||||
|
@ -101,6 +101,10 @@ let HomeBanner = {
|
||||
if (Object.keys(this._messages).length == 1) {
|
||||
Services.obs.addObserver(this, "HomeBanner:Get", false);
|
||||
Services.obs.addObserver(this, "HomeBanner:Click", false);
|
||||
|
||||
// Send a message to Java, in case there's an active HomeBanner
|
||||
// waiting for a response.
|
||||
this._handleGet();
|
||||
}
|
||||
|
||||
return message.id;
|
||||
|
@ -483,6 +483,9 @@ pref("toolkit.telemetry.debugSlowSql", false);
|
||||
pref("toolkit.identity.enabled", false);
|
||||
pref("toolkit.identity.debug", false);
|
||||
|
||||
// AsyncShutdown delay before crashing in case of shutdown freeze
|
||||
pref("toolkit.asyncshutdown.timeout.crash", 60000);
|
||||
|
||||
// Enable deprecation warnings.
|
||||
pref("devtools.errorconsole.deprecation_warnings", true);
|
||||
|
||||
|
@ -79,6 +79,9 @@ endif
|
||||
endif
|
||||
|
||||
ifeq (Android,$(OS_TARGET))
|
||||
ifdef MOZ_NUWA_PROCESS
|
||||
CPPSRCS += Nuwa.cpp
|
||||
endif
|
||||
SHARED_LIBRARY_LIBS += $(call EXPAND_LIBNAME_PATH,android,$(DEPTH)/other-licenses/android)
|
||||
endif
|
||||
|
||||
|
1746
mozglue/build/Nuwa.cpp
Normal file
1746
mozglue/build/Nuwa.cpp
Normal file
File diff suppressed because it is too large
Load Diff
171
mozglue/build/Nuwa.h
Normal file
171
mozglue/build/Nuwa.h
Normal file
@ -0,0 +1,171 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=2 autoindent cindent expandtab: */
|
||||
/* 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 __NUWA_H_
|
||||
#define __NUWA_H_
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "mozilla/Types.h"
|
||||
|
||||
/**
|
||||
* Nuwa is a goddess who created mankind according to her figure in the
|
||||
* ancient Chinese mythology.
|
||||
*/
|
||||
|
||||
// Max number of top level protocol that can be handled by Nuwa process.
|
||||
#ifndef NUWA_TOPLEVEL_MAX
|
||||
#define NUWA_TOPLEVEL_MAX 8
|
||||
#endif
|
||||
|
||||
struct NuwaProtoFdInfo {
|
||||
int protoId;
|
||||
int originFd;
|
||||
int newFds[2];
|
||||
};
|
||||
|
||||
#define NUWA_NEWFD_PARENT 0
|
||||
#define NUWA_NEWFD_CHILD 1
|
||||
|
||||
extern "C" {
|
||||
|
||||
/**
|
||||
* The following 3 methods are used to synchronously spawn the Nuwa process.
|
||||
* The typical usage is:
|
||||
*
|
||||
* The IPC thread The main thread
|
||||
* 1. Receives the request.
|
||||
* 2. NuwaSpawnPrepare().
|
||||
* 3. Request main thread to
|
||||
* spawn. --------------------------------------+
|
||||
* 4. NuwaSpawnWait(). V
|
||||
* 5. NuwaSpawn() to clone all
|
||||
* the threads in the Nuwa
|
||||
* process (including main
|
||||
* & IPC threads).
|
||||
* 6. NuwaSpawn() finishes and
|
||||
* +-------------------------------------- signals NuwaSpawnWait().
|
||||
* V
|
||||
* 7. NuwaSpawnWait() returns.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prepare for spawning a new process. The calling thread (typically the IPC
|
||||
* thread) will block further requests to spawn a new process.
|
||||
*/
|
||||
MFBT_API void NuwaSpawnPrepare();
|
||||
|
||||
/**
|
||||
* Called on the thread that receives the request. Used to wait until
|
||||
* NuwaSpawn() finishes and maintain the stack of the calling thread.
|
||||
*/
|
||||
MFBT_API void NuwaSpawnWait();
|
||||
|
||||
/**
|
||||
* Spawn a new content process, called in the Nuwa process. This has to run on
|
||||
* the main thread.
|
||||
*
|
||||
* @return The process ID of the new process.
|
||||
*/
|
||||
MFBT_API pid_t NuwaSpawn();
|
||||
|
||||
/**
|
||||
* The following methods are for marking threads created in the Nuwa process so
|
||||
* they can be frozen and then recreated in the spawned process. All threads
|
||||
* except the main thread must be marked by one of the following 4 methods;
|
||||
* otherwise, we have a thread that is created but unknown to us. The Nuwa
|
||||
* process will never become ready and this needs to be fixed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mark the current thread as supporting Nuwa. The thread will implicitly freeze
|
||||
* by calling a blocking method such as pthread_mutex_lock() or epoll_wait().
|
||||
*
|
||||
* @param recreate The custom function that will be called in the spawned
|
||||
* process, after the thread is recreated. Can be nullptr if no
|
||||
* custom function to be called after the thread is recreated.
|
||||
* @param arg The argument passed to the custom function. Can be nullptr.
|
||||
*/
|
||||
MFBT_API void NuwaMarkCurrentThread(void (*recreate)(void *), void *arg);
|
||||
|
||||
/**
|
||||
* Mark the current thread as not supporting Nuwa. The calling thread will not
|
||||
* be automatically cloned from the Nuwa process to spawned process. If this
|
||||
* thread needs to be created in the spawned process, use NuwaAddConstructor()
|
||||
* or NuwaAddFinalConstructor() to do it.
|
||||
*/
|
||||
MFBT_API void NuwaSkipCurrentThread();
|
||||
|
||||
/**
|
||||
* Force the current thread to freeze explicitly at the current point.
|
||||
*/
|
||||
MFBT_API void NuwaFreezeCurrentThread();
|
||||
|
||||
/**
|
||||
* Helper functions for the NuwaCheckpointCurrentThread() macro.
|
||||
*/
|
||||
MFBT_API jmp_buf* NuwaCheckpointCurrentThread1();
|
||||
|
||||
MFBT_API bool NuwaCheckpointCurrentThread2(int setjmpCond);
|
||||
|
||||
/**
|
||||
* Set the point to recover after thread recreation. The calling thread is not
|
||||
* blocked. This is used for the thread that cannot freeze (i.e. the IPC
|
||||
* thread).
|
||||
*/
|
||||
#define NuwaCheckpointCurrentThread() \
|
||||
NuwaCheckpointCurrentThread2(setjmp(*NuwaCheckpointCurrentThread1()))
|
||||
|
||||
/**
|
||||
* The following methods are called in the initialization stage of the Nuwa
|
||||
* process.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prepare for making the Nuwa process.
|
||||
*/
|
||||
MFBT_API void PrepareNuwaProcess();
|
||||
|
||||
/**
|
||||
* Make the current process a Nuwa process. Start to freeze the threads.
|
||||
*/
|
||||
MFBT_API void MakeNuwaProcess();
|
||||
|
||||
/**
|
||||
* Register a method to be invoked after a new process is spawned. The method
|
||||
* will be invoked on the main thread.
|
||||
*
|
||||
* @param construct The method to be invoked.
|
||||
* @param arg The argument passed to the method.
|
||||
*/
|
||||
MFBT_API void NuwaAddConstructor(void (*construct)(void *), void *arg);
|
||||
|
||||
/**
|
||||
* Register a method to be invoked after a new process is spawned and threads
|
||||
* are recreated. The method will be invoked on the main thread.
|
||||
*
|
||||
* @param construct The method to be invoked.
|
||||
* @param arg The argument passed to the method.
|
||||
*/
|
||||
MFBT_API void NuwaAddFinalConstructor(void (*construct)(void *), void *arg);
|
||||
|
||||
/**
|
||||
* The methods to query the Nuwa-related states.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return If the current process is the Nuwa process.
|
||||
*/
|
||||
MFBT_API bool IsNuwaProcess();
|
||||
|
||||
/**
|
||||
* @return If the Nuwa process is ready for spawning new processes.
|
||||
*/
|
||||
MFBT_API bool IsNuwaReady();
|
||||
};
|
||||
|
||||
#endif /* __NUWA_H_ */
|
@ -30,6 +30,11 @@ if CONFIG['OS_TARGET'] == 'Android':
|
||||
'BionicGlue.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_NUWA_PROCESS']:
|
||||
EXPORTS.ipc += [
|
||||
'Nuwa.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'SSE.h',
|
||||
'arm.h',
|
||||
|
@ -634,11 +634,23 @@ nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::Run()
|
||||
{
|
||||
PR_SetCurrentThreadName("Socket Thread");
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
|
||||
"NuwaMarkCurrentThread is undefined!");
|
||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
SOCKET_LOG(("STS thread init\n"));
|
||||
|
||||
psm::InitializeSSLServerCertVerificationThreads();
|
||||
|
@ -425,6 +425,38 @@ nsOutputStreamTransport::IsNonBlocking(bool *result)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
|
||||
class STSThreadPoolListener : public nsIThreadPoolListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITHREADPOOLLISTENER
|
||||
|
||||
STSThreadPoolListener() {}
|
||||
~STSThreadPoolListener() {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(STSThreadPoolListener, nsIThreadPoolListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
STSThreadPoolListener::OnThreadCreated()
|
||||
{
|
||||
if (IsNuwaProcess()) {
|
||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
STSThreadPoolListener::OnThreadShuttingDown()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsStreamTransportService
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -445,6 +477,11 @@ nsStreamTransportService::Init()
|
||||
mPool->SetThreadLimit(25);
|
||||
mPool->SetIdleThreadLimit(1);
|
||||
mPool->SetIdleThreadTimeout(PR_SecondsToInterval(30));
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
mPool->SetListener(new STSThreadPoolListener());
|
||||
}
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsSvc =
|
||||
mozilla::services::GetObserverService();
|
||||
|
@ -127,6 +127,18 @@ CookieServiceParent::RecvSetCookieString(const URIParams& aHost,
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::ipc::IProtocol*
|
||||
CookieServiceParent::CloneProtocol(Channel* aChannel,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx)
|
||||
{
|
||||
NeckoParent* manager = aCtx->GetNeckoParent();
|
||||
nsAutoPtr<PCookieServiceParent> actor(manager->AllocPCookieServiceParent());
|
||||
if (!actor || !manager->RecvPCookieServiceConstructor(actor)) {
|
||||
return nullptr;
|
||||
}
|
||||
return actor.forget();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,10 @@ protected:
|
||||
const IPC::SerializedLoadContext&
|
||||
loadContext) MOZ_OVERRIDE;
|
||||
|
||||
virtual mozilla::ipc::IProtocol*
|
||||
CloneProtocol(Channel* aChannel,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
|
||||
|
||||
nsRefPtr<nsCookieService> mCookieService;
|
||||
};
|
||||
|
||||
|
@ -505,4 +505,24 @@ NeckoParent::RecvCancelHTMLDNSPrefetch(const nsString& hostname,
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
NeckoParent::CloneManagees(ProtocolBase* aSource,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx)
|
||||
{
|
||||
aCtx->SetNeckoParent(this); // For cloning protocols managed by this.
|
||||
PNeckoParent::CloneManagees(aSource, aCtx);
|
||||
}
|
||||
|
||||
mozilla::ipc::IProtocol*
|
||||
NeckoParent::CloneProtocol(Channel* aChannel,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx)
|
||||
{
|
||||
ContentParent* contentParent = aCtx->GetContentParent();
|
||||
nsAutoPtr<PNeckoParent> actor(contentParent->AllocPNeckoParent());
|
||||
if (!actor || !contentParent->RecvPNeckoConstructor(actor)) {
|
||||
return nullptr;
|
||||
}
|
||||
return actor.forget();
|
||||
}
|
||||
|
||||
}} // mozilla::net
|
||||
|
@ -57,6 +57,16 @@ public:
|
||||
const SerializedLoadContext& aSerialized,
|
||||
nsCOMPtr<nsILoadContext> &aResult);
|
||||
|
||||
virtual void
|
||||
CloneManagees(ProtocolBase* aSource,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
|
||||
virtual PCookieServiceParent* AllocPCookieServiceParent() MOZ_OVERRIDE;
|
||||
virtual bool
|
||||
RecvPCookieServiceConstructor(PCookieServiceParent* aActor) MOZ_OVERRIDE
|
||||
{
|
||||
return PNeckoParent::RecvPCookieServiceConstructor(aActor);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual PHttpChannelParent*
|
||||
AllocPHttpChannelParent(PBrowserParent*, const SerializedLoadContext&,
|
||||
@ -68,7 +78,6 @@ protected:
|
||||
const SerializedLoadContext& aSerialized,
|
||||
const HttpChannelCreationArgs& aOpenArgs);
|
||||
virtual bool DeallocPHttpChannelParent(PHttpChannelParent*);
|
||||
virtual PCookieServiceParent* AllocPCookieServiceParent();
|
||||
virtual bool DeallocPCookieServiceParent(PCookieServiceParent*);
|
||||
virtual PWyciwygChannelParent* AllocPWyciwygChannelParent();
|
||||
virtual bool DeallocPWyciwygChannelParent(PWyciwygChannelParent*);
|
||||
@ -112,6 +121,11 @@ protected:
|
||||
virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname,
|
||||
const uint16_t& flags,
|
||||
const nsresult& reason);
|
||||
|
||||
virtual mozilla::ipc::IProtocol*
|
||||
CloneProtocol(Channel* aChannel,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
nsCString mCoreAppsBasePath;
|
||||
nsCString mWebAppsBasePath;
|
||||
|
@ -779,7 +779,11 @@ SimpleTest.finish = function () {
|
||||
/* We're running in an iframe, and the parent has a TestRunner */
|
||||
parentRunner.testFinished(SimpleTest._tests);
|
||||
} else {
|
||||
SimpleTest.showReport();
|
||||
SpecialPowers.flushPermissions(function () {
|
||||
SpecialPowers.flushPrefEnv(function() {
|
||||
SimpleTest.showReport();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -22,16 +22,6 @@ let main = this;
|
||||
function test() {
|
||||
info("test_osfile_front.xul: Starting test");
|
||||
|
||||
// Test OS.Constants.Path
|
||||
|
||||
Components.classes["@mozilla.org/net/osfileconstantsservice;1"].
|
||||
getService(Components.interfaces.nsIOSFileConstantsService).
|
||||
init();
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
is(OS.Constants.Path.tmpDir, Services.dirsvc.get("TmpD", Components.interfaces.nsIFile).path, "OS.Constants.Path.tmpDir is correct");
|
||||
is(OS.Constants.Path.profileDir, Services.dirsvc.get("ProfD", Components.interfaces.nsIFile).path, "OS.Constants.Path.profileDir is correct");
|
||||
|
||||
// Test the OS.File worker
|
||||
|
||||
worker = new ChromeWorker("worker_test_osfile_front.js");
|
||||
|
@ -0,0 +1,45 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function compare_paths(ospath, key) {
|
||||
let file;
|
||||
try {
|
||||
file = Services.dirsvc.get(key, Components.interfaces.nsIFile);
|
||||
} catch(ex) {}
|
||||
|
||||
if (file) {
|
||||
do_check_true(!!ospath);
|
||||
do_check_eq(ospath, file.path);
|
||||
} else {
|
||||
do_check_false(!!ospath);
|
||||
}
|
||||
}
|
||||
|
||||
add_test(function() {
|
||||
do_check_true(!!OS.Constants.Path.tmpDir);
|
||||
do_check_eq(OS.Constants.Path.tmpDir, Services.dirsvc.get("TmpD", Components.interfaces.nsIFile).path);
|
||||
|
||||
do_check_true(!!OS.Constants.Path.homeDir);
|
||||
do_check_eq(OS.Constants.Path.homeDir, Services.dirsvc.get("Home", Components.interfaces.nsIFile).path);
|
||||
|
||||
do_check_true(!!OS.Constants.Path.desktopDir);
|
||||
do_check_eq(OS.Constants.Path.desktopDir, Services.dirsvc.get("Desk", Components.interfaces.nsIFile).path);
|
||||
|
||||
compare_paths(OS.Constants.Path.winAppDataDir, "AppData");
|
||||
compare_paths(OS.Constants.Path.winStartMenuProgsDir, "Progs");
|
||||
|
||||
compare_paths(OS.Constants.Path.macUserLibDir, "ULibDir");
|
||||
compare_paths(OS.Constants.Path.macLocalApplicationsDir, "LocApp");
|
||||
|
||||
run_next_test();
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user