Bug 1262251 - Make openWindow() launch Fennec if it isn't already running. r=catalinb
☠☠ backed out by 1145a10a2fd5 ☠ ☠
authorDylan Roeh <droeh@mozilla.com>
Thu, 27 Oct 2016 13:58:39 -0500
changeset 319875 582e5f96a727af4a6da4ec26dbe882ef57914bfb
parent 319874 d1e487f723a0154cbd9752f37fcbaebf17cbcf1c
child 319877 a8c8bb56737d5f5407d80c731bfe70fa3cba7f77
push id20749
push userryanvm@gmail.com
push dateSat, 29 Oct 2016 13:21:21 +0000
treeherderfx-team@1b170b39ed6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscatalinb
bugs1262251
milestone52.0a1
Bug 1262251 - Make openWindow() launch Fennec if it isn't already running. r=catalinb
dom/workers/ServiceWorkerClients.cpp
dom/workers/ServiceWorkerPrivate.cpp
dom/workers/ServiceWorkerPrivate.h
mobile/android/chrome/content/browser.js
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
--- a/dom/workers/ServiceWorkerClients.cpp
+++ b/dom/workers/ServiceWorkerClients.cpp
@@ -28,16 +28,20 @@
 #include "nsIWebProgressListener.h"
 #include "nsIWindowMediator.h"
 #include "nsIWindowWatcher.h"
 #include "nsNetUtil.h"
 #include "nsPIWindowWatcher.h"
 #include "nsWindowWatcher.h"
 #include "nsWeakReference.h"
 
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::workers;
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClients)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClients)
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ServiceWorkerClients, mWorkerScope)
 
@@ -493,16 +497,22 @@ public:
   {
     AssertIsOnMainThread();
 
     MutexAutoLock lock(mPromiseProxy->Lock());
     if (mPromiseProxy->CleanedUp()) {
       return NS_OK;
     }
 
+#ifdef MOZ_WIDGET_ANDROID
+    // This fires an intent that will start launching Fennec and foreground it,
+    // if necessary.
+    java::GeckoAppShell::OpenWindowForNotification();
+#endif
+
     nsCOMPtr<nsPIDOMWindowOuter> window;
     nsresult rv = OpenWindow(getter_AddRefs(window));
     if (NS_SUCCEEDED(rv)) {
       MOZ_ASSERT(window);
 
       rv = nsContentUtils::DispatchFocusChromeEvent(window);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
@@ -545,16 +555,51 @@ public:
         new WebProgressListener(mPromiseProxy, serviceWorkerInfo->WorkerPrivate(),
                                 window, baseURI);
 
       rv = webProgress->AddProgressListener(listener,
                                             nsIWebProgress::NOTIFY_STATE_DOCUMENT);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
       return NS_OK;
     }
+#ifdef MOZ_WIDGET_ANDROID
+    else if (rv == NS_ERROR_NOT_AVAILABLE) {
+      // We couldn't get a browser window, so Fennec must not be running.
+      // Send an Intent to launch Fennec and wait for "BrowserChrome:Ready"
+      // to try opening a window again.
+      nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+      NS_ENSURE_STATE(os);
+
+      WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
+      MOZ_ASSERT(workerPrivate);
+
+      RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+      MOZ_ASSERT(swm);
+
+      nsCOMPtr<nsIPrincipal> principal = workerPrivate->GetPrincipal();
+      MOZ_ASSERT(principal);
+
+      RefPtr<ServiceWorkerRegistrationInfo> registration =
+        swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope));
+      if (NS_WARN_IF(!registration)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      RefPtr<ServiceWorkerInfo> serviceWorkerInfo =
+        registration->GetServiceWorkerInfoById(workerPrivate->ServiceWorkerID());
+      if (NS_WARN_IF(!serviceWorkerInfo)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      os->AddObserver(static_cast<nsIObserver*>(serviceWorkerInfo->WorkerPrivate()),
+                      "BrowserChrome:Ready", true);
+      serviceWorkerInfo->WorkerPrivate()->AddPendingWindow(this);
+      return NS_OK;
+    }
+#endif
 
     RefPtr<ResolveOpenWindowRunnable> resolveRunnable =
       new ResolveOpenWindowRunnable(mPromiseProxy, nullptr, rv);
 
     Unused << NS_WARN_IF(!resolveRunnable->Dispatch());
 
     return NS_OK;
   }
@@ -622,17 +667,17 @@ private:
 
     // Find the most recent browser window and open a new tab in it.
     nsCOMPtr<nsPIDOMWindowOuter> browserWindow =
       nsContentUtils::GetMostRecentNonPBWindow();
     if (!browserWindow) {
       // It is possible to be running without a browser window on Mac OS, so
       // we need to open a new chrome window.
       // TODO(catalinb): open new chrome window. Bug 1218080
-      return NS_ERROR_FAILURE;
+      return NS_ERROR_NOT_AVAILABLE;
     }
 
     nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(browserWindow);
     if (NS_WARN_IF(!chromeWin)) {
       return NS_ERROR_FAILURE;
     }
 
     nsCOMPtr<nsIBrowserDOMWindow> bwin;
@@ -753,17 +798,17 @@ ServiceWorkerClients::OpenWindow(const n
   if (!promiseProxy) {
     return nullptr;
   }
 
   nsString scope;
   mWorkerScope->GetScope(scope);
 
   RefPtr<OpenWindowRunnable> r = new OpenWindowRunnable(promiseProxy,
-                                                          aUrl, scope);
+                                                        aUrl, scope);
   MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerClients::Claim(ErrorResult& aRv)
 {
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -36,17 +36,17 @@ using namespace mozilla::dom;
 
 BEGIN_WORKERS_NAMESPACE
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerPrivate)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerPrivate)
 NS_IMPL_CYCLE_COLLECTION(ServiceWorkerPrivate, mSupportsArray)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerPrivate)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END
 
 // Tracks the "dom.disable_open_click_delay" preference.  Modified on main
 // thread, read on worker threads.
 // It is updated every time a "notificationclick" event is dispatched. While
 // this is done without synchronization, at the worst, the thread will just get
 // an older value within which a popup is allowed to be displayed, which will
 // still be a valid value since it was set prior to dispatching the runnable.
@@ -2007,9 +2007,41 @@ ServiceWorkerPrivate::CreateEventKeepAli
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(mWorkerPrivate);
   MOZ_ASSERT(mIdleKeepAliveToken);
   RefPtr<KeepAliveToken> ref = new KeepAliveToken(this);
   return ref.forget();
 }
 
+void
+ServiceWorkerPrivate::AddPendingWindow(Runnable* aPendingWindow)
+{
+  AssertIsOnMainThread();
+  pendingWindows.AppendElement(aPendingWindow);
+}
+
+nsresult
+ServiceWorkerPrivate::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
+{
+  AssertIsOnMainThread();
+
+  nsCString topic(aTopic);
+  if (!topic.Equals(NS_LITERAL_CSTRING("BrowserChrome:Ready"))) {
+    MOZ_ASSERT(false, "Unexpected topic.");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+  NS_ENSURE_STATE(os);
+  os->RemoveObserver(static_cast<nsIObserver*>(this), "BrowserChrome:Ready");
+
+  size_t len = pendingWindows.Length();
+  for (int i = len-1; i >= 0; i--) {
+    RefPtr<Runnable> runnable = pendingWindows[i];
+    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+    pendingWindows.RemoveElementAt(i);
+  }
+
+  return NS_OK;
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/ServiceWorkerPrivate.h
+++ b/dom/workers/ServiceWorkerPrivate.h
@@ -57,23 +57,24 @@ public:
 // 2. If the worker stopped controlling documents and it is not handling push
 // events.
 // 3. The content process is shutting down.
 //
 // Adding an API function for a new event requires calling |SpawnWorkerIfNeeded|
 // with an appropriate reason before any runnable is dispatched to the worker.
 // If the event is extendable then the runnable should inherit
 // ExtendableEventWorkerRunnable.
-class ServiceWorkerPrivate final : public nsISupports
+class ServiceWorkerPrivate final : public nsIObserver
 {
   friend class KeepAliveToken;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(ServiceWorkerPrivate)
+  NS_DECL_NSIOBSERVER
 
   explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo);
 
   nsresult
   SendMessageEvent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                    const Optional<Sequence<JS::Value>>& aTransferable,
                    UniquePtr<ServiceWorkerClientInfo>&& aClientInfo);
 
@@ -144,16 +145,19 @@ public:
   AttachDebugger();
 
   nsresult
   DetachDebugger();
 
   bool
   IsIdle() const;
 
+  void
+  AddPendingWindow(Runnable* aPendingWindow);
+
 private:
   enum WakeUpReason {
     FetchEvent = 0,
     PushEvent,
     PushSubscriptionChangeEvent,
     MessageEvent,
     NotificationClickEvent,
     NotificationCloseEvent,
@@ -216,15 +220,17 @@ private:
   // on the main thread. Access to this array is provided through
   // |StoreISupports| and |RemoveISupports|. Note that the array is also
   // cleared whenever the worker is terminated.
   nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
 
   // Array of function event worker runnables that are pending due to
   // the worker activating.  Main thread only.
   nsTArray<RefPtr<WorkerRunnable>> mPendingFunctionalEvents;
+
+  nsTArray<Runnable*> pendingWindows;
 };
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_workers_serviceworkerprivate_h
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -348,16 +348,17 @@ var BrowserApp = {
     return this.isOnLowMemoryPlatform = memory.isLowMemoryPlatform();
   },
 
   deck: null,
 
   startup: function startup() {
     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess();
     dump("zerdatime " + Date.now() + " - browser chrome startup finished.");
+    Services.obs.notifyObservers(this.browser, "BrowserChrome:Ready", null);
 
     this.deck = document.getElementById("browsers");
 
     BrowserEventHandler.init();
 
     ViewportHandler.init();
 
     Services.androidBridge.browserApp = this;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -314,16 +314,25 @@ public class GeckoAppShell
         return CrashHandler.getExceptionStackTrace(CrashHandler.getRootException(e));
     }
 
     @WrapForJNI(exceptionMode = "ignore")
     private static void handleUncaughtException(Throwable e) {
         CRASH_HANDLER.uncaughtException(null, e);
     }
 
+    @WrapForJNI
+    public static void openWindowForNotification() {
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+        intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
+
+        getApplicationContext().startActivity(intent);
+    }
+
     private static float getLocationAccuracy(Location location) {
         float radius = location.getAccuracy();
         return (location.hasAccuracy() && radius > 0) ? radius : 1001;
     }
 
     @SuppressLint("MissingPermission") // Permissions are explicitly checked for in enableLocation()
     private static Location getLastKnownLocation(LocationManager lm) {
         Location lastKnownLocation = null;
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -532,16 +532,24 @@ constexpr char GeckoAppShell::OnSensorCh
 constexpr char GeckoAppShell::OpenUriExternal_t::name[];
 constexpr char GeckoAppShell::OpenUriExternal_t::signature[];
 
 auto GeckoAppShell::OpenUriExternal(mozilla::jni::String::Param a0, mozilla::jni::String::Param a1, mozilla::jni::String::Param a2, mozilla::jni::String::Param a3, mozilla::jni::String::Param a4, mozilla::jni::String::Param a5) -> bool
 {
     return mozilla::jni::Method<OpenUriExternal_t>::Call(GeckoAppShell::Context(), nullptr, a0, a1, a2, a3, a4, a5);
 }
 
+constexpr char GeckoAppShell::OpenWindowForNotification_t::name[];
+constexpr char GeckoAppShell::OpenWindowForNotification_t::signature[];
+
+auto GeckoAppShell::OpenWindowForNotification() -> void
+{
+    return mozilla::jni::Method<OpenWindowForNotification_t>::Call(GeckoAppShell::Context(), nullptr);
+}
+
 constexpr char GeckoAppShell::PerformHapticFeedback_t::name[];
 constexpr char GeckoAppShell::PerformHapticFeedback_t::signature[];
 
 auto GeckoAppShell::PerformHapticFeedback(bool a0) -> void
 {
     return mozilla::jni::Method<PerformHapticFeedback_t>::Call(GeckoAppShell::Context(), nullptr, a0);
 }
 
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -1447,16 +1447,35 @@ public:
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::GECKO;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
     static auto OpenUriExternal(mozilla::jni::String::Param, mozilla::jni::String::Param, mozilla::jni::String::Param, mozilla::jni::String::Param, mozilla::jni::String::Param, mozilla::jni::String::Param) -> bool;
 
+    struct OpenWindowForNotification_t {
+        typedef GeckoAppShell Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "openWindowForNotification";
+        static constexpr char signature[] =
+                "()V";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto OpenWindowForNotification() -> void;
+
     struct PerformHapticFeedback_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 bool> Args;
         static constexpr char name[] = "performHapticFeedback";
         static constexpr char signature[] =