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 362727 582e5f96a727af4a6da4ec26dbe882ef57914bfb
parent 362726 d1e487f723a0154cbd9752f37fcbaebf17cbcf1c
child 362728 a8c8bb56737d5f5407d80c731bfe70fa3cba7f77
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscatalinb
bugs1262251
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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[] =