Bug 1368701 - [1.2] Add shutdown intent action to shutdown Fennec. r=snorp,bc a=jcristau
authorEugen Sawin <esawin@mozilla.com>
Tue, 06 Jun 2017 18:17:10 +0200
changeset 414263 ce3be5cd53c8b65670c5f8620adbfd011dd3a286
parent 414262 7d7f50458f9c9bf1d577ff158fc3e59fd4e38b34
child 414264 dc3b1b9d449361ee8e395cab6a8193605764bc96
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp, bc, jcristau
bugs1368701
milestone55.0
Bug 1368701 - [1.2] Add shutdown intent action to shutdown Fennec. r=snorp,bc a=jcristau
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
widget/android/GeneratedJNINatives.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
widget/android/nsAppShell.cpp
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -141,16 +141,17 @@ public abstract class GeckoApp extends G
     public static final String ACTION_ALERT_CALLBACK       = "org.mozilla.gecko.ALERT_CALLBACK";
     public static final String ACTION_HOMESCREEN_SHORTCUT  = "org.mozilla.gecko.BOOKMARK";
     public static final String ACTION_WEBAPP               = "org.mozilla.gecko.WEBAPP";
     public static final String ACTION_DEBUG                = "org.mozilla.gecko.DEBUG";
     public static final String ACTION_LAUNCH_SETTINGS      = "org.mozilla.gecko.SETTINGS";
     public static final String ACTION_LOAD                 = "org.mozilla.gecko.LOAD";
     public static final String ACTION_INIT_PW              = "org.mozilla.gecko.INIT_PW";
     public static final String ACTION_SWITCH_TAB           = "org.mozilla.gecko.SWITCH_TAB";
+    public static final String ACTION_SHUTDOWN             = "org.mozilla.gecko.SHUTDOWN";
 
     public static final String INTENT_REGISTER_STUMBLER_LISTENER = "org.mozilla.gecko.STUMBLER_REGISTER_LOCAL_LISTENER";
 
     public static final String EXTRA_STATE_BUNDLE          = "stateBundle";
 
     protected static final String LAST_SELECTED_TAB        = "lastSelectedTab";
     protected static final String LAST_SESSION_UUID        = "lastSessionUUID";
     protected static final String STARTUP_SELECTED_TAB     = "restoredSelectedTab";
@@ -2113,16 +2114,22 @@ public abstract class GeckoApp extends G
                                .build());
     }
 
     @Override
     protected void onNewIntent(Intent externalIntent) {
         final SafeIntent intent = new SafeIntent(externalIntent);
         final String action = intent.getAction();
 
+        if (ACTION_SHUTDOWN.equals(action)) {
+            mShutdownOnDestroy = true;
+            GeckoThread.forceQuit();
+            return;
+        }
+
         final boolean isFirstTab = !mWasFirstTabShownAfterActivityUnhidden;
         mWasFirstTabShownAfterActivityUnhidden = true; // Reset since we'll be loading a tab.
         if (!Intent.ACTION_MAIN.equals(action)) {
             mIgnoreLastSelectedTab = true;
         }
 
         // if we were previously OOM killed, we can end up here when launching
         // from external shortcuts, so set this as the intent for initialization
--- a/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
@@ -52,16 +52,18 @@ public class LauncherActivity extends Ac
         GeckoAppShell.ensureCrashHandling();
 
         final SafeIntent safeIntent = new SafeIntent(getIntent());
 
         // Is this deep link?
         if (isDeepLink(safeIntent)) {
             dispatchDeepLink(safeIntent);
 
+        } else if (isShutdownIntent(safeIntent)) {
+            dispatchShutdownIntent();
         // Is this web app?
         } else if (isWebAppIntent(safeIntent)) {
             dispatchWebAppIntent();
 
         // If it's not a view intent, it won't be a custom tabs intent either. Just launch!
         } else if (!isViewIntentWithURL(safeIntent)) {
             dispatchNormalIntent();
 
@@ -79,16 +81,23 @@ public class LauncherActivity extends Ac
         // Dispatch this VIEW action intent to the browser.
         } else {
             dispatchNormalIntent();
         }
 
         finish();
     }
 
+    private void dispatchShutdownIntent() {
+        Intent intent = new Intent(getIntent());
+        intent.setClassName(getApplicationContext(), AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        startActivity(intent);
+    }
+
     /**
      * Launch tab queue service to display overlay.
      */
     private void dispatchTabQueueIntent() {
         Intent intent = new Intent(getIntent());
         intent.setClass(getApplicationContext(), TabQueueService.class);
         startService(intent);
     }
@@ -157,16 +166,20 @@ public class LauncherActivity extends Ac
     private static boolean isWebAppIntent(@NonNull final SafeIntent safeIntent) {
         return GeckoApp.ACTION_WEBAPP.equals(safeIntent.getAction());
     }
 
     private boolean isCustomTabsEnabled() {
         return GeckoSharedPrefs.forApp(this).getBoolean(GeckoPreferences.PREFS_CUSTOM_TABS, false);
     }
 
+    private static boolean isShutdownIntent(@NonNull final SafeIntent safeIntent) {
+        return GeckoApp.ACTION_SHUTDOWN.equals(safeIntent.getAction());
+    }
+
     private boolean isDeepLink(SafeIntent intent) {
         if (intent == null || intent.getData() == null || intent.getData().getScheme() == null
                 || intent.getAction() == null) {
             return false;
         }
         boolean schemeMatched = intent.getData().getScheme().equalsIgnoreCase(DEEP_LINK_SCHEME);
         boolean actionMatched = intent.getAction().equals(Intent.ACTION_VIEW);
         return schemeMatched && actionMatched;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
@@ -541,16 +541,19 @@ public class GeckoThread extends Thread 
     }
 
     // Implemented in mozglue/android/APKOpen.cpp.
     /* package */ static native void registerUiThread();
 
     @WrapForJNI(calledFrom = "ui")
     /* package */ static native long runUiThreadCallback();
 
+    @WrapForJNI(dispatchTo = "gecko")
+    public static native void forceQuit();
+
     @WrapForJNI
     private static void requestUiThreadCallback(long delay) {
         ThreadUtils.getUiHandler().postDelayed(UI_THREAD_CALLBACK, delay);
     }
 
     /**
      * Queue a call to the given static method until Gecko is in the RUNNING state.
      */
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -215,22 +215,26 @@ const JNINativeMethod GeckoScreenOrienta
             mozilla::jni::NativeStub<GeckoScreenOrientation::OnOrientationChange_t, Impl>
             ::template Wrap<&Impl::OnOrientationChange>)
 };
 
 template<class Impl>
 class GeckoThread::Natives : public mozilla::jni::NativeImpl<GeckoThread, Impl>
 {
 public:
-    static const JNINativeMethod methods[6];
+    static const JNINativeMethod methods[7];
 };
 
 template<class Impl>
 const JNINativeMethod GeckoThread::Natives<Impl>::methods[] = {
 
+    mozilla::jni::MakeNativeMethod<GeckoThread::ForceQuit_t>(
+            mozilla::jni::NativeStub<GeckoThread::ForceQuit_t, Impl>
+            ::template Wrap<&Impl::ForceQuit>),
+
     mozilla::jni::MakeNativeMethod<GeckoThread::CreateServices_t>(
             mozilla::jni::NativeStub<GeckoThread::CreateServices_t, Impl>
             ::template Wrap<&Impl::CreateServices>),
 
     mozilla::jni::MakeNativeMethod<GeckoThread::OnPause_t>(
             mozilla::jni::NativeStub<GeckoThread::OnPause_t, Impl>
             ::template Wrap<&Impl::OnPause>),
 
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -730,16 +730,19 @@ const char GeckoThread::name[] =
 constexpr char GeckoThread::CheckAndSetState_t::name[];
 constexpr char GeckoThread::CheckAndSetState_t::signature[];
 
 auto GeckoThread::CheckAndSetState(mozilla::jni::Object::Param a0, mozilla::jni::Object::Param a1) -> bool
 {
     return mozilla::jni::Method<CheckAndSetState_t>::Call(GeckoThread::Context(), nullptr, a0, a1);
 }
 
+constexpr char GeckoThread::ForceQuit_t::name[];
+constexpr char GeckoThread::ForceQuit_t::signature[];
+
 constexpr char GeckoThread::IsChildProcess_t::name[];
 constexpr char GeckoThread::IsChildProcess_t::signature[];
 
 auto GeckoThread::IsChildProcess() -> bool
 {
     return mozilla::jni::Method<IsChildProcess_t>::Call(GeckoThread::Context(), nullptr);
 }
 
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -2241,16 +2241,33 @@ public:
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::GECKO;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
     static auto CheckAndSetState(mozilla::jni::Object::Param, mozilla::jni::Object::Param) -> bool;
 
+    struct ForceQuit_t {
+        typedef GeckoThread Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "forceQuit";
+        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::GECKO;
+    };
+
     struct IsChildProcess_t {
         typedef GeckoThread Owner;
         typedef bool ReturnType;
         typedef bool SetterType;
         typedef mozilla::jni::Args<> Args;
         static constexpr char name[] = "isChildProcess";
         static constexpr char signature[] =
                 "()Z";
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -237,16 +237,27 @@ public:
                 category.get(),
                 aData ? aData->ToString().get() : nullptr);
     }
 
     static int64_t RunUiThreadCallback()
     {
         return RunAndroidUiTasks();
     }
+
+    static void ForceQuit()
+    {
+        nsCOMPtr<nsIAppStartup> appStartup =
+            do_GetService(NS_APPSTARTUP_CONTRACTID);
+
+        if (appStartup) {
+            appStartup->Quit(nsIAppStartup::eForceQuit);
+        }
+
+    }
 };
 
 int32_t GeckoThreadSupport::sPauseCount;
 
 
 class GeckoAppShellSupport final
     : public java::GeckoAppShell::Natives<GeckoAppShellSupport>
 {