diff --git a/dom/workers/ServiceWorkerClients.cpp b/dom/workers/ServiceWorkerClients.cpp index a30f15235676..fe9c17937b24 100644 --- a/dom/workers/ServiceWorkerClients.cpp +++ b/dom/workers/ServiceWorkerClients.cpp @@ -33,6 +33,10 @@ #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; @@ -498,6 +502,12 @@ public: 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 window; nsresult rv = OpenWindow(getter_AddRefs(window)); if (NS_SUCCEEDED(rv)) { @@ -550,6 +560,41 @@ public: 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 os = services::GetObserverService(); + NS_ENSURE_STATE(os); + + WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + + RefPtr swm = ServiceWorkerManager::GetInstance(); + MOZ_ASSERT(swm); + + nsCOMPtr principal = workerPrivate->GetPrincipal(); + MOZ_ASSERT(principal); + + RefPtr registration = + swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope)); + if (NS_WARN_IF(!registration)) { + return NS_ERROR_FAILURE; + } + + RefPtr serviceWorkerInfo = + registration->GetServiceWorkerInfoById(workerPrivate->ServiceWorkerID()); + if (NS_WARN_IF(!serviceWorkerInfo)) { + return NS_ERROR_FAILURE; + } + + os->AddObserver(static_cast(serviceWorkerInfo->WorkerPrivate()), + "BrowserChrome:Ready", true); + serviceWorkerInfo->WorkerPrivate()->AddPendingWindow(this); + return NS_OK; + } +#endif RefPtr resolveRunnable = new ResolveOpenWindowRunnable(mPromiseProxy, nullptr, rv); @@ -627,7 +672,7 @@ private: // 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 chromeWin = do_QueryInterface(browserWindow); @@ -758,7 +803,7 @@ ServiceWorkerClients::OpenWindow(const nsAString& aUrl, mWorkerScope->GetScope(scope); RefPtr r = new OpenWindowRunnable(promiseProxy, - aUrl, scope); + aUrl, scope); MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget())); return promise.forget(); diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp index 15d9d09d8903..baac47985eb0 100644 --- a/dom/workers/ServiceWorkerPrivate.cpp +++ b/dom/workers/ServiceWorkerPrivate.cpp @@ -42,6 +42,7 @@ NS_IMPL_CYCLE_COLLECTION(ServiceWorkerPrivate, mSupportsArray) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerPrivate) NS_INTERFACE_MAP_ENTRY(nsISupports) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver) NS_INTERFACE_MAP_END // Tracks the "dom.disable_open_click_delay" preference. Modified on main @@ -2012,4 +2013,36 @@ ServiceWorkerPrivate::CreateEventKeepAliveToken() 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 os = services::GetObserverService(); + NS_ENSURE_STATE(os); + os->RemoveObserver(static_cast(this), "BrowserChrome:Ready"); + + size_t len = pendingWindows.Length(); + for (int i = len-1; i >= 0; i--) { + RefPtr runnable = pendingWindows[i]; + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable)); + pendingWindows.RemoveElementAt(i); + } + + return NS_OK; +} + END_WORKERS_NAMESPACE diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h index 127c2ee2e63d..16b1c436941b 100644 --- a/dom/workers/ServiceWorkerPrivate.h +++ b/dom/workers/ServiceWorkerPrivate.h @@ -62,13 +62,14 @@ public: // 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); @@ -149,6 +150,9 @@ public: bool IsIdle() const; + void + AddPendingWindow(Runnable* aPendingWindow); + private: enum WakeUpReason { FetchEvent = 0, @@ -221,6 +225,8 @@ private: // Array of function event worker runnables that are pending due to // the worker activating. Main thread only. nsTArray> mPendingFunctionalEvents; + + nsTArray pendingWindows; }; } // namespace workers diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 7e2b9f131392..a2d2d17b41be 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -353,6 +353,7 @@ var BrowserApp = { 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"); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java index de70089a4263..0a98f34e279e 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java @@ -319,6 +319,15 @@ public class GeckoAppShell 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; diff --git a/widget/android/GeneratedJNIWrappers.cpp b/widget/android/GeneratedJNIWrappers.cpp index 1982a51b021c..5970ec683f85 100644 --- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -537,6 +537,14 @@ auto GeckoAppShell::OpenUriExternal(mozilla::jni::String::Param a0, mozilla::jni return mozilla::jni::Method::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::Call(GeckoAppShell::Context(), nullptr); +} + constexpr char GeckoAppShell::PerformHapticFeedback_t::name[]; constexpr char GeckoAppShell::PerformHapticFeedback_t::signature[]; diff --git a/widget/android/GeneratedJNIWrappers.h b/widget/android/GeneratedJNIWrappers.h index 6662b7927450..a098e604f40e 100644 --- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -1452,6 +1452,25 @@ public: 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;