Bug 1471517 - Converts ChromeUtils.requestPerformanceMetrics as Promise - r?baku draft
authorTarek Ziadé <tarek@mozilla.com>
Thu, 05 Jul 2018 16:32:03 +0200
changeset 814628 79c8c3c5c558afae193efb6303634091c771289c
parent 814627 5b4927795c0b6638f312ce2013df5d2dd56cf66a
push id115284
push usertziade@mozilla.com
push dateThu, 05 Jul 2018 19:55:24 +0000
reviewersbaku
bugs1471517
milestone63.0a1
Bug 1471517 - Converts ChromeUtils.requestPerformanceMetrics as Promise - r?baku This ChromeUtils API now returns a promise that gets resolved once all the data has been collected via IPDL and the main process. The existing notification design and its related XPCOM classes are removed. MozReview-Commit-ID: CYKukBOC8yh
dom/base/ChromeUtils.cpp
dom/base/ChromeUtils.h
dom/base/moz.build
dom/base/nsIPerformanceMetrics.idl
dom/base/nsPerformanceMetrics.cpp
dom/base/nsPerformanceMetrics.h
dom/chrome-webidl/ChromeUtils.webidl
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/tests/browser/browser_test_performance_metrics.js
dom/workers/WorkerDebugger.cpp
toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp
toolkit/components/perfmonitoring/PerformanceMetricsCollector.h
toolkit/components/perfmonitoring/PerformanceUtils.cpp
toolkit/components/perfmonitoring/PerformanceUtils.h
toolkit/components/perfmonitoring/moz.build
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -7,17 +7,17 @@
 #include "ChromeUtils.h"
 
 #include "jsfriendapi.h"
 #include "WrapperFactory.h"
 
 #include "mozilla/Base64.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
-#include "mozilla/PerformanceUtils.h"
+#include "mozilla/PerformanceMetricsCollector.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/IdleDeadline.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/dom/WindowBinding.h" // For IdleRequestCallback/Options
 #include "IOActivityMonitor.h"
 #include "nsThreadUtils.h"
@@ -650,39 +650,37 @@ ChromeUtils::ClearRecentJSDevError(Globa
 {
   auto runtime = CycleCollectedJSRuntime::Get();
   MOZ_ASSERT(runtime);
 
   runtime->ClearRecentDevError();
 }
 #endif // NIGHTLY_BUILD
 
-/* static */ void
-ChromeUtils::RequestPerformanceMetrics(GlobalObject&)
+/* static */
+already_AddRefed<Promise>
+ChromeUtils::RequestPerformanceMetrics(GlobalObject& aGlobal,
+                                       ErrorResult& aRv)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
-  // calling all content processes via IPDL (async)
-  nsTArray<ContentParent*> children;
-  ContentParent::GetAll(children);
-  for (uint32_t i = 0; i < children.Length(); i++) {
-    mozilla::Unused << children[i]->SendRequestPerformanceMetrics();
+  // Creating a promise
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  MOZ_ASSERT(global);
+  RefPtr<Promise> domPromise = Promise::Create(global, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
   }
-
+  MOZ_ASSERT(domPromise);
 
-  // collecting the current process counters and notifying them
-  nsTArray<PerformanceInfo> info;
-  CollectPerformanceInfo(info);
-  SystemGroup::Dispatch(TaskCategory::Performance,
-    NS_NewRunnableFunction(
-      "RequestPerformanceMetrics",
-      [info]() { mozilla::Unused << NS_WARN_IF(NS_FAILED(NotifyPerformanceInfo(info))); }
-    )
-  );
+  // requesting metrics, that will be returned into the promise
+  PerformanceMetricsCollector::RequestMetrics(domPromise);
 
+  // sending back the promise instance
+  return domPromise.forget();
 }
 
 constexpr auto kSkipSelfHosted = JS::SavedFrameSelfHosted::Exclude;
 
 /* static */ void
 ChromeUtils::GetCallerLocation(const GlobalObject& aGlobal, nsIPrincipal* aPrincipal,
                                JS::MutableHandle<JSObject*> aRetval)
 {
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -155,17 +155,19 @@ public:
                            ErrorResult& aRv);
 
   static void GetRecentJSDevError(GlobalObject& aGlobal,
                                   JS::MutableHandleValue aRetval,
                                   ErrorResult& aRv);
 
   static void ClearRecentJSDevError(GlobalObject& aGlobal);
 
-  static void RequestPerformanceMetrics(GlobalObject& aGlobal);
+  static already_AddRefed<Promise>
+  RequestPerformanceMetrics(GlobalObject& aGlobal,
+                            ErrorResult& aRv);
 
   static void Import(const GlobalObject& aGlobal,
                      const nsAString& aResourceURI,
                      const Optional<JS::Handle<JSObject*>>& aTargetObj,
                      JS::MutableHandle<JSObject*> aRetval,
                      ErrorResult& aRv);
 
   static void DefineModuleGetter(const GlobalObject& global,
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -17,17 +17,16 @@ XPIDL_SOURCES += [
     'nsIContentPolicy.idl',
     'nsIDocumentEncoder.idl',
     'nsIDOMRequestService.idl',
     'nsIDroppedLinkHandler.idl',
     'nsIFrameLoaderOwner.idl',
     'nsIImageLoadingContent.idl',
     'nsIMessageManager.idl',
     'nsIObjectLoadingContent.idl',
-    'nsIPerformanceMetrics.idl',
     'nsIRemoteWindowContext.idl',
     'nsIScriptChannel.idl',
     'nsISelectionController.idl',
     'nsISelectionDisplay.idl',
     'nsISelectionListener.idl',
     'nsISlowScriptDebug.idl',
 ]
 
@@ -93,17 +92,16 @@ EXPORTS += [
     'nsITimeoutHandler.h',
     'nsJSEnvironment.h',
     'nsJSUtils.h',
     'nsLineBreaker.h',
     'nsMappedAttributeElement.h',
     'nsNameSpaceManager.h',
     'nsNodeInfoManager.h',
     'nsNodeUtils.h',
-    'nsPerformanceMetrics.h',
     'nsPIDOMWindow.h',
     'nsPIDOMWindowInlines.h',
     'nsPIWindowRoot.h',
     'nsPropertyTable.h',
     'nsRange.h',
     'nsSandboxFlags.h',
     'nsStructuredCloneContainer.h',
     'nsStubAnimationObserver.h',
@@ -323,17 +321,16 @@ UNIFIED_SOURCES += [
     'nsMappedAttributeElement.cpp',
     'nsMappedAttributes.cpp',
     'nsMimeTypeArray.cpp',
     'nsNameSpaceManager.cpp',
     'nsNoDataProtocolContentPolicy.cpp',
     'nsNodeInfoManager.cpp',
     'nsNodeUtils.cpp',
     'nsOpenURIInFrameParams.cpp',
-    'nsPerformanceMetrics.cpp',
     'nsPlainTextSerializer.cpp',
     'nsPropertyTable.cpp',
     'nsQueryContentEventResult.cpp',
     'nsRange.cpp',
     'nsScreen.cpp',
     'nsStructuredCloneContainer.cpp',
     'nsStubAnimationObserver.cpp',
     'nsStubDocumentObserver.cpp',
deleted file mode 100644
--- a/dom/base/nsIPerformanceMetrics.idl
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-*/
-/* vim: set ts=8 sts=2 et sw=2 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
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-#include "nsIArray.idl"
-
-/*
- * nsIPerformanceMetricsData is used to store performance data collected
- * in all content processes by nsThread and nsWorkerThread.
- *
- * Each (host, category, pid, wid, pwid) is unique to a given DocGroup or
- * Worker, and we collect the number of dispatches and execution duration.
- *
- * This XPCOM interface reflects the data collected in Performance counters.
- * see xpcom/threads/PerformanceCounter.h
- */
-[scriptable, builtinclass, uuid(1f9a58c9-be37-4463-8996-c7f5b9a5bef8)]
-interface nsIPerformanceMetricsDispatchCategory : nsISupports
-{
-  // DispatchCategory value
-  readonly attribute unsigned long category;
-  // Number of dispatch.
-  readonly attribute unsigned long count;
-};
-
-
-[scriptable, builtinclass, uuid(02b0cdc6-4be2-4154-a8a9-e8d462073200)]
-interface nsIPerformanceMetricsData : nsISupports
-{
-  // Host of the document, if any
-  readonly attribute AUTF8String host;
-  // process id
-  readonly attribute unsigned long pid;
-  // window id
-  readonly attribute unsigned long long wid;
-  // "parent" window id
-  readonly attribute unsigned long long pwid;
-  // Execution time in microseconds
-  readonly attribute unsigned long long duration;
-  // True if the data is collected in a worker
-  readonly attribute bool worker;
-  // Dispatch Category counters
-  readonly attribute nsIArray items;
-};
-
-
deleted file mode 100644
--- a/dom/base/nsPerformanceMetrics.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include <nsIMutableArray.h>
-#include <nsArrayUtils.h>
-#include <nsPerformanceMetrics.h>
-#include "nsComponentManagerUtils.h"
-
-/* ------------------------------------------------------
- *
- * class PerformanceMetricsDispatchCategory
- *
- */
-
-PerformanceMetricsDispatchCategory::PerformanceMetricsDispatchCategory(uint32_t aCategory, uint32_t aCount)
-  : mCategory(aCategory), mCount(aCount)
-{
-}
-
-NS_IMPL_ISUPPORTS(PerformanceMetricsDispatchCategory,
-                  nsIPerformanceMetricsDispatchCategory);
-
-
-NS_IMETHODIMP
-PerformanceMetricsDispatchCategory::GetCategory(uint32_t* aCategory)
-{
-  *aCategory = mCategory;
-  return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsDispatchCategory::GetCount(uint32_t* aCount)
-{
-  *aCount = mCount;
-  return NS_OK;
-};
-
-/* ------------------------------------------------------
- *
- * class PerformanceMetricsData
- *
- */
-
-PerformanceMetricsData::PerformanceMetricsData(uint32_t aPid, uint64_t aWid,
-                                               uint64_t aPwid, const nsCString& aHost,
-                                               uint64_t aDuration, bool aWorker,
-                                               nsIArray* aItems)
-  : mPid(aPid), mWid(aWid), mPwid(aPwid), mHost(aHost)
-  , mDuration(aDuration), mWorker(aWorker)
-{
-    uint32_t len;
-    nsresult rv = aItems->GetLength(&len);
-    if (NS_FAILED(rv)) {
-      NS_ASSERTION(rv == NS_OK, "Failed to ge the length");
-    }
-    for (uint32_t i = 0; i < len; i++) {
-        nsCOMPtr<nsIPerformanceMetricsDispatchCategory> item = do_QueryElementAt(aItems, i);
-        mItems.AppendElement(item);
-    }
-};
-
-NS_IMPL_ISUPPORTS(PerformanceMetricsData, nsIPerformanceMetricsData);
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetHost(nsACString& aHost)
-{
-  aHost = mHost;
-  return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetWorker(bool* aWorker)
-{
-  *aWorker = mWorker;
-  return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetPid(uint32_t* aPid)
-{
-  *aPid = mPid;
-  return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetWid(uint64_t* aWid)
-{
-  *aWid = mWid;
-  return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetDuration(uint64_t* aDuration)
-{
-  *aDuration = mDuration;
-  return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetPwid(uint64_t* aPwid)
-{
-  *aPwid = mPwid;
-  return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetItems(nsIArray** aItems)
-{
-  NS_ENSURE_ARG_POINTER(aItems);
-  *aItems = nullptr;
-
-  nsresult rv = NS_OK;
-  nsCOMPtr<nsIMutableArray> items =
-    do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  uint32_t len = mItems.Length();
-  for (uint32_t i = 0; i < len; i++) {
-    items->AppendElement(mItems[i]);
-  }
-
-  items.forget(aItems);
-  return NS_OK;
-}
deleted file mode 100644
--- a/dom/base/nsPerformanceMetrics.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 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
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsPerformanceMetrics_h___
-#define nsPerformanceMetrics_h___
-
-#include "nsCOMArray.h"
-#include "nsIPerformanceMetrics.h"
-#include "nsString.h"
-
-
-class PerformanceMetricsDispatchCategory final : public nsIPerformanceMetricsDispatchCategory
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIPERFORMANCEMETRICSDISPATCHCATEGORY
-  PerformanceMetricsDispatchCategory(uint32_t aCategory, uint32_t aCount);
-private:
-  ~PerformanceMetricsDispatchCategory() = default;
-
-  uint32_t mCategory;
-  uint32_t mCount;
-};
-
-
-class PerformanceMetricsData final : public nsIPerformanceMetricsData
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIPERFORMANCEMETRICSDATA
-  PerformanceMetricsData(uint32_t aPid, uint64_t aWid, uint64_t aPwid, const nsCString& aHost,
-                         uint64_t aDuration, bool aWorker, nsIArray* aItems);
-private:
-  ~PerformanceMetricsData() = default;
-
-  uint32_t mPid;
-  uint64_t mWid;
-  uint64_t mPwid;
-  nsCString mHost;
-  uint64_t mDuration;
-  bool mWorker;
-  nsCOMArray<nsIPerformanceMetricsDispatchCategory> mItems;
-};
-
-#endif  // end nsPerformanceMetrics_h__
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -340,27 +340,47 @@ partial namespace ChromeUtils {
    *
    * If a stack object is provided, the error object is created in the global
    * that it belongs to.
    */
   [Throws]
   object createError(DOMString message, optional object? stack = null);
 
   /**
-   * Request performance metrics to the current process & all ontent processes.
+   * Request performance metrics to the current process & all content processes.
    */
-  void requestPerformanceMetrics();
+  [Throws]
+  Promise<sequence<PerformanceInfoDictionary>> requestPerformanceMetrics();
 
   /**
   * Returns a Promise containing a sequence of I/O activities
   */
   [Throws]
   Promise<sequence<IOActivityDataDictionary>> requestIOActivity();
 };
 
+/**
+ * Dictionaries duplicating IPDL types in dom/ipc/DOMTypes.ipdlh
+ * Used by requestPerformanceMetrics
+ */
+dictionary CategoryDispatchDictionary
+{
+  unsigned short category = 0;
+  unsigned short count = 0;
+};
+
+dictionary PerformanceInfoDictionary {
+  DOMString host = "";
+  unsigned long pid = 0;
+  unsigned long long wid = 0;
+  unsigned long long pwid = 0;
+  unsigned long long duration = 0;
+  boolean worker = false;
+  sequence<CategoryDispatchDictionary> items = [];
+};
 
 /**
  * Used by requestIOActivity() to return the number of bytes
  * that were read (rx) and/or written (tx) for a given location.
  *
  * Locations can be sockets or files.
  */
 dictionary IOActivityDataDictionary {
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -66,16 +66,17 @@
 #include "mozilla/layers/CompositorManagerChild.h"
 #include "mozilla/layers/ContentProcessController.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layout/RenderFrameChild.h"
 #include "mozilla/loader/ScriptCacheActors.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/CaptivePortalService.h"
+#include "mozilla/PerformanceMetricsCollector.h"
 #include "mozilla/PerformanceUtils.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/plugins/PluginModuleParent.h"
 #include "mozilla/widget/ScreenManager.h"
 #include "mozilla/widget/WidgetMessageUtils.h"
 #include "nsBaseDragService.h"
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/BasePrincipal.h"
@@ -1382,22 +1383,22 @@ ContentChild::GetResultForRenderingInitF
 
   // If we are talking to the GPU process, then we should recover from this on
   // the next ContentChild::RecvReinitRendering call.
   gfxCriticalNote << "Could not initialize rendering with GPU process";
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-ContentChild::RecvRequestPerformanceMetrics()
+ContentChild::RecvRequestPerformanceMetrics(const nsID& aID)
 {
   MOZ_ASSERT(mozilla::StaticPrefs::dom_performance_enable_scheduler_timing());
   nsTArray<PerformanceInfo> info;
   CollectPerformanceInfo(info);
-  SendAddPerformanceMetrics(info);
+  SendAddPerformanceMetrics(aID, info);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvInitRendering(Endpoint<PCompositorManagerChild>&& aCompositor,
                                 Endpoint<PImageBridgeChild>&& aImageBridge,
                                 Endpoint<PVRManagerChild>&& aVRBridge,
                                 Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -187,17 +187,17 @@ public:
   RecvInitRendering(
     Endpoint<PCompositorManagerChild>&& aCompositor,
     Endpoint<PImageBridgeChild>&& aImageBridge,
     Endpoint<PVRManagerChild>&& aVRBridge,
     Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
     nsTArray<uint32_t>&& namespaces) override;
 
   mozilla::ipc::IPCResult
-  RecvRequestPerformanceMetrics() override;
+  RecvRequestPerformanceMetrics(const nsID& aID) override;
 
   mozilla::ipc::IPCResult
   RecvReinitRendering(
     Endpoint<PCompositorManagerChild>&& aCompositor,
     Endpoint<PImageBridgeChild>&& aImageBridge,
     Endpoint<PVRManagerChild>&& aVRBridge,
     Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
     nsTArray<uint32_t>&& namespaces) override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -183,17 +183,17 @@
 #include "mozilla/net/NeckoMessageUtils.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "prio.h"
 #include "private/pprio.h"
 #include "ContentProcessManager.h"
 #include "mozilla/dom/BlobURLProtocolHandler.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
-#include "mozilla/PerformanceUtils.h"
+#include "mozilla/PerformanceMetricsCollector.h"
 #include "mozilla/psm/PSMContentListener.h"
 #include "nsPluginHost.h"
 #include "nsPluginTags.h"
 #include "nsIBlocklistService.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "nsICaptivePortalService.h"
 #include "nsIObjectLoadingContent.h"
@@ -3334,24 +3334,26 @@ ContentParent::RecvFinishMemoryReport(co
   if (mMemoryReportRequest) {
     mMemoryReportRequest->Finish(aGeneration);
     mMemoryReportRequest = nullptr;
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-ContentParent::RecvAddPerformanceMetrics(nsTArray<PerformanceInfo>&& aMetrics)
+ContentParent::RecvAddPerformanceMetrics(const nsID& aID,
+                                         nsTArray<PerformanceInfo>&& aMetrics)
 {
   if (!mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) {
     // The pref is off, we should not get a performance metrics from the content
     // child
     return IPC_OK();
   }
-  Unused << NS_WARN_IF(NS_FAILED(mozilla::NotifyPerformanceInfo(aMetrics)));
+  nsresult rv = PerformanceMetricsCollector::DataReceived(aID, aMetrics);
+  Unused << NS_WARN_IF(NS_FAILED(rv));
   return IPC_OK();
 }
 
 PCycleCollectWithLogsParent*
 ContentParent::AllocPCycleCollectWithLogsParent(const bool& aDumpAllTraces,
                                                 const FileDescriptor& aGCLog,
                                                 const FileDescriptor& aCCLog)
 {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -851,17 +851,17 @@ private:
    * Get or create the corresponding content parent array to |aContentProcessType|.
    */
   static nsTArray<ContentParent*>& GetOrCreatePool(const nsAString& aContentProcessType);
 
   virtual mozilla::ipc::IPCResult RecvInitBackground(Endpoint<mozilla::ipc::PBackgroundParent>&& aEndpoint) override;
 
   mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport) override;
   mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration) override;
-  mozilla::ipc::IPCResult RecvAddPerformanceMetrics(nsTArray<PerformanceInfo>&& aMetrics) override;
+  mozilla::ipc::IPCResult RecvAddPerformanceMetrics(const nsID& aID, nsTArray<PerformanceInfo>&& aMetrics) override;
 
   virtual bool
   DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override;
 
   virtual bool
   DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*) override;
 
   virtual PBrowserParent* AllocPBrowserParent(const TabId& aTabId,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -403,17 +403,17 @@ child:
      * abnormally exit if this fails; the details are OS-specific.
      */
     async SetProcessSandbox(MaybeFileDesc aBroker);
 
     async RequestMemoryReport(uint32_t generation,
                               bool anonymize,
                               bool minimizeMemoryUsage,
                               MaybeFileDesc DMDFile);
-    async RequestPerformanceMetrics();
+    async RequestPerformanceMetrics(nsID aID);
 
     /**
      * Communication between the PuppetBidiKeyboard and the actual
      * BidiKeyboard hosted by the parent
      */
     async BidiKeyboardNotify(bool isLangRTL, bool haveBidiKeyboards);
 
     /**
@@ -1145,17 +1145,17 @@ parent:
 
     async AddMemoryReport(MemoryReport aReport);
     async FinishMemoryReport(uint32_t aGeneration);
 
     async MaybeReloadPlugins();
 
     async BHRThreadHang(HangDetails aHangDetails);
 
-    async AddPerformanceMetrics(PerformanceInfo[] aMetrics);
+    async AddPerformanceMetrics(nsID aID, PerformanceInfo[] aMetrics);
 both:
      async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
                         Principal aPrincipal, ClonedMessageData aData);
 
     /**
      * Notify `push-subscription-modified` observers in the parent and child.
      */
     async NotifyPushSubscriptionModifiedObservers(nsCString scope,
--- a/dom/tests/browser/browser_test_performance_metrics.js
+++ b/dom/tests/browser/browser_test_performance_metrics.js
@@ -63,59 +63,46 @@ add_task(async function test() {
   await BrowserTestUtils.withNewTab({ gBrowser, url: WORKER_URL },
     async function(browser) {
     // grab events..
     let worker_duration = 0;
     let worker_total = 0;
     let duration = 0;
     let total = 0;
 
-    function getInfoFromService(subject, topic, value) {
-      subject = subject.QueryInterface(Ci.nsIMutableArray);
-      let enumerator = subject.enumerate();
-      while (enumerator.hasMoreElements()) {
-        let entry = enumerator.getNext();
-        entry = entry.QueryInterface(Ci.nsIPerformanceMetricsData);
+    function exploreResults(data) {
+      for (let entry of data) {
         if (entry.pid == Services.appinfo.processID) {
           parent_process_event = true;
         }
         if (entry.worker) {
           worker_event = true;
           worker_duration += entry.duration;
         } else {
           duration += entry.duration;
         }
-        // let's look at the XPCOM data we got back
-        let items = entry.items.QueryInterface(Ci.nsIMutableArray);
-        let enumerator2 = items.enumerate();
-        while (enumerator2.hasMoreElements()) {
-          let item = enumerator2.getNext();
-          item = item.QueryInterface(Ci.nsIPerformanceMetricsDispatchCategory);
+        // let's look at the data we got back
+        for (let item of entry.items) {
           if (entry.worker) {
             worker_total += item.count;
           } else {
             total += item.count;
           }
         }
       }
     }
 
-    Services.obs.addObserver(getInfoFromService, "performance-metrics");
-
-    // wait until we get some events back by triggering requestPerformanceMetrics
-    await BrowserTestUtils.waitForCondition(() => {
-      ChromeUtils.requestPerformanceMetrics();
-      return worker_duration > 0 && duration > 0 && parent_process_event;
-    }, "wait for events to come in", 250, 20);
-
-    BrowserTestUtils.removeTab(page1);
-    BrowserTestUtils.removeTab(page2);
-    BrowserTestUtils.removeTab(page3);
+    // get all metrics via the promise
+    let results = await ChromeUtils.requestPerformanceMetrics();
+    exploreResults(results);
 
     Assert.ok(worker_duration > 0, "Worker duration should be positive");
     Assert.ok(worker_total > 0, "Worker count should be positive");
     Assert.ok(duration > 0, "Duration should be positive");
     Assert.ok(total > 0, "Should get a positive count");
     Assert.ok(parent_process_event, "parent process sent back some events");
   });
 
+  BrowserTestUtils.removeTab(page1);
+  BrowserTestUtils.removeTab(page2);
+  BrowserTestUtils.removeTab(page3);
   SpecialPowers.clearUserPref('dom.performance.enable_scheduler_timing');
 });
--- a/dom/workers/WorkerDebugger.cpp
+++ b/dom/workers/WorkerDebugger.cpp
@@ -473,16 +473,17 @@ WorkerDebugger::ReportErrorToDebuggerOnM
   report.mFilename = aFilename;
   WorkerErrorReport::LogErrorToConsole(report, 0);
 }
 
 PerformanceInfo
 WorkerDebugger::ReportPerformanceInfo()
 {
   AssertIsOnMainThread();
+
 #if defined(XP_WIN)
   uint32_t pid = GetCurrentProcessId();
 #else
   uint32_t pid = getpid();
 #endif
   uint64_t wid = mWorkerPrivate->WindowID();
   uint64_t pwid = wid;
   nsPIDOMWindowInner* win = mWorkerPrivate->GetWindow();
@@ -490,30 +491,37 @@ WorkerDebugger::ReportPerformanceInfo()
     nsPIDOMWindowOuter* outer = win->GetOuterWindow();
     if (outer) {
       nsCOMPtr<nsPIDOMWindowOuter> top = outer->GetTop();
       if (top) {
         pwid = top->WindowID();
       }
     }
   }
-  RefPtr<PerformanceCounter> perf = mWorkerPrivate->GetPerformanceCounter();
-  uint16_t count =  perf->GetTotalDispatchCount();
-  uint64_t duration = perf->GetExecutionDuration();
-  RefPtr<nsIURI> uri = mWorkerPrivate->GetResolvedScriptURI();
+
 
   // Workers only produce metrics for a single category - DispatchCategory::Worker.
   // We still return an array of CategoryDispatch so the PerformanceInfo
   // struct is common to all performance counters throughout Firefox.
   FallibleTArray<CategoryDispatch> items;
-  CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
-  if (!items.AppendElement(item, fallible)) {
-    NS_ERROR("Could not complete the operation");
-    return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration,
+  uint64_t duration = 0;
+  uint16_t count = 0;
+  RefPtr<nsIURI> uri = mWorkerPrivate->GetResolvedScriptURI();
+
+  RefPtr<PerformanceCounter> perf = mWorkerPrivate->GetPerformanceCounter();
+  if (perf) {
+    count =  perf->GetTotalDispatchCount();
+    duration = perf->GetExecutionDuration();
+    CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
+    if (!items.AppendElement(item, fallible)) {
+      NS_ERROR("Could not complete the operation");
+      return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration,
                             true, items);
+    }
+    perf->ResetPerformanceCounters();
   }
-  perf->ResetPerformanceCounters();
+
   return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration,
                          true, items);
 }
 
 } // dom namespace
 } // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsThreadUtils.h"
+#include "mozilla/Logging.h"
+#include "mozilla/PerformanceUtils.h"
+#include "mozilla/PerformanceMetricsCollector.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/WorkerDebugger.h"
+#include "mozilla/dom/WorkerDebuggerManager.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+static mozilla::LazyLogModule sPerfLog("PerformanceMetricsCollector");
+#ifdef LOG
+#undef LOG
+#endif
+#define LOG(args) MOZ_LOG(sPerfLog, mozilla::LogLevel::Debug, args)
+
+namespace mozilla {
+
+//
+// class AggregatedResults
+//
+AggregatedResults::AggregatedResults(nsID aUUID,
+                                     PerformanceMetricsCollector* aCollector,
+                                     dom::Promise* aPromise)
+  : mPromise(aPromise)
+  , mPendingResults(0)
+  , mCollector(aCollector)
+  , mUUID(aUUID)
+{
+  MOZ_ASSERT(aCollector);
+  MOZ_ASSERT(aPromise);
+}
+
+void
+AggregatedResults::Abort(nsresult aReason)
+{
+  MOZ_ASSERT(mPromise);
+  MOZ_ASSERT(NS_FAILED(aReason));
+  mPromise->MaybeReject(aReason);
+  mPromise = nullptr;
+  mPendingResults = 0;
+}
+
+void
+AggregatedResults::AppendResult(const nsTArray<dom::PerformanceInfo>& aMetrics)
+{
+  if (!mPromise) {
+    // A previous call failed and the promise was already rejected
+    return;
+  }
+  MOZ_ASSERT(mPendingResults > 0);
+
+  // Each PerformanceInfo is converted into a PerformanceInfoDictionary
+  for (const PerformanceInfo& result : aMetrics) {
+    mozilla::dom::Sequence<mozilla::dom::CategoryDispatchDictionary> items;
+
+    for (const CategoryDispatch& entry : result.items()) {
+      CategoryDispatchDictionary* item = items.AppendElement(fallible);
+      if (NS_WARN_IF(!item)) {
+        Abort(NS_ERROR_OUT_OF_MEMORY);
+        return;
+      }
+      item->mCategory = entry.category();
+      item->mCount = entry.count();
+    }
+
+    PerformanceInfoDictionary* data = mData.AppendElement(fallible);
+    if (NS_WARN_IF(!data)) {
+      Abort(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
+    data->mPid = result.pid();
+    data->mWid = result.wid();
+    data->mPwid = result.pwid();
+    data->mHost = *result.host().get();
+    data->mDuration = result.pid();
+    data->mWorker = result.worker();
+    data->mItems = items;
+  }
+
+  mPendingResults--;
+  if (mPendingResults) {
+    return;
+  }
+
+  LOG(("[%s] All data collected, resolving promise", nsIDToCString(mUUID).get()));
+  mPromise->MaybeResolve(mData);
+  mCollector->ForgetAggregatedResults(mUUID);
+}
+
+void
+AggregatedResults::SetNumResultsRequired(uint32_t aNumResultsRequired)
+{
+  MOZ_ASSERT(!mPendingResults && aNumResultsRequired);
+  mPendingResults = aNumResultsRequired;
+}
+
+//
+// class PerformanceMetricsCollector (singleton)
+//
+
+// raw pointer for the singleton
+PerformanceMetricsCollector* gInstance = nullptr;
+
+PerformanceMetricsCollector::~PerformanceMetricsCollector()
+{
+  MOZ_ASSERT(gInstance == this);
+  gInstance = nullptr;
+}
+
+void
+PerformanceMetricsCollector::ForgetAggregatedResults(const nsID& aUUID)
+{
+  MOZ_ASSERT(gInstance);
+  MOZ_ASSERT(XRE_IsParentProcess());
+  // This Remove() call will trigger AggregatedResults DTOR and if its
+  // the last in the table, the DTOR of PerformanceMetricsCollector.
+  // That's why we need to make sure we hold a reference here before the call
+  RefPtr<PerformanceMetricsCollector> kungFuDeathGrip = this;
+  LOG(("[%s] Removing from the table", nsIDToCString(aUUID).get()));
+  mAggregatedResults.Remove(aUUID);
+}
+
+// static
+void
+PerformanceMetricsCollector::RequestMetrics(dom::Promise* aPromise)
+{
+  MOZ_ASSERT(aPromise);
+  MOZ_ASSERT(XRE_IsParentProcess());
+  RefPtr<PerformanceMetricsCollector> pmc = gInstance;
+  if (!pmc) {
+    pmc = new PerformanceMetricsCollector();
+    gInstance = pmc;
+  }
+  pmc->RequestMetricsInternal(aPromise);
+}
+
+void
+PerformanceMetricsCollector::RequestMetricsInternal(dom::Promise* aPromise)
+{
+  // each request has its own UUID
+  nsID uuid;
+  nsresult rv = nsContentUtils::GenerateUUIDInPlace(uuid);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aPromise->MaybeReject(rv);
+    return;
+  }
+
+  LOG(("[%s] Requesting Performance Metrics", nsIDToCString(uuid).get()));
+
+  // Getting all content processes
+  nsTArray<ContentParent*> children;
+  ContentParent::GetAll(children);
+
+  uint32_t numChildren = children.Length();
+  LOG(("[%s] Expecting %d results back", nsIDToCString(uuid).get(), numChildren + 1));
+
+  // keep track of all results in an AggregatedResults instance
+  UniquePtr<AggregatedResults> results = MakeUnique<AggregatedResults>(uuid, this, aPromise);
+
+  // We want to get back as many results as children, plus the result
+  // from the content parent itself
+  results->SetNumResultsRequired(numChildren + 1);
+  mAggregatedResults.Put(uuid, std::move(results));
+
+  // calling all content processes via IPDL (async)
+  for (uint32_t i = 0; i < numChildren; i++) {
+    if (NS_WARN_IF(!children[i]->SendRequestPerformanceMetrics(uuid))) {
+      LOG(("[%s] Failed to send request to child %d", nsIDToCString(uuid).get(), i));
+      mAggregatedResults.GetValue(uuid)->get()->Abort(NS_ERROR_FAILURE);
+      return;
+    }
+  }
+
+  // collecting the current process PerformanceInfo
+  nsTArray<PerformanceInfo> info;
+  CollectPerformanceInfo(info);
+  DataReceived(uuid, info);
+}
+
+
+// static
+nsresult
+PerformanceMetricsCollector::DataReceived(const nsID& aUUID,
+                                          const nsTArray<PerformanceInfo>& aMetrics)
+{
+  MOZ_ASSERT(gInstance);
+  MOZ_ASSERT(XRE_IsParentProcess());
+  return gInstance->DataReceivedInternal(aUUID, aMetrics);
+}
+
+nsresult
+PerformanceMetricsCollector::DataReceivedInternal(const nsID& aUUID,
+                                                  const nsTArray<PerformanceInfo>& aMetrics)
+{
+  MOZ_ASSERT(gInstance == this);
+  UniquePtr<AggregatedResults>* results = mAggregatedResults.GetValue(aUUID);
+  if (!results) {
+    return NS_ERROR_FAILURE;
+  }
+
+  LOG(("[%s] Received one PerformanceInfo array", nsIDToCString(aUUID).get()));
+  AggregatedResults* aggregatedResults = results->get();
+  MOZ_ASSERT(aggregatedResults);
+
+  // If this is the last result, APpendResult() will trigger the deletion
+  // of this collector, nothing should be done after this line.
+  aggregatedResults->AppendResult(aMetrics);
+  return NS_OK;
+}
+
+} // namespace
new file mode 100644
--- /dev/null
+++ b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PerformanceMetricsCollector_h
+#define PerformanceMetricsCollector_h
+
+#include "nsID.h"
+#include "mozilla/dom/ChromeUtilsBinding.h"  // defines PerformanceInfoDictionary
+#include "mozilla/dom/DOMTypes.h"   // defines PerformanceInfo
+
+namespace mozilla {
+
+namespace dom {
+  class Promise;
+}
+
+class PerformanceMetricsCollector;
+
+// AggregatedResults receives PerformanceInfo results that are collected
+// via IPDL from all content processes and the main process. They
+// are converted into an array of PerformanceInfoDictionary dictionaries (webidl)
+//
+// The class is instanciated with a Promise and a number of processes
+// that are supposed to send back results.
+//
+// Once every process have sent back its results, AggregatedResults will
+// resolve the promise with all the collected data and send back the
+// dictionnary.
+//
+class AggregatedResults final
+{
+public:
+  AggregatedResults(nsID aUUID, PerformanceMetricsCollector* aCollector,
+                    dom::Promise* aPromise);
+  ~AggregatedResults() = default;
+  void AppendResult(const nsTArray<dom::PerformanceInfo>& aMetrics);
+  void SetNumResultsRequired(uint32_t aNumResultsRequired);
+  void Abort(nsresult aReason);
+
+private:
+  RefPtr<dom::Promise> mPromise;
+  uint32_t mPendingResults;
+  FallibleTArray<dom::PerformanceInfoDictionary> mData;
+
+  // AggregatedResults keeps a reference on the collector
+  // so it gets destructed when all pending AggregatedResults
+  // are themselves destructed when removed from
+  // PerformanceMetricsCollector::mAggregatedResults.
+  //
+  // This lifecycle ensures that everything is released once
+  // all pending results are sent.
+  RefPtr<PerformanceMetricsCollector> mCollector;
+  nsID mUUID;
+};
+
+//
+// PerformanceMetricsCollector is instanciated as a singleton, and creates
+// one AggregatedResults instance everytime metrics are requested.
+//
+// Each AggregatedResults has a unique identifier (UUID) that is used
+// to send metrics requests via IPDL. When metrics are back in an
+// asynchronous fashion, the UUID is used to append the data to the
+// right AggregatedResults instance and eventually let it resolve the
+// linked promise.
+//
+class PerformanceMetricsCollector final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(PerformanceMetricsCollector)
+
+  static void RequestMetrics(dom::Promise* aPromise);
+  static nsresult DataReceived(const nsID& aUUID,
+                               const nsTArray<dom::PerformanceInfo>& aMetrics);
+  void ForgetAggregatedResults(const nsID& aUUID);
+
+private:
+  ~PerformanceMetricsCollector();
+  void RequestMetricsInternal(dom::Promise* aPromise);
+  nsresult DataReceivedInternal(const nsID& aUUID,
+                                const nsTArray<dom::PerformanceInfo>& aMetrics);
+  nsDataHashtable<nsIDHashKey, UniquePtr<AggregatedResults>> mAggregatedResults;
+};
+
+} // namespace mozilla
+#endif   // PerformanceMetricsCollector_h
--- a/toolkit/components/perfmonitoring/PerformanceUtils.cpp
+++ b/toolkit/components/perfmonitoring/PerformanceUtils.cpp
@@ -1,16 +1,14 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsIMutableArray.h"
-#include "nsPerformanceMetrics.h"
 #include "nsThreadUtils.h"
 #include "mozilla/PerformanceUtils.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/WorkerDebugger.h"
 #include "mozilla/dom/WorkerDebuggerManager.h"
 
 using namespace mozilla;
@@ -36,55 +34,9 @@ CollectPerformanceInfo(nsTArray<Performa
     return;
   }
   for (uint32_t i = 0; i < wdm->GetDebuggersLength(); i++) {
     WorkerDebugger* debugger = wdm->GetDebuggerAt(i);
     aMetrics.AppendElement(debugger->ReportPerformanceInfo());
   }
 }
 
-nsresult
-NotifyPerformanceInfo(const nsTArray<PerformanceInfo>& aMetrics)
-{
-  nsresult rv;
-
-  nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
-  if (NS_WARN_IF(!array)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // Each PerformanceInfo is converted into a nsIPerformanceMetricsData
-  for (const PerformanceInfo& info : aMetrics) {
-    nsCOMPtr<nsIMutableArray> items = do_CreateInstance(NS_ARRAY_CONTRACTID);
-    if (NS_WARN_IF(!items)) {
-      return rv;
-    }
-    for (const CategoryDispatch& entry : info.items()) {
-      nsCOMPtr<nsIPerformanceMetricsDispatchCategory> item =
-        new PerformanceMetricsDispatchCategory(entry.category(),
-                                               entry.count());
-      rv = items->AppendElement(item);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-    nsCOMPtr<nsIPerformanceMetricsData> data;
-    data = new PerformanceMetricsData(info.pid(), info.wid(), info.pwid(),
-                                      info.host(), info.duration(),
-                                      info.worker(), items);
-    rv = array->AppendElement(data);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (NS_WARN_IF(!obs)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  rv = obs->NotifyObservers(array, "performance-metrics", nullptr);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return NS_OK;
-}
-
 } // namespace
--- a/toolkit/components/perfmonitoring/PerformanceUtils.h
+++ b/toolkit/components/perfmonitoring/PerformanceUtils.h
@@ -1,26 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef PerformanceCollector_h
-#define PerformanceCollector_h
+#ifndef PerformanceUtils_h
+#define PerformanceUtils_h
 
 #include "mozilla/dom/DOMTypes.h"   // defines PerformanceInfo
 
 namespace mozilla {
 
 /**
  * Collects all performance info in the current process
- * and adds then in the aMetrics arrey
+ * and adds then in the aMetrics array
  */
 void CollectPerformanceInfo(nsTArray<dom::PerformanceInfo>& aMetrics);
 
-/**
- * Converts a PerformanceInfo array into a nsIPerformanceMetricsData and
- * sends a performance-metrics notification with it
- */
-nsresult NotifyPerformanceInfo(const nsTArray<dom::PerformanceInfo>& aMetrics);
-
 } // namespace mozilla
-#endif   // PerformanceCollector_h
+#endif   // PerformanceUtils_h
--- a/toolkit/components/perfmonitoring/moz.build
+++ b/toolkit/components/perfmonitoring/moz.build
@@ -22,20 +22,22 @@ XPIDL_SOURCES += [
     'nsIPerformanceStats.idl',
 ]
 
 UNIFIED_SOURCES += [
     'nsPerformanceStats.cpp'
 ]
 
 UNIFIED_SOURCES += [
+    'PerformanceMetricsCollector.cpp',
     'PerformanceUtils.cpp'
 ]
 
 EXPORTS.mozilla += [
+    'PerformanceMetricsCollector.h',
     'PerformanceUtils.h'
 ]
 
 EXPORTS += [
     'nsPerformanceStats.h'
 ]
 
 LOCAL_INCLUDES += [