1 /*
2  * Copyright 2022 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 
17 package com.example.androidx.webkit;
18 
19 import android.net.Uri;
20 import android.os.Bundle;
21 import android.webkit.WebSettings;
22 import android.webkit.WebView;
23 import android.widget.RadioGroup;
24 
25 import androidx.appcompat.app.AppCompatActivity;
26 import androidx.webkit.WebSettingsCompat;
27 import androidx.webkit.WebViewFeature;
28 
29 import org.jspecify.annotations.NonNull;
30 import org.jspecify.annotations.Nullable;
31 
32 import java.util.Collections;
33 import java.util.Set;
34 
35 /**
36  * Demo activity to show how to set an allow-list to preserve the legacy behavior for the {@code
37  * X-Requested-With} header.
38  */
39 public class RequestedWithHeaderActivity  extends AppCompatActivity {
40 
41     private HttpServer mServer;
42     private WebView mWebView;
43 
44     @Override
onCreate(@ullable Bundle savedInstanceState)45     protected void onCreate(@Nullable Bundle savedInstanceState) {
46         super.onCreate(savedInstanceState);
47         setContentView(R.layout.activity_requested_with_header);
48 
49         setTitle(R.string.requested_with_activity_title);
50         WebkitHelpers.appendWebViewVersionToTitle(this);
51 
52         // Check if allow-list feature is enabled
53         if (!WebViewFeature.isFeatureSupported(WebViewFeature.REQUESTED_WITH_HEADER_ALLOW_LIST)) {
54             WebkitHelpers.showMessageInActivity(this, R.string.webkit_api_not_available);
55             return;
56         }
57 
58         // Initialize http server to show us what we requested
59         mServer = new HttpServer(/*port=*/0, HttpServer.EchoRequestHandler::new, null);
60         mServer.start();
61 
62         // Set up our WebView
63         mWebView = findViewById(R.id.requested_with_header_webview);
64         // Disable caching to always get fresh requests to the test server
65         mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
66 
67         // Get the current allow-list to initialize the UI
68         Set<String> allowList = WebSettingsCompat.getRequestedWithHeaderOriginAllowList(
69                 mWebView.getSettings());
70 
71         // Wire up the UI
72         RadioGroup radioGroup = findViewById(R.id.requested_with_header_radio_group);
73         // Check the radio button corresponding to the current setting
74         radioGroup.check(allowList.isEmpty()
75                         ? R.id.requested_with_header_empty_mode
76                         : R.id.requested_with_header_allowlist_mode);
77         // Register a change handler
78         radioGroup.setOnCheckedChangeListener(this::onRadioGroupChanged);
79 
80         // Send a request to our echo server to show the headers
81         refreshView(!allowList.isEmpty());
82     }
83 
refreshView(boolean useAllowList)84     private void refreshView(boolean useAllowList) {
85         if (useAllowList) {
86             WebSettingsCompat.setRequestedWithHeaderOriginAllowList(mWebView.getSettings(),
87                     Collections.singleton("http://localhost:" + mServer.getPort()));
88         } else {
89             WebSettingsCompat.setRequestedWithHeaderOriginAllowList(mWebView.getSettings(),
90                     Collections.emptySet());
91         }
92         String requestUrl = new Uri.Builder()
93                 .scheme("http")
94                 .authority("localhost:" + mServer.getPort())
95                 .build()
96                 .toString();
97         mWebView.loadUrl(requestUrl);
98     }
99 
100     /**
101      * Handler for selecting a new header mode through the radio group.
102      * @param unused Triggering radio group
103      * @param checkedId ID of checked radio button
104      */
onRadioGroupChanged(@onNull RadioGroup unused, int checkedId)105     public void onRadioGroupChanged(@NonNull RadioGroup unused, int checkedId) {
106         refreshView(checkedId == R.id.requested_with_header_allowlist_mode);
107     }
108 
109     @Override
onDestroy()110     protected void onDestroy() {
111         if (mServer != null) {
112             mServer.shutdown();
113         }
114         super.onDestroy();
115     }
116 }
117