Bug 1235431 - Implement windowClient.focus() for Fennec. r=catalinb
authorDylan Roeh <droeh@mozilla.com>
Mon, 14 Nov 2016 17:22:13 -0600
changeset 325040 6ee056796f7b0a6855930807249a51eb667631d2
parent 325039 f34bb84d28838a18a3dee2842dc72841c810ef3a
child 325041 2d8e3c16f55b492e77cd0d9147d235141d3f4287
push id31026
push usercbook@mozilla.com
push dateFri, 02 Dec 2016 08:24:04 +0000
treeherdermozilla-central@f65ad27efe83 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscatalinb
bugs1235431
milestone53.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 1235431 - Implement windowClient.focus() for Fennec. r=catalinb
dom/workers/ServiceWorkerClients.cpp
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/Tabs.java
mobile/android/chrome/content/browser.js
mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java
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
@@ -500,17 +500,17 @@ public:
     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();
+    java::GeckoAppShell::LaunchOrBringToFront();
 #endif
 
     nsCOMPtr<nsPIDOMWindowOuter> window;
     nsresult rv = OpenWindow(getter_AddRefs(window));
     if (NS_SUCCEEDED(rv)) {
       MOZ_ASSERT(window);
 
       rv = nsContentUtils::DispatchFocusChromeEvent(window);
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -202,16 +202,18 @@ public abstract class GeckoApp
     private final HashMap<String, PowerManager.WakeLock> mWakeLocks = new HashMap<String, PowerManager.WakeLock>();
 
     protected boolean mLastSessionCrashed;
     protected boolean mShouldRestore;
     private boolean mSessionRestoreParsingFinished = false;
 
     private int lastSelectedTabId = -1;
 
+    private boolean foregrounded = false;
+
     private static final class LastSessionParser extends SessionParser {
         private JSONArray tabs;
         private JSONObject windowObject;
         private boolean isExternalURL;
 
         private boolean selectNextTab;
         private boolean tabsWereSkipped;
         private boolean tabsWereProcessed;
@@ -2066,25 +2068,32 @@ public abstract class GeckoApp
         return intent.getDataString();
     }
 
     protected int getOrientation() {
         return GeckoScreenOrientation.getInstance().getAndroidOrientation();
     }
 
     @Override
+    public boolean isForegrounded() {
+        return foregrounded;
+    }
+
+    @Override
     public void onResume()
     {
         // After an onPause, the activity is back in the foreground.
         // Undo whatever we did in onPause.
         super.onResume();
         if (mIsAbortingAppLaunch) {
             return;
         }
 
+        foregrounded = true;
+
         GeckoAppShell.setGeckoInterface(this);
 
         if (lastSelectedTabId >= 0 && (lastActiveGeckoApp == null || lastActiveGeckoApp.get() != this)) {
             Tabs.getInstance().selectTab(lastSelectedTabId);
         }
 
         int newOrientation = getResources().getConfiguration().orientation;
         if (GeckoScreenOrientation.getInstance().update(newOrientation)) {
@@ -2155,16 +2164,18 @@ public abstract class GeckoApp
     @Override
     public void onPause()
     {
         if (mIsAbortingAppLaunch) {
             super.onPause();
             return;
         }
 
+        foregrounded = false;
+
         final Tab selectedTab = Tabs.getInstance().getSelectedTab();
         if (selectedTab != null) {
             lastSelectedTabId = selectedTab.getId();
         }
         lastActiveGeckoApp = new WeakReference<GeckoApp>(this);
 
         final HealthRecorder rec = mHealthRecorder;
         final Context context = this;
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -103,16 +103,17 @@ public class Tabs implements GeckoEventL
         }
     };
 
     private Tabs() {
         EventDispatcher.getInstance().registerGeckoThreadListener(this,
             "Tab:Added",
             "Tab:Close",
             "Tab:Select",
+            "Tab:SelectAndForeground",
             "Content:LocationChange",
             "Content:SecurityChange",
             "Content:StateChange",
             "Content:LoadError",
             "Content:PageShow",
             "DOMTitleChanged",
             "Link:Favicon",
             "Link:Touchicon",
@@ -519,16 +520,19 @@ public class Tabs implements GeckoEventL
             // Tab was already closed; abort
             if (tab == null)
                 return;
 
             if (event.equals("Tab:Close")) {
                 closeTab(tab);
             } else if (event.equals("Tab:Select")) {
                 selectTab(tab.getId());
+            } else if (event.equals("Tab:SelectAndForeground")) {
+                GeckoAppShell.launchOrBringToFront();
+                selectTab(tab.getId());
             } else if (event.equals("Content:LocationChange")) {
                 tab.handleLocationChange(message);
             } else if (event.equals("Content:SecurityChange")) {
                 tab.updateIdentityData(message.getJSONObject("identity"));
                 notifyListeners(tab, TabEvents.SECURITY_CHANGE);
             } else if (event.equals("Content:StateChange")) {
                 int state = message.getInt("state");
                 if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) {
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3526,16 +3526,17 @@ Tab.prototype = {
     this.browser.addEventListener("DOMAudioPlaybackStopped", this, true);
     this.browser.addEventListener("DOMWindowClose", this, true);
     this.browser.addEventListener("DOMWillOpenModalDialog", this, true);
     this.browser.addEventListener("DOMAutoComplete", this, true);
     this.browser.addEventListener("blur", this, true);
     this.browser.addEventListener("pageshow", this, true);
     this.browser.addEventListener("MozApplicationManifest", this, true);
     this.browser.addEventListener("TabPreZombify", this, true);
+    this.browser.addEventListener("DOMServiceWorkerFocusClient", this, true);
 
     // Note that the XBL binding is untrusted
     this.browser.addEventListener("PluginBindingAttached", this, true, true);
     this.browser.addEventListener("VideoBindingAttached", this, true, true);
     this.browser.addEventListener("VideoBindingCast", this, true, true);
 
     Services.obs.addObserver(this, "before-first-paint", false);
     Services.obs.addObserver(this, "media-playback", false);
@@ -3638,16 +3639,17 @@ Tab.prototype = {
     this.browser.removeEventListener("DOMAudioPlaybackStopped", this, true);
     this.browser.removeEventListener("DOMWindowClose", this, true);
     this.browser.removeEventListener("DOMWillOpenModalDialog", this, true);
     this.browser.removeEventListener("DOMAutoComplete", this, true);
     this.browser.removeEventListener("blur", this, true);
     this.browser.removeEventListener("pageshow", this, true);
     this.browser.removeEventListener("MozApplicationManifest", this, true);
     this.browser.removeEventListener("TabPreZombify", this, true);
+    this.browser.removeEventListener("DOMServiceWorkerFocusClient", this, true);
 
     this.browser.removeEventListener("PluginBindingAttached", this, true, true);
     this.browser.removeEventListener("VideoBindingAttached", this, true, true);
     this.browser.removeEventListener("VideoBindingCast", this, true, true);
 
     Services.obs.removeObserver(this, "before-first-paint");
     Services.obs.removeObserver(this, "media-playback", false);
     Services.obs.removeObserver(this, "media-playback-resumed", false);
@@ -4075,16 +4077,24 @@ Tab.prototype = {
         break;
       }
 
       case "MozApplicationManifest": {
         OfflineApps.offlineAppRequested(aEvent.originalTarget.defaultView);
         break;
       }
 
+      case "DOMServiceWorkerFocusClient": {
+        Messaging.sendRequest({
+          type: "Tab:SelectAndForeground",
+          tabID: this.id
+        });
+        break;
+      }
+
       case "pageshow": {
         LoginManagerContent.onPageShow(aEvent, this.browser.contentWindow);
 
         // The rest of this only handles pageshow for the top-level document.
         if (aEvent.originalTarget.defaultView != this.browser.contentWindow)
           return;
 
         let target = aEvent.originalTarget;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java
@@ -161,9 +161,14 @@ public class BaseGeckoInterface implemen
         return new String[] {};
     }
 
     @Override
     public String getDefaultChromeURI() {
         // By default, use the GeckoView-specific chrome URI.
         return "chrome://browser/content/geckoview.xul";
     }
+
+    @Override
+    public boolean isForegrounded() {
+        return false;
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -318,22 +318,25 @@ public class GeckoAppShell
     }
 
     @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);
+    public static void launchOrBringToFront() {
+        GeckoInterface gi = getGeckoInterface();
+        if (gi == null || !gi.isForegrounded()) {
+            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);
+            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()
@@ -1731,16 +1734,17 @@ public class GeckoAppShell
         public void disableOrientationListener();
         public void addAppStateListener(AppStateListener listener);
         public void removeAppStateListener(AppStateListener listener);
         public void notifyWakeLockChanged(String topic, String state);
         public boolean areTabsShown();
         public AbsoluteLayout getPluginContainer();
         public void notifyCheckUpdateResult(String result);
         public void invalidateOptionsMenu();
+        public boolean isForegrounded();
 
         /**
          * Create a shortcut -- generally a home-screen icon -- linking the given title to the given URI.
          * <p>
          * This method is always invoked on the Gecko thread.
          *
          * @param title of URI to link to.
          * @param URI to link to.
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -522,16 +522,24 @@ auto GeckoAppShell::IsTablet() -> bool
 constexpr char GeckoAppShell::KillAnyZombies_t::name[];
 constexpr char GeckoAppShell::KillAnyZombies_t::signature[];
 
 auto GeckoAppShell::KillAnyZombies() -> void
 {
     return mozilla::jni::Method<KillAnyZombies_t>::Call(GeckoAppShell::Context(), nullptr);
 }
 
+constexpr char GeckoAppShell::LaunchOrBringToFront_t::name[];
+constexpr char GeckoAppShell::LaunchOrBringToFront_t::signature[];
+
+auto GeckoAppShell::LaunchOrBringToFront() -> void
+{
+    return mozilla::jni::Method<LaunchOrBringToFront_t>::Call(GeckoAppShell::Context(), nullptr);
+}
+
 constexpr char GeckoAppShell::LoadPluginClass_t::name[];
 constexpr char GeckoAppShell::LoadPluginClass_t::signature[];
 
 auto GeckoAppShell::LoadPluginClass(mozilla::jni::String::Param a0, mozilla::jni::String::Param a1) -> mozilla::jni::Class::LocalRef
 {
     return mozilla::jni::Method<LoadPluginClass_t>::Call(GeckoAppShell::Context(), nullptr, a0, a1);
 }
 
@@ -588,24 +596,16 @@ 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
@@ -1421,16 +1421,35 @@ public:
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::GECKO;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
     static auto KillAnyZombies() -> void;
 
+    struct LaunchOrBringToFront_t {
+        typedef GeckoAppShell Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "launchOrBringToFront";
+        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 LaunchOrBringToFront() -> void;
+
     struct LoadPluginClass_t {
         typedef GeckoAppShell Owner;
         typedef mozilla::jni::Class::LocalRef ReturnType;
         typedef mozilla::jni::Class::Param SetterType;
         typedef mozilla::jni::Args<
                 mozilla::jni::String::Param,
                 mozilla::jni::String::Param> Args;
         static constexpr char name[] = "loadPluginClass";
@@ -1670,35 +1689,16 @@ 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[] =