Shimi Zhang | aa4a1a4eb | 2020-03-21 01:20:14 | [diff] [blame] | 1 | # How does [`WebChromeClient#onCreateWindow`](https://developer.android.com/reference/android/webkit/WebChromeClient#onCreateWindow(android.webkit.WebView,%20boolean,%20boolean,%20android.os.Message)) work? |
| 2 | |
| 3 | [TOC] |
| 4 | |
| 5 | ## Summary |
| 6 | |
| 7 | This is a technical explanation of how `onCreateWindow` and the related API are |
| 8 | implemented from content layer APIs. |
| 9 | |
| 10 | ## Example usage |
| 11 | |
| 12 | Let's look at example code snippets first to see how an app could use these API: |
| 13 | |
| 14 | On the app side (in Java): |
| 15 | |
| 16 | ```java |
| 17 | // Configure parent WebView. |
| 18 | WebView webView = ...; |
| 19 | webView.getSettings().setJavaScriptEnabled(true); |
| 20 | webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); |
| 21 | webView.getSettings().setSupportMultipleWindows(true); |
| 22 | |
| 23 | webView.setWebChromeClient(new WebChromeClient() { |
| 24 | @Override |
| 25 | public boolean onCreateWindow( |
| 26 | WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { |
| 27 | // Create child WebView. It is better to not reuse an existing WebView. |
| 28 | WebView childWebView = ...; |
| 29 | |
| 30 | WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; |
| 31 | transport.setWebView(childWebView); |
| 32 | resultMsg.sentToTarget(); |
| 33 | return true; |
| 34 | } |
| 35 | }); |
| 36 | |
| 37 | webView.loadUrl(...); |
| 38 | ``` |
| 39 | |
| 40 | On the web page side (in JavaScript): |
| 41 | |
| 42 | ```javascript |
| 43 | window.open("www.example.com"); |
| 44 | ``` |
| 45 | |
| 46 | ## What happened under the hood |
| 47 | |
| 48 | 1. When the parent WebView loads the web page and runs the JavaScript snippet, |
Nate Fischer | 8924a23f | 2021-05-04 00:05:25 | [diff] [blame] | 49 | [`AwWebContentsDelegate::AddNewContents`](https://source.chromium.org/chromium/chromium/src/+/main:android_webview/browser/aw_web_contents_delegate.h;l=43;drc=3abb32da2944ffe178dd66f404e7e1bb88a58ed0) |
Shimi Zhang | aa4a1a4eb | 2020-03-21 01:20:14 | [diff] [blame] | 50 | will be called. The corresponding Java side |
Nate Fischer | 8924a23f | 2021-05-04 00:05:25 | [diff] [blame] | 51 | [`AwWebContentsDelegate#addNewContents`](https://source.chromium.org/chromium/chromium/src/+/main:android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegate.java;l=30;drc=a19051603849d7810b3569daf158aceb23aad1da) |
Shimi Zhang | aa4a1a4eb | 2020-03-21 01:20:14 | [diff] [blame] | 52 | is called from the native. |
| 53 | |
| 54 | 1. At the same time, |
Nate Fischer | 8924a23f | 2021-05-04 00:05:25 | [diff] [blame] | 55 | [`AwContents::SetPendingWebContentsForPopup`](https://source.chromium.org/chromium/chromium/src/+/main:android_webview/browser/aw_contents.cc;l=1099;drc=7776bbb38c4e394b5be085bc8c5bc02df5fa22dc) |
Shimi Zhang | aa4a1a4eb | 2020-03-21 01:20:14 | [diff] [blame] | 56 | creates native popup AwContents with the given `WebContents` and stores it as |
| 57 | `pending_contents_` in the parent `AwContents` object without Java |
| 58 | counterpart created. Note that since `pending_contents_` can only store one |
| 59 | popup AwContents, WebView doesn't support multiple pending popups. |
| 60 | |
| 61 | 1. `WebChromeClient#onCreateWindow` is called from step 1, with the code snippet |
| 62 | above, `childWebView` is set to the `WebViewTransport` and |
| 63 | `resultMsg.sendToTarget()` will send the `childWebView` to its receiver. |
| 64 | |
| 65 | 1. `WebViewContentsClientAdapter` has a handler that receives the message sent |
| 66 | from `resultMsg.sendToTarget()`. It will trigger |
Nate Fischer | 8924a23f | 2021-05-04 00:05:25 | [diff] [blame] | 67 | [`WebViewChromium#completeWindowCreation`](https://source.chromium.org/chromium/chromium/src/+/main:android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java;l=265;drc=da3bb54157d4603b9c820d6cfdf61859f804dfb2), |
Shimi Zhang | aa4a1a4eb | 2020-03-21 01:20:14 | [diff] [blame] | 68 | then |
Nate Fischer | 8924a23f | 2021-05-04 00:05:25 | [diff] [blame] | 69 | [`AwContents#supplyContentsForPopup`](https://source.chromium.org/chromium/chromium/src/+/main:android_webview/java/src/org/chromium/android_webview/AwContents.java;l=1455;drc=4afe92995db1279895f8a40b69c374bc298d750f) |
Shimi Zhang | aa4a1a4eb | 2020-03-21 01:20:14 | [diff] [blame] | 70 | is called on the parent WebView/AwContents. |
| 71 | |
| 72 | 1. `AwContents#supplyContentsForPopup` calls |
Nate Fischer | 8924a23f | 2021-05-04 00:05:25 | [diff] [blame] | 73 | [`AwContents#receivePopupContents`](https://source.chromium.org/chromium/chromium/src/+/main:android_webview/java/src/org/chromium/android_webview/AwContents.java;l=1475;drc=4afe92995db1279895f8a40b69c374bc298d750f) |
Shimi Zhang | aa4a1a4eb | 2020-03-21 01:20:14 | [diff] [blame] | 74 | on the child WebView/AwContents. Child AwContents deletes the existing native |
| 75 | AwContents from the child WebView/AwContents, and pairs it with the |
| 76 | `pending_contents_` from the parent WebView/AwContents. In order to preserve |
| 77 | the status of the child WebView, all the flags and configurations need to be |
| 78 | re-applied to the `pending_contents_`. Loading on the native AwContents is |
| 79 | also resumed. |