Move view-source handling to the //content layer.
This CL moves most of handling of view-source into the //content layer,
exposing it through a single public API:
void RenderFrameHost::ViewSource()
This move helps to
- Avoid passing PageState to the //chrome layer (e.g. via
content::CustomContextMenuContext).
- Create browser tests that can simulate triggerring a view-source
from the code (e.g. without having to simulate mouse clicks).
- Ensure that the right navigation entries are used - preventing
incorrect reusing of main frame's site instance for showing
a subframe's view-source (https://crbug.com/770946)
This CL adds regression tests for https://crbug.com/523 that ensure that
view-source for HTTP POST works fine in case of main frame and subframe.
Works fine = no new network requests are issued (verified by a response
nonce added to the /echoall default handler in the embedded http test
server).
Bug: 770487, 770946, 774691, 699493
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_site_isolation
Change-Id: Ic0afeed898b4f0900f3f8ad47a903f83f2c589d3
Reviewed-on: https://chromium-review.googlesource.com/695913
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: Matt Menke <[email protected]>
Reviewed-by: Charlie Reis <[email protected]>
Reviewed-by: Scott Violet <[email protected]>
Commit-Queue: Ćukasz Anforowicz <[email protected]>
Cr-Commit-Position: refs/heads/master@{#512625}
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 8a788f6..219ad19 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -105,6 +105,7 @@
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_widget_host_iterator.h"
#include "content/public/browser/resource_request_details.h"
+#include "content/public/browser/restore_type.h"
#include "content/public/browser/security_style_explanations.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/storage_partition.h"
@@ -116,6 +117,7 @@
#include "content/public/common/child_process_host.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/content_switches.h"
+#include "content/public/common/page_state.h"
#include "content/public/common/page_zoom.h"
#include "content/public/common/result_codes.h"
#include "content/public/common/url_utils.h"
@@ -2339,7 +2341,7 @@
create_params.renderer_initiated_creation =
main_frame_route_id != MSG_ROUTING_NONE;
- WebContentsImpl* new_contents = NULL;
+ WebContentsImpl* new_contents = nullptr;
if (!is_guest) {
create_params.context = view_->GetNativeView();
create_params.initial_size = GetContainerBounds().size();
@@ -3455,25 +3457,6 @@
return closed_by_user_gesture_;
}
-void WebContentsImpl::ViewSource() {
- if (!delegate_)
- return;
-
- NavigationEntry* entry = GetController().GetLastCommittedEntry();
- if (!entry)
- return;
-
- delegate_->ViewSourceForTab(this, entry->GetURL());
-}
-
-void WebContentsImpl::ViewFrameSource(const GURL& url,
- const PageState& page_state) {
- if (!delegate_)
- return;
-
- delegate_->ViewSourceForFrame(this, url, page_state);
-}
-
int WebContentsImpl::GetMinimumZoomPercent() const {
return minimum_zoom_percent_;
}
@@ -3923,6 +3906,83 @@
web_contents, allowed_per_prefs, origin, resource_url);
}
+void WebContentsImpl::ViewSource(RenderFrameHostImpl* frame) {
+ DCHECK_EQ(this, WebContents::FromRenderFrameHost(frame));
+
+ // Don't do anything if there is no |delegate_| that could accept and show the
+ // new WebContents containing the view-source.
+ if (!delegate_)
+ return;
+
+ // Use the last committed entry, since the pending entry hasn't loaded yet and
+ // won't be copied into the cloned tab.
+ NavigationEntryImpl* last_committed_entry =
+ static_cast<NavigationEntryImpl*>(frame->frame_tree_node()
+ ->navigator()
+ ->GetController()
+ ->GetLastCommittedEntry());
+ if (!last_committed_entry)
+ return;
+
+ FrameNavigationEntry* frame_entry =
+ last_committed_entry->GetFrameEntry(frame->frame_tree_node());
+ if (!frame_entry)
+ return;
+
+ // Any new WebContents opened while this WebContents is in fullscreen can be
+ // used to confuse the user, so drop fullscreen.
+ if (IsFullscreenForCurrentTab())
+ ExitFullscreen(true);
+
+ // We intentionally don't share the SiteInstance with the original frame so
+ // that view source has a consistent process model and always ends up in a new
+ // process (https://crbug.com/699493).
+ scoped_refptr<SiteInstanceImpl> site_instance_for_view_source = nullptr;
+ // Referrer is not important, because view-source should not hit the network,
+ // but should be served from the cache instead.
+ Referrer referrer_for_view_source;
+ // Do not restore title, derive it from the url.
+ base::string16 title_for_view_source;
+ auto navigation_entry = std::make_unique<NavigationEntryImpl>(
+ site_instance_for_view_source, frame_entry->url(),
+ referrer_for_view_source, title_for_view_source, ui::PAGE_TRANSITION_LINK,
+ /* is_renderer_initiated = */ false);
+ navigation_entry->SetVirtualURL(GURL(content::kViewSourceScheme +
+ std::string(":") +
+ frame_entry->url().spec()));
+
+ // Do not restore scroller position.
+ // TODO(creis, lukasza, arthursonzogni): Do not reuse the original PageState,
+ // but start from a new one and only copy the needed data.
+ const PageState& new_page_state =
+ frame_entry->page_state().RemoveScrollOffset();
+
+ scoped_refptr<FrameNavigationEntry> new_frame_entry =
+ navigation_entry->root_node()->frame_entry;
+ new_frame_entry->set_method(frame_entry->method());
+ new_frame_entry->SetPageState(new_page_state);
+
+ // Create a new WebContents, which is used to display the source code.
+ WebContentsImpl* view_source_contents =
+ static_cast<WebContentsImpl*>(Create(CreateParams(GetBrowserContext())));
+
+ // Restore the previously created NavigationEntry.
+ std::vector<std::unique_ptr<NavigationEntry>> navigation_entries;
+ navigation_entries.push_back(std::move(navigation_entry));
+ view_source_contents->GetController().Restore(0, RestoreType::CURRENT_SESSION,
+ &navigation_entries);
+
+ // Add |view_source_contents| as a new tab.
+ gfx::Rect initial_rect;
+ constexpr bool kUserGesture = true;
+ bool ignored_was_blocked;
+ delegate_->AddNewContents(this, view_source_contents,
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ initial_rect, kUserGesture, &ignored_was_blocked);
+ // Note that the |delegate_| could have deleted |view_source_contents| during
+ // AddNewContents method call.
+}
+
#if defined(OS_ANDROID)
base::android::ScopedJavaLocalRef<jobject>
WebContentsImpl::GetJavaRenderFrameHostDelegate() {