Merge m-c to inbound.

This commit is contained in:
Ryan VanderMeulen 2013-09-30 16:51:06 -04:00
commit 2a773817b4
114 changed files with 4552 additions and 425 deletions

View File

@ -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 {

View File

@ -1,4 +1,4 @@
{
"revision": "290fc4628fbf4de6c75fd2ea796b1065a6e50043",
"revision": "a4a7f42eac6eb7813b097c716b264d675fceeae8",
"repo_path": "/integration/gaia-central"
}

View File

@ -55,4 +55,6 @@ MOZ_PAY=1
MOZ_TOOLKIT_SEARCH=
MOZ_PLACES=
MOZ_B2G=1
#MOZ_NUWA_PROCESS=1
MOZ_FOLD_LIBS=1

View File

@ -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);
}
};

View 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);
}
};

View File

@ -22,6 +22,7 @@ EXTRA_JS_MODULES = [
'SessionMigration.jsm',
'SessionStorage.jsm',
'SessionWorker.js',
'TabStateCache.jsm',
'XPathGenerator.jsm',
'_SessionFile.jsm',
]

View File

@ -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 \

View File

@ -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");

View 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);
});

View File

@ -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() {

View File

@ -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)

View File

@ -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)

View File

@ -300,6 +300,8 @@ public:
static bool HasPushNotificationsSupport(JSContext* /* unused */,
JSObject* aGlobal);
static bool HasInputMethodSupport(JSContext* /* unused */, JSObject* aGlobal);
nsPIDOMWindow* GetParentObject() const
{
return GetWindow();

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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:

View File

@ -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
}

View File

@ -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();

View File

@ -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)
{

View File

@ -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;

View File

@ -37,5 +37,5 @@ ContentProcess::CleanUp()
mXREEmbed.Stop();
}
} // namespace tabs
} // namespace dom
} // namespace mozilla

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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;
}

View File

@ -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++) {

View File

@ -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",

View File

@ -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;

View File

@ -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;

View File

@ -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*

View File

@ -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();

View File

@ -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

View File

@ -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);
/**

View File

@ -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()

View File

@ -114,7 +114,7 @@ public:
*/
static void StartUp();
static bool
static PImageBridgeChild*
StartUpInChildProcess(Transport* aTransport, ProcessId aOtherProcess);
/**

View File

@ -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

View File

@ -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();

View File

@ -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";

View File

@ -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:

View File

@ -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 {

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -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;

View File

@ -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; }

View File

@ -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));

View File

@ -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);

View File

@ -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;

View File

@ -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_;

View File

@ -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();

View File

@ -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() {

View File

@ -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();

View File

@ -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 */

View File

@ -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 */

View File

@ -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) {

View 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;
};
}
}

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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)
{

View File

@ -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)
{

View File

@ -96,6 +96,7 @@ CPP_SOURCES += [
IPDL_SOURCES = [
'InputStreamParams.ipdlh',
'ProtocolTypes.ipdlh',
'URIParams.ipdlh',
]

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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();
}

View File

@ -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);

View File

@ -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"

View File

@ -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();
}

View File

@ -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();

View File

@ -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
}
}

View File

@ -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));
}
}
});
}
});
}

View File

@ -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;

View File

@ -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)

View File

@ -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 +

View File

@ -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) {

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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

File diff suppressed because it is too large Load Diff

171
mozglue/build/Nuwa.h Normal file
View 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_ */

View File

@ -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',

View File

@ -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();

View File

@ -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();

View File

@ -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();
}
}
}

View File

@ -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;
};

View File

@ -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

View File

@ -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;

View File

@ -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();
});
});
}
};

View File

@ -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");

View File

@ -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