summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael BrĂ¼ning <michael.bruning@qt.io>2019-03-18 13:47:04 +0100
committerMichael BrĂ¼ning <michael.bruning@qt.io>2019-03-27 15:17:34 +0000
commitebe1e7068ca00c34cd92e183888f4e601faf7da6 (patch)
treec54e9a4a3c527b8ec8797797673fd1e27e23a069
parentc7fa9a16957aee6a42fd26bf48270e5258fc68ae (diff)
downloadqtwebengine-chromium-ebe1e7068ca00c34cd92e183888f4e601faf7da6.tar.gz
[Backport] Dependency for CVE-2019-5802 (1/5)
Manual backport of original patch by Camille Lamy <clamy@chromium.org>: Create NavigationRequest from LoadURLParams This CL allows to create the NavigationRequest directly from LoadURLParams for new navigations. Bug: 803859 Reviewed-on: https://chromium-review.googlesource.com/c/1097407 Change-Id: I06c9462cb15c604d511de67a6473f73712fcac72 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
-rw-r--r--chromium/content/browser/frame_host/navigation_controller_impl.cc482
-rw-r--r--chromium/content/browser/frame_host/navigation_controller_impl.h45
-rw-r--r--chromium/content/browser/frame_host/navigation_entry_impl.cc15
-rw-r--r--chromium/content/browser/frame_host/navigation_request.cc51
-rw-r--r--chromium/content/browser/frame_host/navigation_request.h17
-rw-r--r--chromium/content/browser/frame_host/navigator_impl.cc3
6 files changed, 439 insertions, 174 deletions
diff --git a/chromium/content/browser/frame_host/navigation_controller_impl.cc b/chromium/content/browser/frame_host/navigation_controller_impl.cc
index b79785d8e81..9a55d0de98f 100644
--- a/chromium/content/browser/frame_host/navigation_controller_impl.cc
+++ b/chromium/content/browser/frame_host/navigation_controller_impl.cc
@@ -62,6 +62,7 @@
#include "content/browser/frame_host/navigator.h"
#include "content/browser/renderer_host/render_view_host_impl.h" // Temporary
#include "content/browser/site_instance_impl.h"
+#include "content/common/content_constants_internal.h"
#include "content/common/frame_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
@@ -133,6 +134,24 @@ bool ShouldKeepOverride(const NavigationEntry* last_entry) {
return last_entry && last_entry->GetIsOverridingUserAgent();
}
+// Determines whether to override user agent for a navigation.
+bool ShouldOverrideUserAgent(
+ NavigationController::UserAgentOverrideOption override_user_agent,
+ const NavigationEntry* last_committed_entry) {
+ switch (override_user_agent) {
+ case NavigationController::UA_OVERRIDE_INHERIT:
+ return ShouldKeepOverride(last_committed_entry);
+ case NavigationController::UA_OVERRIDE_TRUE:
+ return true;
+ case NavigationController::UA_OVERRIDE_FALSE:
+ return false;
+ default:
+ break;
+ }
+ NOTREACHED();
+ return false;
+}
+
// Returns true this navigation should be treated as a reload. For e.g.
// navigating to the last committed url via the address bar or clicking on a
// link which results in a navigation to the last committed or pending
@@ -215,6 +234,34 @@ bool ShouldTreatNavigationAsReload(
return true;
}
+bool IsValidURLForNavigation(bool is_main_frame,
+ const GURL& virtual_url,
+ const GURL& dest_url) {
+ // Don't attempt to navigate if the virtual URL is non-empty and invalid.
+ if (is_main_frame && !virtual_url.is_valid() && !virtual_url.is_empty()) {
+ LOG(WARNING) << "Refusing to load for invalid virtual URL: "
+ << virtual_url.possibly_invalid_spec();
+ return false;
+ }
+
+ // Don't attempt to navigate to non-empty invalid URLs.
+ if (!dest_url.is_valid() && !dest_url.is_empty()) {
+ LOG(WARNING) << "Refusing to load invalid URL: "
+ << dest_url.possibly_invalid_spec();
+ return false;
+ }
+
+ // The renderer will reject IPC messages with URLs longer than
+ // this limit, so don't attempt to navigate with a longer URL.
+ if (dest_url.spec().size() > url::kMaxURLChars) {
+ LOG(WARNING) << "Refusing to load URL as it exceeds " << url::kMaxURLChars
+ << " characters.";
+ return false;
+ }
+
+ return true;
+}
+
// See replaced_navigation_entry_data.h for details: this information is meant
// to ensure |*output_entry| keeps track of its original URL (landing page in
// case of server redirects) as it gets replaced (e.g. history.replaceState()),
@@ -286,6 +333,72 @@ FrameMsg_Navigate_Type::Value GetNavigationType(
}
}
+// Adjusts the original input URL if needed, to get the URL to actually load and
+// the virtual URL, which may differ.
+void RewriteUrlForNavigation(const GURL& original_url,
+ BrowserContext* browser_context,
+ GURL* url_to_load,
+ GURL* virtual_url,
+ bool* reverse_on_redirect) {
+ // Fix up the given URL before letting it be rewritten, so that any minor
+ // cleanup (e.g., removing leading dots) will not lead to a virtual URL.
+ *virtual_url = original_url;
+ BrowserURLHandlerImpl::GetInstance()->FixupURLBeforeRewrite(virtual_url,
+ browser_context);
+
+ // Allow the browser URL handler to rewrite the URL. This will, for example,
+ // remove "view-source:" from the beginning of the URL to get the URL that
+ // will actually be loaded. This real URL won't be shown to the user, just
+ // used internally.
+ *url_to_load = *virtual_url;
+ BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary(
+ url_to_load, browser_context, reverse_on_redirect);
+}
+
+#if DCHECK_IS_ON()
+// Helper sanity check function used in debug mode.
+void ValidateRequestMatchesEntry(NavigationRequest* request,
+ NavigationEntryImpl* entry) {
+ if (request->frame_tree_node()->IsMainFrame()) {
+ DCHECK_EQ(request->browser_initiated(), !entry->is_renderer_initiated());
+ DCHECK(ui::PageTransitionTypeIncludingQualifiersIs(
+ request->common_params().transition, entry->GetTransitionType()));
+ }
+ DCHECK_EQ(request->common_params().should_replace_current_entry,
+ entry->should_replace_entry());
+ DCHECK_EQ(request->request_params().should_clear_history_list,
+ entry->should_clear_history_list());
+ DCHECK_EQ(request->common_params().has_user_gesture,
+ entry->has_user_gesture());
+ DCHECK_EQ(request->common_params().base_url_for_data_url,
+ entry->GetBaseURLForDataURL());
+ DCHECK_EQ(request->request_params().can_load_local_resources,
+ entry->GetCanLoadLocalResources());
+ DCHECK_EQ(request->common_params().started_from_context_menu,
+ entry->has_started_from_context_menu());
+
+ FrameNavigationEntry* frame_entry =
+ entry->GetFrameEntry(request->frame_tree_node());
+ if (!frame_entry) {
+ NOTREACHED();
+ return;
+ }
+
+ DCHECK_EQ(request->common_params().url, frame_entry->url());
+ DCHECK_EQ(request->common_params().method, frame_entry->method());
+
+ size_t redirect_size = request->request_params().redirects.size();
+ if (redirect_size == frame_entry->redirect_chain().size()) {
+ for (size_t i = 0; i < redirect_size; ++i) {
+ DCHECK_EQ(request->request_params().redirects[i],
+ frame_entry->redirect_chain()[i]);
+ }
+ } else {
+ NOTREACHED();
+ }
+}
+#endif // DCHECK_IS_ON()
+
} // namespace
// NavigationControllerImpl ----------------------------------------------------
@@ -309,28 +422,19 @@ std::unique_ptr<NavigationEntry> NavigationController::CreateNavigationEntry(
const std::string& extra_headers,
BrowserContext* browser_context,
scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
- // Fix up the given URL before letting it be rewritten, so that any minor
- // cleanup (e.g., removing leading dots) will not lead to a virtual URL.
- GURL dest_url(url);
- BrowserURLHandlerImpl::GetInstance()->FixupURLBeforeRewrite(&dest_url,
- browser_context);
-
- // Allow the browser URL handler to rewrite the URL. This will, for example,
- // remove "view-source:" from the beginning of the URL to get the URL that
- // will actually be loaded. This real URL won't be shown to the user, just
- // used internally.
- GURL loaded_url(dest_url);
+ GURL url_to_load;
+ GURL virtual_url;
bool reverse_on_redirect = false;
- BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary(
- &loaded_url, browser_context, &reverse_on_redirect);
+ RewriteUrlForNavigation(url, browser_context, &url_to_load, &virtual_url,
+ &reverse_on_redirect);
NavigationEntryImpl* entry = new NavigationEntryImpl(
nullptr, // The site instance for tabs is sent on navigation
// (WebContents::GetSiteInstance).
- loaded_url, referrer, base::string16(), transition, is_renderer_initiated,
- blob_url_loader_factory);
- entry->SetVirtualURL(dest_url);
- entry->set_user_typed_url(dest_url);
+ url_to_load, referrer, base::string16(), transition,
+ is_renderer_initiated, blob_url_loader_factory);
+ entry->SetVirtualURL(virtual_url);
+ entry->set_user_typed_url(virtual_url);
entry->set_update_virtual_url_with_url(reverse_on_redirect);
entry->set_extra_headers(extra_headers);
return base::WrapUnique(entry);
@@ -1820,6 +1924,31 @@ void NavigationControllerImpl::SetPendingNavigationSSLError(bool error) {
pending_entry_->set_ssl_error(error);
}
+#if defined(OS_ANDROID)
+// static
+bool NavigationControllerImpl::ValidateDataURLAsString(
+ const scoped_refptr<const base::RefCountedString>& data_url_as_string) {
+ if (!data_url_as_string)
+ return false;
+
+ if (data_url_as_string->size() > kMaxLengthOfDataURLString)
+ return false;
+
+ // The number of characters that is enough for validating a data: URI.
+ // From the GURL's POV, the only important part here is scheme, it doesn't
+ // check the actual content. Thus we can take only the prefix of the url, to
+ // avoid unneeded copying of a potentially long string.
+ const size_t kDataUriPrefixMaxLen = 64;
+ GURL data_url(
+ std::string(data_url_as_string->front_as<char>(),
+ std::min(data_url_as_string->size(), kDataUriPrefixMaxLen)));
+ if (!data_url.is_valid() || !data_url.SchemeIs(url::kDataScheme))
+ return false;
+
+ return true;
+}
+#endif
+
bool NavigationControllerImpl::StartHistoryNavigationInNewSubframe(
RenderFrameHostImpl* render_frame_host,
const GURL& default_url) {
@@ -1853,10 +1982,10 @@ bool NavigationControllerImpl::StartHistoryNavigationInNewSubframe(
}
}
- std::unique_ptr<NavigationRequest> request = CreateNavigationRequest(
+ std::unique_ptr<NavigationRequest> request = CreateNavigationRequestFromEntry(
render_frame_host->frame_tree_node(), *entry, frame_entry,
ReloadType::NONE, false /* is_same_document_history_load */,
- true /* is_history_navigation_in_new_child */, nullptr, nullptr);
+ true /* is_history_navigation_in_new_child */);
if (!request)
return false;
@@ -1924,9 +2053,11 @@ void NavigationControllerImpl::NavigateFromFrameProxy(
// http://crbug.com/457149
if (should_replace_current_entry && GetEntryCount() > 0)
entry->set_should_replace_entry(true);
+ bool override_user_agent = false;
if (GetLastCommittedEntry() &&
GetLastCommittedEntry()->GetIsOverridingUserAgent()) {
entry->SetIsOverridingUserAgent(true);
+ override_user_agent = true;
}
// TODO(creis): Set user gesture and intent received timestamp on Android.
@@ -1943,10 +2074,37 @@ void NavigationControllerImpl::NavigateFromFrameProxy(
std::vector<GURL>(), PageState(), method, -1, blob_url_loader_factory);
}
- std::unique_ptr<NavigationRequest> request = CreateNavigationRequest(
- render_frame_host->frame_tree_node(), *entry, frame_entry.get(),
- ReloadType::NONE, false /* is_same_document_history_load */,
- false /* is_history_navigation_in_new_child */, post_body, nullptr);
+ LoadURLParams params(url);
+ params.source_site_instance = source_site_instance;
+ params.load_type = method == "POST" ? LOAD_TYPE_HTTP_POST : LOAD_TYPE_DEFAULT;
+ params.transition_type = page_transition;
+ params.frame_tree_node_id =
+ render_frame_host->frame_tree_node()->frame_tree_node_id();
+ params.referrer = referrer;
+ /* params.redirect_chain: skip */
+ params.extra_headers = extra_headers;
+ params.is_renderer_initiated = is_renderer_initiated;
+ params.override_user_agent = UA_OVERRIDE_INHERIT;
+ /* params.base_url_for_data_url: skip */
+ /* params.virtual_url_for_data_url: skip */
+ /* params.data_url_as_string: skip */
+ params.post_data = post_body;
+ params.can_load_local_resources = false;
+ params.should_replace_current_entry = false;
+ /* params.frame_name: skip */
+ // TODO(clamy): See if user gesture should be propagated to this function.
+ params.has_user_gesture = false;
+ params.should_clear_history_list = false;
+ params.started_from_context_menu = false;
+ /* params.navigation_ui_data: skip */
+ /* params.input_start: skip */
+ params.was_activated = WasActivatedOption::kUnknown;
+
+ std::unique_ptr<NavigationRequest> request =
+ CreateNavigationRequestFromLoadParams(
+ render_frame_host->frame_tree_node(), params, override_user_agent,
+ should_replace_current_entry, false /* has_user_gesture */,
+ ReloadType::NONE, *entry, frame_entry.get());
if (!request)
return;
@@ -2200,10 +2358,10 @@ void NavigationControllerImpl::NavigateToExistingPendingEntry(
// frame to navigate based on names if this were a same document
// navigation, so we can safely assume this is the different document case.
std::unique_ptr<NavigationRequest> navigation_request =
- CreateNavigationRequest(
+ CreateNavigationRequestFromEntry(
root, *pending_entry_, pending_entry_->GetFrameEntry(root),
reload_type, false /* is_same_document_history_load */,
- false /* is_history_navigation_in_new_child */, nullptr, nullptr);
+ false /* is_history_navigation_in_new_child */);
if (!navigation_request) {
// This navigation cannot start (e.g. the URL is invalid), delete the
// pending NavigationEntry.
@@ -2276,10 +2434,10 @@ void NavigationControllerImpl::FindFramesToNavigate(
old_item->document_sequence_number() &&
!frame->current_frame_host()->GetLastCommittedURL().is_empty()) {
std::unique_ptr<NavigationRequest> navigation_request =
- CreateNavigationRequest(
+ CreateNavigationRequestFromEntry(
frame, *pending_entry_, new_item, reload_type,
true /* is_same_document_history_load */,
- false /* is_history_navigation_in_new_child */, nullptr, nullptr);
+ false /* is_history_navigation_in_new_child */);
if (navigation_request) {
// Only add the request if was properly created. It's possible for the
// creation to fail in certain cases, e.g. when the URL is invalid.
@@ -2302,10 +2460,10 @@ void NavigationControllerImpl::FindFramesToNavigate(
return;
} else {
std::unique_ptr<NavigationRequest> navigation_request =
- CreateNavigationRequest(
+ CreateNavigationRequestFromEntry(
frame, *pending_entry_, new_item, reload_type,
false /* is_same_document_history_load */,
- false /* is_history_navigation_in_new_child */, nullptr, nullptr);
+ false /* is_history_navigation_in_new_child */);
if (navigation_request) {
// Only add the request if was properly created. It's possible for the
// creation to fail in certain cases, e.g. when the URL is invalid.
@@ -2338,11 +2496,40 @@ void NavigationControllerImpl::NavigateWithoutEntry(
if (!node)
node = delegate_->GetFrameTree()->root();
+ // Compute overrides to the LoadURLParams for |override_user_agent|,
+ // |should_replace_current_entry| and |has_user_gesture| that will be used
+ // both in the creation of the NavigationEntry and the NavigationRequest.
+ // Ideally, the LoadURLParams themselves would be updated, but since they are
+ // passed as a const reference, this is not possible.
+ // TODO(clamy): When we only create a NavigationRequest, move this to
+ // CreateNavigationRequestFromLoadURLParams.
+ bool override_user_agent = ShouldOverrideUserAgent(params.override_user_agent,
+ GetLastCommittedEntry());
+
+ // Don't allow an entry replacement if there is no entry to replace.
+ // http://crbug.com/457149
+ bool should_replace_current_entry =
+ params.should_replace_current_entry && entries_.size();
+
+ // Always propagate `has_user_gesture` on Android but only when the request
+ // was originated by the renderer on other platforms. This is merely for
+ // backward compatibility as browser process user gestures create confusion in
+ // many tests.
+ bool has_user_gesture = false;
+#if defined(OS_ANDROID)
+ has_user_gesture = params.has_user_gesture;
+#else
+ if (params.is_renderer_initiated)
+ has_user_gesture = params.has_user_gesture;
+#endif
+
// Javascript URLs should not create NavigationEntries. All other navigations
// do, including navigations to chrome renderer debug URLs.
std::unique_ptr<NavigationEntryImpl> entry;
if (!params.url.SchemeIs(url::kJavaScriptScheme)) {
- entry = CreateNavigationEntryFromLoadParams(node, params);
+ entry = CreateNavigationEntryFromLoadParams(
+ node, params, override_user_agent, should_replace_current_entry,
+ has_user_gesture);
DiscardPendingEntry(false);
SetPendingEntry(std::move(entry));
}
@@ -2382,14 +2569,12 @@ void NavigationControllerImpl::NavigateWithoutEntry(
// navigation_ui_data should only be present for main frame navigations.
DCHECK(node->IsMainFrame() || !params.navigation_ui_data);
- // TODO(clamy): Create the NavigationRequest directly from the LoadURLParams
- // instead of relying on the NavigationEntry.
DCHECK(pending_entry_);
- std::unique_ptr<NavigationRequest> request = CreateNavigationRequest(
- node, *pending_entry_, pending_entry_->GetFrameEntry(node), reload_type,
- false /* is_same_document_history_load */,
- false /* is_history_navigation_in_new_child */, nullptr,
- params.navigation_ui_data ? params.navigation_ui_data->Clone() : nullptr);
+ std::unique_ptr<NavigationRequest> request =
+ CreateNavigationRequestFromLoadParams(
+ node, params, override_user_agent, should_replace_current_entry,
+ has_user_gesture, reload_type, *pending_entry_,
+ pending_entry_->GetFrameEntry(node));
// If the navigation couldn't start, return immediately and discard the
// pending NavigationEntry.
@@ -2398,6 +2583,11 @@ void NavigationControllerImpl::NavigateWithoutEntry(
return;
}
+#if DCHECK_IS_ON()
+ // Safety check that NavigationRequest and NavigationEntry match.
+ ValidateRequestMatchesEntry(request.get(), pending_entry_);
+#endif
+
// If an interstitial page is showing, the previous renderer is blocked and
// cannot make new requests. Unblock (and disable) it to allow this
// navigation to succeed. The interstitial will stay visible until the
@@ -2437,7 +2627,10 @@ void NavigationControllerImpl::HandleRendererDebugURL(
std::unique_ptr<NavigationEntryImpl>
NavigationControllerImpl::CreateNavigationEntryFromLoadParams(
FrameTreeNode* node,
- const LoadURLParams& params) {
+ const LoadURLParams& params,
+ bool override_user_agent,
+ bool should_replace_current_entry,
+ bool has_user_gesture) {
// Browser initiated navigations might not have a blob_url_loader_factory set
// in params even if the navigation is to a blob URL. If that happens, lookup
// the correct url loader factory to use here.
@@ -2479,39 +2672,11 @@ NavigationControllerImpl::CreateNavigationEntryFromLoadParams(
// Set the FTN ID (only used in non-site-per-process, for tests).
entry->set_frame_tree_node_id(node->frame_tree_node_id());
- // Don't allow an entry replacement if there is no entry to replace.
- // http://crbug.com/457149
- if (params.should_replace_current_entry && entries_.size() > 0)
- entry->set_should_replace_entry(true);
+ entry->set_should_replace_entry(should_replace_current_entry);
entry->set_should_clear_history_list(params.should_clear_history_list);
- bool override = false;
- switch (params.override_user_agent) {
- case UA_OVERRIDE_INHERIT:
- override = ShouldKeepOverride(GetLastCommittedEntry());
- break;
- case UA_OVERRIDE_TRUE:
- override = true;
- break;
- case UA_OVERRIDE_FALSE:
- override = false;
- break;
- default:
- NOTREACHED();
- break;
- }
- entry->SetIsOverridingUserAgent(override);
-
-// Always propagate `has_user_gesture` on Android but only when the request
-// was originated by the renderer on other platforms. This is merely for
-// backward compatibility as browser process user gestures create confusion in
-// many tests.
-#if defined(OS_ANDROID)
- entry->set_has_user_gesture(params.has_user_gesture);
-#else
- if (params.is_renderer_initiated)
- entry->set_has_user_gesture(params.has_user_gesture);
-#endif
+ entry->SetIsOverridingUserAgent(override_user_agent);
+ entry->set_has_user_gesture(has_user_gesture);
switch (params.load_type) {
case LOAD_TYPE_DEFAULT:
@@ -2543,15 +2708,121 @@ NavigationControllerImpl::CreateNavigationEntryFromLoadParams(
}
std::unique_ptr<NavigationRequest>
-NavigationControllerImpl::CreateNavigationRequest(
+NavigationControllerImpl::CreateNavigationRequestFromLoadParams(
+ FrameTreeNode* node,
+ const LoadURLParams& params,
+ bool override_user_agent,
+ bool should_replace_current_entry,
+ bool has_user_gesture,
+ ReloadType reload_type,
+ const NavigationEntryImpl& entry,
+ FrameNavigationEntry* frame_entry) {
+ DCHECK_EQ(-1, GetIndexOfEntry(&entry));
+ GURL url_to_load;
+ GURL virtual_url;
+ bool reverse_on_redirect = false;
+ RewriteUrlForNavigation(params.url, browser_context_, &url_to_load,
+ &virtual_url, &reverse_on_redirect);
+
+ // For DATA loads, override the virtual URL.
+ if (params.load_type == LOAD_TYPE_DATA)
+ virtual_url = params.virtual_url_for_data_url;
+
+ if (virtual_url.is_empty())
+ virtual_url = url_to_load;
+
+ CHECK(!node->IsMainFrame() || virtual_url == entry.GetVirtualURL());
+ CHECK_EQ(url_to_load, frame_entry->url());
+
+ // TODO(clamy): In order to remove the pending NavigationEntry, |virtual_url|
+ // and |reverse_on_redirect| should be stored in the NavigationRequest.
+
+ if (!IsValidURLForNavigation(node->IsMainFrame(), virtual_url, url_to_load))
+ return nullptr;
+
+ // Determine if Previews should be used for the navigation.
+ PreviewsState previews_state = PREVIEWS_UNSPECIFIED;
+ if (!node->IsMainFrame()) {
+ // For subframes, use the state of the top-level frame.
+ previews_state = node->frame_tree()
+ ->root()
+ ->current_frame_host()
+ ->last_navigation_previews_state();
+ }
+
+ // Give the delegate an opportunity to adjust the previews state.
+ if (delegate_)
+ delegate_->AdjustPreviewsStateForNavigation(&previews_state);
+
+ // This will be used to set the Navigation Timing API navigationStart
+ // parameter for browser navigations in new tabs (intents, tabs opened through
+ // "Open link in new tab"). If the navigation must wait on the current
+ // RenderFrameHost to execute its BeforeUnload event, the navigation start
+ // will be updated when the BeforeUnload ack is received.
+ base::TimeTicks navigation_start = base::TimeTicks::Now();
+
+ FrameMsg_Navigate_Type::Value navigation_type =
+ GetNavigationType(node->current_url(), // old_url
+ url_to_load, // new_url
+ reload_type, // reload_type
+ entry, // entry
+ *frame_entry, // frame_entry
+ false); // is_same_document_history_load
+
+ // Create the NavigationParams based on |params|.
+
+ bool is_view_source_mode = virtual_url.SchemeIs(kViewSourceScheme);
+ const GURL& history_url_for_data_url =
+ params.base_url_for_data_url.is_empty() ? GURL() : virtual_url;
+ CommonNavigationParams common_params(
+ url_to_load, params.referrer, params.transition_type, navigation_type,
+ !is_view_source_mode, should_replace_current_entry,
+ params.base_url_for_data_url, history_url_for_data_url, previews_state,
+ navigation_start,
+ params.load_type == LOAD_TYPE_HTTP_POST ? "POST" : "GET",
+ params.post_data, base::Optional<SourceLocation>(),
+ params.started_from_context_menu, has_user_gesture, InitiatorCSPInfo(),
+ params.input_start);
+
+ RequestNavigationParams request_params(
+ override_user_agent, params.redirect_chain, common_params.url,
+ common_params.method, params.can_load_local_resources,
+ frame_entry->page_state(), entry.GetUniqueID(),
+ false /* is_history_navigation_in_new_child */,
+ entry.GetSubframeUniqueNames(node), true /* intended_as_new_entry */,
+ -1 /* pending_history_list_offset */,
+ params.should_clear_history_list ? -1 : GetLastCommittedEntryIndex(),
+ params.should_clear_history_list ? 0 : GetEntryCount(),
+ is_view_source_mode, params.should_clear_history_list);
+#if defined(OS_ANDROID)
+ if (ValidateDataURLAsString(params.data_url_as_string)) {
+ request_params.data_url_as_string = params.data_url_as_string->data();
+ }
+#endif
+
+ request_params.was_activated = params.was_activated;
+
+ // A form submission may happen here if the navigation is a renderer-initiated
+ // form submission that took the OpenURL path.
+ scoped_refptr<network::ResourceRequestBody> request_body = params.post_data;
+
+ // extra_headers in params are \n separated; NavigationRequests want \r\n.
+ std::string extra_headers_crlf;
+ base::ReplaceChars(params.extra_headers, "\n", "\r\n", &extra_headers_crlf);
+ return NavigationRequest::CreateBrowserInitiated(
+ node, common_params, request_params, !params.is_renderer_initiated,
+ extra_headers_crlf, *frame_entry, entry, request_body,
+ params.navigation_ui_data ? params.navigation_ui_data->Clone() : nullptr);
+}
+
+std::unique_ptr<NavigationRequest>
+NavigationControllerImpl::CreateNavigationRequestFromEntry(
FrameTreeNode* frame_tree_node,
const NavigationEntryImpl& entry,
FrameNavigationEntry* frame_entry,
ReloadType reload_type,
bool is_same_document_history_load,
- bool is_history_navigation_in_new_child,
- const scoped_refptr<network::ResourceRequestBody>& post_body,
- std::unique_ptr<NavigationUIData> navigation_ui_data) {
+ bool is_history_navigation_in_new_child) {
GURL dest_url = frame_entry->url();
Referrer dest_referrer = frame_entry->referrer();
if (reload_type == ReloadType::ORIGINAL_REQUEST_URL &&
@@ -2564,28 +2835,8 @@ NavigationControllerImpl::CreateNavigationRequest(
dest_referrer = Referrer();
}
- // Don't attempt to navigate if the virtual URL is non-empty and invalid.
- if (frame_tree_node->IsMainFrame()) {
- const GURL& virtual_url = entry.GetVirtualURL();
- if (!virtual_url.is_valid() && !virtual_url.is_empty()) {
- LOG(WARNING) << "Refusing to load for invalid virtual URL: "
- << virtual_url.possibly_invalid_spec();
- return nullptr;
- }
- }
-
- // Don't attempt to navigate to non-empty invalid URLs.
- if (!dest_url.is_valid() && !dest_url.is_empty()) {
- LOG(WARNING) << "Refusing to load invalid URL: "
- << dest_url.possibly_invalid_spec();
- return nullptr;
- }
-
- // The renderer will reject IPC messages with URLs longer than
- // this limit, so don't attempt to navigate with a longer URL.
- if (dest_url.spec().size() > url::kMaxURLChars) {
- LOG(WARNING) << "Refusing to load URL as it exceeds " << url::kMaxURLChars
- << " characters.";
+ if (!IsValidURLForNavigation(frame_tree_node->IsMainFrame(),
+ entry.GetVirtualURL(), dest_url)) {
return nullptr;
}
@@ -2609,9 +2860,6 @@ NavigationControllerImpl::CreateNavigationRequest(
// RenderFrameHost to execute its BeforeUnload event, the navigation start
// will be updated when the BeforeUnload ack is received.
base::TimeTicks navigation_start = base::TimeTicks::Now();
- TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
- "navigation,rail", "NavigationTiming navigationStart",
- TRACE_EVENT_SCOPE_GLOBAL, navigation_start);
FrameMsg_Navigate_Type::Value navigation_type = GetNavigationType(
frame_tree_node->current_url(), // old_url
@@ -2620,11 +2868,41 @@ NavigationControllerImpl::CreateNavigationRequest(
entry, // entry
*frame_entry, // frame_entry
is_same_document_history_load); // is_same_document_history_load
+
+ // A form submission may happen here if the navigation is a
+ // back/forward/reload navigation that does a form resubmission.
+ scoped_refptr<network::ResourceRequestBody> request_body;
+ std::string post_content_type;
+ if (frame_entry->method() == "POST") {
+ request_body = frame_entry->GetPostData(&post_content_type);
+ // Might have a LF at end.
+ post_content_type =
+ base::TrimWhitespaceASCII(post_content_type, base::TRIM_ALL)
+ .as_string();
+ }
+
+ // Create the NavigationParams based on |entry| and |frame_entry|.
+ CommonNavigationParams common_params = entry.ConstructCommonNavigationParams(
+ *frame_entry, request_body, dest_url, dest_referrer, navigation_type,
+ previews_state, navigation_start, base::TimeTicks() /* input_start */);
+
+ // TODO(clamy): |intended_as_new_entry| below should always be false once
+ // Reload no longer leads to this being called for a pending NavigationEntry
+ // of index -1.
+ RequestNavigationParams request_params =
+ entry.ConstructRequestNavigationParams(
+ *frame_entry, common_params.url, common_params.method,
+ is_history_navigation_in_new_child,
+ entry.GetSubframeUniqueNames(frame_tree_node),
+ GetPendingEntryIndex() == -1 /* intended_as_new_entry */,
+ GetIndexOfEntry(&entry), GetLastCommittedEntryIndex(),
+ GetEntryCount());
+ request_params.post_content_type = post_content_type;
+
return NavigationRequest::CreateBrowserInitiated(
- frame_tree_node, dest_url, dest_referrer, *frame_entry, entry,
- navigation_type, previews_state, is_same_document_history_load,
- is_history_navigation_in_new_child, post_body, navigation_start, this,
- std::move(navigation_ui_data));
+ frame_tree_node, common_params, request_params,
+ !entry.is_renderer_initiated(), entry.extra_headers(), *frame_entry,
+ entry, request_body, nullptr /* navigation_ui_data */);
}
void NavigationControllerImpl::NotifyNavigationEntryCommitted(
diff --git a/chromium/content/browser/frame_host/navigation_controller_impl.h b/chromium/content/browser/frame_host/navigation_controller_impl.h
index 8b3502ccbc1..6e673629d65 100644
--- a/chromium/content/browser/frame_host/navigation_controller_impl.h
+++ b/chromium/content/browser/frame_host/navigation_controller_impl.h
@@ -231,6 +231,14 @@ class CONTENT_EXPORT NavigationControllerImpl : public NavigationController {
// navigation failed due to an SSL error.
void SetPendingNavigationSSLError(bool error);
+// Returns true if the string corresponds to a valid data URL, false
+// otherwise.
+#if defined(OS_ANDROID)
+ static bool ValidateDataURLAsString(
+ const scoped_refptr<const base::RefCountedString>& data_url_as_string);
+
+#endif
+
private:
friend class RestoreHelper;
@@ -278,22 +286,47 @@ class CONTENT_EXPORT NavigationControllerImpl : public NavigationController {
// Creates and returns a NavigationEntry based on |load_params| for a
// navigation in |node|.
+ // |override_user_agent|, |should_replace_current_entry| and
+ // |has_user_gesture| will override the values from |load_params|. The same
+ // values should be passed to CreateNavigationRequestFromLoadParams.
std::unique_ptr<NavigationEntryImpl> CreateNavigationEntryFromLoadParams(
FrameTreeNode* node,
- const LoadURLParams& load_params);
+ const LoadURLParams& load_params,
+ bool override_user_agent,
+ bool should_replace_current_entry,
+ bool has_user_gesture);
- // Creates and returns a NavigationRequest based on the provided parameters.
+ // Creates and returns a NavigationRequest based on |load_params| for a
+ // new navigation in |node|.
// Will return nullptr if the parameters are invalid and the navigation cannot
// start.
- std::unique_ptr<NavigationRequest> CreateNavigationRequest(
+ // |override_user_agent|, |should_replace_current_entry| and
+ // |has_user_gesture| will override the values from |load_params|. The same
+ // values should be passed to CreateNavigationEntryFromLoadParams.
+ // TODO(clamy): Remove the dependency on NavigationEntry and
+ // FrameNavigationEntry.
+ std::unique_ptr<NavigationRequest> CreateNavigationRequestFromLoadParams(
+ FrameTreeNode* node,
+ const LoadURLParams& load_params,
+ bool override_user_agent,
+ bool should_replace_current_entry,
+ bool has_user_gesture,
+ ReloadType reload_type,
+ const NavigationEntryImpl& entry,
+ FrameNavigationEntry* frame_entry);
+
+ // Creates and returns a NavigationRequest for a navigation to |entry|. Will
+ // return nullptr if the parameters are invalid and the navigation cannot
+ // start.
+ // TODO(clamy): Ensure this is only called for navigations to existing
+ // NavigationEntries.
+ std::unique_ptr<NavigationRequest> CreateNavigationRequestFromEntry(
FrameTreeNode* frame_tree_node,
const NavigationEntryImpl& entry,
FrameNavigationEntry* frame_entry,
ReloadType reload_type,
bool is_same_document_history_load,
- bool is_history_navigation_in_new_child,
- const scoped_refptr<network::ResourceRequestBody>& post_body,
- std::unique_ptr<NavigationUIData> navigation_ui_data);
+ bool is_history_navigation_in_new_child);
// Returns whether there is a pending NavigationEntry whose unique ID matches
// the given NavigationHandle's pending_nav_entry_id.
diff --git a/chromium/content/browser/frame_host/navigation_entry_impl.cc b/chromium/content/browser/frame_host/navigation_entry_impl.cc
index aa9c80e2ce7..b3f2141a93b 100644
--- a/chromium/content/browser/frame_host/navigation_entry_impl.cc
+++ b/chromium/content/browser/frame_host/navigation_entry_impl.cc
@@ -20,6 +20,7 @@
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/url_formatter/url_formatter.h"
+#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/common/content_constants_internal.h"
#include "content/common/navigation_params.h"
#include "content/common/page_state_serialization.h"
@@ -735,18 +736,8 @@ RequestNavigationParams NavigationEntryImpl::ConstructRequestNavigationParams(
intended_as_new_entry, pending_offset_to_send, current_offset_to_send,
current_length_to_send, IsViewSourceMode(), should_clear_history_list());
#if defined(OS_ANDROID)
- if (GetDataURLAsString() &&
- GetDataURLAsString()->size() <= kMaxLengthOfDataURLString) {
- // The number of characters that is enough for validating a data: URI. From
- // the GURL's POV, the only important part here is scheme, it doesn't check
- // the actual content. Thus we can take only the prefix of the url, to avoid
- // unneeded copying of a potentially long string.
- const size_t kDataUriPrefixMaxLen = 64;
- GURL data_url(std::string(
- GetDataURLAsString()->front_as<char>(),
- std::min(GetDataURLAsString()->size(), kDataUriPrefixMaxLen)));
- if (data_url.is_valid() && data_url.SchemeIs(url::kDataScheme))
- request_params.data_url_as_string = GetDataURLAsString()->data();
+ if (NavigationControllerImpl::ValidateDataURLAsString(GetDataURLAsString())) {
+ request_params.data_url_as_string = GetDataURLAsString()->data();
}
#endif
return request_params;
diff --git a/chromium/content/browser/frame_host/navigation_request.cc b/chromium/content/browser/frame_host/navigation_request.cc
index 8a745a1c531..57c43b97955 100644
--- a/chromium/content/browser/frame_host/navigation_request.cc
+++ b/chromium/content/browser/frame_host/navigation_request.cc
@@ -278,34 +278,14 @@ bool ShouldPropagateUserActivation(const url::Origin& previous_origin,
// static
std::unique_ptr<NavigationRequest> NavigationRequest::CreateBrowserInitiated(
FrameTreeNode* frame_tree_node,
- const GURL& dest_url,
- const Referrer& dest_referrer,
+ const CommonNavigationParams& common_params,
+ const RequestNavigationParams& request_params,
+ bool browser_initiated,
+ const std::string& extra_headers,
const FrameNavigationEntry& frame_entry,
const NavigationEntryImpl& entry,
- FrameMsg_Navigate_Type::Value navigation_type,
- PreviewsState previews_state,
- bool is_same_document_history_load,
- bool is_history_navigation_in_new_child,
const scoped_refptr<network::ResourceRequestBody>& post_body,
- const base::TimeTicks& navigation_start,
- NavigationControllerImpl* controller,
std::unique_ptr<NavigationUIData> navigation_ui_data) {
- // A form submission happens either because the navigation is a
- // renderer-initiated form submission that took the OpenURL path or a
- // back/forward/reload navigation the does a form resubmission.
- scoped_refptr<network::ResourceRequestBody> request_body;
- std::string post_content_type;
- if (post_body) {
- // Standard form submission from the renderer.
- request_body = post_body;
- } else if (frame_entry.method() == "POST") {
- // Form resubmission during a back/forward/reload navigation.
- request_body = frame_entry.GetPostData(&post_content_type);
- // Might have a LF at end.
- post_content_type =
- base::TrimWhitespaceASCII(post_content_type, base::TRIM_ALL)
- .as_string();
- }
// TODO(arthursonzogni): Form submission with the "GET" method is possible.
// This is not currently handled here.
bool is_form_submission = !!request_body;
@@ -316,30 +296,11 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateBrowserInitiated(
: base::Optional<url::Origin>(
frame_tree_node->frame_tree()->root()->current_origin());
- // While the navigation was started via the LoadURL path it may have come from
- // the renderer in the first place as part of OpenURL.
- bool browser_initiated = !entry.is_renderer_initiated();
-
- CommonNavigationParams common_params = entry.ConstructCommonNavigationParams(
- frame_entry, request_body, dest_url, dest_referrer, navigation_type,
- previews_state, navigation_start);
-
- RequestNavigationParams request_params =
- entry.ConstructRequestNavigationParams(
- frame_entry, common_params.url, common_params.method,
- is_history_navigation_in_new_child,
- entry.GetSubframeUniqueNames(frame_tree_node),
- controller->GetPendingEntryIndex() == -1,
- controller->GetIndexOfEntry(&entry),
- controller->GetLastCommittedEntryIndex(),
- controller->GetEntryCount());
- request_params.post_content_type = post_content_type;
-
std::unique_ptr<NavigationRequest> navigation_request(new NavigationRequest(
frame_tree_node, common_params,
mojom::BeginNavigationParams::New(
- entry.extra_headers(), net::LOAD_NORMAL,
- false /* skip_service_worker */, REQUEST_CONTEXT_TYPE_LOCATION,
+ extra_headers, net::LOAD_NORMAL, false /* skip_service_worker */,
+ REQUEST_CONTEXT_TYPE_LOCATION,
blink::WebMixedContentContextType::kBlockable, is_form_submission,
GURL() /* searchable_form_url */,
std::string() /* searchable_form_encoding */, initiator,
diff --git a/chromium/content/browser/frame_host/navigation_request.h b/chromium/content/browser/frame_host/navigation_request.h
index 10440a4a70c..d358e5c8840 100644
--- a/chromium/content/browser/frame_host/navigation_request.h
+++ b/chromium/content/browser/frame_host/navigation_request.h
@@ -32,7 +32,6 @@ namespace content {
class FrameNavigationEntry;
class FrameTreeNode;
-class NavigationControllerImpl;
class NavigationHandleImpl;
class NavigationURLLoader;
class NavigationData;
@@ -80,19 +79,19 @@ class CONTENT_EXPORT NavigationRequest : public NavigationURLLoaderDelegate {
};
// Creates a request for a browser-intiated navigation.
+ // Note: this is sometimes called for renderer-initiated navigations going
+ // through the OpenURL path. |browser_initiated| should be false in that case.
+ // TODO(clamy): Rename this function and consider merging it with
+ // CreateRendererInitiated.
static std::unique_ptr<NavigationRequest> CreateBrowserInitiated(
FrameTreeNode* frame_tree_node,
- const GURL& dest_url,
- const Referrer& dest_referrer,
+ const CommonNavigationParams& common_params,
+ const RequestNavigationParams& request_params,
+ bool browser_initiated,
+ const std::string& extra_headers,
const FrameNavigationEntry& frame_entry,
const NavigationEntryImpl& entry,
- FrameMsg_Navigate_Type::Value navigation_type,
- PreviewsState previews_state,
- bool is_same_document_history_load,
- bool is_history_navigation_in_new_child,
const scoped_refptr<network::ResourceRequestBody>& post_body,
- const base::TimeTicks& navigation_start,
- NavigationControllerImpl* controller,
std::unique_ptr<NavigationUIData> navigation_ui_data);
// Creates a request for a renderer-intiated navigation.
diff --git a/chromium/content/browser/frame_host/navigator_impl.cc b/chromium/content/browser/frame_host/navigator_impl.cc
index c4850e0f88c..c6f47ecde41 100644
--- a/chromium/content/browser/frame_host/navigator_impl.cc
+++ b/chromium/content/browser/frame_host/navigator_impl.cc
@@ -353,6 +353,9 @@ void NavigatorImpl::Navigate(std::unique_ptr<NavigationRequest> request,
ReloadType reload_type,
RestoreType restore_type) {
TRACE_EVENT0("browser,navigation", "NavigatorImpl::Navigate");
+ TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
+ "navigation,rail", "NavigationTiming navigationStart",
+ TRACE_EVENT_SCOPE_GLOBAL, request->common_params().navigation_start);
const GURL& dest_url = request->common_params().url;
FrameTreeNode* frame_tree_node = request->frame_tree_node();