Merge m-c to inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-10-21 15:23:54 -04:00
commit faec375652
41 changed files with 1039 additions and 488 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="82174cee5ede9f23aedad8a39f8b8cdc1ae710c4"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="82174cee5ede9f23aedad8a39f8b8cdc1ae710c4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="82174cee5ede9f23aedad8a39f8b8cdc1ae710c4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
@ -131,10 +131,10 @@
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="197cd9492b9fadaa915c5daf36ff557f8f4a8d1c"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
<project name="libnfcemu" path="external/libnfcemu" remote="b2g" revision="125ccf9bd5986c7728ea44508b3e1d1185ac028b"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d259117b4976decbe2f76eeed85218bf0109190f"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c5f8d282efe4a4e8b1e31a37300944e338e60e4f"/>
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d872fc1462d367eb67833de6942c297d6c4dc874"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="a28a47cf4de9cc676e8a14d58826f7927d77f5d5"/>
<project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
<project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
</manifest>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="82174cee5ede9f23aedad8a39f8b8cdc1ae710c4"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="82174cee5ede9f23aedad8a39f8b8cdc1ae710c4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="82174cee5ede9f23aedad8a39f8b8cdc1ae710c4"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -151,7 +151,7 @@
<project name="platform/hardware/ril" path="hardware/ril" revision="12b1977cc704b35f2e9db2bb423fa405348bc2f3"/>
<project name="platform/system/bluetooth" path="system/bluetooth" revision="985bf15264d865fe7b9c5b45f61c451cbaafa43d"/>
<project name="platform/system/core" path="system/core" revision="350eac5403124dacb2a5fd9e28ac290a59fc3b8e"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d872fc1462d367eb67833de6942c297d6c4dc874"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="a28a47cf4de9cc676e8a14d58826f7927d77f5d5"/>
<project name="platform/system/qcom" path="system/qcom" revision="63e3f6f176caad587d42bba4c16b66d953fb23c2"/>
<project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="d8952a42771045fca73ec600e2b42a4c7129d723"/>
<project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="7704e16da545f4207812e593743d6743e1afb9c5"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="82174cee5ede9f23aedad8a39f8b8cdc1ae710c4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
@ -145,7 +145,7 @@
<project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
<project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
<project name="platform/system/core" path="system/core" revision="adc485d8755af6a61641d197de7cfef667722580"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d872fc1462d367eb67833de6942c297d6c4dc874"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="a28a47cf4de9cc676e8a14d58826f7927d77f5d5"/>
<project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
<project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4ae5df252123591d5b941191790e7abed1bce5a4"/>
<project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="ce18b47b4a4f93a581d672bbd5cb6d12fe796ca9"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "ce8e42aa3688f56113d68bc82409a7fea055547b",
"revision": "2bf3274b9e149c6a0ffc13be4c7d3a2f7236e311",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="82174cee5ede9f23aedad8a39f8b8cdc1ae710c4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="82174cee5ede9f23aedad8a39f8b8cdc1ae710c4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="82174cee5ede9f23aedad8a39f8b8cdc1ae710c4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
@ -129,7 +129,7 @@
<project name="device-mako" path="device/lge/mako" remote="b2g" revision="78d17f0c117f0c66dd55ee8d5c5dde8ccc93ecba"/>
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
<project name="device/lge/mako-kernel" path="device/lge/mako-kernel" revision="d1729e53d71d711c8fde25eab8728ff2b9b4df0e"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d872fc1462d367eb67833de6942c297d6c4dc874"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="a28a47cf4de9cc676e8a14d58826f7927d77f5d5"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
<project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="0e1929fa3aa38bf9d40e9e953d619fab8164c82e"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="82174cee5ede9f23aedad8a39f8b8cdc1ae710c4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -2576,8 +2576,8 @@ let BrowserOnClick = {
TabCrashReporter.submitCrashReport(browser);
}
#endif
TabCrashReporter.reloadCrashedTab(browser);
let tab = gBrowser.getTabForBrowser(browser);
SessionStore.reviveCrashedTab(tab);
}
},

View File

@ -44,7 +44,8 @@ const WINDOW_HIDEABLE_FEATURES = [
"menubar", "toolbar", "locationbar", "personalbar", "statusbar", "scrollbars"
];
const MESSAGES = [
// Messages that will be received via the Frame Message Manager.
const FMM_MESSAGES = [
// The content script gives us a reference to an object that performs
// synchronous collection of session data.
"SessionStore:setupSyncHandler",
@ -73,6 +74,16 @@ const MESSAGES = [
"SessionStore:reloadPendingTab",
];
// Messages that will be received via the Parent Process Message Manager.
const PPMM_MESSAGES = [
// A tab is being revived from the crashed state. The sender of this
// message should actually be running in the parent process, since this
// will be the crashed tab interface. We use the Child and Parent Process
// Message Managers because the message is sent during framescript unload
// when the Frame Message Manager is not available.
"SessionStore:RemoteTabRevived",
];
// These are tab events that we listen to.
const TAB_EVENTS = [
"TabOpen", "TabClose", "TabSelect", "TabShow", "TabHide", "TabPinned",
@ -97,7 +108,9 @@ XPCOMUtils.defineLazyServiceGetter(this, "gScreenManager",
"@mozilla.org/gfx/screenmanager;1", "nsIScreenManager");
XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
"@mozilla.org/base/telemetry;1", "nsITelemetry");
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageListenerManager");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
@ -263,6 +276,10 @@ this.SessionStore = {
return SessionStoreInternal.getCurrentState(aUpdateAll);
},
reviveCrashedTab(aTab) {
return SessionStoreInternal.reviveCrashedTab(aTab);
},
/**
* Backstage pass to implementation details, used for testing purpose.
* Controlled by preference "browser.sessionstore.testmode".
@ -297,6 +314,11 @@ let SessionStoreInternal = {
// For each <browser> element being restored, records the current epoch.
_browserEpochs: new WeakMap(),
// Any browsers that fires the oop-browser-crashed event gets stored in
// here - that way we know which browsers to ignore messages from (until
// they get restored).
_crashedBrowsers: new WeakSet(),
// whether a setBrowserState call is in progress
_browserSetState: false,
@ -383,6 +405,8 @@ let SessionStoreInternal = {
Services.obs.addObserver(this, aTopic, true);
}, this);
PPMM_MESSAGES.forEach(msg => ppmm.addMessageListener(msg, this));
this._initPrefs();
this._initialized = true;
},
@ -509,6 +533,8 @@ let SessionStoreInternal = {
// Make sure to cancel pending saves.
SessionSaver.cancel();
PPMM_MESSAGES.forEach(msg => ppmm.removeMessageListener(msg, this));
},
/**
@ -551,9 +577,18 @@ let SessionStoreInternal = {
/**
* This method handles incoming messages sent by the session store content
* script and thus enables communication with OOP tabs.
* script via the Frame Message Manager or Parent Process Message Manager,
* and thus enables communication with OOP tabs.
*/
receiveMessage: function ssi_receiveMessage(aMessage) {
receiveMessage(aMessage) {
// We'll deal with any Parent Process Message Manager messages first...
if (aMessage.name == "SessionStore:RemoteTabRevived") {
this._crashedBrowsers.delete(aMessage.objects.browser.permanentKey);
return;
}
// If we got here, that means we're dealing with a frame message
// manager message, so the target will be a <xul:browser>.
var browser = aMessage.target;
var win = browser.ownerDocument.defaultView;
let tab = this._getTabForBrowser(browser);
@ -567,6 +602,11 @@ let SessionStoreInternal = {
TabState.setSyncHandler(browser, aMessage.objects.handler);
break;
case "SessionStore:update":
if (this._crashedBrowsers.has(browser.permanentKey)) {
// Ignore messages from <browser> elements that have crashed
// and not yet been revived.
return;
}
this.recordTelemetry(aMessage.data.telemetry);
TabState.update(browser, aMessage.data);
this.saveStateDelayed(win);
@ -651,7 +691,7 @@ let SessionStoreInternal = {
}
break;
default:
debug("received unknown message '" + aMessage.name + "'");
debug(`received unknown message '${aMessage.name}'`);
break;
}
},
@ -675,7 +715,6 @@ let SessionStoreInternal = {
*/
handleEvent: function ssi_handleEvent(aEvent) {
var win = aEvent.currentTarget.ownerDocument.defaultView;
let browser;
switch (aEvent.type) {
case "TabOpen":
this.onTabAdd(win, aEvent.originalTarget);
@ -700,6 +739,9 @@ let SessionStoreInternal = {
case "SwapDocShells":
this.saveStateDelayed(win);
break;
case "oop-browser-crashed":
this._crashedBrowsers.add(aEvent.originalTarget.permanentKey);
break;
}
this._clearRestoringWindows();
},
@ -738,7 +780,7 @@ let SessionStoreInternal = {
aWindow.__SSi = this._generateWindowID();
let mm = aWindow.getGroupMessageManager("browsers");
MESSAGES.forEach(msg => mm.addMessageListener(msg, this));
FMM_MESSAGES.forEach(msg => mm.addMessageListener(msg, this));
// Load the frame script after registering listeners.
mm.loadFrameScript("chrome://browser/content/content-sessionStore.js", true);
@ -1067,7 +1109,7 @@ let SessionStoreInternal = {
DyingWindowCache.set(aWindow, winData);
let mm = aWindow.getGroupMessageManager("browsers");
MESSAGES.forEach(msg => mm.removeMessageListener(msg, this));
FMM_MESSAGES.forEach(msg => mm.removeMessageListener(msg, this));
delete aWindow.__SSi;
},
@ -1260,6 +1302,7 @@ let SessionStoreInternal = {
onTabAdd: function ssi_onTabAdd(aWindow, aTab, aNoNotification) {
let browser = aTab.linkedBrowser;
browser.addEventListener("SwapDocShells", this);
browser.addEventListener("oop-browser-crashed", this);
if (!aNoNotification) {
this.saveStateDelayed(aWindow);
}
@ -1278,6 +1321,7 @@ let SessionStoreInternal = {
let browser = aTab.linkedBrowser;
delete browser.__SS_data;
browser.removeEventListener("SwapDocShells", this);
browser.removeEventListener("oop-browser-crashed", this);
// If this tab was in the middle of restoring or still needs to be restored,
// we need to reset that state. If the tab was restoring, we will attempt to
@ -1908,6 +1952,35 @@ let SessionStoreInternal = {
LastSession.clear();
},
/**
* Revive a crashed tab and restore its state from before it crashed.
*
* @param aTab
* A <xul:tab> linked to a crashed browser. This is a no-op if the
* browser hasn't actually crashed, or is not associated with a tab.
* This function will also throw if the browser happens to be remote.
*/
reviveCrashedTab(aTab) {
if (!aTab) {
throw new Error("SessionStore.reviveCrashedTab expected a tab, but got null.");
}
let browser = aTab.linkedBrowser;
if (!this._crashedBrowsers.has(browser.permanentKey)) {
return;
}
// Sanity check - the browser to be revived should not be remote
// at this point.
if (browser.isRemoteBrowser) {
throw new Error("SessionStore.reviveCrashedTab: " +
"Somehow a crashed browser is still remote.")
}
let data = TabState.collect(aTab);
this.restoreTab(aTab, data);
},
/**
* See if aWindow is usable for use when restoring a previous session via
* restoreLastSession. If usable, prepare it for use.

View File

@ -29,6 +29,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "SessionHistory",
XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",
"resource:///modules/sessionstore/SessionStorage.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsISyncMessageSender");
Cu.import("resource:///modules/sessionstore/FrameTree.jsm", this);
let gFrameTree = new FrameTree(this);
@ -711,7 +715,42 @@ ScrollPositionListener.init();
DocShellCapabilitiesListener.init();
PrivacyListener.init();
function handleRevivedTab() {
if (!content) {
removeEventListener("pagehide", handleRevivedTab);
return;
}
if (content.document.documentURI.startsWith("about:tabcrashed")) {
if (Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT) {
// Sanity check - we'd better be loading this in a non-remote browser.
throw new Error("We seem to be navigating away from about:tabcrashed in " +
"a non-remote browser. This should really never happen.");
}
removeEventListener("pagehide", handleRevivedTab);
// We can't send a message using the frame message manager because by
// the time we reach the unload event handler, it's "too late", and messages
// won't be sent or received. The child-process message manager works though,
// despite the fact that we're really running in the parent process.
let browser = docShell.chromeEventHandler;
cpmm.sendSyncMessage("SessionStore:RemoteTabRevived", null, {browser: browser});
}
}
// If we're browsing from the tab crashed UI to a blacklisted URI that keeps
// this browser non-remote, we'll handle that in a pagehide event.
addEventListener("pagehide", handleRevivedTab);
addEventListener("unload", () => {
// If we're browsing from the tab crashed UI to a URI that causes the tab
// to go remote again, we catch this in the unload event handler, because
// swapping out the non-remote browser for a remote one in
// tabbrowser.xml's updateBrowserRemoteness doesn't cause the pagehide
// event to be fired.
handleRevivedTab();
// Remove all registered nsIObservers.
PageStyleListener.uninit();
SessionStorageListener.uninit();

View File

@ -1,5 +1,4 @@
[DEFAULT]
skip-if = e10s # Most of these tests fail due to Bug ?????? - SessionStore is disabled in e10s
support-files =
dummy_page.html
head.js
@ -23,6 +22,7 @@ skip-if = buildapp == 'mulet'
[browser_tabview_bug587990.js]
[browser_tabview_bug588265.js]
[browser_tabview_bug589324.js]
skip-if = e10s # Bug 1086190
[browser_tabview_bug590606.js]
[browser_tabview_bug591706.js]
[browser_tabview_bug593283.js]
@ -36,6 +36,7 @@ skip-if = buildapp == 'mulet'
[browser_tabview_bug595930.js]
[browser_tabview_bug595943.js]
[browser_tabview_bug595965.js]
skip-if = e10s # Bug 1086190
[browser_tabview_bug596781.js]
[browser_tabview_bug597360.js]
[browser_tabview_bug597399.js]
@ -48,6 +49,7 @@ skip-if = true # Bug 711907
skip-if = os == 'linux' || e10s # Disabled on Linux: Bug 939620, much fail, so amaze; Disabled for e10s: Bug ??????
[browser_tabview_bug600645.js]
[browser_tabview_bug600812.js]
skip-if = e10s # Bug 1086190
[browser_tabview_bug602432.js]
skip-if = true # Bug 704417
[browser_tabview_bug604098.js]
@ -56,6 +58,7 @@ skip-if = true # Bug 704417
[browser_tabview_bug607108.js]
skip-if = os == 'linux' # Bug 947521
[browser_tabview_bug608037.js]
skip-if = e10s # Bug 1086190
[browser_tabview_bug608153.js]
[browser_tabview_bug608158.js]
[browser_tabview_bug608184.js]
@ -67,6 +70,7 @@ skip-if = true # Bug 736036
[browser_tabview_bug613541.js]
skip-if = os == "win" # Bug 951477
[browser_tabview_bug616729.js]
skip-if = e10s # Bug 1086190
[browser_tabview_bug616967.js]
[browser_tabview_bug618816.js]
[browser_tabview_bug618828.js]
@ -74,12 +78,14 @@ skip-if = buildapp == 'mulet'
[browser_tabview_bug619937.js]
[browser_tabview_bug622835.js]
[browser_tabview_bug623768.js]
skip-if = e10s # Bug 1086190
[browser_tabview_bug624265_perwindowpb.js]
skip-if = true # Bug 921984, hopefully fixed by bug 930202
[browser_tabview_bug624692.js]
[browser_tabview_bug624727_perwindowpb.js]
skip-if = buildapp == 'mulet'
[browser_tabview_bug624847.js]
skip-if = e10s # Bug 1086190
[browser_tabview_bug624931.js]
[browser_tabview_bug624953.js]
[browser_tabview_bug625195.js]
@ -87,7 +93,9 @@ skip-if = buildapp == 'mulet'
[browser_tabview_bug625424.js]
[browser_tabview_bug625955.js]
[browser_tabview_bug626368.js]
skip-if = e10s # Bug 1086190
[browser_tabview_bug626455.js]
skip-if = e10s # Bug 1086190
[browser_tabview_bug626525.js]
[browser_tabview_bug626791.js]
skip-if = buildapp == 'mulet'
@ -133,6 +141,7 @@ skip-if = true # Bug 754222
[browser_tabview_bug656778.js]
skip-if = os == "mac" # Bug 946918
[browser_tabview_bug656913.js]
skip-if = e10s # Bug 1086190
[browser_tabview_bug659594.js]
skip-if = os == "mac" || e10s # mac: Bug 939617; e10s - Bug ?????? - "leaked until shutdown [nsGlobalWindow #82 about:blank]"
[browser_tabview_bug662266.js]
@ -173,6 +182,7 @@ skip-if = buildapp == 'mulet'
skip-if = os == "mac" || os == "win" # Bug 945687
[browser_tabview_launch.js]
[browser_tabview_multiwindow_search.js]
skip-if = e10s # Bug 1086190
[browser_tabview_pending_tabs.js]
[browser_tabview_privatebrowsing_perwindowpb.js]
skip-if = os == 'linux' || e10s # linux: Bug 944300; e10s: bug ?????? - "leaked until shutdown [nsGlobalWindow #82 about:blank]"

View File

@ -63,9 +63,8 @@ function endGame() {
}
function newWindowWithTabView(callback, completeCallback) {
let charsetArg = "charset=" + window.content.document.characterSet;
let win = window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no,height=800,width=800",
"about:blank", charsetArg, null, null, true);
"about:blank", null, null, null, true);
let onLoad = function() {
win.removeEventListener("load", onLoad, false);
let onShown = function() {

View File

@ -9,10 +9,9 @@ function animateZoom() prefsBranch.getBoolPref("animate_zoom");
function test() {
waitForExplicitFinish();
let charsetArg = "charset=" + window.content.document.characterSet;
let win = window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no",
"about:blank", charsetArg, null, null, true);
"about:blank", null, null, null, true);
registerCleanupFunction(function() {
prefsBranch.setBoolPref("animate_zoom", true);

View File

@ -103,7 +103,7 @@ loop-call-button3.label = Hello
loop-call-button3.tooltiptext = Start a conversation
social-share-button.label = Share This Page
social-share-button.tooltiptext = Share This Page
social-share-button.tooltiptext = Share this page
panic-button.label = Forget
panic-button.tooltiptext = Forget about some browsing history

View File

@ -87,18 +87,6 @@ this.TabCrashReporter = {
}
},
reloadCrashedTab: function (browser) {
if (browser.isRemoteBrowser)
return;
let doc = browser.contentDocument;
if (!doc.documentURI.startsWith("about:tabcrashed"))
return;
let url = browser.currentURI.spec;
browser.loadURIWithFlags(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
},
onAboutTabCrashedLoad: function (aBrowser) {
if (!this.childMap)
return;

View File

@ -1937,6 +1937,10 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
background-clip: padding-box;
}
#urlbar[readonly] {
background-color: -moz-field;
}
@media (-moz-mac-lion-theme) {
#urlbar,
.searchbar-textbox {

View File

@ -1536,7 +1536,6 @@ if test "$GNU_CXX"; then
# -Wreturn-type - catches missing returns, zero false positives
# -Wsequence-point - catches undefined order behavior like `a = a++`
# -Wsign-compare - catches comparison of signed and unsigned types
# -Wswitch - catches switches without all enum cases or default case
# -Wtrigraphs - catches unlikely use of trigraphs
# -Wtype-limits - catches overflow bugs, few false positives
# -Wunused-label - catches unused goto labels
@ -1555,7 +1554,6 @@ if test "$GNU_CXX"; then
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=pointer-arith"
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=return-type"
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=sequence-point"
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=switch"
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unused-label"
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=trigraphs"
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=type-limits"

View File

@ -143,6 +143,11 @@ public:
return mConsumer.get();
}
SocketBase* GetSocketBase()
{
return GetConsumer();
}
/**
* Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
* directly from main thread. All non-main-thread accesses should happen with
@ -661,7 +666,8 @@ BluetoothSocket::SendSocketData(UnixSocketRawData* aData)
MOZ_ASSERT(!mImpl->IsShutdownOnMainThread());
XRE_GetIOMessageLoop()->PostTask(
FROM_HERE, new SocketIOSendTask<DroidSocketImpl>(mImpl, aData));
FROM_HERE,
new SocketIOSendTask<DroidSocketImpl, UnixSocketRawData>(mImpl, aData));
return true;
}

View File

@ -143,6 +143,11 @@ public:
return mConsumer.get();
}
SocketBase* GetSocketBase()
{
return GetConsumer();
}
/**
* Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
* directly from main thread. All non-main-thread accesses should happen with
@ -661,7 +666,7 @@ BluetoothSocket::SendSocketData(UnixSocketRawData* aData)
MOZ_ASSERT(!mImpl->IsShutdownOnMainThread());
XRE_GetIOMessageLoop()->PostTask(
FROM_HERE, new SocketIOSendTask<DroidSocketImpl>(mImpl, aData));
FROM_HERE, new SocketIOSendTask<DroidSocketImpl, UnixSocketRawData>(mImpl, aData));
return true;
}

View File

@ -216,7 +216,7 @@ let FormAssistant = {
'range'
]),
isKeyboardOpened: false,
isHandlingFocus: false,
selectionStart: -1,
selectionEnd: -1,
textBeforeCursor: "",
@ -304,7 +304,7 @@ let FormAssistant = {
});
});
if (del && element === self.focusedElement) {
self.hideKeyboard();
self.unhandleFocus();
self.selectionStart = -1;
self.selectionEnd = -1;
}
@ -351,7 +351,7 @@ let FormAssistant = {
if (this._editing) {
return;
}
this.sendKeyboardState(this.focusedElement);
this.sendInputState(this.focusedElement);
},
handleEvent: function fa_handleEvent(evt) {
@ -379,13 +379,13 @@ let FormAssistant = {
}
if (isContentEditable(target)) {
this.showKeyboard(this.getTopLevelEditable(target));
this.handleFocus(this.getTopLevelEditable(target));
this.updateSelection();
break;
}
if (this.isFocusableElement(target)) {
this.showKeyboard(target);
this.handleFocus(target);
this.updateSelection();
}
break;
@ -406,14 +406,14 @@ let FormAssistant = {
case "blur":
if (this.focusedElement) {
this.hideKeyboard();
this.unhandleFocus();
this.selectionStart = -1;
this.selectionEnd = -1;
}
break;
case "resize":
if (!this.isKeyboardOpened)
if (!this.isHandlingFocus)
return;
if (this.scrollIntoViewTimeout) {
@ -672,7 +672,7 @@ let FormAssistant = {
},
showKeyboard: function fa_showKeyboard(target) {
handleFocus: function fa_handleFocus(target) {
if (this.focusedElement === target)
return;
@ -682,18 +682,17 @@ let FormAssistant = {
this.setFocusedElement(target);
let count = this._focusCounter;
this.waitForNextTick(function fa_showKeyboardSync() {
this.waitForNextTick(function fa_handleFocusSync() {
if (count !== this._focusCounter) {
return;
}
let kbOpened = this.sendKeyboardState(target);
if (this.isTextInputElement(target))
this.isKeyboardOpened = kbOpened;
let isHandlingFocus = this.sendInputState(target);
this.isHandlingFocus = isHandlingFocus;
}.bind(this));
},
hideKeyboard: function fa_hideKeyboard() {
unhandleFocus: function fa_unhandleFocus() {
this.setFocusedElement(null);
let count = this._focusCounter;
@ -701,13 +700,13 @@ let FormAssistant = {
// Wait for the next tick before unset the focused element and etc.
// If the user move from one input from another,
// the remote process should get one Forms:Input message instead of two.
this.waitForNextTick(function fa_hideKeyboardSync() {
this.waitForNextTick(function fa_unhandleFocusSync() {
if (count !== this._focusCounter ||
!this.isKeyboardOpened) {
!this.isHandlingFocus) {
return;
}
this.isKeyboardOpened = false;
this.isHandlingFocus = false;
sendAsyncMessage("Forms:Input", { "type": "blur" });
}.bind(this));
},
@ -725,12 +724,6 @@ let FormAssistant = {
!this.ignoredInputTypes.has(element.type));
},
isTextInputElement: function fa_isTextInputElement(element) {
return element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement ||
isContentEditable(element);
},
getTopLevelEditable: function fa_getTopLevelEditable(element) {
function retrieveTopLevelEditable(element) {
while (element && !isContentEditable(element))
@ -742,7 +735,7 @@ let FormAssistant = {
return retrieveTopLevelEditable(element) || element;
},
sendKeyboardState: function(element) {
sendInputState: function(element) {
// FIXME/bug 729623: work around apparent bug in the IME manager
// in gecko.
let readonly = element.getAttribute("readonly");

View File

@ -24,3 +24,4 @@ support-files =
[test_delete_focused_element.html]
[test_sendkey_cancel.html]
[test_two_inputs.html]
[test_two_selects.html]

View File

@ -0,0 +1,151 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1079728
-->
<head>
<title>Test switching between two inputs</title>
<script type="application/javascript;version=1.7" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript;version=1.7" src="inputmethod_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1079728">Mozilla Bug 1079728</a>
<p id="display"></p>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.7">
inputmethod_setup(function() {
runTest();
});
let appFrameScript = function appFrameScript() {
let select1 = content.document.body.firstElementChild;
let select2 = content.document.body.children[1];
let i = 1;
select1.focus();
addMessageListener('test:next', function() {
i++;
switch (i) {
case 2:
select2.focus();
break;
case 3:
select2.blur();
select2.focus();
break;
case 4:
select2.blur();
break;
case 5:
select2.focus();
select2.blur();
select1.focus();
break;
case 6:
select1.blur();
break;
}
});
};
function runTest() {
let im = navigator.mozInputMethod;
let i = 0;
im.oninputcontextchange = function(evt) {
var inputcontext = navigator.mozInputMethod.inputcontext;
i++;
switch (i) {
// focus on the first input receives the first input context.
case 1:
ok(!!inputcontext, 'Receving the first input context');
is(inputcontext.textAfterCursor, 'First');
mm.sendAsyncMessage('test:next');
break;
// focus on the second input (implicitly blur the first input)
// results the second input context.
case 2:
ok(!!inputcontext, 'Receving the second input context');
is(inputcontext.textAfterCursor, 'Second');
mm.sendAsyncMessage('test:next');
break;
// blur and re-focus on the second input results updated
// input context for the second input.
case 3:
ok(!!inputcontext, 'Receving the second input context');
is(inputcontext.textAfterCursor, 'Second');
mm.sendAsyncMessage('test:next');
break;
// blur on the second input results null input context
case 4:
is(inputcontext, null, 'Receving null inputcontext');
mm.sendAsyncMessage('test:next');
break;
// focus and blur on the second input sends no message;
// focus on the first input receives the first input context.
case 5:
ok(!!inputcontext, 'Receving the first input context');
is(inputcontext.textAfterCursor, 'First');
mm.sendAsyncMessage('test:next');
break;
// blur on the first input results null input context
case 6:
is(inputcontext, null, 'Receving null inputcontext');
inputmethod_cleanup();
break;
default:
ok(false, 'Receving extra inputcontextchange calls');
inputmethod_cleanup();
break;
}
};
// Set current page as an input method.
SpecialPowers.wrap(im).setActive(true);
let iframe = document.createElement('iframe');
iframe.src = 'data:text/html,<html><body><select><option>First</option></select><select><option>Second</option></select></html>';
iframe.setAttribute('mozbrowser', true);
document.body.appendChild(iframe);
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
iframe.addEventListener('mozbrowserloadend', function() {
mm.loadFrameScript('data:,(' + encodeURIComponent(appFrameScript.toString()) + ')();', false);
});
}
</script>
</pre>
</body>
</html>

View File

@ -8,7 +8,7 @@
namespace mozilla {
#define NFCD_MAJOR_VERSION 1
#define NFCD_MINOR_VERSION 12
#define NFCD_MINOR_VERSION 13
enum NfcRequest {
ConfigReq = 0,

View File

@ -25,36 +25,24 @@ this.DEBUG_NFC = false || DEBUG_ALL;
// nfcd error codes
this.NFC_SUCCESS = 0;
this.NFC_ERROR_IO = -1;
this.NFC_ERROR_CANCELLED = -2;
this.NFC_ERROR_TIMEOUT = -3;
this.NFC_ERROR_BUSY = -4;
this.NFC_ERROR_CONNECT = -5;
this.NFC_ERROR_DISCONNECT = -6;
this.NFC_ERROR_READ = -7;
this.NFC_ERROR_WRITE = -8;
this.NFC_ERROR_INVALID_PARAM = -9;
this.NFC_ERROR_INSUFFICIENT_RESOURCES = -10;
this.NFC_ERROR_SOCKET_CREATION = -11;
this.NFC_ERROR_SOCKET_NOT_CONNECTED = -12;
this.NFC_ERROR_BUFFER_TOO_SMALL = -13;
this.NFC_ERROR_SAP_USED = -14;
this.NFC_ERROR_SERVICE_NAME_USED = -15;
this.NFC_ERROR_SOCKET_OPTIONS = -16;
this.NFC_ERROR_FAIL_ENABLE_DISCOVERY = -17;
this.NFC_ERROR_FAIL_DISABLE_DISCOVERY = -18;
this.NFC_ERROR_NOT_INITIALIZED = -19;
this.NFC_ERROR_INITIALIZE_FAIL = -20;
this.NFC_ERROR_DEINITIALIZE_FAIL = -21;
this.NFC_ERROR_SE_CONNECTED = -22;
this.NFC_ERROR_NO_SE_CONNECTED = -23;
this.NFC_ERROR_NOT_SUPPORTED = -24;
this.NFC_ERROR_BAD_SESSION_ID = -25;
this.NFC_ERROR_LOST_TECH = -26;
this.NFC_ERROR_BAD_TECH_TYPE = -27;
this.NFC_ERROR_SELECT_SE_FAIL = -28;
this.NFC_ERROR_DESELECT_SE_FAIL = -29;
this.NFC_ERROR_FAIL_ENABLE_LOW_POWER_MODE = -30;
this.NFC_ERROR_FAIL_DISABLE_LOW_POWER_MODE = -31;
this.NFC_ERROR_TIMEOUT = -2;
this.NFC_ERROR_BUSY = -3;
this.NFC_ERROR_CONNECT = -4;
this.NFC_ERROR_DISCONNECT = -5;
this.NFC_ERROR_READ = -6;
this.NFC_ERROR_WRITE = -7;
this.NFC_ERROR_INVALID_PARAM = -8;
this.NFC_ERROR_INSUFFICIENT_RESOURCES = -9;
this.NFC_ERROR_SOCKET_CREATION = -10;
this.NFC_ERROR_FAIL_ENABLE_DISCOVERY = -11;
this.NFC_ERROR_FAIL_DISABLE_DISCOVERY = -12;
this.NFC_ERROR_NOT_INITIALIZED = -13;
this.NFC_ERROR_INITIALIZE_FAIL = -14;
this.NFC_ERROR_DEINITIALIZE_FAIL = -15;
this.NFC_ERROR_NOT_SUPPORTED = -16;
this.NFC_ERROR_BAD_SESSION_ID = -17,
this.NFC_ERROR_FAIL_ENABLE_LOW_POWER_MODE = -18;
this.NFC_ERROR_FAIL_DISABLE_LOW_POWER_MODE = -19;
// Gecko specific error codes
this.NFC_GECKO_ERROR_GENERIC_FAILURE = 1;
@ -64,7 +52,6 @@ this.NFC_GECKO_ERROR_SEND_FILE_FAILED = 4;
this.NFC_ERROR_MSG = {};
this.NFC_ERROR_MSG[this.NFC_ERROR_IO] = "NfcIoError";
this.NFC_ERROR_MSG[this.NFC_ERROR_CANCELLED] = "NfcCancelledError";
this.NFC_ERROR_MSG[this.NFC_ERROR_TIMEOUT] = "NfcTimeoutError";
this.NFC_ERROR_MSG[this.NFC_ERROR_BUSY] = "NfcBusyError";
this.NFC_ERROR_MSG[this.NFC_ERROR_CONNECT] = "NfcConnectError";
@ -74,24 +61,13 @@ this.NFC_ERROR_MSG[this.NFC_ERROR_WRITE] = "NfcWriteError";
this.NFC_ERROR_MSG[this.NFC_ERROR_INVALID_PARAM] = "NfcInvalidParamError";
this.NFC_ERROR_MSG[this.NFC_ERROR_INSUFFICIENT_RESOURCES] = "NfcInsufficentResourcesError";
this.NFC_ERROR_MSG[this.NFC_ERROR_SOCKET_CREATION] = "NfcSocketCreationError";
this.NFC_ERROR_MSG[this.NFC_ERROR_SOCKET_NOT_CONNECTED] = "NfcSocketNotConntectedError";
this.NFC_ERROR_MSG[this.NFC_ERROR_BUFFER_TOO_SMALL] = "NfcBufferTooSmallError";
this.NFC_ERROR_MSG[this.NFC_ERROR_SAP_USED] = "NfcSapUsedError";
this.NFC_ERROR_MSG[this.NFC_ERROR_SERVICE_NAME_USED] = "NfcServiceNameUsedError";
this.NFC_ERROR_MSG[this.NFC_ERROR_SOCKET_OPTIONS] = "NfcSocketOptionsError";
this.NFC_ERROR_MSG[this.NFC_ERROR_FAIL_ENABLE_DISCOVERY] = "NfcFailEnableDiscoveryError";
this.NFC_ERROR_MSG[this.NFC_ERROR_FAIL_DISABLE_DISCOVERY] = "NfcFailDisableDiscoveryError";
this.NFC_ERROR_MSG[this.NFC_ERROR_NOT_INITIALIZED] = "NfcNotInitializedError";
this.NFC_ERROR_MSG[this.NFC_ERROR_INITIALIZE_FAIL] = "NfcInitializeFailError";
this.NFC_ERROR_MSG[this.NFC_ERROR_DEINITIALIZE_FAIL] = "NfcDeinitializeFailError";
this.NFC_ERROR_MSG[this.NFC_ERROR_SE_CONNECTED] = "NfcSeConnectedError";
this.NFC_ERROR_MSG[this.NFC_ERROR_NO_SE_CONNECTED] = "NfcNoSeConnectedError";
this.NFC_ERROR_MSG[this.NFC_ERROR_NOT_SUPPORTED] = "NfcNotSupportedError";
this.NFC_ERROR_MSG[this.NFC_ERROR_BAD_SESSION_ID] = "NfcBadSessionIdError";
this.NFC_ERROR_MSG[this.NFC_ERROR_LOST_TECH] = "NfcLostTechError";
this.NFC_ERROR_MSG[this.NFC_ERROR_BAD_TECH_TYPE] = "NfcBadTechTypeError";
this.NFC_ERROR_MSG[this.NFC_ERROR_SELECT_SE_FAIL] = "SelectSecureElementFailed";
this.NFC_ERROR_MSG[this.NFC_ERROR_DESELECT_SE_FAIL] = "DeselectSecureElementFailed";
this.NFC_ERROR_MSG[this.NFC_ERROR_FAIL_ENABLE_LOW_POWER_MODE] = "EnableLowPowerModeFail";
this.NFC_ERROR_MSG[this.NFC_ERROR_FAIL_DISABLE_LOW_POWER_MODE] = "DisableLowPowerModeFail";
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_GENERIC_FAILURE] = "NfcGenericFailureError";

View File

@ -704,7 +704,9 @@ let SettingsRequestManager = {
removeObserver: function(aMsgMgr) {
if (DEBUG) {
let principal = this.mmPrincipals.get(aMsgMgr);
debug("Remove observer for " + principal.origin);
if (principal) {
debug("Remove observer for " + principal.origin);
}
}
let index = this.children.indexOf(aMsgMgr);
if (index != -1) {
@ -745,26 +747,31 @@ let SettingsRequestManager = {
removeMessageManager: function(aMsgMgr, aPrincipal) {
if (DEBUG) debug("Removing message manager");
let msgMgrPrincipal = this.mmPrincipals.get(aMsgMgr);
this.removeObserver(aMsgMgr);
let closedLockIDs = [];
let lockIDs = Object.keys(this.lockInfo);
for (let i in lockIDs) {
let lock = this.lockInfo[lockIDs[i]];
if (lock._mm == aMsgMgr) {
let lockId = lockIDs[i];
let lock = this.lockInfo[lockId];
if (lock._mm === aMsgMgr && msgMgrPrincipal === aPrincipal) {
let is_finalizing = false;
for (let task_index in lock.tasks) {
if (lock.tasks[task_index].operation === "finalize") {
let task_index;
// Go in reverse order because finalize should be the last one
for (task_index = lock.tasks.length; task_index >= 0; task_index--) {
if (lock.tasks[task_index]
&& lock.tasks[task_index].operation === "finalize") {
is_finalizing = true;
break;
}
}
if (!is_finalizing) {
this.queueTask("finalize", {lockID: lockIDs[i]}, aPrincipal).then(
this.queueTask("finalize", {lockID: lockId}, aPrincipal).then(
function() {
if (DEBUG) debug("Lock " + lockIDs[i] + " with dead message manager finalized");
if (DEBUG) debug("Lock " + lockId + " with dead message manager finalized");
},
function(error) {
if (DEBUG) debug("Lock " + lockIDs[i] + " with dead message manager NOT FINALIZED due to error: " + error);
if (DEBUG) debug("Lock " + lockId + " with dead message manager NOT FINALIZED due to error: " + error);
}
);
}

View File

@ -23,3 +23,5 @@ EXTRA_JS_MODULES += [
]
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']

View File

@ -0,0 +1,154 @@
"use strict";
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsIMessageSender");
let principal = Services.scriptSecurityManager.getSystemPrincipal();
let lockID = "{435d2192-4f21-48d4-90b7-285f147a56be}";
// Helper to start the Settings Request Manager
function startSettingsRequestManager() {
Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
}
// Helper function to add a listener, send message and treat the reply
function addAndSend(msg, reply, callback, payload, runNext = true) {
let handler = {
receiveMessage: function(message) {
if (message.name === reply) {
cpmm.removeMessageListener(reply, handler);
callback(message);
if (runNext) {
run_next_test();
}
}
}
};
cpmm.addMessageListener(reply, handler);
cpmm.sendAsyncMessage(msg, payload, undefined, principal);
}
// We need to trigger a Settings:Run message to make the queue progress
function send_settingsRun() {
let msg = {lockID: lockID, isServiceLock: true};
cpmm.sendAsyncMessage("Settings:Run", msg, undefined, principal);
}
function kill_child() {
let msg = {lockID: lockID, isServiceLock: true};
cpmm.sendAsyncMessage("child-process-shutdown", msg, undefined, principal);
}
function run_test() {
do_get_profile();
startSettingsRequestManager();
run_next_test();
}
add_test(function test_createLock() {
let msg = {lockID: lockID, isServiceLock: true};
cpmm.sendAsyncMessage("Settings:CreateLock", msg, undefined, principal);
cpmm.sendAsyncMessage(
"Settings:RegisterForMessages", undefined, undefined, principal);
ok(true);
run_next_test();
});
add_test(function test_get_empty() {
let requestID = 10;
let msgReply = "Settings:Get:OK";
let msgHandler = function(message) {
equal(requestID, message.data.requestID);
equal(lockID, message.data.lockID);
ok(Object.keys(message.data.settings).length >= 0);
};
addAndSend("Settings:Get", msgReply, msgHandler, {
requestID: requestID,
lockID: lockID,
name: "language.current"
});
send_settingsRun();
});
add_test(function test_set_get_nonempty() {
let settings = { "language.current": "fr-FR:XPC" };
let requestIDSet = 20;
let msgReplySet = "Settings:Set:OK";
let msgHandlerSet = function(message) {
equal(requestIDSet, message.data.requestID);
equal(lockID, message.data.lockID);
};
addAndSend("Settings:Set", msgReplySet, msgHandlerSet, {
requestID: requestIDSet,
lockID: lockID,
settings: settings
}, false);
let requestIDGet = 25;
let msgReplyGet = "Settings:Get:OK";
let msgHandlerGet = function(message) {
equal(requestIDGet, message.data.requestID);
equal(lockID, message.data.lockID);
for(let p in settings) {
equal(settings[p], message.data.settings[p]);
}
};
addAndSend("Settings:Get", msgReplyGet, msgHandlerGet, {
requestID: requestIDGet,
lockID: lockID,
name: Object.keys(settings)[0]
});
// Set and Get have been push into the queue, let's run
send_settingsRun();
});
// This test exposes bug 1076597 behavior
add_test(function test_wait_for_finalize() {
let settings = { "language.current": "en-US:XPC" };
let requestIDSet = 30;
let msgReplySet = "Settings:Set:OK";
let msgHandlerSet = function(message) {
equal(requestIDSet, message.data.requestID);
equal(lockID, message.data.lockID);
};
addAndSend("Settings:Set", msgReplySet, msgHandlerSet, {
requestID: requestIDSet,
lockID: lockID,
settings: settings
}, false);
let requestIDGet = 35;
let msgReplyGet = "Settings:Get:OK";
let msgHandlerGet = function(message) {
equal(requestIDGet, message.data.requestID);
equal(lockID, message.data.lockID);
for(let p in settings) {
equal(settings[p], message.data.settings[p]);
}
};
addAndSend("Settings:Get", msgReplyGet, msgHandlerGet, {
requestID: requestIDGet,
lockID: lockID,
name: Object.keys(settings)[0]
});
// We simulate a child death, which will force previous requests to be set
// into finalize state
kill_child();
// Then when we issue Settings:Run, those finalized should be triggered
send_settingsRun();
});

View File

@ -0,0 +1,6 @@
[DEFAULT]
head =
tail =
[test_settingsrequestmanager_messages.js]
skip-if = ((buildapp != 'b2g') || ((toolkit == 'gonk') && debug)) # bug 1080377: for some reason, this is not working on emulator-b2g debug build

View File

@ -348,7 +348,7 @@ function RilObject(aContext) {
this.telephonyRequestQueue = new TelephonyRequestQueue(this);
this.currentCalls = {};
this.currentConference = {state: null, participants: {}};
this.currentConferenceState = CALL_STATE_UNKNOWN;
this.currentDataCalls = {};
this._pendingSentSmsMap = {};
this.pendingNetworkType = {};
@ -375,9 +375,9 @@ RilObject.prototype = {
currentCalls: null,
/**
* Existing conference call and its participants.
* Call state of current conference group.
*/
currentConference: null,
currentConferenceState: null,
/**
* Existing data calls.
@ -1919,24 +1919,11 @@ RilObject.prototype = {
}
},
// Flag indicating whether user has requested making a conference call.
_hasConferenceRequest: false,
conferenceCall: function(options) {
if (this._isCdma) {
options.featureStr = "";
this.sendCdmaFlashCommand(options);
} else {
// Only accept one conference request at a time..
if (this._hasConferenceRequest) {
options.success = false;
options.errorName = "addError";
options.errorMsg = GECKO_ERROR_GENERIC_FAILURE;
this.sendChromeMessage(options);
return;
}
this._hasConferenceRequest = true;
this.telephonyRequestQueue.push(REQUEST_CONFERENCE,
this.sendRilRequestConference, options);
}
@ -3833,171 +3820,184 @@ RilObject.prototype = {
},
/**
* Helpers for processing call state and handle the active call.
* Classify new calls into three groups: (removed, remained, added).
*/
_processCalls: function(newCalls) {
let conferenceChanged = false;
let clearConferenceRequest = false;
_classifyCalls: function(newCalls) {
newCalls = newCalls || {};
let removedCalls = [];
let remainedCalls = [];
let addedCalls = [];
// Go through the calls we currently have on file and see if any of them
// changed state. Remove them from the newCalls map as we deal with them
// so that only new calls remain in the map after we're done.
for each (let currentCall in this.currentCalls) {
let newCall;
if (newCalls) {
newCall = newCalls[currentCall.callIndex];
delete newCalls[currentCall.callIndex];
}
// Call is no longer reported by the radio. Remove from our map and send
// disconnected state change.
let newCall = newCalls[currentCall.callIndex];
if (!newCall) {
if (this.currentConference.participants[currentCall.callIndex]) {
conferenceChanged = true;
}
this._removeVoiceCall(currentCall,
currentCall.hangUpLocal ?
GECKO_CALL_ERROR_NORMAL_CALL_CLEARING : null);
continue;
}
// Call is still valid.
if (newCall.state == currentCall.state &&
newCall.isMpty == currentCall.isMpty) {
continue;
}
// State has changed.
if (newCall.state == CALL_STATE_INCOMING &&
currentCall.state == CALL_STATE_WAITING) {
// Update the call internally but we don't notify chrome since these two
// states are viewed as the same one there.
currentCall.state = newCall.state;
continue;
}
if (!currentCall.started && newCall.state == CALL_STATE_ACTIVE) {
currentCall.started = new Date().getTime();
}
if (currentCall.isMpty == newCall.isMpty &&
newCall.state != currentCall.state) {
currentCall.state = newCall.state;
if (currentCall.isConference) {
conferenceChanged = true;
}
this._handleChangedCallState(currentCall);
continue;
}
// '.isMpty' becomes false when the conference call is put on hold.
// We need to introduce additional 'isConference' to correctly record the
// real conference status
// Update a possible conference participant when .isMpty changes.
if (!currentCall.isMpty && newCall.isMpty) {
if (this._hasConferenceRequest) {
conferenceChanged = true;
clearConferenceRequest = true;
currentCall.state = newCall.state;
currentCall.isMpty = newCall.isMpty;
currentCall.isConference = true;
this.currentConference.participants[currentCall.callIndex] = currentCall;
this._handleChangedCallState(currentCall);
} else if (currentCall.isConference) {
// The case happens when resuming a held conference call.
conferenceChanged = true;
currentCall.state = newCall.state;
currentCall.isMpty = newCall.isMpty;
this.currentConference.participants[currentCall.callIndex] = currentCall;
this._handleChangedCallState(currentCall);
} else {
// Weird. This sometimes happens when we switch two calls, but it is
// not a conference call.
currentCall.state = newCall.state;
this._handleChangedCallState(currentCall);
}
} else if (currentCall.isMpty && !newCall.isMpty) {
if (!this.currentConference.participants[newCall.callIndex]) {
continue;
}
// '.isMpty' of a conference participant is set to false by rild when
// the conference call is put on hold. We don't actually know if the call
// still attends the conference until updating all calls finishes. We
// cache it for further determination.
if (newCall.state != CALL_STATE_HOLDING) {
delete this.currentConference.participants[newCall.callIndex];
currentCall.state = newCall.state;
currentCall.isMpty = newCall.isMpty;
currentCall.isConference = false;
conferenceChanged = true;
this._handleChangedCallState(currentCall);
continue;
}
if (!this.currentConference.cache) {
this.currentConference.cache = {};
}
this.currentConference.cache[currentCall.callIndex] = newCall;
currentCall.state = newCall.state;
currentCall.isMpty = newCall.isMpty;
conferenceChanged = true;
}
}
// We have a successful dialing request. Check whether we could find a new
// call for it.
if (this.pendingMO) {
let options = this.pendingMO.options;
this.pendingMO = null;
// Find the callIndex of the new outgoing call.
let callIndex = -1;
for (let i in newCalls) {
if (newCalls[i].state !== CALL_STATE_INCOMING) {
callIndex = newCalls[i].callIndex;
newCalls[i].isEmergency = options.isEmergency;
break;
}
}
if (callIndex === -1) {
// The call doesn't exist.
options.success = false;
options.errorMsg = GECKO_CALL_ERROR_UNSPECIFIED;
this.sendChromeMessage(options);
removedCalls.push(currentCall);
} else {
options.success = true;
options.callIndex = callIndex;
this.sendChromeMessage(options);
remainedCalls.push(newCall);
delete newCalls[currentCall.callIndex];
}
}
// Go through any remaining calls that are new to us.
for each (let newCall in newCalls) {
if (!newCall.isVoice) {
if (newCall.isVoice) {
addedCalls.push(newCall);
}
}
return [removedCalls, remainedCalls, addedCalls];
},
/**
* Check the calls in addedCalls and assign an appropriate one to pendingMO.
* Also update the |isEmergency| on that call.
*/
_assignPendingMO: function(addedCalls) {
let options = this.pendingMO.options;
this.pendingMO = null;
for (let call of addedCalls) {
if (call.state !== CALL_STATE_INCOMING) {
call.isEmergency = options.isEmergency;
options.success = true;
options.callIndex = call.callIndex;
this.sendChromeMessage(options);
return;
}
}
// The call doesn't exist.
options.success = false;
options.errorMsg = GECKO_CALL_ERROR_UNSPECIFIED;
this.sendChromeMessage(options);
},
/**
* Check the currentCalls and identify the conference group.
* Return the conference state and the group as a set.
*/
_detectConference: function() {
// There are some difficuties to identify the conference by |.isMpty| so we
// don't rely on this flag.
// - |.isMpty| becomes false when the conference call is put on hold.
// - |.isMpty| may remain true when other participants left the conference.
// All the calls in the conference should have the same state and it is
// either ACTIVE or HOLDING. That means, if we find a group of call with
// the same state and its size is larger than 2, it must be a conference.
let activeCalls = new Set();
let holdingCalls = new Set();
for each (let call in this.currentCalls) {
if (call.state === CALL_STATE_ACTIVE) {
activeCalls.add(call);
} else if (call.state === CALL_STATE_HOLDING) {
holdingCalls.add(call);
}
}
if (activeCalls.size >= 2) {
return [CALL_STATE_ACTIVE, activeCalls];
} else if (holdingCalls.size >= 2) {
return [CALL_STATE_HOLDING, holdingCalls];
}
return [CALL_STATE_UNKNOWN, new Set()];
},
/**
* Helpers for processing call state changes.
*/
_processClassifiedCalls: function(removedCalls, remainedCalls, addedCalls,
failCause) {
// Handle removed calls.
for (let call of removedCalls) {
this._removeVoiceCall(call, call.hangUpLocal ?
GECKO_CALL_ERROR_NORMAL_CALL_CLEARING : failCause);
}
let changedCalls = new Set();
// Handle remained calls.
for (let newCall of remainedCalls) {
let oldCall = this.currentCalls[newCall.callIndex];
if (oldCall.state == newCall.state) {
continue;
}
if (newCall.isMpty) {
conferenceChanged = true;
if (oldCall.state == CALL_STATE_WAITING &&
newCall.state == CALL_STATE_INCOMING) {
// Update the call internally but we don't notify chrome since these two
// states are viewed as the same one there.
oldCall.state = newCall.state;
continue;
}
this._addNewVoiceCall(newCall);
if (!oldCall.started && newCall.state == CALL_STATE_ACTIVE) {
oldCall.started = new Date().getTime();
}
oldCall.state = newCall.state;
changedCalls.add(oldCall);
}
if (clearConferenceRequest) {
this._hasConferenceRequest = false;
}
if (conferenceChanged) {
this._ensureConference();
// Handle pendingMO.
if (this.pendingMO) {
this._assignPendingMO(addedCalls);
}
// Update audio state.
let message = {rilMessageType: "audioStateChanged",
state: this._detectAudioState()};
this.sendChromeMessage(message);
// Handle added calls.
for (let call of addedCalls) {
this._addVoiceCall(call);
changedCalls.add(call);
}
// Detect conference and update isConference flag.
let [newConferenceState, conference] = this._detectConference();
for each (let call in this.currentCalls) {
let isConference = conference.has(call);
if (call.isConference != isConference) {
call.isConference = isConference;
changedCalls.add(call);
}
}
// Update audio state. We have to send the message before callstatechange
// to make sure that the audio state is ready first.
this.sendChromeMessage({
rilMessageType: "audioStateChanged",
state: this._detectAudioState()
});
// Notify call state change.
for (let call of changedCalls) {
this._handleChangedCallState(call);
}
// Notify conference state change.
if (this.currentConferenceState != newConferenceState) {
this.currentConferenceState = newConferenceState;
let message = {rilMessageType: "conferenceCallStateChanged",
state: newConferenceState};
this.sendChromeMessage(message);
}
},
_processCalls: function(newCalls) {
let [removed, remained, added] = this._classifyCalls(newCalls);
// Let's get the failCause first if there are removed calls. Otherwise, we
// need to trigger another async request when removing call and it cause
// the order of callDisconnected and conferenceCallStateChanged
// unpredictable.
if (removed.length) {
this.getFailCauseCode((function(removed, remained, added, failCause) {
this._processClassifiedCalls(removed, remained, added, failCause);
}).bind(this, removed, remained, added));
} else {
this._processClassifiedCalls(removed, remained, added);
}
},
_detectAudioState: function() {
@ -4015,90 +4015,23 @@ RilObject.prototype = {
return AUDIO_STATE_IN_CALL;
},
_addNewVoiceCall: function(newCall) {
_addVoiceCall: function(newCall) {
// Format international numbers appropriately.
if (newCall.number && newCall.toa == TOA_INTERNATIONAL &&
newCall.number[0] != "+") {
newCall.number = "+" + newCall.number;
}
if (newCall.state == CALL_STATE_INCOMING) {
newCall.isOutgoing = false;
} else if (newCall.state == CALL_STATE_DIALING) {
newCall.isOutgoing = true;
}
newCall.isOutgoing = !(newCall.state == CALL_STATE_INCOMING);
newCall.isConference = false;
// Set flag for conference.
newCall.isConference = newCall.isMpty ? true : false;
// Add to our map.
if (newCall.isMpty) {
this.currentConference.participants[newCall.callIndex] = newCall;
}
this._handleChangedCallState(newCall);
this.currentCalls[newCall.callIndex] = newCall;
},
_removeVoiceCall: function(removedCall, failCause) {
if (this.currentConference.participants[removedCall.callIndex]) {
removedCall.isConference = false;
delete this.currentConference.participants[removedCall.callIndex];
delete this.currentCalls[removedCall.callIndex];
// We don't query the fail cause here as it triggers another asynchrouns
// request that leads to a problem of updating all conferece participants
// in one task.
this._handleDisconnectedCall(removedCall);
} else {
delete this.currentCalls[removedCall.callIndex];
if (failCause) {
removedCall.failCause = failCause;
this._handleDisconnectedCall(removedCall);
} else {
this.getFailCauseCode((function(call, failCause) {
call.failCause = failCause;
this._handleDisconnectedCall(call);
}).bind(this, removedCall));
}
}
},
_ensureConference: function() {
let oldState = this.currentConference.state;
let remaining = Object.keys(this.currentConference.participants);
if (remaining.length == 1) {
// Remove that if only does one remain in a conference call.
let call = this.currentCalls[remaining[0]];
call.isConference = false;
this._handleChangedCallState(call);
delete this.currentConference.participants[call.callIndex];
} else if (remaining.length > 1) {
for each (let call in this.currentConference.cache) {
call.isConference = true;
this.currentConference.participants[call.callIndex] = call;
this.currentCalls[call.callIndex] = call;
this._handleChangedCallState(call);
}
}
delete this.currentConference.cache;
// Update the conference call's state.
let state = CALL_STATE_UNKNOWN;
for each (let call in this.currentConference.participants) {
if (state != CALL_STATE_UNKNOWN && state != call.state) {
// Each participant should have the same state, otherwise something
// wrong happens.
state = CALL_STATE_UNKNOWN;
break;
}
state = call.state;
}
if (oldState != state) {
this.currentConference.state = state;
let message = {rilMessageType: "conferenceCallStateChanged",
state: state};
this.sendChromeMessage(message);
}
_removeVoiceCall: function(call, failCause) {
delete this.currentCalls[call.callIndex];
call.failCause = failCause;
this._handleDisconnectedCall(call);
},
_handleChangedCallState: function(changedCall) {
@ -5528,7 +5461,6 @@ RilObject.prototype[REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE] = function REQ
RilObject.prototype[REQUEST_CONFERENCE] = function REQUEST_CONFERENCE(length, options) {
options.success = (options.rilRequestError === 0);
if (!options.success) {
this._hasConferenceRequest = false;
options.errorName = "addError";
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
this.sendChromeMessage(options);

View File

@ -495,6 +495,11 @@ Telephony::CallStateChanged(uint32_t aServiceId, uint32_t aCallIndex,
modifiedCall->UpdateMergeable(aIsMergeable);
if (modifiedCall->CallState() != aCallState) {
if (aCallState == nsITelephonyService::CALL_STATE_DISCONNECTED) {
modifiedCall->ChangeStateInternal(aCallState, true);
return NS_OK;
}
// We don't fire the statechange event on a call in conference here.
// Instead, the event will be fired later in
// TelephonyCallGroup::ChangeState(). Thus the sequence of firing the

View File

@ -227,15 +227,26 @@ let emulator = (function() {
is(call.state, state, "call state");
}
/**
* Convenient helper to compare two call lists. Size should be the same and
* order is not important.
*/
function checkCalls(actualCalls, expectedCalls) {
if (actualCalls.length == expectedCalls.length) {
let expectedSet = new Set(expectedCalls);
for (let i = 0; i < actualCalls.length; ++i) {
ok(expectedSet.has(actualCalls[i]), "should contain the call");
}
}
}
/**
* Convenient helper to check mozTelephony.active and mozTelephony.calls.
*/
function checkTelephonyActiveAndCalls(active, calls) {
is(telephony.active, active, "telephony.active");
is(telephony.calls.length, calls.length, "telephony.calls");
for (let i = 0; i < calls.length; ++i) {
is(telephony.calls[i], calls[i]);
}
checkCalls(telephony.calls, calls);
}
/**
@ -245,9 +256,7 @@ let emulator = (function() {
function checkConferenceStateAndCalls(state, calls) {
is(conference.state, state, "conference.state");
is(conference.calls.length, calls.length, "conference.calls");
for (let i = 0; i < calls.length; i++) {
is(conference.calls[i], calls[i]);
}
checkCalls(conference.calls, calls);
}
/**

View File

@ -15,10 +15,10 @@ namespace mozilla {
namespace ipc {
//
// UnixSocketRawData
// UnixSocketIOBuffer
//
UnixSocketRawData::UnixSocketRawData(const void* aData, size_t aSize)
UnixSocketIOBuffer::UnixSocketIOBuffer(const void* aData, size_t aSize)
: mSize(aSize)
, mOffset(0)
, mAvailableSpace(aSize)
@ -29,14 +29,90 @@ UnixSocketRawData::UnixSocketRawData(const void* aData, size_t aSize)
memcpy(mData, aData, mSize);
}
UnixSocketRawData::UnixSocketRawData(size_t aSize)
UnixSocketIOBuffer::UnixSocketIOBuffer(size_t aAvailableSpace)
: mSize(0)
, mOffset(0)
, mAvailableSpace(aSize)
, mAvailableSpace(aAvailableSpace)
{
mData = new uint8_t[mAvailableSpace];
}
UnixSocketIOBuffer::~UnixSocketIOBuffer()
{ }
const uint8_t*
UnixSocketIOBuffer::Consume(size_t aLen)
{
if (NS_WARN_IF(GetSize() < aLen)) {
return nullptr;
}
uint8_t* data = mData + mOffset;
mOffset += aLen;
return data;
}
nsresult
UnixSocketIOBuffer::Read(void* aValue, size_t aLen)
{
const uint8_t* data = Consume(aLen);
if (!data) {
return NS_ERROR_OUT_OF_MEMORY;
}
memcpy(aValue, data, aLen);
return NS_OK;
}
uint8_t*
UnixSocketIOBuffer::Append(size_t aLen)
{
if (((mAvailableSpace - mSize) < aLen)) {
size_t availableSpace = mAvailableSpace + std::max(mAvailableSpace, aLen);
uint8_t* data = new uint8_t[availableSpace];
memcpy(data, mData, mSize);
mData = data;
mAvailableSpace = availableSpace;
}
uint8_t* data = mData + mSize;
mSize += aLen;
return data;
}
nsresult
UnixSocketIOBuffer::Write(const void* aValue, size_t aLen)
{
uint8_t* data = Append(aLen);
if (!data) {
return NS_ERROR_OUT_OF_MEMORY;
}
memcpy(data, aValue, aLen);
return NS_OK;
}
void
UnixSocketIOBuffer::CleanupLeadingSpace()
{
if (GetLeadingSpace()) {
if (GetSize() <= GetLeadingSpace()) {
memcpy(mData, GetData(), GetSize());
} else {
memmove(mData, GetData(), GetSize());
}
mOffset = 0;
}
}
//
// UnixSocketRawData
//
UnixSocketRawData::UnixSocketRawData(const void* aData, size_t aSize)
: UnixSocketIOBuffer(aData, aSize)
{ }
UnixSocketRawData::UnixSocketRawData(size_t aSize)
: UnixSocketIOBuffer(aSize)
{ }
ssize_t
UnixSocketRawData::Receive(int aFd)
{
@ -45,12 +121,7 @@ UnixSocketRawData::Receive(int aFd)
return -1; /* buffer is full */
}
/* free up space at the end of data buffer */
if (GetSize() <= GetLeadingSpace()) {
memcpy(mData, GetData(), GetSize());
} else {
memmove(mData, GetData(), GetSize());
}
mOffset = 0;
CleanupLeadingSpace();
}
ssize_t res =
@ -64,7 +135,7 @@ UnixSocketRawData::Receive(int aFd)
return 0;
}
mSize += res;
Append(res); /* mark read data as 'valid' */
return res;
}
@ -94,16 +165,16 @@ UnixSocketRawData::Send(int aFd)
}
//
// SocketConsumerBase
// SocketBase
//
SocketConsumerBase::~SocketConsumerBase()
SocketBase::~SocketBase()
{
MOZ_ASSERT(mConnectionStatus == SOCKET_DISCONNECTED);
}
SocketConnectionStatus
SocketConsumerBase::GetConnectionStatus() const
SocketBase::GetConnectionStatus() const
{
MOZ_ASSERT(NS_IsMainThread());
@ -111,7 +182,7 @@ SocketConsumerBase::GetConnectionStatus() const
}
int
SocketConsumerBase::GetSuggestedConnectDelayMs() const
SocketBase::GetSuggestedConnectDelayMs() const
{
MOZ_ASSERT(NS_IsMainThread());
@ -119,7 +190,7 @@ SocketConsumerBase::GetSuggestedConnectDelayMs() const
}
void
SocketConsumerBase::NotifySuccess()
SocketBase::NotifySuccess()
{
MOZ_ASSERT(NS_IsMainThread());
@ -129,7 +200,7 @@ SocketConsumerBase::NotifySuccess()
}
void
SocketConsumerBase::NotifyError()
SocketBase::NotifyError()
{
MOZ_ASSERT(NS_IsMainThread());
@ -140,7 +211,7 @@ SocketConsumerBase::NotifyError()
}
void
SocketConsumerBase::NotifyDisconnect()
SocketBase::NotifyDisconnect()
{
MOZ_ASSERT(NS_IsMainThread());
@ -151,7 +222,7 @@ SocketConsumerBase::NotifyDisconnect()
}
uint32_t
SocketConsumerBase::CalculateConnectDelayMs() const
SocketBase::CalculateConnectDelayMs() const
{
MOZ_ASSERT(NS_IsMainThread());
@ -170,19 +241,25 @@ SocketConsumerBase::CalculateConnectDelayMs() const
return connectDelayMs;
}
SocketConsumerBase::SocketConsumerBase()
SocketBase::SocketBase()
: mConnectionStatus(SOCKET_DISCONNECTED)
, mConnectTimestamp(0)
, mConnectDelayMs(0)
{ }
void
SocketConsumerBase::SetConnectionStatus(
SocketConnectionStatus aConnectionStatus)
SocketBase::SetConnectionStatus(SocketConnectionStatus aConnectionStatus)
{
mConnectionStatus = aConnectionStatus;
}
//
// SocketConsumerBase
//
SocketConsumerBase::~SocketConsumerBase()
{ }
//
// SocketIOBase
//

View File

@ -22,11 +22,153 @@ using namespace mozilla::tasktracer;
namespace mozilla {
namespace ipc {
//
// UnixSocketIOBuffer
//
class UnixSocketIOBuffer
{
public:
const uint8_t* GetData() const
{
return mData + mOffset;
}
size_t GetSize() const
{
return mSize - mOffset;
}
const uint8_t* Consume(size_t aLen);
nsresult Read(void* aValue, size_t aLen);
nsresult Read(int8_t& aValue)
{
return Read(&aValue, sizeof(aValue));
}
nsresult Read(uint8_t& aValue)
{
return Read(&aValue, sizeof(aValue));
}
nsresult Read(int16_t& aValue)
{
return Read(&aValue, sizeof(aValue));
}
nsresult Read(uint16_t& aValue)
{
return Read(&aValue, sizeof(aValue));
}
nsresult Read(int32_t& aValue)
{
return Read(&aValue, sizeof(aValue));
}
nsresult Read(uint32_t& aValue)
{
return Read(&aValue, sizeof(aValue));
}
uint8_t* Append(size_t aLen);
nsresult Write(const void* aValue, size_t aLen);
nsresult Write(int8_t aValue)
{
return Write(&aValue, sizeof(aValue));
}
nsresult Write(uint8_t aValue)
{
return Write(&aValue, sizeof(aValue));
}
nsresult Write(int16_t aValue)
{
return Write(&aValue, sizeof(aValue));
}
nsresult Write(uint16_t aValue)
{
return Write(&aValue, sizeof(aValue));
}
nsresult Write(int32_t aValue)
{
return Write(&aValue, sizeof(aValue));
}
nsresult Write(uint32_t aValue)
{
return Write(&aValue, sizeof(aValue));
}
protected:
/* This constructor copies aData of aSize bytes length into the
* new instance of |UnixSocketIOBuffer|.
*/
UnixSocketIOBuffer(const void* aData, size_t aSize);
/* This constructor reserves aAvailableSpace bytes of space.
*/
UnixSocketIOBuffer(size_t aAvailableSpace);
~UnixSocketIOBuffer();
size_t GetLeadingSpace() const
{
return mOffset;
}
size_t GetTrailingSpace() const
{
return mAvailableSpace - mSize;
}
size_t GetAvailableSpace() const
{
return mAvailableSpace;
}
void* GetTrailingBytes()
{
return mData + mSize;
}
uint8_t* GetData(size_t aOffset)
{
MOZ_ASSERT(aOffset <= mSize);
return mData + aOffset;
}
void SetRange(size_t aOffset, size_t aSize)
{
MOZ_ASSERT((aOffset + aSize) <= mAvailableSpace);
mOffset = aOffset;
mSize = mOffset + aSize;
}
void CleanupLeadingSpace();
private:
size_t mSize;
size_t mOffset;
size_t mAvailableSpace;
nsAutoArrayPtr<uint8_t> mData;
};
//
// UnixSocketRawData
//
class UnixSocketRawData
class UnixSocketRawData MOZ_FINAL : public UnixSocketIOBuffer
{
public:
/* This constructor copies aData of aSize bytes length into the
@ -51,51 +193,6 @@ public:
* is the number of bytes written, or a negative value on error.
*/
ssize_t Send(int aFd);
const uint8_t* GetData() const
{
return mData + mOffset;
}
size_t GetSize() const
{
return mSize;
}
void Consume(size_t aSize)
{
MOZ_ASSERT(aSize <= mSize);
mSize -= aSize;
mOffset += aSize;
}
protected:
size_t GetLeadingSpace() const
{
return mOffset;
}
size_t GetTrailingSpace() const
{
return mAvailableSpace - (mOffset + mSize);
}
size_t GetAvailableSpace() const
{
return mAvailableSpace;
}
void* GetTrailingBytes()
{
return mData + mOffset + mSize;
}
private:
size_t mSize;
size_t mOffset;
size_t mAvailableSpace;
nsAutoArrayPtr<uint8_t> mData;
};
enum SocketConnectionStatus {
@ -106,15 +203,15 @@ enum SocketConnectionStatus {
};
//
// SocketConsumerBase
// SocketBase
//
class SocketConsumerBase
class SocketBase
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SocketConsumerBase)
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SocketBase)
virtual ~SocketConsumerBase();
virtual ~SocketBase();
SocketConnectionStatus GetConnectionStatus() const;
@ -126,24 +223,6 @@ public:
*/
virtual void CloseSocket() = 0;
/**
* Function to be called whenever data is received. This is only called on the
* main thread.
*
* @param aMessage Data received from the socket.
*/
virtual void ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage) = 0;
/**
* Queue data to be sent to the socket on the IO thread. Can only be called on
* originating thread.
*
* @param aMessage Data to be sent to socket
*
* @return true if data is queued, false otherwise (i.e. not connected)
*/
virtual bool SendSocketData(UnixSocketRawData* aMessage) = 0;
/**
* Callback for socket connect/accept success. Called after connect/accept has
* finished. Will be run on main thread, before any reads take place.
@ -176,7 +255,7 @@ public:
void NotifyDisconnect();
protected:
SocketConsumerBase();
SocketBase();
void SetConnectionStatus(SocketConnectionStatus aConnectionStatus);
@ -188,6 +267,34 @@ private:
uint32_t mConnectDelayMs;
};
//
// SocketConsumerBase
//
class SocketConsumerBase : public SocketBase
{
public:
virtual ~SocketConsumerBase();
/**
* Function to be called whenever data is received. This is only called on the
* main thread.
*
* @param aMessage Data received from the socket.
*/
virtual void ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage) = 0;
/**
* Queue data to be sent to the socket on the IO thread. Can only be called on
* originating thread.
*
* @param aMessage Data to be sent to socket
*
* @return true if data is queued, false otherwise (i.e. not connected)
*/
virtual bool SendSocketData(UnixSocketRawData* aMessage) = 0;
};
//
// Socket I/O runnables
//
@ -249,15 +356,15 @@ public:
return NS_OK;
}
SocketConsumerBase* consumer = io->GetConsumer();
MOZ_ASSERT(consumer);
SocketBase* base = io->GetSocketBase();
MOZ_ASSERT(base);
if (mEvent == CONNECT_SUCCESS) {
consumer->NotifySuccess();
base->NotifySuccess();
} else if (mEvent == CONNECT_ERROR) {
consumer->NotifyError();
base->NotifyError();
} else if (mEvent == DISCONNECT) {
consumer->NotifyDisconnect();
base->NotifyDisconnect();
}
return NS_OK;
@ -325,10 +432,10 @@ public:
return NS_OK;
}
SocketConsumerBase* consumer = io->GetConsumer();
MOZ_ASSERT(consumer);
SocketBase* base = io->GetSocketBase();
MOZ_ASSERT(base);
consumer->CloseSocket();
base->CloseSocket();
return NS_OK;
}
@ -453,14 +560,14 @@ private:
/* |SocketIOTask| holds a reference to a Socket I/O object. It's
* supposed to run on the I/O thread.
*/
template <typename T>
template<typename Tio>
class SocketIOTask : public CancelableTask
{
public:
virtual ~SocketIOTask()
{ }
T* GetIO() const
Tio* GetIO() const
{
return mIO;
}
@ -476,25 +583,26 @@ public:
}
protected:
SocketIOTask(T* aIO)
SocketIOTask(Tio* aIO)
: mIO(aIO)
{
MOZ_ASSERT(mIO);
}
private:
T* mIO;
Tio* mIO;
};
/* |SocketIOSendTask| transfers an instance of |UnixSocketRawData| to
* the I/O thread and queues it up for sending the contained data.
/* |SocketIOSendTask| transfers an instance of |Tdata|, such as
* |UnixSocketRawData|, to the I/O thread and queues it up for
* sending the contained data.
*/
template <typename T>
class SocketIOSendTask MOZ_FINAL : public SocketIOTask<T>
template<typename Tio, typename Tdata>
class SocketIOSendTask MOZ_FINAL : public SocketIOTask<Tio>
{
public:
SocketIOSendTask(T* aIO, UnixSocketRawData* aData)
: SocketIOTask<T>(aIO)
SocketIOSendTask(Tio* aIO, Tdata* aData)
: SocketIOTask<Tio>(aIO)
, mData(aData)
{
MOZ_ASSERT(aData);
@ -503,34 +611,34 @@ public:
void Run() MOZ_OVERRIDE
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(!SocketIOTask<T>::IsCanceled());
MOZ_ASSERT(!SocketIOTask<Tio>::IsCanceled());
T* io = SocketIOTask<T>::GetIO();
Tio* io = SocketIOTask<Tio>::GetIO();
MOZ_ASSERT(!io->IsShutdownOnIOThread());
io->Send(mData);
}
private:
UnixSocketRawData* mData;
Tdata* mData;
};
/* |SocketIOShutdownTask| signals shutdown to the Socket I/O object on
* the I/O thread and sends it to the main thread for destruction.
*/
template <typename T>
class SocketIOShutdownTask MOZ_FINAL : public SocketIOTask<T>
template<typename Tio>
class SocketIOShutdownTask MOZ_FINAL : public SocketIOTask<Tio>
{
public:
SocketIOShutdownTask(T* aIO)
: SocketIOTask<T>(aIO)
SocketIOShutdownTask(Tio* aIO)
: SocketIOTask<Tio>(aIO)
{ }
void Run() MOZ_OVERRIDE
{
MOZ_ASSERT(!NS_IsMainThread());
T* io = SocketIOTask<T>::GetIO();
Tio* io = SocketIOTask<Tio>::GetIO();
// At this point, there should be no new events on the I/O thread
// after this one with the possible exception of an accept task,
@ -539,7 +647,7 @@ public:
// |io| safely knowing that it's not reference any longer.
io->ShutdownOnIOThread();
nsRefPtr<nsRunnable> r = new SocketIODeleteInstanceRunnable<T>(io);
nsRefPtr<nsRunnable> r = new SocketIODeleteInstanceRunnable<Tio>(io);
nsresult rv = NS_DispatchToMainThread(r);
NS_ENSURE_SUCCESS_VOID(rv);
}

View File

@ -31,6 +31,7 @@ public:
void GetSocketAddr(nsAString& aAddrStr) const;
SocketConsumerBase* GetConsumer();
SocketBase* GetSocketBase();
// Shutdown state
//
@ -157,6 +158,12 @@ UnixSocketConsumerIO::GetConsumer()
return mConsumer.get();
}
SocketBase*
UnixSocketConsumerIO::GetSocketBase()
{
return GetConsumer();
}
bool
UnixSocketConsumerIO::IsShutdownOnMainThread() const
{
@ -548,7 +555,8 @@ UnixSocketConsumer::SendSocketData(UnixSocketRawData* aData)
MOZ_ASSERT(!mIO->IsShutdownOnMainThread());
XRE_GetIOMessageLoop()->PostTask(
FROM_HERE, new SocketIOSendTask<UnixSocketConsumerIO>(mIO, aData));
FROM_HERE,
new SocketIOSendTask<UnixSocketConsumerIO, UnixSocketRawData>(mIO, aData));
return true;
}

View File

@ -77,6 +77,7 @@ function init() {
// Fill "Last visited site" input with most recent history entry URL.
Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
aData = aData.substring(0, 200);
document.getElementById("last-url").value = aData;
// Enable the parent div iff the URL is valid.
if (aData.length != 0) {

View File

@ -59,7 +59,7 @@
<h1 class="header">&sad.header;</h1>
<form>
<div class="message">&sad.message;</div>
<textarea class="description" placeholder="&sad.placeholder;" rows="4" required="true"/>
<textarea class="description" placeholder="&sad.placeholder;" rows="4" required="true" maxlength="10000"/>
<div class="message" id="last-url-div">
<span>&sad.lastSite2;</span>
<input id="last-checkbox" type="checkbox" checked="checked"/>