Commit ae5cf783 authored by Hazem Ashmawy's avatar Hazem Ashmawy Committed by Commit Bot

AW: Expose WebView developer UI in WebView shell browser menu

Add menu option in WebView shell browser to launch developer UI from the
current WebView package.

Add intent-filter to WebView developer UI MainActivity with a new action
"com.android.webview.SHOW_DEV_UI".

Fixed: 1014827
Test: Manually inspect and launch dev ui from shell browser menu
Test: Build and install SystemWebView and Monochrome switch providers and verify the right UI is launched
Test: Build and install tip-of-the-tree SystemWebView and verify that a failure toast is shown
Change-Id: Ia00ae05118e2974ac1e6ddd3b95c70717da862a2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1893864
Auto-Submit: Hazem Ashmawy <hazems@chromium.org>
Reviewed-by: default avatarRichard Coles <torne@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Reviewed-by: default avatarNate Fischer <ntfschr@chromium.org>
Commit-Queue: Hazem Ashmawy <hazems@chromium.org>
Cr-Commit-Position: refs/heads/master@{#712313}
parent 4fcabb81
...@@ -36,6 +36,10 @@ ...@@ -36,6 +36,10 @@
android:theme="@android:style/Theme.DeviceDefault" android:theme="@android:style/Theme.DeviceDefault"
android:launchMode="singleTask" android:launchMode="singleTask"
android:process=":webview_apk"> {# Explicit process required for monochrome compatibility. #} android:process=":webview_apk"> {# Explicit process required for monochrome compatibility. #}
<intent-filter>
<action android:name="com.android.webview.SHOW_DEV_UI" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity> </activity>
<activity android:name="org.chromium.android_webview.devui.CrashesListActivity" <activity android:name="org.chromium.android_webview.devui.CrashesListActivity"
android:label="WebView Crashes" android:label="WebView Crashes"
......
...@@ -29,6 +29,7 @@ android_apk("system_webview_shell_apk") { ...@@ -29,6 +29,7 @@ android_apk("system_webview_shell_apk") {
"apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java", "apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java",
"apk/src/org/chromium/webview_shell/WebViewCreateDestroyActivity.java", "apk/src/org/chromium/webview_shell/WebViewCreateDestroyActivity.java",
"apk/src/org/chromium/webview_shell/WebViewLayoutTestActivity.java", "apk/src/org/chromium/webview_shell/WebViewLayoutTestActivity.java",
"apk/src/org/chromium/webview_shell/WebViewPackageHelper.java",
"apk/src/org/chromium/webview_shell/WebViewThreadTestActivity.java", "apk/src/org/chromium/webview_shell/WebViewThreadTestActivity.java",
"apk/src/org/chromium/webview_shell/WebViewTracingActivity.java", "apk/src/org/chromium/webview_shell/WebViewTracingActivity.java",
] ]
......
...@@ -17,4 +17,6 @@ ...@@ -17,4 +17,6 @@
android:title="@string/menu_start_animation_activity"/> android:title="@string/menu_start_animation_activity"/>
<item android:id="@+id/menu_about" <item android:id="@+id/menu_about"
android:title="@string/menu_about"/> android:title="@string/menu_about"/>
<item android:id="@+id/menu_devui"
android:title="@string/menu_devui"/>
</menu> </menu>
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
<string name="menu_start_animation_activity">Animation test</string> <string name="menu_start_animation_activity">Animation test</string>
<string name="menu_print">Print</string> <string name="menu_print">Print</string>
<string name="menu_about">About WebView</string> <string name="menu_about">About WebView</string>
<string name="menu_devui">WebView DevTools</string>
<string name="load_url">Load URL</string> <string name="load_url">Load URL</string>
<!-- activity_webview_animation_test strings --> <!-- activity_webview_animation_test strings -->
......
...@@ -13,6 +13,7 @@ import android.content.ActivityNotFoundException; ...@@ -13,6 +13,7 @@ import android.content.ActivityNotFoundException;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.graphics.Bitmap; import android.graphics.Bitmap;
...@@ -579,6 +580,9 @@ public class WebViewBrowserActivity extends AppCompatActivity { ...@@ -579,6 +580,9 @@ public class WebViewBrowserActivity extends AppCompatActivity {
about(); about();
hideKeyboard(mUrlBar); hideKeyboard(mUrlBar);
return true; return true;
case R.id.menu_devui:
launchWebViewDevUI();
return true;
default: default:
break; break;
} }
...@@ -635,6 +639,31 @@ public class WebViewBrowserActivity extends AppCompatActivity { ...@@ -635,6 +639,31 @@ public class WebViewBrowserActivity extends AppCompatActivity {
dialog.getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); dialog.getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
} }
private void launchWebViewDevUI() {
PackageInfo currentWebViewPackage = WebViewPackageHelper.getCurrentWebViewPackage(this);
if (currentWebViewPackage == null) {
Log.e(TAG, "Couldn't find current WebView package");
Toast.makeText(this, "WebView package isn't found", Toast.LENGTH_LONG).show();
return;
}
String currentWebViewPackageName = currentWebViewPackage.packageName;
Intent intent = new Intent("com.android.webview.SHOW_DEV_UI");
intent.setPackage(currentWebViewPackageName);
// Check if the intent is resolved, i.e current WebView package has a developer UI that
// responds to "com.android.webview.SHOW_DEV_UI" action.
List<ResolveInfo> intentResolveInfo = getPackageManager().queryIntentActivities(intent, 0);
if (intentResolveInfo.size() > 0) {
startActivity(intent);
} else {
Log.e(TAG,
"Couldn't launch developer UI from current WebView package: "
+ currentWebViewPackage);
Toast.makeText(this, "No DevTools in " + currentWebViewPackageName, Toast.LENGTH_LONG)
.show();
}
}
// Returns true is a method has no arguments and returns either a boolean or a String. // Returns true is a method has no arguments and returns either a boolean or a String.
private boolean methodIsSimpleInspector(Method method) { private boolean methodIsSimpleInspector(Method method) {
Class<?> returnType = method.getReturnType(); Class<?> returnType = method.getReturnType();
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.webview_shell;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.webkit.WebView;
import java.lang.reflect.InvocationTargetException;
/**
* A helper class to get info about WebView package.
*/
// TODO(crbug.com/1020024) use androidx.webkit.WebViewCompat#getCurrentWebViewPackage and remove
// this class.
public final class WebViewPackageHelper {
/**
* If WebView has already been loaded into the current process this method will return the
* package that was used to load it. Otherwise, the package that would be used if the WebView
* was loaded right now will be returned; this does not cause WebView to be loaded, so this
* information may become outdated at any time.
* The WebView package changes either when the current WebView package is updated, disabled, or
* uninstalled. It can also be changed through a Developer Setting.
* If the WebView package changes, any app process that has loaded WebView will be killed. The
* next time the app starts and loads WebView it will use the new WebView package instead.
* @return the current WebView package, or {@code null} if there is none.
*/
// This method is copied from androidx.webkit.WebViewCompat.
public static PackageInfo getCurrentWebViewPackage(Context context) {
// There was no WebView Package before Lollipop, the WebView code was part of the framework
// back then.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return null;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return WebView.getCurrentWebViewPackage();
} else { // L-N
try {
PackageInfo loadedWebViewPackageInfo = getLoadedWebViewPackageInfo();
if (loadedWebViewPackageInfo != null) return loadedWebViewPackageInfo;
} catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException
| NoSuchMethodException e) {
return null;
}
// If WebViewFactory.getLoadedPackageInfo() returns null then WebView hasn't been loaded
// yet, in that case we need to fetch the name of the WebView package, and fetch the
// corresponding PackageInfo through the PackageManager
return getNotYetLoadedWebViewPackageInfo(context);
}
}
/**
* Return the PackageInfo of the currently loaded WebView APK. This method uses reflection and
* propagates any exceptions thrown, to the caller.
*/
// This method is copied from androidx.webkit.WebViewCompat.
@SuppressLint("PrivateApi")
private static PackageInfo getLoadedWebViewPackageInfo()
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
IllegalAccessException {
Class<?> webViewFactoryClass = Class.forName("android.webkit.WebViewFactory");
return (PackageInfo) webViewFactoryClass.getMethod("getLoadedPackageInfo").invoke(null);
}
/**
* Return the PackageInfo of the WebView APK that would have been used as WebView implementation
* if WebView was to be loaded right now.
*/
// This method is copied from androidx.webkit.WebViewCompat.
@SuppressLint("PrivateApi")
private static PackageInfo getNotYetLoadedWebViewPackageInfo(Context context) {
String webviewPackageName;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
Class<?> webViewFactoryClass = Class.forName("android.webkit.WebViewFactory");
webviewPackageName = (String) webViewFactoryClass.getMethod("getWebViewPackageName")
.invoke(null);
} else {
Class<?> webviewUpdateServiceClass =
Class.forName("android.webkit.WebViewUpdateService");
webviewPackageName =
(String) webviewUpdateServiceClass.getMethod("getCurrentWebViewPackageName")
.invoke(null);
}
} catch (ClassNotFoundException e) {
return null;
} catch (IllegalAccessException e) {
return null;
} catch (InvocationTargetException e) {
return null;
} catch (NoSuchMethodException e) {
return null;
}
if (webviewPackageName == null) return null;
PackageManager pm = context.getPackageManager();
try {
return pm.getPackageInfo(webviewPackageName, 0);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
// Do not instantiate this class.
private WebViewPackageHelper() {}
}
...@@ -1018,7 +1018,12 @@ ...@@ -1018,7 +1018,12 @@
android:launchMode="singleTask" android:launchMode="singleTask"
android:name="org.chromium.android_webview.devui.MainActivity" android:name="org.chromium.android_webview.devui.MainActivity"
android:process=":webview_apk" android:process=":webview_apk"
android:theme="@android:style/Theme.DeviceDefault"/> android:theme="@android:style/Theme.DeviceDefault">
<intent-filter>
<action android:name="com.android.webview.SHOW_DEV_UI"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity <activity
android:enabled="false" android:enabled="false"
android:excludeFromRecents="true" android:excludeFromRecents="true"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment