diff --git a/accessible/atk/moz.build b/accessible/atk/moz.build
index f5abe301fd55..9b945da81a41 100644
--- a/accessible/atk/moz.build
+++ b/accessible/atk/moz.build
@@ -52,4 +52,8 @@ if CONFIG['MOZ_ENABLE_DBUS']:
include('/ipc/chromium/chromium-config.mozbuild')
+if CONFIG['CLANG_CXX']:
+ # Suppress clang warning about unused function from gobject's RTTI macros.
+ CXXFLAGS += ['-Wno-unused-function']
+
FAIL_ON_WARNINGS = True
diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js
index e97b74bb2771..86cce513e05b 100644
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -995,10 +995,10 @@ pref("apz.pan_repaint_interval", 16);
// APZ physics settings, tuned by UX designers
pref("apz.fling_curve_function_x1", "0.41");
pref("apz.fling_curve_function_y1", "0.0");
-pref("apz.fling_curve_function_x2", "0.76");
+pref("apz.fling_curve_function_x2", "0.80");
pref("apz.fling_curve_function_y2", "1.0");
pref("apz.fling_curve_threshold_inches_per_ms", "0.01");
-pref("apz.fling_friction", "0.0024");
+pref("apz.fling_friction", "0.00238");
pref("apz.max_velocity_inches_per_ms", "0.07");
// Tweak default displayport values to reduce the risk of running out of
diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml
index 9a86bd510c31..aff28e8c6e2d 100644
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml
index 63e5fc174d3e..d4fb914b7098 100644
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml
index df313f49a0ee..af6a072e14bf 100644
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml
index 310fe85e02b9..1bb8741d4735 100644
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml
index 63e5fc174d3e..d4fb914b7098 100644
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml
index b33c35956211..5a018c6f335a 100644
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml
index 0b7787e40148..24e59db19738 100644
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index 80771b5f22e0..b3c4c1b09909 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
- "revision": "9e971d42c915f999f3e352fe43fca7ba0a5245b4",
+ "revision": "0928af6ba8010181592fc8eedae9550ac8597f53",
"repo_path": "integration/gaia-central"
}
diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml
index eae0673590e2..cf73e3496011 100644
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml
index b1c629d019b5..34f304163307 100644
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml
index 938025589249..5b7f49802e77 100644
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml
index 4a726ab83a20..3e3572f12be0 100644
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in
index 5f62bbc9fa7c..59ace3c21438 100644
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -451,6 +451,8 @@
@BINPATH@/components/RILContentHelper.js
@BINPATH@/components/RILSystemMessengerHelper.js
@BINPATH@/components/RILSystemMessengerHelper.manifest
+@BINPATH@/components/SmsService.js
+@BINPATH@/components/SmsService.manifest
@BINPATH@/components/TelephonyAudioService.js
@BINPATH@/components/TelephonyAudioService.manifest
@BINPATH@/components/TelephonyService.js
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index 734d867923e4..101065afc3a2 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1849,3 +1849,6 @@ pref("extensions.interposition.enabled", true);
#endif
pref("browser.defaultbrowser.notificationbar", false);
+
+// How many milliseconds to wait for a CPOW response from the child process.
+pref("dom.ipc.cpow.timeout", 0);
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index f46a9ec5fc62..692f008f8f0c 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6746,7 +6746,7 @@ function isTabEmpty(aTab) {
if (!gMultiProcessBrowser && browser.contentWindow.opener)
return false;
- if (browser.sessionHistory && browser.sessionHistory.count >= 2)
+ if (browser.canGoForward || browser.canGoBack)
return false;
return true;
diff --git a/browser/base/content/newtab/cells.js b/browser/base/content/newtab/cells.js
index 23cbd23bfdbc..47d4ef52d7d8 100644
--- a/browser/base/content/newtab/cells.js
+++ b/browser/base/content/newtab/cells.js
@@ -29,7 +29,7 @@ Cell.prototype = {
/**
* The cell's DOM node.
*/
- get node() this._node,
+ get node() { return this._node; },
/**
* The cell's offset in the grid.
diff --git a/browser/base/content/newtab/drag.js b/browser/base/content/newtab/drag.js
index dfbe8199a95c..8f0bf674ee92 100644
--- a/browser/base/content/newtab/drag.js
+++ b/browser/base/content/newtab/drag.js
@@ -18,15 +18,15 @@ let gDrag = {
* The site that is dragged.
*/
_draggedSite: null,
- get draggedSite() this._draggedSite,
+ get draggedSite() { return this._draggedSite; },
/**
* The cell width/height at the point the drag started.
*/
_cellWidth: null,
_cellHeight: null,
- get cellWidth() this._cellWidth,
- get cellHeight() this._cellHeight,
+ get cellWidth() { return this._cellWidth; },
+ get cellHeight() { return this._cellHeight; },
/**
* Start a new drag operation.
@@ -146,6 +146,6 @@ let gDrag = {
// After the 'dragstart' event has been processed we can remove the
// temporary drag element from the DOM.
- setTimeout(function () scrollbox.removeChild(dragElement), 0);
+ setTimeout(() => scrollbox.removeChild(dragElement), 0);
}
};
diff --git a/browser/base/content/newtab/drop.js b/browser/base/content/newtab/drop.js
index 7363396e81e4..d7bf30506881 100644
--- a/browser/base/content/newtab/drop.js
+++ b/browser/base/content/newtab/drop.js
@@ -73,7 +73,7 @@ let gDrop = {
});
// Re-pin all shifted pinned cells.
- pinnedSites.forEach(function (aSite) aSite.pin(sites.indexOf(aSite)), this);
+ pinnedSites.forEach(aSite => aSite.pin(sites.indexOf(aSite)));
},
/**
diff --git a/browser/base/content/newtab/grid.js b/browser/base/content/newtab/grid.js
index 74b63d2d52e0..bdf9ab54aa92 100644
--- a/browser/base/content/newtab/grid.js
+++ b/browser/base/content/newtab/grid.js
@@ -18,7 +18,7 @@ let gGrid = {
* The DOM node of the grid.
*/
_node: null,
- get node() this._node,
+ get node() { return this._node; },
/**
* The cached DOM fragment for sites.
@@ -29,18 +29,18 @@ let gGrid = {
* All cells contained in the grid.
*/
_cells: [],
- get cells() this._cells,
+ get cells() { return this._cells; },
/**
* All sites contained in the grid's cells. Sites may be empty.
*/
- get sites() [for (cell of this.cells) cell.site],
+ get sites() { return [for (cell of this.cells) cell.site]; },
// Tells whether the grid has already been initialized.
- get ready() !!this._ready,
+ get ready() { return !!this._ready; },
// Returns whether the page has finished loading yet.
- get isDocumentLoaded() document.readyState == "complete",
+ get isDocumentLoaded() { return document.readyState == "complete"; },
/**
* Initializes the grid.
diff --git a/browser/base/content/newtab/sites.js b/browser/base/content/newtab/sites.js
index d7ba6124d3f7..9ee9f62d9979 100644
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -22,22 +22,22 @@ Site.prototype = {
/**
* The site's DOM node.
*/
- get node() this._node,
+ get node() { return this._node; },
/**
* The site's link.
*/
- get link() this._link,
+ get link() { return this._link; },
/**
* The url of the site's link.
*/
- get url() this.link.url,
+ get url() { return this.link.url; },
/**
* The title of the site's link.
*/
- get title() this.link.title,
+ get title() { return this.link.title; },
/**
* The site's parent cell.
diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js
index 2911189c8295..4dc54af0cdcc 100644
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1067,7 +1067,7 @@ nsContextMenu.prototype = {
mm.removeMessageListener("ContextMenu:SaveVideoFrameAsImage:Result", onMessage);
let dataURL = message.data.dataURL;
saveImageURL(dataURL, name, "SaveImageTitle", true, false,
- document.documentURIObject, this.target.ownerDocument);
+ document.documentURIObject, document);
};
mm.addMessageListener("ContextMenu:SaveVideoFrameAsImage:Result", onMessage);
},
diff --git a/browser/components/sessionstore/content/aboutSessionRestore.js b/browser/components/sessionstore/content/aboutSessionRestore.js
index 3cdaa1981635..bc9aea8aee52 100644
--- a/browser/components/sessionstore/content/aboutSessionRestore.js
+++ b/browser/components/sessionstore/content/aboutSessionRestore.js
@@ -124,7 +124,7 @@ function restoreSession() {
if (gTreeData[t].checked === 0)
// this window will be restored partially
gStateObject.windows[ix].tabs =
- gStateObject.windows[ix].tabs.filter(function(aTabData, aIx)
+ gStateObject.windows[ix].tabs.filter((aTabData, aIx) =>
gTreeData[t].tabs[aIx].checked);
else if (!gTreeData[t].checked)
// this window won't be restored at all
@@ -218,12 +218,14 @@ function getBrowserWindow() {
}
function toggleRowChecked(aIx) {
+ function isChecked(aItem) {
+ return aItem.checked;
+ }
+
var item = gTreeData[aIx];
item.checked = !item.checked;
treeView.treeBox.invalidateRow(aIx);
- function isChecked(aItem) aItem.checked;
-
if (treeView.isContainer(aIx)) {
// (un)check all tabs of this window as well
for (let tab of item.tabs) {
diff --git a/browser/components/sessionstore/test/browser_500328.js b/browser/components/sessionstore/test/browser_500328.js
index 767099bd8f3b..45f784bdc02a 100644
--- a/browser/components/sessionstore/test/browser_500328.js
+++ b/browser/components/sessionstore/test/browser_500328.js
@@ -42,9 +42,9 @@ function checkState(tab) {
// deserialized in the content scope. And in this case, since RegExps are
// not currently Xrayable (see bug 1014991), trying to pull |obj3| (a RegExp)
// off of an Xrayed Object won't work. So we need to waive.
- runInContent(tab.linkedBrowser, function(win, event) {
- return Cu.waiveXrays(event.state).obj3.toString();
- }, aEvent).then(function(stateStr) {
+ runInContent(tab.linkedBrowser, function(win, state) {
+ return Cu.waiveXrays(state).obj3.toString();
+ }, aEvent.state).then(function(stateStr) {
is(stateStr, '/^a$/', "second popstate object.");
// Make sure that the new-elem node is present in the document. If it's
diff --git a/build/gecko_templates.mozbuild b/build/gecko_templates.mozbuild
index d8855520a349..9d07947ce412 100644
--- a/build/gecko_templates.mozbuild
+++ b/build/gecko_templates.mozbuild
@@ -154,12 +154,11 @@ def GeckoFramework(name, **kwargs):
@template
-def XPCOMBinaryComponent(name):
+def XPCOMBinaryComponent(name, **kwargs):
'''Template defining an XPCOM binary component for Gecko.
`name` is the name of the component.
'''
- GeckoSharedLibrary(name)
+ GeckoSharedLibrary(name, **kwargs)
IS_COMPONENT = True
-
diff --git a/dom/apps/Webapps.jsm b/dom/apps/Webapps.jsm
index 432dfb3adb8d..8d50a1c97774 100755
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -725,6 +725,18 @@ this.DOMApplicationRegistry = {
yield this.loadCurrentRegistry();
+ // Sanity check and roll back previous incomplete app updates.
+ for (let id in this.webapps) {
+ let oldDir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id + ".old"], false, true);
+ if (oldDir.exists()) {
+ let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], false, true);
+ if (dir.exists()) {
+ dir.remove(true);
+ }
+ oldDir.moveTo(null, id);
+ }
+ }
+
try {
let systemManifestURL =
Services.prefs.getCharPref("b2g.system_manifest_url");
@@ -1854,8 +1866,6 @@ this.DOMApplicationRegistry = {
app.readyToApplyDownload = true;
app.updateTime = Date.now();
- yield this._saveApps();
-
this.broadcastMessage("Webapps:UpdateState", {
app: app,
id: app.id
@@ -1890,18 +1900,33 @@ this.DOMApplicationRegistry = {
let appFile = tmpDir.clone();
appFile.append("application.zip");
- let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
+ // In order to better control the potential inconsistency due to unexpected
+ // shutdown during the update process, a separate folder is used to accommodate
+ // the updated files and to replace the current one. Some sanity check and
+ // correspondent rollback logic may be necessary during the initialization
+ // of this component to recover it at next system boot-up.
+ let oldDir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
+ let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id + ".new"], true, true);
appFile.moveTo(dir, "application.zip");
manFile.moveTo(dir, "manifest.webapp");
- // Move the staged update manifest to a non staged one.
- let staged = dir.clone();
+ // Copy the staged update manifest to a non staged one.
+ let staged = oldDir.clone();
staged.append("staged-update.webapp");
// If we are applying after a restarted download, we have no
// staged update manifest.
if (staged.exists()) {
- staged.moveTo(dir, "update.webapp");
+ staged.copyTo(dir, "update.webapp");
+ }
+
+ oldDir.moveTo(null, id + ".old");
+ dir.moveTo(null, id);
+
+ try {
+ oldDir.remove(true);
+ } catch(e) {
+ oldDir.moveTo(tmpDir, "old." + app.updateTime);
}
try {
diff --git a/dom/apps/tests/addons/script2.js b/dom/apps/tests/addons/script2.js
new file mode 100644
index 000000000000..a116f46d356d
--- /dev/null
+++ b/dom/apps/tests/addons/script2.js
@@ -0,0 +1,4 @@
+document.addEventListener("DOMContentLoaded", function() {
+ var head = document.getElementById("header2");
+ head.innerHTML = "Customized content";
+}, false);
\ No newline at end of file
diff --git a/dom/apps/tests/addons/style2.css b/dom/apps/tests/addons/style2.css
new file mode 100644
index 000000000000..0e5a21e1787e
--- /dev/null
+++ b/dom/apps/tests/addons/style2.css
@@ -0,0 +1,3 @@
+#header2 {
+ color: blue;
+}
\ No newline at end of file
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
index 81752c673f46..bafb53ba05f8 100644
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1200,6 +1200,9 @@ Navigator::SendBeacon(const nsAString& aUrl,
principal,
true);
+ rv = cors->Init(channel, true);
+ NS_ENSURE_SUCCESS(rv, false);
+
// Start a preflight if cross-origin and content type is not whitelisted
rv = secMan->CheckSameOriginURI(documentURI, uri, false);
bool crossOrigin = NS_FAILED(rv);
diff --git a/dom/base/test/chrome/cpows_parent.xul b/dom/base/test/chrome/cpows_parent.xul
index 38f76f88ade7..556ce2738e40 100644
--- a/dom/base/test/chrome/cpows_parent.xul
+++ b/dom/base/test/chrome/cpows_parent.xul
@@ -197,6 +197,24 @@
let savedElement = null;
function recvDomTest(message) {
savedElement = message.objects.element;
+
+ // Test to ensure that we don't pass CPOWs to C++-implemented interfaces.
+ // See bug 1072980.
+ if (test_state == "remote") {
+ let walker = Components.classes["@mozilla.org/inspector/deep-tree-walker;1"]
+ .createInstance(Components.interfaces.inIDeepTreeWalker);
+ const SHOW_ELEMENT = Components.interfaces.nsIDOMNodeFilter.SHOW_ELEMENT;
+ walker.showAnonymousContent = true;
+ walker.showSubDocuments = false;
+
+ try {
+ walker.init(savedElement, SHOW_ELEMENT);
+ ok(false, "expected exception passing CPOW to C++");
+ } catch (e) {
+ is(e.result, Components.results.NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE,
+ "got exception when passing CPOW to C++");
+ }
+ }
}
function recvDomTestAfterGC(message) {
diff --git a/dom/bluetooth2/BluetoothAdapter.cpp b/dom/bluetooth2/BluetoothAdapter.cpp
index 87a7e584ff64..16ffb395c6d4 100644
--- a/dom/bluetooth2/BluetoothAdapter.cpp
+++ b/dom/bluetooth2/BluetoothAdapter.cpp
@@ -6,8 +6,9 @@
#include "BluetoothReplyRunnable.h"
#include "BluetoothService.h"
-#include "BluetoothUtils.h"
#include "DOMRequest.h"
+#include "nsIDocument.h"
+#include "nsIPrincipal.h"
#include "nsTArrayHelpers.h"
#include "mozilla/dom/BluetoothAdapter2Binding.h"
@@ -193,11 +194,15 @@ BluetoothAdapter::BluetoothAdapter(nsPIDOMWindow* aWindow,
, mState(BluetoothAdapterState::Disabled)
, mDiscoverable(false)
, mDiscovering(false)
+ , mPairingReqs(nullptr)
, mDiscoveryHandleInUse(nullptr)
{
MOZ_ASSERT(aWindow);
- mPairingReqs = BluetoothPairingListener::Create(aWindow);
+ // Only allow certified bluetooth application to receive pairing requests
+ if (IsBluetoothCertifiedApp()) {
+ mPairingReqs = BluetoothPairingListener::Create(aWindow);
+ }
const InfallibleTArray& values =
aValue.get_ArrayOfBluetoothNamedValue();
@@ -329,8 +334,6 @@ BluetoothAdapter::Notify(const BluetoothSignal& aData)
if (mDiscoveryHandleInUse) {
HandleDeviceFound(v);
}
- } else if (aData.name().EqualsLiteral("PairingRequest")) {
- HandlePairingRequest(v);
} else if (aData.name().EqualsLiteral(DEVICE_PAIRED_ID)) {
HandleDevicePaired(aData.value());
} else if (aData.name().EqualsLiteral(DEVICE_UNPAIRED_ID)) {
@@ -762,6 +765,23 @@ BluetoothAdapter::IsAdapterAttributeChanged(BluetoothAdapterAttribute aType,
}
}
+bool
+BluetoothAdapter::IsBluetoothCertifiedApp()
+{
+ // Retrieve the app status and origin for permission checking
+ nsCOMPtr doc = GetOwner()->GetExtantDoc();
+ NS_ENSURE_TRUE(doc, false);
+
+ uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
+ nsAutoCString appOrigin;
+
+ doc->NodePrincipal()->GetAppStatus(&appStatus);
+ doc->NodePrincipal()->GetOrigin(getter_Copies(appOrigin));
+
+ return appStatus == nsIPrincipal::APP_STATUS_CERTIFIED &&
+ appOrigin.EqualsLiteral(BLUETOOTH_APP_ORIGIN);
+}
+
void
BluetoothAdapter::SetAdapterState(BluetoothAdapterState aState)
{
@@ -831,42 +851,6 @@ BluetoothAdapter::HandleDeviceFound(const BluetoothValue& aValue)
mDiscoveryHandleInUse->DispatchDeviceEvent(discoveredDevice);
}
-void
-BluetoothAdapter::HandlePairingRequest(const BluetoothValue& aValue)
-{
- MOZ_ASSERT(mPairingReqs);
- MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
-
- const InfallibleTArray& arr =
- aValue.get_ArrayOfBluetoothNamedValue();
-
- MOZ_ASSERT(arr.Length() == 3 &&
- arr[0].value().type() == BluetoothValue::TnsString && // address
- arr[1].value().type() == BluetoothValue::TnsString && // passkey
- arr[2].value().type() == BluetoothValue::TnsString); // type
-
- nsString deviceAddress = arr[0].value().get_nsString();
- nsString passkey = arr[1].value().get_nsString();
- nsString type = arr[2].value().get_nsString();
-
- // Create a temporary device with deviceAddress for searching
- InfallibleTArray props;
- BT_APPEND_NAMED_VALUE(props, "Address", deviceAddress);
- nsRefPtr device =
- BluetoothDevice::Create(GetOwner(), props);
-
- // Find the remote device by address
- size_t index = mDevices.IndexOf(device);
- if (index == mDevices.NoIndex) {
- BT_WARNING("Cannot find the remote device with address %s",
- NS_ConvertUTF16toUTF8(deviceAddress).get());
- return;
- }
-
- // Notify application of pairing requests
- mPairingReqs->DispatchPairingEvent(mDevices[index], passkey, type);
-}
-
void
BluetoothAdapter::DispatchAttributeEvent(const nsTArray& aTypes)
{
diff --git a/dom/bluetooth2/BluetoothAdapter.h b/dom/bluetooth2/BluetoothAdapter.h
index 063fa0b069dc..5c65ede7702a 100644
--- a/dom/bluetooth2/BluetoothAdapter.h
+++ b/dom/bluetooth2/BluetoothAdapter.h
@@ -248,13 +248,6 @@ private:
*/
void HandleDeviceFound(const BluetoothValue& aValue);
- /**
- * Handle "PairingRequest" bluetooth signal.
- *
- * @param aValue [in] Array of information about the pairing request.
- */
- void HandlePairingRequest(const BluetoothValue& aValue);
-
/**
* Fire BluetoothAttributeEvent to trigger onattributechanged event handler.
*/
@@ -287,6 +280,13 @@ private:
bool IsAdapterAttributeChanged(BluetoothAdapterAttribute aType,
const BluetoothValue& aValue);
+ /**
+ * Check whether this adapter is owned by Bluetooth certified app.
+ *
+ * @return a boolean value to indicate whether it's owned by Bluetooth app.
+ */
+ bool IsBluetoothCertifiedApp();
+
/****************************************************************************
* Variables
***************************************************************************/
diff --git a/dom/bluetooth2/BluetoothCommon.h b/dom/bluetooth2/BluetoothCommon.h
index d70cb0d15893..e7ddb7c28e4e 100644
--- a/dom/bluetooth2/BluetoothCommon.h
+++ b/dom/bluetooth2/BluetoothCommon.h
@@ -143,6 +143,7 @@ extern bool gBluetoothDebugFlag;
#define KEY_REMOTE_AGENT "/B2G/bluetooth/remote_device_agent"
#define KEY_MANAGER "/B2G/bluetooth/manager"
#define KEY_ADAPTER "/B2G/bluetooth/adapter"
+#define KEY_PAIRING_LISTENER "/B2G/bluetooth/pairing_listener"
/**
* When the connection status of a Bluetooth profile is changed, we'll notify
@@ -170,6 +171,18 @@ extern bool gBluetoothDebugFlag;
#define PAIRING_REQ_TYPE_CONFIRMATION "pairingconfirmationreq"
#define PAIRING_REQ_TYPE_CONSENT "pairingconsentreq"
+/**
+ * System message to launch bluetooth app if no pairing listener is ready to
+ * receive pairing requests.
+ */
+#define SYS_MSG_BT_PAIRING_REQ "bluetooth-pairing-request"
+
+/**
+ * The app origin of bluetooth app, which is responsible for listening pairing
+ * requests.
+ */
+#define BLUETOOTH_APP_ORIGIN "app://bluetooth.gaiamobile.org"
+
/**
* When a remote device gets paired / unpaired with local bluetooth adapter,
* we'll dispatch an event.
diff --git a/dom/bluetooth2/BluetoothPairingListener.cpp b/dom/bluetooth2/BluetoothPairingListener.cpp
index 305cf9fe2923..b0d31aad1c4a 100644
--- a/dom/bluetooth2/BluetoothPairingListener.cpp
+++ b/dom/bluetooth2/BluetoothPairingListener.cpp
@@ -6,8 +6,10 @@
#include "mozilla/dom/bluetooth/BluetoothPairingListener.h"
#include "mozilla/dom/bluetooth/BluetoothPairingHandle.h"
+#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/dom/BluetoothPairingEvent.h"
#include "mozilla/dom/BluetoothPairingListenerBinding.h"
+#include "BluetoothService.h"
USING_BLUETOOTH_NAMESPACE
@@ -21,6 +23,11 @@ BluetoothPairingListener::BluetoothPairingListener(nsPIDOMWindow* aWindow)
: DOMEventTargetHelper(aWindow)
{
MOZ_ASSERT(aWindow);
+
+ BluetoothService* bs = BluetoothService::Get();
+ NS_ENSURE_TRUE_VOID(bs);
+ bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
+ this);
}
already_AddRefed
@@ -37,6 +44,11 @@ BluetoothPairingListener::Create(nsPIDOMWindow* aWindow)
BluetoothPairingListener::~BluetoothPairingListener()
{
+ BluetoothService* bs = BluetoothService::Get();
+ // It can be nullptr on shutdown.
+ NS_ENSURE_TRUE_VOID(bs);
+ bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
+ this);
}
void
@@ -67,8 +79,55 @@ BluetoothPairingListener::DispatchPairingEvent(BluetoothDevice* aDevice,
DispatchTrustedEvent(event);
}
+void
+BluetoothPairingListener::Notify(const BluetoothSignal& aData)
+{
+ InfallibleTArray arr;
+
+ BluetoothValue value = aData.value();
+ if (aData.name().EqualsLiteral("PairingRequest")) {
+
+ MOZ_ASSERT(value.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
+
+ const InfallibleTArray& arr =
+ value.get_ArrayOfBluetoothNamedValue();
+
+ MOZ_ASSERT(arr.Length() == 3 &&
+ arr[0].value().type() == BluetoothValue::TnsString && // address
+ arr[1].value().type() == BluetoothValue::TnsString && // passkey
+ arr[2].value().type() == BluetoothValue::TnsString); // type
+
+ nsString deviceAddress = arr[0].value().get_nsString();
+ nsString passkey = arr[1].value().get_nsString();
+ nsString type = arr[2].value().get_nsString();
+
+ // Create a temporary device with deviceAddress for searching
+ InfallibleTArray props;
+ BT_APPEND_NAMED_VALUE(props, "Address", deviceAddress);
+ nsRefPtr device =
+ BluetoothDevice::Create(GetOwner(), props);
+
+ // Notify pairing listener of pairing requests
+ DispatchPairingEvent(device, passkey, type);
+ } else {
+ BT_WARNING("Not handling pairing listener signal: %s",
+ NS_ConvertUTF16toUTF8(aData.name()).get());
+ }
+}
+
JSObject*
BluetoothPairingListener::WrapObject(JSContext* aCx)
{
return BluetoothPairingListenerBinding::Wrap(aCx, this);
}
+
+void
+BluetoothPairingListener::DisconnectFromOwner()
+{
+ DOMEventTargetHelper::DisconnectFromOwner();
+
+ BluetoothService* bs = BluetoothService::Get();
+ NS_ENSURE_TRUE_VOID(bs);
+ bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
+ this);
+}
diff --git a/dom/bluetooth2/BluetoothPairingListener.h b/dom/bluetooth2/BluetoothPairingListener.h
index 9958d2fc7b74..909fbc0e6952 100644
--- a/dom/bluetooth2/BluetoothPairingListener.h
+++ b/dom/bluetooth2/BluetoothPairingListener.h
@@ -14,8 +14,10 @@
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothDevice;
+class BluetoothSignal;
class BluetoothPairingListener MOZ_FINAL : public DOMEventTargetHelper
+ , public BluetoothSignalObserver
{
public:
NS_DECL_ISUPPORTS_INHERITED
@@ -27,12 +29,15 @@ public:
const nsAString& aPasskey,
const nsAString& aType);
+ void Notify(const BluetoothSignal& aParam); // BluetoothSignalObserver
+
nsPIDOMWindow* GetParentObject() const
{
return GetOwner();
}
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+ virtual void DisconnectFromOwner() MOZ_OVERRIDE;
IMPL_EVENT_HANDLER(displaypasskeyreq);
IMPL_EVENT_HANDLER(enterpincodereq);
diff --git a/dom/bluetooth2/BluetoothService.cpp b/dom/bluetooth2/BluetoothService.cpp
index 1a95101cd14b..e422d7cdd724 100644
--- a/dom/bluetooth2/BluetoothService.cpp
+++ b/dom/bluetooth2/BluetoothService.cpp
@@ -273,6 +273,17 @@ BluetoothService::RegisterBluetoothSignalHandler(
}
ol->AddObserver(aHandler);
+
+ // Distribute pending pairing requests when pairing listener has been added
+ // to signal observer table.
+ if (IsMainProcess() &&
+ !mPendingPairReqSignals.IsEmpty() &&
+ aNodeName.EqualsLiteral(KEY_PAIRING_LISTENER)) {
+ for (uint32_t i = 0; i < mPendingPairReqSignals.Length(); ++i) {
+ DistributeSignal(mPendingPairReqSignals[i]);
+ }
+ mPendingPairReqSignals.Clear();
+ }
}
void
@@ -331,8 +342,19 @@ BluetoothService::DistributeSignal(const BluetoothSignal& aSignal)
BluetoothSignalObserverList* ol;
if (!mBluetoothSignalObserverTable.Get(aSignal.path(), &ol)) {
- BT_WARNING("No observer registered for path %s",
- NS_ConvertUTF16toUTF8(aSignal.path()).get());
+ // If there is no BluetoohPairingListener in observer table, put the signal
+ // into a pending queue of pairing requests and send a system message to
+ // launch bluetooth certified app.
+ if (aSignal.path().EqualsLiteral(KEY_PAIRING_LISTENER)) {
+ mPendingPairReqSignals.AppendElement(aSignal);
+
+ BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(
+ NS_LITERAL_STRING(SYS_MSG_BT_PAIRING_REQ),
+ BluetoothValue(EmptyString()));
+ } else {
+ BT_WARNING("No observer registered for path %s",
+ NS_ConvertUTF16toUTF8(aSignal.path()).get());
+ }
return;
}
diff --git a/dom/bluetooth2/BluetoothService.h b/dom/bluetooth2/BluetoothService.h
index 56092a432865..8879ea3f8194 100644
--- a/dom/bluetooth2/BluetoothService.h
+++ b/dom/bluetooth2/BluetoothService.h
@@ -8,6 +8,7 @@
#define mozilla_dom_bluetooth_bluetootheventservice_h__
#include "BluetoothCommon.h"
+#include "BluetoothInterface.h"
#include "BluetoothProfileManagerBase.h"
#include "nsAutoPtr.h"
#include "nsClassHashtable.h"
@@ -401,6 +402,8 @@ protected:
BluetoothSignalObserverTable mBluetoothSignalObserverTable;
+ nsTArray mPendingPairReqSignals;
+
bool mEnabled;
};
diff --git a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
index c734833400b3..8a17df692d4a 100644
--- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
@@ -1473,7 +1473,7 @@ BluetoothServiceBluedroid::PinRequestNotification(const nsAString& aRemoteBdAddr
NS_LITERAL_STRING(PAIRING_REQ_TYPE_ENTERPINCODE));
DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("PairingRequest"),
- NS_LITERAL_STRING(KEY_ADAPTER),
+ NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
BluetoothValue(propertiesArray)));
}
@@ -1518,7 +1518,7 @@ BluetoothServiceBluedroid::SspRequestNotification(
BT_APPEND_NAMED_VALUE(propertiesArray, "type", pairingType);
DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("PairingRequest"),
- NS_LITERAL_STRING(KEY_ADAPTER),
+ NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
BluetoothValue(propertiesArray)));
}
diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini
index f37ab466b6c7..72dea7a1175b 100644
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -53,7 +53,7 @@ skip-if = buildapp == 'mulet'
skip-if = buildapp == 'mulet'
[test_bug456273.html]
[test_bug457672.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #CRASH_DUMP, RANDOM
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #CRASH_DUMP, RANDOM
[test_bug489671.html]
[test_bug493251.html]
skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
diff --git a/dom/interfaces/base/nsIServiceWorkerManager.idl b/dom/interfaces/base/nsIServiceWorkerManager.idl
index 22616d20249f..f0aa315177fb 100644
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -17,7 +17,7 @@ interface nsIServiceWorkerUnregisterCallback : nsISupports
[noscript] void UnregisterFailed();
};
-[builtinclass, uuid(79ad5b57-d65d-46d3-b5e9-32d27e16052d)]
+[builtinclass, uuid(78fdfe7c-0e2c-4157-ac6e-3952b61df36a)]
interface nsIServiceWorkerManager : nsISupports
{
/**
@@ -83,6 +83,11 @@ interface nsIServiceWorkerManager : nsISupports
*/
[noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow);
+ /*
+ * This implements the update algorithm.
+ */
+ void update(in DOMString aScope);
+
// Testing
DOMString getScopeForUrl(in DOMString path);
};
diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp
index 791adf40d106..b0f9ae79eb37 100644
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2458,7 +2458,7 @@ ContentChild::RecvStopProfiler()
}
bool
-ContentChild::AnswerGetProfile(nsCString* aProfile)
+ContentChild::RecvGetProfile(nsCString* aProfile)
{
char* profile = profiler_get_profile();
if (profile) {
diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h
index 303142066cf2..8315b6f180fa 100644
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -365,7 +365,7 @@ public:
const nsTArray& aFeatures,
const nsTArray& aThreadNameFilters) MOZ_OVERRIDE;
virtual bool RecvStopProfiler() MOZ_OVERRIDE;
- virtual bool AnswerGetProfile(nsCString* aProfile) MOZ_OVERRIDE;
+ virtual bool RecvGetProfile(nsCString* aProfile) MOZ_OVERRIDE;
#ifdef ANDROID
gfxIntSize GetScreenSize() { return mScreenSize; }
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
index 76f854b7018e..7cfd823bf1c2 100755
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -927,7 +927,7 @@ ContentParent::RecvCreateChildProcess(const IPCTabContext& aContext,
}
bool
-ContentParent::AnswerBridgeToChildProcess(const ContentParentId& aCpId)
+ContentParent::RecvBridgeToChildProcess(const ContentParentId& aCpId)
{
ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
ContentParent* cp = cpm->GetContentProcessById(aCpId);
@@ -966,7 +966,7 @@ static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement)
}
bool
-ContentParent::AnswerLoadPlugin(const uint32_t& aPluginId)
+ContentParent::RecvLoadPlugin(const uint32_t& aPluginId)
{
return mozilla::plugins::SetupBridge(aPluginId, this);
}
@@ -1219,7 +1219,7 @@ ContentParent::CreateContentBridgeParent(const TabContext& aContext,
if (cpId == 0) {
return nullptr;
}
- if (!child->CallBridgeToChildProcess(cpId)) {
+ if (!child->SendBridgeToChildProcess(cpId)) {
return nullptr;
}
ContentBridgeParent* parent = child->GetLastBridge();
@@ -2011,6 +2011,9 @@ ContentParent::ContentParent(mozIApplication* aApp,
true /* Send registered chrome */);
ContentProcessManager::GetSingleton()->AddContentProcess(this);
+
+ // Set a reply timeout for CPOWs.
+ SetReplyTimeoutMs(Preferences::GetInt("dom.ipc.cpow.timeout", 0));
}
#ifdef MOZ_NUWA_PROCESS
@@ -2866,7 +2869,7 @@ ContentParent::Observe(nsISupports* aSubject,
nsCOMPtr pse = do_QueryInterface(aSubject);
if (pse) {
nsCString result;
- unused << CallGetProfile(&result);
+ unused << SendGetProfile(&result);
if (!result.IsEmpty()) {
pse->AddSubProfile(result.get());
}
@@ -4263,11 +4266,11 @@ ContentParent::RecvOpenAnonymousTemporaryFile(FileDescriptor *aFD)
static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);
bool
-ContentParent::RecvFormProcessValue(const nsString& oldValue,
- const nsString& challenge,
- const nsString& keytype,
- const nsString& keyparams,
- nsString* newValue)
+ContentParent::RecvKeygenProcessValue(const nsString& oldValue,
+ const nsString& challenge,
+ const nsString& keytype,
+ const nsString& keyparams,
+ nsString* newValue)
{
nsCOMPtr formProcessor =
do_GetService(kFormProcessorCID);
@@ -4282,8 +4285,8 @@ ContentParent::RecvFormProcessValue(const nsString& oldValue,
}
bool
-ContentParent::RecvFormProvideContent(nsString* aAttribute,
- nsTArray* aContent)
+ContentParent::RecvKeygenProvideContent(nsString* aAttribute,
+ nsTArray* aContent)
{
nsCOMPtr formProcessor =
do_GetService(kFormProcessorCID);
diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h
index 91c73b1acf8c..70f88647d4fc 100644
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -150,9 +150,9 @@ public:
bool* aIsForApp,
bool* aIsForBrowser,
TabId* aTabId) MOZ_OVERRIDE;
- virtual bool AnswerBridgeToChildProcess(const ContentParentId& aCpId) MOZ_OVERRIDE;
+ virtual bool RecvBridgeToChildProcess(const ContentParentId& aCpId) MOZ_OVERRIDE;
- virtual bool AnswerLoadPlugin(const uint32_t& aPluginId) MOZ_OVERRIDE;
+ virtual bool RecvLoadPlugin(const uint32_t& aPluginId) MOZ_OVERRIDE;
virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch,
nsTArray* aPlugins,
uint32_t* aNewPluginEpoch) MOZ_OVERRIDE;
@@ -722,13 +722,13 @@ private:
RecvOpenAnonymousTemporaryFile(FileDescriptor* aFD) MOZ_OVERRIDE;
virtual bool
- RecvFormProcessValue(const nsString& oldValue, const nsString& challenge,
- const nsString& keytype, const nsString& keyparams,
- nsString* newValue) MOZ_OVERRIDE;
+ RecvKeygenProcessValue(const nsString& oldValue, const nsString& challenge,
+ const nsString& keytype, const nsString& keyparams,
+ nsString* newValue) MOZ_OVERRIDE;
virtual bool
- RecvFormProvideContent(nsString* aAttribute,
- nsTArray* aContent) MOZ_OVERRIDE;
+ RecvKeygenProvideContent(nsString* aAttribute,
+ nsTArray* aContent) MOZ_OVERRIDE;
virtual PFileDescriptorSetParent*
AllocPFileDescriptorSetParent(const mozilla::ipc::FileDescriptor&) MOZ_OVERRIDE;
diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl
index 67637dda0f4d..cd49ba903215 100644
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -521,7 +521,7 @@ child:
async StartProfiler(uint32_t aEntries, double aInterval, nsCString[] aFeatures,
nsCString[] aThreadNameFilters);
async StopProfiler();
- intr GetProfile()
+ prio(high) sync GetProfile()
returns (nsCString aProfile);
NuwaFreeze();
@@ -557,7 +557,7 @@ parent:
ProcessPriority priority,
TabId openerTabId)
returns (ContentParentId cpId, bool isForApp, bool isForBrowser, TabId tabId);
- intr BridgeToChildProcess(ContentParentId cpId);
+ sync BridgeToChildProcess(ContentParentId cpId);
/**
* This call connects the content process to a plugin process. While this
@@ -566,7 +566,7 @@ parent:
* process. We use intr semantics here to ensure that the PluginModuleParent
* allocation message is dispatched before LoadPlugin returns.
*/
- intr LoadPlugin(uint32_t pluginId);
+ sync LoadPlugin(uint32_t pluginId);
/**
* This call returns the set of plugins loaded in the chrome
@@ -798,16 +798,17 @@ parent:
* before one is submitted. This is urgent because an extension might use
* a CPOW to synchronously submit a keygen element.
*/
- prio(urgent) sync FormProcessValue(nsString oldValue,
- nsString challenge,
- nsString keytype,
- nsString keyparams)
+ prio(urgent) sync KeygenProcessValue(nsString oldValue,
+ nsString challenge,
+ nsString keytype,
+ nsString keyparams)
returns (nsString newValue);
/**
* Called to provide the options for elements.
*/
- sync FormProvideContent() returns (nsString aAttribute, nsString[] aContent);
+ sync KeygenProvideContent()
+ returns (nsString aAttribute, nsString[] aContent);
// Use only for testing!
sync GetFileReferences(PersistenceType persistenceType,
diff --git a/dom/ipc/ScreenManagerParent.cpp b/dom/ipc/ScreenManagerParent.cpp
index cc460ef4ac20..19c7f42235fb 100644
--- a/dom/ipc/ScreenManagerParent.cpp
+++ b/dom/ipc/ScreenManagerParent.cpp
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=4 ts=8 et tw=80 : */
/* 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
@@ -131,14 +131,18 @@ ScreenManagerParent::RecvScreenForBrowser(PBrowserParent* aBrowser,
// the nsIScreen it's on.
TabParent* tabParent = static_cast(aBrowser);
nsCOMPtr widget = tabParent->GetWidget();
- if (!widget) {
- return true;
- }
nsCOMPtr screen;
- if (widget->GetNativeData(NS_NATIVE_WINDOW)) {
- mScreenMgr->ScreenForNativeWidget(widget->GetNativeData(NS_NATIVE_WINDOW),
- getter_AddRefs(screen));
+ if (widget) {
+ if (widget->GetNativeData(NS_NATIVE_WINDOW)) {
+ mScreenMgr->ScreenForNativeWidget(widget->GetNativeData(NS_NATIVE_WINDOW),
+ getter_AddRefs(screen));
+ }
+ } else {
+ nsresult rv = mScreenMgr->GetPrimaryScreen(getter_AddRefs(screen));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return true;
+ }
}
NS_ENSURE_TRUE(screen, true);
diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp
index 0803ddc0e534..768da386a521 100644
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -248,11 +248,10 @@ TabChildBase::InitializeRootMetrics()
mLastRootMetrics.SetDevPixelsPerCSSPixel(WebWidget()->GetDefaultScale());
// We use ParentLayerToLayerScale(1) below in order to turn the
// async zoom amount into the gecko zoom amount.
- mLastRootMetrics.mCumulativeResolution =
- mLastRootMetrics.GetZoom() / mLastRootMetrics.GetDevPixelsPerCSSPixel() * ParentLayerToLayerScale(1);
+ mLastRootMetrics.SetCumulativeResolution(mLastRootMetrics.GetZoom() / mLastRootMetrics.GetDevPixelsPerCSSPixel() * ParentLayerToLayerScale(1));
// This is the root layer, so the cumulative resolution is the same
// as the resolution.
- mLastRootMetrics.mPresShellResolution = mLastRootMetrics.mCumulativeResolution.scale;
+ mLastRootMetrics.mPresShellResolution = mLastRootMetrics.GetCumulativeResolution().scale;
mLastRootMetrics.SetScrollOffset(CSSPoint(0, 0));
TABC_LOG("After InitializeRootMetrics, mLastRootMetrics is %s\n",
@@ -430,12 +429,12 @@ TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
}
}
- metrics.mCumulativeResolution = metrics.GetZoom()
+ metrics.SetCumulativeResolution(metrics.GetZoom()
/ metrics.GetDevPixelsPerCSSPixel()
- * ParentLayerToLayerScale(1);
+ * ParentLayerToLayerScale(1));
// This is the root layer, so the cumulative resolution is the same
// as the resolution.
- metrics.mPresShellResolution = metrics.mCumulativeResolution.scale;
+ metrics.mPresShellResolution = metrics.GetCumulativeResolution().scale;
utils->SetResolution(metrics.mPresShellResolution, metrics.mPresShellResolution);
CSSSize scrollPort = metrics.CalculateCompositedSizeInCssPixels();
diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp
index e0dcb057a7a3..f6f02f443f4e 100644
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -216,6 +216,7 @@ namespace dom {
TabParent* sEventCapturer;
TabParent *TabParent::mIMETabParent = nullptr;
+TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr;
NS_IMPL_ISUPPORTS(TabParent,
nsITabParent,
@@ -261,6 +262,37 @@ TabParent::~TabParent()
{
}
+TabParent*
+TabParent::GetTabParentFromLayersId(uint64_t aLayersId)
+{
+ if (!sLayerToTabParentTable) {
+ return nullptr;
+ }
+ return sLayerToTabParentTable->Get(aLayersId);
+}
+
+void
+TabParent::AddTabParentToTable(uint64_t aLayersId, TabParent* aTabParent)
+{
+ if (!sLayerToTabParentTable) {
+ sLayerToTabParentTable = new LayerToTabParentTable();
+ }
+ sLayerToTabParentTable->Put(aLayersId, aTabParent);
+}
+
+void
+TabParent::RemoveTabParentFromTable(uint64_t aLayersId)
+{
+ if (!sLayerToTabParentTable) {
+ return;
+ }
+ sLayerToTabParentTable->Remove(aLayersId);
+ if (sLayerToTabParentTable->Count() == 0) {
+ delete sLayerToTabParentTable;
+ sLayerToTabParentTable = nullptr;
+ }
+}
+
void
TabParent::SetOwnerElement(Element* aElement)
{
@@ -306,6 +338,7 @@ TabParent::Destroy()
unused << SendDestroy();
if (RenderFrameParent* frame = GetRenderFrame()) {
+ RemoveTabParentFromTable(frame->GetLayersId());
frame->Destroy();
}
mIsDestroyed = true;
@@ -600,6 +633,7 @@ TabParent::Show(const nsIntSize& size)
&layersId,
&success);
MOZ_ASSERT(success);
+ AddTabParentToTable(layersId, this);
unused << SendPRenderFrameConstructor(renderFrame);
}
}
@@ -2009,6 +2043,7 @@ TabParent::AllocPRenderFrameParent()
&layersId,
&success);
MOZ_ASSERT(success);
+ AddTabParentToTable(layersId, this);
return renderFrame;
} else {
return nullptr;
@@ -2115,6 +2150,11 @@ TabParent::MaybeForwardEventToRenderFrame(WidgetInputEvent& aEvent,
if (aOutInputBlockId) {
*aOutInputBlockId = InputAPZContext::GetInputBlockId();
}
+
+ // Let the widget know that the event will be sent to the child process,
+ // which will (hopefully) send a confirmation notice back to APZ.
+ InputAPZContext::SetRoutedToChildProcess();
+
return nsEventStatus_eIgnore;
}
diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h
index 44c02175f3fd..bb0570f8a453 100644
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -461,6 +461,18 @@ private:
nsCOMPtr mLoadContext;
TabId mTabId;
+
+private:
+ // This is used when APZ needs to find the TabParent associated with a layer
+ // to dispatch events.
+ typedef nsDataHashtable LayerToTabParentTable;
+ static LayerToTabParentTable* sLayerToTabParentTable;
+
+ static void AddTabParentToTable(uint64_t aLayersId, TabParent* aTabParent);
+ static void RemoveTabParentFromTable(uint64_t aLayersId);
+
+public:
+ static TabParent* GetTabParentFromLayersId(uint64_t aLayersId);
};
} // namespace dom
diff --git a/dom/media/MediaCache.cpp b/dom/media/MediaCache.cpp
index 927b2af0cbbe..aa29dd9ce81b 100644
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -194,6 +194,10 @@ public:
// This queues a call to Update() on the main thread.
void QueueUpdate();
+ // Notify all streams for the resource ID that the suspended status changed
+ // at the end of MediaCache::Update.
+ void QueueSuspendedStatusUpdate(int64_t aResourceID);
+
// Updates the cache state asynchronously on the main thread:
// -- try to trim the cache back to its desired size, if necessary
// -- suspend channels that are going to read data that's lower priority
@@ -347,6 +351,8 @@ protected:
#ifdef DEBUG
bool mInUpdate;
#endif
+ // A list of resource IDs to notify about the change in suspended status.
+ nsTArray mSuspendedStatusToNotify;
};
NS_IMETHODIMP
@@ -1351,10 +1357,12 @@ MediaCache::Update()
case RESUME:
CACHE_LOG(PR_LOG_DEBUG, ("Stream %p Resumed", stream));
rv = stream->mClient->CacheClientResume();
+ QueueSuspendedStatusUpdate(stream->mResourceID);
break;
case SUSPEND:
CACHE_LOG(PR_LOG_DEBUG, ("Stream %p Suspended", stream));
rv = stream->mClient->CacheClientSuspend();
+ QueueSuspendedStatusUpdate(stream->mResourceID);
break;
default:
rv = NS_OK;
@@ -1369,6 +1377,15 @@ MediaCache::Update()
stream->CloseInternal(mon);
}
}
+
+ // Notify streams about the suspended status changes.
+ for (uint32_t i = 0; i < mSuspendedStatusToNotify.Length(); ++i) {
+ MediaCache::ResourceStreamIterator iter(mSuspendedStatusToNotify[i]);
+ while (MediaCacheStream* stream = iter.Next()) {
+ stream->mClient->CacheClientNotifySuspendedStatusChanged();
+ }
+ }
+ mSuspendedStatusToNotify.Clear();
}
class UpdateEvent : public nsRunnable
@@ -1399,6 +1416,15 @@ MediaCache::QueueUpdate()
NS_DispatchToMainThread(event);
}
+void
+MediaCache::QueueSuspendedStatusUpdate(int64_t aResourceID)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+ if (!mSuspendedStatusToNotify.Contains(aResourceID)) {
+ mSuspendedStatusToNotify.AppendElement(aResourceID);
+ }
+}
+
#ifdef DEBUG_VERIFY_CACHE
void
MediaCache::Verify()
@@ -1980,6 +2006,10 @@ MediaCacheStream::CloseInternal(ReentrantMonitorAutoEnter& aReentrantMonitor)
if (mClosed)
return;
mClosed = true;
+ // Closing a stream will change the return value of
+ // MediaCacheStream::AreAllStreamsForResourceSuspended as well as
+ // ChannelMediaResource::IsSuspendedByCache. Let's notify it.
+ gMediaCache->QueueSuspendedStatusUpdate(mResourceID);
gMediaCache->ReleaseStreamBlocks(this);
// Wake up any blocked readers
aReentrantMonitor.NotifyAll();
diff --git a/dom/media/MediaResource.cpp b/dom/media/MediaResource.cpp
index 18e6e3933d23..a56100f9b455 100644
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -1009,6 +1009,13 @@ ChannelMediaResource::CacheClientNotifyPrincipalChanged()
mDecoder->NotifyPrincipalChanged();
}
+void
+ChannelMediaResource::CacheClientNotifySuspendedStatusChanged()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
+ mDecoder->NotifySuspendedStatusChanged();
+}
+
nsresult
ChannelMediaResource::CacheClientSeek(int64_t aOffset, bool aResume)
{
@@ -1067,8 +1074,6 @@ nsresult
ChannelMediaResource::CacheClientSuspend()
{
Suspend(false);
-
- mDecoder->NotifySuspendedStatusChanged();
return NS_OK;
}
@@ -1076,8 +1081,6 @@ nsresult
ChannelMediaResource::CacheClientResume()
{
Resume();
-
- mDecoder->NotifySuspendedStatusChanged();
return NS_OK;
}
diff --git a/dom/media/MediaResource.h b/dom/media/MediaResource.h
index 4ee81e041f5d..379ab6cebacc 100644
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -538,6 +538,8 @@ public:
void CacheClientNotifyDataEnded(nsresult aStatus);
// Notify that the principal for the cached resource changed.
void CacheClientNotifyPrincipalChanged();
+ // Notify the decoder that the cache suspended status changed.
+ void CacheClientNotifySuspendedStatusChanged();
// These are called on the main thread by MediaCache. These shouldn't block,
// but they may grab locks --- the media cache is not holding its lock
diff --git a/dom/media/gmp/gmp-api/gmp-storage.h b/dom/media/gmp/gmp-api/gmp-storage.h
index c070c01245c8..43ad12b01a4e 100644
--- a/dom/media/gmp/gmp-api/gmp-storage.h
+++ b/dom/media/gmp/gmp-api/gmp-storage.h
@@ -118,6 +118,7 @@ class GMPRecordClient {
class GMPRecordIterator {
public:
// Retrieve the name for the current record.
+ // aOutName is null terminated at character at index (*aOutNameLength).
// Returns GMPNoErr if successful, or GMPEndOfEnumeration if iteration has
// reached the end.
virtual GMPErr GetName(const char ** aOutName, uint32_t * aOutNameLength) = 0;
diff --git a/dom/media/mediasource/MediaSourceReader.cpp b/dom/media/mediasource/MediaSourceReader.cpp
index 0818e9f9d8f5..58815262cf6a 100644
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -327,6 +327,7 @@ MediaSourceReader::Shutdown()
void
MediaSourceReader::ContinueShutdown()
{
+ ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (mTrackBuffers.Length()) {
mTrackBuffers[0]->Shutdown()->Then(GetTaskQueue(), __func__, this,
&MediaSourceReader::ContinueShutdown,
diff --git a/dom/media/mediasource/TrackBuffer.cpp b/dom/media/mediasource/TrackBuffer.cpp
index d8ec425aef9a..a1ed3ce53ea2 100644
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -39,12 +39,14 @@ TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& a
: mParentDecoder(aParentDecoder)
, mType(aType)
, mLastStartTimestamp(0)
+ , mShutdown(false)
{
MOZ_COUNT_CTOR(TrackBuffer);
mParser = ContainerParser::CreateForMIMEType(aType);
mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
aParentDecoder->AddTrackBuffer(this);
mDecoderPerSegment = Preferences::GetBool("media.mediasource.decoder-per-segment", false);
+ MSE_DEBUG("TrackBuffer(%p) created for parent decoder %p", this, aParentDecoder);
}
TrackBuffer::~TrackBuffer()
@@ -101,6 +103,9 @@ private:
nsRefPtr
TrackBuffer::Shutdown()
{
+ mParentDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
+ mShutdown = true;
+
MOZ_ASSERT(mShutdownPromise.IsEmpty());
nsRefPtr p = mShutdownPromise.Ensure(__func__);
@@ -370,23 +375,42 @@ TrackBuffer::QueueInitializeDecoder(SourceBufferDecoder* aDecoder)
void
TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
{
- MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
-
- // ReadMetadata may block the thread waiting on data, so it must not be
- // called with the monitor held.
+ // ReadMetadata may block the thread waiting on data, so we must be able
+ // to leave the monitor while we call it. For the rest of this function
+ // we want to hold the monitor though, since we run on a different task queue
+ // from the reader and interact heavily with it.
mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
+ ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
+ // We may be shut down at any time by the reader on another thread. So we need
+ // to check for this each time we acquire the monitor. If that happens, we
+ // need to abort immediately, because the reader has forgotten about us, and
+ // important pieces of our state (like mTaskQueue) have also been torn down.
+ if (mShutdown) {
+ MSE_DEBUG("TrackBuffer(%p) was shut down. Aborting initialization.", this);
+ return;
+ }
+
+ MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
MediaDecoderReader* reader = aDecoder->GetReader();
MSE_DEBUG("TrackBuffer(%p): Initializing subdecoder %p reader %p",
this, aDecoder, reader);
MediaInfo mi;
nsAutoPtr tags; // TODO: Handle metadata.
- nsresult rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
+ nsresult rv;
+ {
+ ReentrantMonitorAutoExit mon(mParentDecoder->GetReentrantMonitor());
+ rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
+ }
+
reader->SetIdle();
+ if (mShutdown) {
+ MSE_DEBUG("TrackBuffer(%p) was shut down while reading metadata. Aborting initialization.", this);
+ return;
+ }
if (NS_SUCCEEDED(rv) && reader->IsWaitingOnCDMResource()) {
- ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
mWaitingDecoders.AppendElement(aDecoder);
return;
}
@@ -442,7 +466,7 @@ TrackBuffer::ValidateTrackFormats(const MediaInfo& aInfo)
bool
TrackBuffer::RegisterDecoder(SourceBufferDecoder* aDecoder)
{
- ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
+ mParentDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
const MediaInfo& info = aDecoder->GetReader()->GetMediaInfo();
// Initialize the track info since this is the first decoder.
if (mInitializedDecoders.IsEmpty()) {
diff --git a/dom/media/mediasource/TrackBuffer.h b/dom/media/mediasource/TrackBuffer.h
index b7f416efd4e0..ecb40daff8b1 100644
--- a/dom/media/mediasource/TrackBuffer.h
+++ b/dom/media/mediasource/TrackBuffer.h
@@ -163,6 +163,7 @@ private:
void ContinueShutdown();
MediaPromiseHolder mShutdownPromise;
bool mDecoderPerSegment;
+ bool mShutdown;
};
} // namespace mozilla
diff --git a/dom/media/test/eme.js b/dom/media/test/eme.js
index c98b20d39de2..bdec0decfd4b 100644
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -3,9 +3,12 @@ const KEYSYSTEM_TYPE = "org.w3.clearkey";
function bail(message)
{
return function(err) {
+ if (err) {
+ message += "; " + String(err)
+ }
ok(false, message);
if (err) {
- info(err);
+ info(String(err));
}
SimpleTest.finish();
}
@@ -70,13 +73,13 @@ function Log(token, msg) {
info(TimeStamp(token) + " " + msg);
}
-function UpdateSessionFunc(test, token) {
+function UpdateSessionFunc(test, token, sessionType) {
return function(ev) {
var msgStr = ArrayBufferToString(ev.message);
var msg = JSON.parse(msgStr);
Log(token, "got message from CDM: " + msgStr);
- is(msg.type, test.sessionType, TimeStamp(token) + " key session type should match");
+ is(msg.type, sessionType, TimeStamp(token) + " key session type should match");
ok(msg.kids, TimeStamp(token) + " message event should contain key ID array");
var outKeys = [];
@@ -211,24 +214,24 @@ function SetupEME(test, token, params)
.then(function(keySystemAccess) {
return keySystemAccess.createMediaKeys();
}, bail(token + " Failed to request key system access."))
-
+
.then(function(mediaKeys) {
Log(token, "created MediaKeys object ok");
mediaKeys.sessions = [];
return v.setMediaKeys(mediaKeys);
}, bail("failed to create MediaKeys object"))
-
+
.then(function() {
Log(token, "set MediaKeys on