1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.content_public.browser;
6 
7 import androidx.annotation.Nullable;
8 import androidx.annotation.VisibleForTesting;
9 
10 import org.chromium.base.annotations.JNINamespace;
11 import org.chromium.base.annotations.NativeMethods;
12 import org.chromium.content_public.browser.navigation_controller.LoadURLType;
13 import org.chromium.content_public.browser.navigation_controller.UserAgentOverrideOption;
14 import org.chromium.content_public.common.Referrer;
15 import org.chromium.content_public.common.ResourceRequestBody;
16 import org.chromium.ui.base.PageTransition;
17 import org.chromium.url.Origin;
18 
19 import java.util.Locale;
20 import java.util.Map;
21 
22 /**
23  * Holds parameters for NavigationController.LoadUrl. Parameters should match
24  * counterparts in NavigationController::LoadURLParams, including default
25  * values.
26  */
27 @JNINamespace("content")
28 public class LoadUrlParams {
29     // Fields with counterparts in NavigationController::LoadURLParams.
30     // Package private so that NavigationController.loadUrl can pass them down to
31     // native code. Should not be accessed directly anywhere else outside of
32     // this class.
33     String mUrl;
34     Origin mInitiatorOrigin;
35     int mLoadUrlType;
36     int mTransitionType;
37     Referrer mReferrer;
38     private Map<String, String> mExtraHeaders;
39     private String mVerbatimHeaders;
40     int mUaOverrideOption;
41     ResourceRequestBody mPostData;
42     String mBaseUrlForDataUrl;
43     String mVirtualUrlForDataUrl;
44     String mDataUrlAsString;
45     boolean mCanLoadLocalResources;
46     boolean mIsRendererInitiated;
47     boolean mShouldReplaceCurrentEntry;
48     long mIntentReceivedTimestamp;
49     long mInputStartTimestamp;
50     boolean mHasUserGesture;
51     boolean mShouldClearHistoryList;
52 
53     /**
54      * Creates an instance with default page transition type.
55      * @param url the url to be loaded
56      */
LoadUrlParams(String url)57     public LoadUrlParams(String url) {
58         this(url, PageTransition.LINK);
59     }
60 
61     /**
62      * Creates an instance with the given page transition type.
63      * @param url the url to be loaded
64      * @param transitionType the PageTransitionType constant corresponding to the load
65      */
LoadUrlParams(String url, int transitionType)66     public LoadUrlParams(String url, int transitionType) {
67         mUrl = url;
68         mTransitionType = transitionType;
69 
70         // Initialize other fields to defaults matching defaults of the native
71         // NavigationController::LoadUrlParams.
72         mLoadUrlType = LoadURLType.DEFAULT;
73         mUaOverrideOption = UserAgentOverrideOption.INHERIT;
74         mPostData = null;
75         mBaseUrlForDataUrl = null;
76         mVirtualUrlForDataUrl = null;
77         mDataUrlAsString = null;
78     }
79 
80     /**
81      * Helper method to create a LoadUrlParams object for data url.
82      * @param data Data to be loaded.
83      * @param mimeType Mime type of the data.
84      * @param isBase64Encoded True if the data is encoded in Base 64 format.
85      */
createLoadDataParams( String data, String mimeType, boolean isBase64Encoded)86     public static LoadUrlParams createLoadDataParams(
87             String data, String mimeType, boolean isBase64Encoded) {
88         return createLoadDataParams(data, mimeType, isBase64Encoded, null);
89     }
90 
91     /**
92      * Helper method to create a LoadUrlParams object for data url.
93      * @param data Data to be loaded.
94      * @param mimeType Mime type of the data.
95      * @param isBase64Encoded True if the data is encoded in Base 64 format.
96      * @param charset The character set for the data. Pass null if the mime type
97      *                does not require a special charset.
98      */
createLoadDataParams( String data, String mimeType, boolean isBase64Encoded, String charset)99     public static LoadUrlParams createLoadDataParams(
100             String data, String mimeType, boolean isBase64Encoded, String charset) {
101         LoadUrlParams params = new LoadUrlParams(
102                 buildDataUri(data, mimeType, isBase64Encoded, charset));
103         params.setLoadType(LoadURLType.DATA);
104         params.setTransitionType(PageTransition.TYPED);
105         return params;
106     }
107 
buildDataUri( String data, String mimeType, boolean isBase64Encoded, String charset)108     private static String buildDataUri(
109             String data, String mimeType, boolean isBase64Encoded, String charset) {
110         StringBuilder dataUrl = new StringBuilder("data:");
111         dataUrl.append(mimeType);
112         if (charset != null && !charset.isEmpty()) {
113             dataUrl.append(";charset=" + charset);
114         }
115         if (isBase64Encoded) {
116             dataUrl.append(";base64");
117         }
118         dataUrl.append(",");
119         dataUrl.append(data);
120         return dataUrl.toString();
121     }
122 
123     /**
124      * Helper method to create a LoadUrlParams object for data url with base
125      * and virtual url.
126      * @param data Data to be loaded.
127      * @param mimeType Mime type of the data.
128      * @param isBase64Encoded True if the data is encoded in Base 64 format.
129      * @param baseUrl Base url of this data load. Note that for WebView compatibility,
130      *                baseUrl and historyUrl are ignored if this is a data: url.
131      *                Defaults to about:blank if null.
132      * @param historyUrl History url for this data load. Note that for WebView compatibility,
133      *                   this is ignored if baseUrl is a data: url. Defaults to about:blank
134      *                   if null.
135      */
createLoadDataParamsWithBaseUrl( String data, String mimeType, boolean isBase64Encoded, String baseUrl, String historyUrl)136     public static LoadUrlParams createLoadDataParamsWithBaseUrl(
137             String data, String mimeType, boolean isBase64Encoded,
138             String baseUrl, String historyUrl) {
139         return createLoadDataParamsWithBaseUrl(data, mimeType, isBase64Encoded,
140                 baseUrl, historyUrl, null);
141     }
142 
143     /**
144      * Helper method to create a LoadUrlParams object for data url with base
145      * and virtual url.
146      * @param data Data to be loaded.
147      * @param mimeType Mime type of the data.
148      * @param isBase64Encoded True if the data is encoded in Base 64 format.
149      * @param baseUrl Base url of this data load. Note that for WebView compatibility,
150      *                baseUrl and historyUrl are ignored if this is a data: url.
151      *                Defaults to about:blank if null.
152      * @param historyUrl History url for this data load. Note that for WebView compatibility,
153      *                   this is ignored if baseUrl is a data: url. Defaults to about:blank
154      *                   if null.
155      * @param charset The character set for the data. Pass null if the mime type
156      *                does not require a special charset.
157      */
createLoadDataParamsWithBaseUrl( String data, String mimeType, boolean isBase64Encoded, String baseUrl, String historyUrl, String charset)158     public static LoadUrlParams createLoadDataParamsWithBaseUrl(
159             String data, String mimeType, boolean isBase64Encoded,
160             String baseUrl, String historyUrl, String charset) {
161         LoadUrlParams params;
162         // For WebView compatibility, when the base URL has the 'data:'
163         // scheme, we treat it as a regular data URL load and skip setting
164         // baseUrl and historyUrl.
165         // TODO(joth): we should just append baseURL and historyURL here, and move the
166         // WebView specific transform up to a wrapper factory function in android_webview/.
167         if (baseUrl == null || !baseUrl.toLowerCase(Locale.US).startsWith("data:")) {
168             params = createLoadDataParams("", mimeType, isBase64Encoded, charset);
169             params.setBaseUrlForDataUrl(baseUrl != null ? baseUrl : "about:blank");
170             params.setVirtualUrlForDataUrl(historyUrl != null ? historyUrl : "about:blank");
171             params.setDataUrlAsString(buildDataUri(data, mimeType, isBase64Encoded, charset));
172         } else {
173             params = createLoadDataParams(data, mimeType, isBase64Encoded, charset);
174         }
175         return params;
176     }
177 
178     /**
179      * Helper method to create a LoadUrlParams object for an HTTP POST load.
180      * @param url URL of the load.
181      * @param postData Post data of the load. Can be null.
182      */
183     @VisibleForTesting
createLoadHttpPostParams( String url, byte[] postData)184     public static LoadUrlParams createLoadHttpPostParams(
185             String url, byte[] postData) {
186         LoadUrlParams params = new LoadUrlParams(url);
187         params.setLoadType(LoadURLType.HTTP_POST);
188         params.setTransitionType(PageTransition.TYPED);
189         params.setPostData(ResourceRequestBody.createFromBytes(postData));
190         return params;
191     }
192 
193     /**
194      * Sets the url.
195      */
setUrl(String url)196     public void setUrl(String url) {
197         mUrl = url;
198     }
199 
200     /**
201      * Return the url.
202      */
getUrl()203     public String getUrl() {
204         return mUrl;
205     }
206 
207     /**
208      * Sets the initiator origin.
209      */
setInitiatorOrigin(@ullable Origin initiatorOrigin)210     public void setInitiatorOrigin(@Nullable Origin initiatorOrigin) {
211         mInitiatorOrigin = initiatorOrigin;
212     }
213 
214     /**
215      * Return the initiator origin.
216      */
getInitiatorOrigin()217     public @Nullable Origin getInitiatorOrigin() {
218         return mInitiatorOrigin;
219     }
220 
221     /**
222      * Return the base url for a data url, otherwise null.
223      */
getBaseUrl()224     public String getBaseUrl() {
225         return mBaseUrlForDataUrl;
226     }
227 
228     /**
229      * Set load type of this load. Defaults to LoadURLType.DEFAULT.
230      * @param loadType One of LOAD_TYPE static constants above.
231      */
setLoadType(int loadType)232     public void setLoadType(int loadType) {
233         mLoadUrlType = loadType;
234     }
235 
236     /**
237      * Set transition type of this load. Defaults to PageTransition.LINK.
238      * @param transitionType One of PAGE_TRANSITION static constants in ContentView.
239      */
setTransitionType(int transitionType)240     public void setTransitionType(int transitionType) {
241         mTransitionType = transitionType;
242     }
243 
244     /**
245      * Return the transition type.
246      */
getTransitionType()247     public int getTransitionType() {
248         return mTransitionType;
249     }
250 
251     /**
252      * Sets the referrer of this load.
253      */
setReferrer(Referrer referrer)254     public void setReferrer(Referrer referrer) {
255         mReferrer = referrer;
256     }
257 
258     /**
259      * @return the referrer of this load.
260      */
getReferrer()261     public Referrer getReferrer() {
262         return mReferrer;
263     }
264 
265     /**
266      * Set extra headers for this load.
267      * @param extraHeaders Extra HTTP headers for this load. Note that these
268      *                     headers will never overwrite existing ones set by Chromium.
269      */
setExtraHeaders(Map<String, String> extraHeaders)270     public void setExtraHeaders(Map<String, String> extraHeaders) {
271         mExtraHeaders = extraHeaders;
272     }
273 
274     /**
275      * Return the extra headers as a map.
276      */
getExtraHeaders()277     public Map<String, String> getExtraHeaders() {
278         return mExtraHeaders;
279     }
280 
281     /**
282      * Return the extra headers as a single String separated by "\n", or null if no extra header is
283      * set. This form is suitable for passing to native
284      * NavigationController::LoadUrlParams::extra_headers. This will return the headers set in an
285      * exploded form through setExtraHeaders(). Embedders that work with extra headers in opaque
286      * collapsed form can use the setVerbatimHeaders() / getVerbatimHeaders() instead.
287      */
getExtraHeadersString()288     public String getExtraHeadersString() {
289         return getExtraHeadersString("\n", false);
290     }
291 
292     /**
293      * Return the extra headers as a single String separated by "\r\n", or null if no extra header
294      * is set. This form is suitable for passing to native
295      * net::HttpRequestHeaders::AddHeadersFromString.
296      */
getExtraHttpRequestHeadersString()297     public String getExtraHttpRequestHeadersString() {
298         return getExtraHeadersString("\r\n", true);
299     }
300 
getExtraHeadersString(String delimiter, boolean addTerminator)301     private String getExtraHeadersString(String delimiter, boolean addTerminator) {
302         if (mExtraHeaders == null) return null;
303 
304         StringBuilder headerBuilder = new StringBuilder();
305         for (Map.Entry<String, String> header : mExtraHeaders.entrySet()) {
306             if (headerBuilder.length() > 0) headerBuilder.append(delimiter);
307 
308             // Header name should be lower case.
309             headerBuilder.append(header.getKey().toLowerCase(Locale.US));
310             headerBuilder.append(":");
311             headerBuilder.append(header.getValue());
312         }
313         if (addTerminator) headerBuilder.append(delimiter);
314 
315         return headerBuilder.toString();
316     }
317 
318     /**
319      * Sets the verbatim extra headers string. This is an alternative to storing the headers in
320      * a map (setExtraHeaders()) for the embedders that use collapsed headers strings.
321      */
setVerbatimHeaders(String headers)322     public void setVerbatimHeaders(String headers) {
323         mVerbatimHeaders = headers;
324     }
325 
326     /**
327      * @return the verbatim extra headers string
328      */
getVerbatimHeaders()329     public String getVerbatimHeaders() {
330         return mVerbatimHeaders;
331     }
332 
333     /**
334      * Set user agent override option of this load. Defaults to UserAgentOverrideOption.INHERIT.
335      * @param uaOption One of UA_OVERRIDE static constants above.
336      */
setOverrideUserAgent(int uaOption)337     public void setOverrideUserAgent(int uaOption) {
338         mUaOverrideOption = uaOption;
339     }
340 
341     /**
342      * Get user agent override option of this load. Defaults to UserAgentOverrideOption.INHERIT.
343      * @param uaOption One of UA_OVERRIDE static constants above.
344      */
getUserAgentOverrideOption()345     public int getUserAgentOverrideOption() {
346         return mUaOverrideOption;
347     }
348 
349     /**
350      * Set the post data of this load. This field is ignored unless load type is
351      * LoadURLType.HTTP_POST.
352      * @param postData Post data for this http post load.
353      */
setPostData(ResourceRequestBody postData)354     public void setPostData(ResourceRequestBody postData) {
355         mPostData = postData;
356     }
357 
358     /**
359      * @return the data to be sent through POST
360      */
getPostData()361     public ResourceRequestBody getPostData() {
362         return mPostData;
363     }
364 
365     /**
366      * Set the base url for data load. It is used both to resolve relative URLs
367      * and when applying JavaScript's same origin policy. It is ignored unless
368      * load type is LoadURLType.DATA.
369      * @param baseUrl The base url for this data load.
370      */
setBaseUrlForDataUrl(String baseUrl)371     public void setBaseUrlForDataUrl(String baseUrl) {
372         mBaseUrlForDataUrl = baseUrl;
373     }
374 
375     /**
376      * Get the virtual url for data load. It is the url displayed to the user.
377      * It is ignored unless load type is LoadURLType.DATA.
378      * @return The virtual url for this data load.
379      */
getVirtualUrlForDataUrl()380     public String getVirtualUrlForDataUrl() {
381         return mVirtualUrlForDataUrl;
382     }
383 
384     /**
385      * Set the virtual url for data load. It is the url displayed to the user.
386      * It is ignored unless load type is LoadURLType.DATA.
387      * @param virtualUrl The virtual url for this data load.
388      */
setVirtualUrlForDataUrl(String virtualUrl)389     public void setVirtualUrlForDataUrl(String virtualUrl) {
390         mVirtualUrlForDataUrl = virtualUrl;
391     }
392 
393     /**
394      * Get the data for data load. This is then passed to the renderer as
395      * a string, not as a GURL object to circumvent GURL size restriction.
396      * @return The data url.
397      */
getDataUrlAsString()398     public String getDataUrlAsString() {
399         return mDataUrlAsString;
400     }
401 
402     /**
403      * Set the data for data load. This is then passed to the renderer as
404      * a string, not as a GURL object to circumvent GURL size restriction.
405      * @param url The data url.
406      */
setDataUrlAsString(String url)407     public void setDataUrlAsString(String url) {
408         mDataUrlAsString = url;
409     }
410 
411     /**
412      * Set whether the load should be able to access local resources. This
413      * defaults to false.
414      */
setCanLoadLocalResources(boolean canLoad)415     public void setCanLoadLocalResources(boolean canLoad) {
416         mCanLoadLocalResources = canLoad;
417     }
418 
419     /**
420      * Get whether the load should be able to access local resources. This
421      * defaults to false.
422      */
getCanLoadLocalResources()423     public boolean getCanLoadLocalResources() {
424         return mCanLoadLocalResources;
425     }
426 
getLoadUrlType()427     public int getLoadUrlType() {
428         return mLoadUrlType;
429     }
430 
431     /**
432      * @param rendererInitiated Whether or not this load was initiated from a renderer.
433      */
setIsRendererInitiated(boolean rendererInitiated)434     public void setIsRendererInitiated(boolean rendererInitiated) {
435         mIsRendererInitiated = rendererInitiated;
436     }
437 
438     /**
439      * @return Whether or not this load was initiated from a renderer or not.
440      */
getIsRendererInitiated()441     public boolean getIsRendererInitiated() {
442         return mIsRendererInitiated;
443     }
444 
445     /**
446      * @param shouldReplaceCurrentEntry Whether this navigation should replace
447      * the current navigation entry.
448      */
setShouldReplaceCurrentEntry(boolean shouldReplaceCurrentEntry)449     public void setShouldReplaceCurrentEntry(boolean shouldReplaceCurrentEntry) {
450         mShouldReplaceCurrentEntry = shouldReplaceCurrentEntry;
451     }
452 
453     /**
454      * @return Whether this navigation should replace the current navigation
455      * entry.
456      */
getShouldReplaceCurrentEntry()457     public boolean getShouldReplaceCurrentEntry() {
458         return mShouldReplaceCurrentEntry;
459     }
460 
461     /**
462      * @param intentReceivedTimestamp the timestamp at which Chrome received the intent that
463      *                                triggered this URL load, as returned by System.currentMillis.
464      */
setIntentReceivedTimestamp(long intentReceivedTimestamp)465     public void setIntentReceivedTimestamp(long intentReceivedTimestamp) {
466         mIntentReceivedTimestamp = intentReceivedTimestamp;
467     }
468 
469     /**
470      * @return The timestamp at which Chrome received the intent that triggered this URL load.
471      */
getIntentReceivedTimestamp()472     public long getIntentReceivedTimestamp() {
473         return mIntentReceivedTimestamp;
474     }
475 
476     /**
477      * @param inputStartTimestamp the timestamp of the event in the location bar that triggered
478      *                            this URL load, as returned by System.currentMillis.
479      */
setInputStartTimestamp(long inputStartTimestamp)480     public void setInputStartTimestamp(long inputStartTimestamp) {
481         mInputStartTimestamp = inputStartTimestamp;
482     }
483 
484     /**
485      * @return The timestamp of the event in the location bar that triggered this URL load.
486      */
getInputStartTimestamp()487     public long getInputStartTimestamp() {
488         return mInputStartTimestamp;
489     }
490 
491     /**
492      * Set whether the load is initiated by a user gesture.
493      *
494      * @param hasUserGesture True if load is initiated by user gesture, or false otherwise.
495      */
setHasUserGesture(boolean hasUserGesture)496     public void setHasUserGesture(boolean hasUserGesture) {
497         mHasUserGesture = hasUserGesture;
498     }
499 
500     /**
501      * @return Whether or not this load was initiated with a user gesture.
502      */
getHasUserGesture()503     public boolean getHasUserGesture() {
504         return mHasUserGesture;
505     }
506 
507     /** Sets whether session history should be cleared once the navigation commits. */
setShouldClearHistoryList(boolean shouldClearHistoryList)508     public void setShouldClearHistoryList(boolean shouldClearHistoryList) {
509         mShouldClearHistoryList = shouldClearHistoryList;
510     }
511 
512     /** Returns whether session history should be cleared once the navigation commits. */
getShouldClearHistoryList()513     public boolean getShouldClearHistoryList() {
514         return mShouldClearHistoryList;
515     }
516 
isBaseUrlDataScheme()517     public boolean isBaseUrlDataScheme() {
518         // If there's no base url set, but this is a data load then
519         // treat the scheme as data:.
520         if (mBaseUrlForDataUrl == null && mLoadUrlType == LoadURLType.DATA) {
521             return true;
522         }
523         return LoadUrlParamsJni.get().isDataScheme(mBaseUrlForDataUrl);
524     }
525 
526     @NativeMethods
527     interface Natives {
528         /**
529          * Parses |url| as a GURL on the native side, and
530          * returns true if it's scheme is data:.
531          */
isDataScheme(String url)532         boolean isDataScheme(String url);
533     }
534 }
535