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