Peter E Conn | fc68648 | 2025-03-21 15:42:51 | [diff] [blame] | 1 | # How WebView Full Screen Works |
| 2 | |
| 3 | [TOC] |
| 4 | |
| 5 | Note: In this doc, `WebView` in code font means the `WebView` class that's a |
| 6 | subclass of `View`, whereas WebView in normal font means the WebView product in |
| 7 | general. |
| 8 | |
| 9 | ## How apps use full screen |
| 10 | |
| 11 | You can see an example of how to support WebView full screen in your application |
| 12 | in [the WebView |
| 13 | shell](https://source.chromium.org/chromium/chromium/src/+/main:android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserFragment.java;l=500;drc=cfb4fb0b1658a915db8c470751f511bb8360a9bc). |
| 14 | The app overrides `WebChromeClient#onShowCustomView` to do something like: |
| 15 | |
| 16 | ```java |
| 17 | @Override |
| 18 | public void onShowCustomView( |
| 19 | View view, WebChromeClient.CustomViewCallback callback) { |
| 20 | if (mFullscreenView != null) { |
| 21 | ((ViewGroup) mFullscreenView.getParent()).removeView(mFullscreenView); |
| 22 | } |
| 23 | mFullscreenView = view; |
| 24 | requireActivity() |
| 25 | .getWindow() |
| 26 | .addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); |
| 27 | requireActivity() |
| 28 | .getWindow() |
| 29 | .addContentView( |
| 30 | mFullscreenView, |
| 31 | new FrameLayout.LayoutParams( |
| 32 | ViewGroup.LayoutParams.MATCH_PARENT, |
| 33 | ViewGroup.LayoutParams.MATCH_PARENT, |
| 34 | Gravity.CENTER)); |
| 35 | } |
| 36 | ``` |
| 37 | |
| 38 | Essentially, when the web contents requests to go full screen, WebView creates a |
| 39 | `View` (a |
| 40 | [FullScreenView](https://source.chromium.org/chromium/chromium/src/+/main:android_webview/java/src/org/chromium/android_webview/FullScreenView.java)) |
| 41 | and hands it to the app to attach to the window. Usually the existing `WebView` |
| 42 | remains attached to the window, but obscured by the full screen `View`. |
| 43 | |
| 44 | When the user exits full screen through the web contents, or through invoking |
| 45 | the callback, `onHideCustomView` is called, where the app should remove |
| 46 | `mFullscreenView` from the window. |
| 47 | |
| 48 | ```java |
| 49 | @Override |
| 50 | public void onHideCustomView() { |
| 51 | if (mFullscreenView == null) { |
| 52 | return; |
| 53 | } |
| 54 | requireActivity() |
| 55 | .getWindow() |
| 56 | .clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); |
| 57 | ((ViewGroup) mFullscreenView.getParent()).removeView(mFullscreenView); |
| 58 | mFullscreenView = null; |
| 59 | } |
| 60 | ``` |
| 61 | |
| 62 | Of all the apps that override `WebChromeClient`, ~85% of them override |
| 63 | `onShowCustomView`. If an app does not, WebView reports to the web page that |
| 64 | fullscreen mode is not supported. |
| 65 | |
| 66 | ## How this works |
| 67 | |
| 68 | Broadly speaking, the API on the `WebView` class can be split into two parts - |
| 69 | stuff to do with the web content (eg, `loadUrl`, `goBack`) and stuff to do with |
| 70 | integrating into the View hierarchy (eg, `onDraw`, `requestFocus`). Inside |
| 71 | `AwContents`, the View responsibilities are collected in the `AwViewMethods` |
| 72 | interface, so we end up with the following: |
| 73 | |
| 74 |  |
| 75 | |
| 76 | When full screen is triggered, the web page starts being drawn through |
| 77 | [FullScreenView](https://source.chromium.org/chromium/chromium/src/+/main:android_webview/java/src/org/chromium/android_webview/FullScreenView.java) |
| 78 | instead, so all the integration with the View hierarchy must come from there |
| 79 | instead. At the same time, we don’t want `AwViewMethods` being triggered by both |
| 80 | the `FullScreenView` and the `WebView`, so we attach the `WebView` to a |
| 81 | [NullAwViewMethods](https://source.chromium.org/chromium/chromium/src/+/main:android_webview/java/src/org/chromium/android_webview/NullAwViewMethods.java), |
| 82 | where everything is effectively a no-op. |
| 83 | |
| 84 |  |
| 85 | |
| 86 | This means that a call to `WebView#loadUrl` will trigger a page load, but it |
| 87 | will still be drawn through the `FullScreenView`. |
| 88 | |
| 89 | ## What you need to know |
| 90 | |
| 91 | The main thing you need to keep in mind is that `AwContents` can be moved |
| 92 | between Views (between `WebView` and `FullScreenView`). |
| 93 | |
| 94 | Currently, the code is a little confused, with |
| 95 | [AwContents#onAttachedToWindow](https://source.chromium.org/chromium/chromium/src/+/main:android_webview/java/src/org/chromium/android_webview/AwContents.java;l=3366;drc=71a590f3fd3c3c34f1fd48bbaf9e5357f2df4832) |
| 96 | tying features (such as the window coverage tracker, the display cutout |
| 97 | controller, the frame metrics listener) to the `WebView` that should be moved |
| 98 | over to the `FullScreenView` when it’s used, but aren’t. These should get fixed |
| 99 | soon. |