1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.example.androidx.webkit;
17 
18 import static androidx.webkit.WebViewAssetLoader.AssetsPathHandler;
19 
20 import android.annotation.SuppressLint;
21 import android.app.Activity;
22 import android.net.Uri;
23 import android.os.Bundle;
24 import android.view.View;
25 import android.webkit.WebResourceRequest;
26 import android.webkit.WebResourceResponse;
27 import android.webkit.WebView;
28 import android.webkit.WebViewClient;
29 import android.widget.Button;
30 
31 import androidx.appcompat.app.AppCompatActivity;
32 import androidx.webkit.JavaScriptReplyProxy;
33 import androidx.webkit.WebMessageCompat;
34 import androidx.webkit.WebViewAssetLoader;
35 import androidx.webkit.WebViewCompat;
36 import androidx.webkit.WebViewFeature;
37 
38 import org.jspecify.annotations.NonNull;
39 import org.jspecify.annotations.Nullable;
40 
41 import java.util.Collections;
42 import java.util.HashSet;
43 
44 /**
45  * An {@link Activity} to exercise
46  * {@link WebViewCompat#addDocumentStartJavaScript(WebView, String, java.util.Set)} related
47  * functionality.
48  */
49 public class DocumentStartJavaScriptActivity extends AppCompatActivity {
50     private final Uri mExampleUri = new Uri.Builder()
51                                             .scheme("https")
52                                             .authority("example.com")
53                                             .appendPath("androidx_webkit")
54                                             .appendPath("example")
55                                             .appendPath("assets")
56                                             .build();
57 
58     private static class MyWebViewClient extends WebViewClient {
59         private final WebViewAssetLoader mAssetLoader;
60 
MyWebViewClient(WebViewAssetLoader loader)61         MyWebViewClient(WebViewAssetLoader loader) {
62             mAssetLoader = loader;
63         }
64 
65         @Override
shouldInterceptRequest(WebView view, WebResourceRequest request)66         public WebResourceResponse shouldInterceptRequest(WebView view,
67                                             WebResourceRequest request) {
68             return mAssetLoader.shouldInterceptRequest(request.getUrl());
69         }
70 
71         @Override
72         @SuppressWarnings("deprecation") // use the old one for compatibility with all API levels.
shouldInterceptRequest(WebView view, String url)73         public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
74             return mAssetLoader.shouldInterceptRequest(Uri.parse(url));
75         }
76     }
77 
78     private static class ReplyMessageListener implements WebViewCompat.WebMessageListener {
79         private JavaScriptReplyProxy mReplyProxy;
80 
ReplyMessageListener(Button button)81         ReplyMessageListener(Button button) {
82             button.setOnClickListener((View v) -> {
83                 if (mReplyProxy == null) return;
84                 mReplyProxy.postMessage("ReplyProxy button clicked.");
85             });
86         }
87 
88         @Override
onPostMessage(@onNull WebView view, WebMessageCompat message, @NonNull Uri sourceOrigin, boolean isMainFrame, @NonNull JavaScriptReplyProxy replyProxy)89         public void onPostMessage(@NonNull WebView view, WebMessageCompat message,
90                 @NonNull Uri sourceOrigin,
91                 boolean isMainFrame, @NonNull JavaScriptReplyProxy replyProxy) {
92             if ("initialization".equals(message.getData())) {
93                 mReplyProxy = replyProxy;
94             }
95         }
96     }
97 
98     @SuppressLint("SetJavascriptEnabled")
99     @Override
onCreate(@ullable Bundle savedInstanceState)100     protected void onCreate(@Nullable Bundle savedInstanceState) {
101         super.onCreate(savedInstanceState);
102         setContentView(R.layout.activity_document_start_javascript);
103         setTitle(R.string.document_start_javascript_activity_title);
104         WebkitHelpers.appendWebViewVersionToTitle(this);
105 
106         if (!WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
107             WebkitHelpers.showMessageInActivity(
108                     DocumentStartJavaScriptActivity.this, R.string.webkit_api_not_available);
109             return;
110         }
111 
112         // Use WebViewAssetLoader to load html page from app's assets.
113         WebViewAssetLoader assetLoader =
114                 new WebViewAssetLoader.Builder()
115                         .setDomain("example.com")
116                         .addPathHandler(mExampleUri.getPath() + "/", new AssetsPathHandler(this))
117                         .build();
118 
119         Button replyProxyButton = findViewById(R.id.button_reply_proxy);
120 
121         WebView webView = findViewById(R.id.webview);
122         webView.setWebViewClient(new MyWebViewClient(assetLoader));
123         webView.getSettings().setJavaScriptEnabled(true);
124 
125         HashSet<String> allowedOriginRules = new HashSet<>(
126                 Collections.singletonList("https://example.com"));
127         // Add WebMessageListeners.
128         WebViewCompat.addWebMessageListener(webView, "replyObject", allowedOriginRules,
129                 new ReplyMessageListener(replyProxyButton));
130         final String jsCode = "replyObject.onmessage = function(event) {"
131                 + "    document.getElementById('result').innerHTML = event.data;"
132                 + "};"
133                 + "replyObject.postMessage('initialization');";
134         WebViewCompat.addDocumentStartJavaScript(webView, jsCode, allowedOriginRules);
135         webView.loadUrl(
136                 Uri.withAppendedPath(mExampleUri, "www/document_start_javascript.html").toString());
137     }
138 }
139