Rebind RF's InterfaceProvider on navigation.
Every RenderFrameHost exposes an InterfaceProvider to its corresponding RenderFrame, which is used by content/renderer/, Blink and WebUI JS code to access frame-scoped services implemented by the RenderFrameHost. Previously, for each frame, there was just a single InterfaceProvider interface connection that was used throughout the entire lifetime of the RenderFrame. The client end of this connection was passed in to the RenderFrame synchronously at construction time, and was then bound to |RenderFrameImpl::remote_interfaces_|. As of this CL, this primordial InterfaceProvider interface connection will only service interface requests originating from the initial empty document in the frame. For each subsequent cross-document navigation, a new message pipe is created, with its client end being bound to the RenderFrame's |remote_interfaces_| when the navigation commits, and its request end sent to the browser process as part of the DidCommitProvisionalLoad message. The one and only exception to this rule is when a browsing context is navigated from the initial empty document to another same-origin document, in which case the global object (i.e. the Window instance) associated with the initial document is reused for the new Document corresponding to the first real navigation committed. This is to support the following use-case: 1) Parent frame dynamically injects an <iframe>. 2) The parent frame calls `child.contentDocument.write(...)` to inject script that may stash objects on the child frame's global object (LocalDOMWindow). Internally, these objects may be using Mojo services exposed by the RenderFrameHost. The InterfaceRequests for these will be en-route to the RenderFrameHost for some time. 3) The `child` frame commits a first real load that is same-origin. 4) The global object in the child frame's browsing context is re-used. 5) When the InterfaceRequests arrive to the browser process, the RenderFrameHost should not discard these requests, so that JS objects stashed on the global object will continue to work. On the browser process side, when the RenderFrameHost receives an InterfaceProvider request along with DidCommitProvisionalLoad, it immediately breaks the old InterfaceProvider connection. Pending interface requests on the old connection are dropped, if any. Interface requests possibly pending on the new InterfaceProvider connection are only dispatched after WebContentsObserver::DidFinishNavigation fires. This ensures that interface requests racing with navigation commit will be either ignored, or correctly serviced in the security context of the document they originated from. In other words, when an interface request is dispatched, the invariant will hold that calling GetLastCommittedURL on the RenderFrameHost will return a URL that matches that of the document from which the interface request actually originated from. Note that |remote_interfaces_| remains a non-Channel-associated interface. Also note that it remains the responsibility of individual features using |remote_interfaces_| to close interface connections already established through the InterfaceProvider when a cross-document navigation commits, if needed in particular use-case. Bug: 729021 Cq-Include-Trybots: master.tryserver.chromium.linux:linux_site_isolation Change-Id: I1eb4aa78be9627f70fdf6e18af570f083b9e306d Reviewed-on: https://chromium-review.googlesource.com/735686 Reviewed-by: Chrome Cunningham <chcunningham@chromium.org> Reviewed-by: Nasko Oskov <nasko@chromium.org> Reviewed-by: Daniel Cheng <dcheng@chromium.org> Reviewed-by: Jeremy Roman <jbroman@chromium.org> Reviewed-by: Ken Rockot <rockot@chromium.org> Reviewed-by: Max Morin <maxmorin@chromium.org> Commit-Queue: Balazs Engedy <engedy@chromium.org> Cr-Commit-Position: refs/heads/master@{#522198}
This commit is contained in:

committed by
Commit Bot

parent
2e6a737d17
commit
0c8d550bb4
content
browser
bad_message.h
frame_host
navigation_controller_impl_browsertest.ccrender_frame_host_impl.ccrender_frame_host_impl.hrender_frame_host_impl_browsertest.cc
interface_provider_filtering.ccinterface_provider_filtering.hrenderer_host
security_exploit_browsertest.ccweb_contents
common
public
test
renderer
shell
test
media/mojo/clients
services/service_manager/public/cpp
third_party/WebKit
Source
core
public
tools/metrics/histograms
@ -211,6 +211,8 @@ enum BadMessageReason {
|
||||
WEBUI_BAD_SCHEME_ACCESS = 185,
|
||||
CSDH_UNEXPECTED_OPERATION = 186,
|
||||
RMF_BAD_URL_CACHEABLE_METADATA = 187,
|
||||
RFH_INTERFACE_PROVIDER_MISSING = 188,
|
||||
RFH_INTERFACE_PROVIDER_SUPERFLUOUS = 189,
|
||||
|
||||
// Please add new elements here. The naming convention is abbreviated class
|
||||
// name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
|
||||
|
@ -6209,7 +6209,9 @@ class HistoryNavigationBeforeCommitInjector
|
||||
// DidCommitProvisionalLoadInterceptor:
|
||||
void WillDispatchDidCommitProvisionalLoad(
|
||||
RenderFrameHost* render_frame_host,
|
||||
::FrameHostMsg_DidCommitProvisionalLoad_Params* params) override {
|
||||
::FrameHostMsg_DidCommitProvisionalLoad_Params* params,
|
||||
service_manager::mojom::InterfaceProviderRequest*
|
||||
interface_provider_request) override {
|
||||
if (!render_frame_host->GetParent() && params->url == url_) {
|
||||
did_trigger_history_navigation_ = true;
|
||||
web_contents()->GetController().GoBack();
|
||||
|
@ -503,7 +503,7 @@ RenderFrameHostImpl::RenderFrameHostImpl(SiteInstance* site_instance,
|
||||
waiting_for_init_(renderer_initiated_creation),
|
||||
has_focused_editable_element_(false),
|
||||
active_sandbox_flags_(blink::WebSandboxFlags::kNone),
|
||||
interface_provider_binding_(this),
|
||||
document_scoped_interface_provider_binding_(this),
|
||||
keep_alive_timeout_(base::TimeDelta::FromSeconds(30)),
|
||||
weak_ptr_factory_(this) {
|
||||
frame_tree_->AddRenderViewHostRef(render_view_host_);
|
||||
@ -1522,7 +1522,9 @@ void RenderFrameHostImpl::OnDidFailLoadWithError(
|
||||
// notification containing parameters identifying the navigation.
|
||||
void RenderFrameHostImpl::DidCommitProvisionalLoad(
|
||||
std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params>
|
||||
validated_params) {
|
||||
validated_params,
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
interface_provider_request) {
|
||||
ScopedCommitStateResetter commit_state_resetter(this);
|
||||
RenderProcessHost* process = GetProcess();
|
||||
|
||||
@ -1548,6 +1550,52 @@ void RenderFrameHostImpl::DidCommitProvisionalLoad(
|
||||
OnBeforeUnloadACK(true, approx_renderer_start_time, base::TimeTicks::Now());
|
||||
}
|
||||
|
||||
// Retroactive sanity check:
|
||||
// - If this is the first real load committing in this frame, then by this
|
||||
// time the RenderFrameHost's InterfaceProvider implementation should have
|
||||
// already been bound to a message pipe whose client end is used to service
|
||||
// interface requests from the initial empty document.
|
||||
// - Otherwise, the InterfaceProvider implementation should at this point be
|
||||
// bound to an interface connection servicing interface requests coming from
|
||||
// the document of the previously committed navigation.
|
||||
DCHECK(document_scoped_interface_provider_binding_.is_bound());
|
||||
|
||||
if (validated_params->was_within_same_document) {
|
||||
// The security origin never changes for same-document navigations, the
|
||||
// RenderFrame is expected to keep using the existing interface connection.
|
||||
if (interface_provider_request.is_pending()) {
|
||||
bad_message::ReceivedBadMessage(
|
||||
process, bad_message::RFH_INTERFACE_PROVIDER_SUPERFLUOUS);
|
||||
return;
|
||||
}
|
||||
} else if (interface_provider_request.is_pending()) {
|
||||
// Otherwise, as a general rule, expect the RenderFrame to have supplied the
|
||||
// request end of a new InterfaceProvider connection that will be used by
|
||||
// the new document to issue interface requests to access RenderFrameHost
|
||||
// services.
|
||||
document_scoped_interface_provider_binding_.Close();
|
||||
BindInterfaceProviderRequest(std::move(interface_provider_request));
|
||||
} else {
|
||||
// If there had already been a real load committed in the frame, and this is
|
||||
// not a same-document navigation, then both the active document as well as
|
||||
// the global object was replaced in this browsing context. The RenderFrame
|
||||
// should have rebound its InterfaceProvider to a new pipe, but failed to do
|
||||
// so. Kill the renderer, and close the old binding to ensure that any
|
||||
// pending interface requests originating from the previous document, hence
|
||||
// possibly from a different security origin, will no longer dispatched.
|
||||
if (frame_tree_node_->has_committed_real_load()) {
|
||||
document_scoped_interface_provider_binding_.Close();
|
||||
bad_message::ReceivedBadMessage(
|
||||
process, bad_message::RFH_INTERFACE_PROVIDER_MISSING);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, it is the first real load commited, for which the RenderFrame
|
||||
// is allowed to, and will re-use the existing InterfaceProvider connection
|
||||
// if the new document is same-origin with the initial empty document, and
|
||||
// therefore the global object is not replaced.
|
||||
}
|
||||
|
||||
// If we're waiting for an unload ack from this renderer and we receive a
|
||||
// Navigate message, then the renderer was navigating before it received the
|
||||
// unload request. It will either respond to the unload request soon or our
|
||||
@ -1883,7 +1931,7 @@ void RenderFrameHostImpl::OnRenderProcessGone(int status, int exit_code) {
|
||||
// reset.
|
||||
SetRenderFrameCreated(false);
|
||||
InvalidateMojoConnection();
|
||||
interface_provider_binding_.Close();
|
||||
document_scoped_interface_provider_binding_.Close();
|
||||
|
||||
// Execute any pending AX tree snapshot callbacks with an empty response,
|
||||
// since we're never going to get a response from this renderer.
|
||||
@ -2768,11 +2816,12 @@ void RenderFrameHostImpl::OnRequestOverlayRoutingToken() {
|
||||
void RenderFrameHostImpl::BindInterfaceProviderRequest(
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
interface_provider_request) {
|
||||
DCHECK(!interface_provider_binding_.is_bound());
|
||||
DCHECK(!document_scoped_interface_provider_binding_.is_bound());
|
||||
DCHECK(interface_provider_request.is_pending());
|
||||
interface_provider_binding_.Bind(FilterRendererExposedInterfaces(
|
||||
mojom::kNavigation_FrameSpec, GetProcess()->GetID(),
|
||||
std::move(interface_provider_request)));
|
||||
document_scoped_interface_provider_binding_.Bind(
|
||||
FilterRendererExposedInterfaces(mojom::kNavigation_FrameSpec,
|
||||
GetProcess()->GetID(),
|
||||
std::move(interface_provider_request)));
|
||||
}
|
||||
|
||||
void RenderFrameHostImpl::SetKeepAliveTimeoutForTesting(
|
||||
@ -4314,6 +4363,9 @@ void RenderFrameHostImpl::BindAuthenticatorRequest(
|
||||
void RenderFrameHostImpl::GetInterface(
|
||||
const std::string& interface_name,
|
||||
mojo::ScopedMessagePipeHandle interface_pipe) {
|
||||
// Requests are serviced on |document_scoped_interface_provider_binding_|. It
|
||||
// is therefore safe to assume that every incoming interface request is coming
|
||||
// from the currently active document in the corresponding RenderFrame.
|
||||
if (!registry_ ||
|
||||
!registry_->TryBindInterface(interface_name, &interface_pipe)) {
|
||||
delegate_->OnInterfaceRequest(this, interface_name, &interface_pipe);
|
||||
|
@ -696,6 +696,10 @@ class CONTENT_EXPORT RenderFrameHostImpl
|
||||
return frame_host_associated_binding_;
|
||||
}
|
||||
|
||||
mojo::Binding<service_manager::mojom::InterfaceProvider>&
|
||||
document_scoped_interface_provider_binding_for_testing() {
|
||||
return document_scoped_interface_provider_binding_;
|
||||
}
|
||||
void SetKeepAliveTimeoutForTesting(base::TimeDelta timeout);
|
||||
|
||||
blink::WebSandboxFlags active_sandbox_flags() {
|
||||
@ -882,7 +886,9 @@ class CONTENT_EXPORT RenderFrameHostImpl
|
||||
void IssueKeepAliveHandle(mojom::KeepAliveHandleRequest request) override;
|
||||
void DidCommitProvisionalLoad(
|
||||
std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params>
|
||||
validated_params) override;
|
||||
validated_params,
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
interface_provider_request) override;
|
||||
void BeginNavigation(const CommonNavigationParams& common_params,
|
||||
mojom::BeginNavigationParamsPtr begin_params) override;
|
||||
void SubresourceResponseStarted(const GURL& url,
|
||||
@ -1379,17 +1385,38 @@ class CONTENT_EXPORT RenderFrameHostImpl
|
||||
std::unique_ptr<JavaInterfaceProvider> java_interface_registry_;
|
||||
#endif
|
||||
|
||||
// Binding for the InterfaceProvider through which this RFHI exposes Mojo
|
||||
// services to the corresonding RenderFrame.
|
||||
// Binding for the InterfaceProvider through which this RenderFrameHostImpl
|
||||
// exposes frame-scoped Mojo services to the currently active document in the
|
||||
// corresponding RenderFrame.
|
||||
//
|
||||
// Normally, whoever creates this RFHI, is responsible for creating a message
|
||||
// pipe, then supplying the request end to BindInterfaceProviderRequest(), and
|
||||
// plumbing the client end to the RenderFrame in the renderer process.
|
||||
// GetInterface messages dispatched through this binding are guaranteed to
|
||||
// originate from the document corresponding to the last committed navigation;
|
||||
// or the inital empty document if no real navigation has ever been committed.
|
||||
//
|
||||
// Currently the only exception to this rule are out-of-process iframes, where
|
||||
// the child RFHI takes care of this internally in CreateRenderFrame().
|
||||
// The InterfaceProvider interface connection is established as follows:
|
||||
//
|
||||
// 1) For the initial empty document, the call site that creates this
|
||||
// RenderFrameHost is responsible for creating a message pipe, binding its
|
||||
// request end to this instance by calling BindInterfaceProviderRequest(),
|
||||
// and plumbing the client end to the renderer process, and ultimately
|
||||
// supplying it to the RenderFrame synchronously at construction time.
|
||||
//
|
||||
// The only exception to this rule are out-of-process child frames, whose
|
||||
// RenderFrameHosts take care of this internally in CreateRenderFrame().
|
||||
//
|
||||
// 2) For subsequent documents, the RenderFrame creates a new message pipe
|
||||
// every time a cross-document navigation is committed, and pushes its
|
||||
// request end to the browser process as part of DidCommitProvisionalLoad.
|
||||
// The client end will be used by the new document corresponding to the
|
||||
// committed naviagation to access services exposed by the RenderFrameHost.
|
||||
//
|
||||
// This is required to prevent GetInterface messages racing with navigation
|
||||
// commit from being serviced in the security context corresponding to the
|
||||
// wrong document in the RenderFrame. The benefit of the approach taken is
|
||||
// that it does not necessitate using channel-associated InterfaceProvider
|
||||
// interfaces.
|
||||
mojo::Binding<service_manager::mojom::InterfaceProvider>
|
||||
interface_provider_binding_;
|
||||
document_scoped_interface_provider_binding_;
|
||||
|
||||
// IPC-friendly token that represents this host for AndroidOverlays, if we
|
||||
// have created one yet.
|
||||
|
@ -6,9 +6,13 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/test/bind_test_util.h"
|
||||
#include "base/test/mock_callback.h"
|
||||
#include "content/browser/frame_host/navigation_handle_impl.h"
|
||||
#include "content/browser/interface_provider_filtering.h"
|
||||
#include "content/browser/web_contents/web_contents_impl.h"
|
||||
#include "content/common/frame_messages.h"
|
||||
#include "content/public/browser/javascript_dialog_manager.h"
|
||||
@ -24,10 +28,13 @@
|
||||
#include "content/public/test/test_navigation_observer.h"
|
||||
#include "content/public/test/test_utils.h"
|
||||
#include "content/shell/browser/shell.h"
|
||||
#include "content/test/did_commit_provisional_load_interceptor.h"
|
||||
#include "content/test/frame_host_test_interface.mojom.h"
|
||||
#include "content/test/test_content_browser_client.h"
|
||||
#include "net/dns/mock_host_resolver.h"
|
||||
#include "net/test/embedded_test_server/embedded_test_server.h"
|
||||
#include "net/test/embedded_test_server/http_request.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "third_party/WebKit/common/page/page_visibility_state.mojom.h"
|
||||
|
||||
namespace content {
|
||||
@ -62,7 +69,6 @@ class PrerenderTestContentBrowserClient : public TestContentBrowserClient {
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrerenderTestContentBrowserClient);
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// TODO(mlamouri): part of these tests were removed because they were dependent
|
||||
@ -875,4 +881,333 @@ IN_PROC_BROWSER_TEST_F(
|
||||
EXPECT_EQ("\"Second part received\"", second_part_received);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Allows injecting a fake, test-provided |interface_provider_request| into
|
||||
// DidCommitProvisionalLoad messages in a given |web_contents| instead of the
|
||||
// real one coming from the renderer process.
|
||||
class ScopedFakeInterfaceProviderRequestInjector
|
||||
: public DidCommitProvisionalLoadInterceptor {
|
||||
public:
|
||||
explicit ScopedFakeInterfaceProviderRequestInjector(WebContents* web_contents)
|
||||
: DidCommitProvisionalLoadInterceptor(web_contents) {}
|
||||
~ScopedFakeInterfaceProviderRequestInjector() override = default;
|
||||
|
||||
// Sets the fake InterfaceProvider |request| to inject into the next incoming
|
||||
// DidCommitProvisionalLoad message.
|
||||
void set_fake_request_for_next_commit(
|
||||
service_manager::mojom::InterfaceProviderRequest request) {
|
||||
next_fake_request_ = std::move(request);
|
||||
}
|
||||
|
||||
const GURL& url_of_last_commit() const { return url_of_last_commit_; }
|
||||
|
||||
const service_manager::mojom::InterfaceProviderRequest&
|
||||
original_request_of_last_commit() const {
|
||||
return original_request_of_last_commit_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void WillDispatchDidCommitProvisionalLoad(
|
||||
RenderFrameHost* render_frame_host,
|
||||
::FrameHostMsg_DidCommitProvisionalLoad_Params* params,
|
||||
service_manager::mojom::InterfaceProviderRequest*
|
||||
interface_provider_request) override {
|
||||
url_of_last_commit_ = params->url;
|
||||
original_request_of_last_commit_ = std::move(*interface_provider_request);
|
||||
*interface_provider_request = std::move(next_fake_request_);
|
||||
}
|
||||
|
||||
private:
|
||||
service_manager::mojom::InterfaceProviderRequest next_fake_request_;
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
original_request_of_last_commit_;
|
||||
GURL url_of_last_commit_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedFakeInterfaceProviderRequestInjector);
|
||||
};
|
||||
|
||||
// Monitors the |document_scoped_interface_provider_binding_| of the given
|
||||
// |render_frame_host| for incoming interface requests for |interface_name|, and
|
||||
// invokes |callback| synchronously just before such a request would be
|
||||
// dispatched.
|
||||
class ScopedInterfaceRequestMonitor
|
||||
: public service_manager::mojom::InterfaceProviderInterceptorForTesting {
|
||||
public:
|
||||
ScopedInterfaceRequestMonitor(RenderFrameHost* render_frame_host,
|
||||
base::StringPiece interface_name,
|
||||
base::RepeatingClosure callback)
|
||||
: rfhi_(static_cast<RenderFrameHostImpl*>(render_frame_host)),
|
||||
impl_(binding().SwapImplForTesting(this)),
|
||||
interface_name_(interface_name),
|
||||
request_callback_(callback) {}
|
||||
|
||||
~ScopedInterfaceRequestMonitor() override {
|
||||
auto* old_impl = binding().SwapImplForTesting(impl_);
|
||||
DCHECK_EQ(old_impl, this);
|
||||
}
|
||||
|
||||
protected:
|
||||
// service_manager::mojom::InterfaceProviderInterceptorForTesting:
|
||||
service_manager::mojom::InterfaceProvider* GetForwardingInterface() override {
|
||||
return impl_;
|
||||
}
|
||||
|
||||
void GetInterface(const std::string& interface_name,
|
||||
mojo::ScopedMessagePipeHandle pipe) override {
|
||||
if (interface_name == interface_name_)
|
||||
request_callback_.Run();
|
||||
GetForwardingInterface()->GetInterface(interface_name, std::move(pipe));
|
||||
}
|
||||
|
||||
private:
|
||||
mojo::Binding<service_manager::mojom::InterfaceProvider>& binding() {
|
||||
return rfhi_->document_scoped_interface_provider_binding_for_testing();
|
||||
}
|
||||
|
||||
RenderFrameHostImpl* rfhi_;
|
||||
service_manager::mojom::InterfaceProvider* impl_;
|
||||
|
||||
std::string interface_name_;
|
||||
base::RepeatingClosure request_callback_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedInterfaceRequestMonitor);
|
||||
};
|
||||
|
||||
// Calls |callback| whenever a navigation finishes in |render_frame_host|.
|
||||
class DidFinishNavigationObserver : public WebContentsObserver {
|
||||
public:
|
||||
DidFinishNavigationObserver(RenderFrameHost* render_frame_host,
|
||||
base::RepeatingClosure callback)
|
||||
: WebContentsObserver(
|
||||
WebContents::FromRenderFrameHost(render_frame_host)),
|
||||
callback_(callback) {}
|
||||
|
||||
protected:
|
||||
// WebContentsObserver:
|
||||
void DidFinishNavigation(NavigationHandle* navigation_handle) override {
|
||||
callback_.Run();
|
||||
}
|
||||
|
||||
private:
|
||||
base::RepeatingClosure callback_;
|
||||
DISALLOW_COPY_AND_ASSIGN(DidFinishNavigationObserver);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// For cross-document navigations, the DidCommitProvisionalLoad message from
|
||||
// the renderer process will have its |interface_provider_request| argument set
|
||||
// to the request end of a new InterfaceProvider interface connection that will
|
||||
// be used by the newly committed document to access services exposed by the
|
||||
// RenderFrameHost.
|
||||
//
|
||||
// This test verifies that even if that |interface_provider_request| already has
|
||||
// pending interface requests, the RenderFrameHost binds the InterfaceProvider
|
||||
// request in such a way that these pending interface requests are dispatched
|
||||
// strictly after WebContentsObserver::DidFinishNavigation has fired, so that
|
||||
// the requests will be served correctly in the security context of the newly
|
||||
// committed document (i.e. GetLastCommittedURL/Origin will have been updated).
|
||||
IN_PROC_BROWSER_TEST_F(
|
||||
RenderFrameHostImplBrowserTest,
|
||||
EarlyInterfaceRequestsFromNewDocumentDispatchedAfterNavigationFinished) {
|
||||
const GURL first_url(embedded_test_server()->GetURL("/title1.html"));
|
||||
const GURL second_url(embedded_test_server()->GetURL("/title2.html"));
|
||||
|
||||
// Load a URL that maps to the same SiteInstance as the second URL, to make
|
||||
// sure the second navigation will not be cross-process.
|
||||
ASSERT_TRUE(NavigateToURL(shell(), first_url));
|
||||
|
||||
// Prepare an InterfaceProviderRequest with pending interface requests.
|
||||
service_manager::mojom::InterfaceProviderPtr
|
||||
interface_provider_with_pending_request;
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
interface_provider_request_with_pending_request =
|
||||
mojo::MakeRequest(&interface_provider_with_pending_request);
|
||||
mojom::FrameHostTestInterfacePtr test_interface;
|
||||
interface_provider_with_pending_request->GetInterface(
|
||||
mojom::FrameHostTestInterface::Name_,
|
||||
mojo::MakeRequest(&test_interface).PassMessagePipe());
|
||||
|
||||
// Replace the |interface_provider_request| argument in the next
|
||||
// DidCommitProvisionalLoad message coming from the renderer with the
|
||||
// rigged |interface_provider_with_pending_request| from above.
|
||||
ScopedFakeInterfaceProviderRequestInjector injector(shell()->web_contents());
|
||||
injector.set_fake_request_for_next_commit(
|
||||
std::move(interface_provider_request_with_pending_request));
|
||||
|
||||
// Set up |dispatched_interface_request_callback| to be invoked when the
|
||||
// interface request for FrameHostTestInterface is dispatched to the
|
||||
// RenderFrameHostImpl.
|
||||
base::MockCallback<base::RepeatingClosure>
|
||||
dispatched_interface_request_callback;
|
||||
auto* main_rfh = shell()->web_contents()->GetMainFrame();
|
||||
ScopedInterfaceRequestMonitor monitor(
|
||||
main_rfh, mojom::FrameHostTestInterface::Name_,
|
||||
dispatched_interface_request_callback.Get());
|
||||
|
||||
// Set up |navigation_finished_callback| to be fired on
|
||||
// WebContentsObserver::DidFinishNavigation.
|
||||
base::MockCallback<base::RepeatingClosure> navigation_finished_callback;
|
||||
DidFinishNavigationObserver navigation_finish_observer(
|
||||
main_rfh, navigation_finished_callback.Get());
|
||||
|
||||
// Expect that DidFinishNavigation takes place first, and dispatching second.
|
||||
testing::InSequence in_sequence;
|
||||
EXPECT_CALL(navigation_finished_callback, Run());
|
||||
EXPECT_CALL(dispatched_interface_request_callback, Run());
|
||||
|
||||
// Start the same-process navigation.
|
||||
test::ScopedInterfaceFilterBypass filter_bypass;
|
||||
ASSERT_TRUE(NavigateToURL(shell(), second_url));
|
||||
ASSERT_EQ(main_rfh, shell()->web_contents()->GetMainFrame());
|
||||
ASSERT_EQ(second_url, injector.url_of_last_commit());
|
||||
ASSERT_TRUE(injector.original_request_of_last_commit().is_pending());
|
||||
}
|
||||
|
||||
// The InterfaceProvider interface, which is used by the RenderFrame to access
|
||||
// Mojo services exposed by the RenderFrameHost, is not Channel-associated,
|
||||
// thus not synchronized with navigation IPC messages. As a result, when the
|
||||
// renderer commits a load, the DidCommitProvisional message might be at race
|
||||
// with GetInterface messages, for example, an interface request issued by the
|
||||
// previous document in its unload handler might arrive to the browser process
|
||||
// just a moment after DidCommitProvisionalLoad.
|
||||
//
|
||||
// This test verifies that even if there is such a last-second GetInterface
|
||||
// message originating from the previous document, it is no longer serviced.
|
||||
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
|
||||
LateInterfaceRequestsFromOldDocumentNotDispatched) {
|
||||
const GURL first_url(embedded_test_server()->GetURL("/title1.html"));
|
||||
const GURL second_url(embedded_test_server()->GetURL("/title2.html"));
|
||||
|
||||
// Prepare an InterfaceProviderRequest with no pending requests.
|
||||
service_manager::mojom::InterfaceProviderPtr interface_provider;
|
||||
service_manager::mojom::InterfaceProviderRequest interface_provider_request =
|
||||
mojo::MakeRequest(&interface_provider);
|
||||
|
||||
// Set up a cunning mechnism to replace the |interface_provider_request|
|
||||
// argument in next DidCommitProvisionalLoad message with the rigged
|
||||
// |interface_provider_request| from above, whose client end is controlled by
|
||||
// this test; then trigger a navigation.
|
||||
{
|
||||
ScopedFakeInterfaceProviderRequestInjector injector(
|
||||
shell()->web_contents());
|
||||
test::ScopedInterfaceFilterBypass filter_bypass;
|
||||
injector.set_fake_request_for_next_commit(
|
||||
std::move(interface_provider_request));
|
||||
|
||||
ASSERT_TRUE(NavigateToURL(shell(), first_url));
|
||||
ASSERT_EQ(first_url, injector.url_of_last_commit());
|
||||
ASSERT_TRUE(injector.original_request_of_last_commit().is_pending());
|
||||
}
|
||||
|
||||
// Prepare an interface request for FrameHostTestInterface.
|
||||
mojom::FrameHostTestInterfacePtr test_interface;
|
||||
auto test_interface_request = mojo::MakeRequest(&test_interface);
|
||||
|
||||
// Set up |dispatched_interface_request_callback| that would be invoked if the
|
||||
// interface request for FrameHostTestInterface was ever dispatched to the
|
||||
// RenderFrameHostImpl.
|
||||
base::MockCallback<base::RepeatingClosure>
|
||||
dispatched_interface_request_callback;
|
||||
auto* main_rfh = shell()->web_contents()->GetMainFrame();
|
||||
ScopedInterfaceRequestMonitor monitor(
|
||||
main_rfh, mojom::FrameHostTestInterface::Name_,
|
||||
dispatched_interface_request_callback.Get());
|
||||
|
||||
// Set up the |test_interface request| to arrive on the InterfaceProvider
|
||||
// connection corresponding to the old document in the middle of the firing of
|
||||
// WebContentsObserver::DidFinishNavigation.
|
||||
// TODO(engedy): Should we PostTask() this instead just before synchronously
|
||||
// invoking DidCommitProvisionalLoad?
|
||||
//
|
||||
// Also set up |navigation_finished_callback| to be invoked afterwards, as a
|
||||
// sanity check to ensure that the request injection is actually executed.
|
||||
base::MockCallback<base::RepeatingClosure> navigation_finished_callback;
|
||||
DidFinishNavigationObserver navigation_finish_observer(
|
||||
main_rfh, base::BindLambdaForTesting([&]() {
|
||||
interface_provider->GetInterface(
|
||||
mojom::FrameHostTestInterface::Name_,
|
||||
test_interface_request.PassMessagePipe());
|
||||
std::move(navigation_finished_callback).Run();
|
||||
}));
|
||||
|
||||
// The InterfaceProvider connection that semantically belongs to the old
|
||||
// document, but whose client end is actually controlled by this test, should
|
||||
// still be alive and well.
|
||||
ASSERT_TRUE(test_interface.is_bound());
|
||||
ASSERT_FALSE(test_interface.encountered_error());
|
||||
|
||||
// Expect that the GetInterface message will never be dispatched, but the
|
||||
// DidFinishNavigation callback wll be invoked.
|
||||
EXPECT_CALL(dispatched_interface_request_callback, Run()).Times(0);
|
||||
EXPECT_CALL(navigation_finished_callback, Run());
|
||||
|
||||
// Start the same-process navigation.
|
||||
ASSERT_TRUE(NavigateToURL(shell(), second_url));
|
||||
|
||||
// Wait for a connection error on the |test_interface| as a signal, after
|
||||
// which it can be safely assumed that no GetInterface message will ever be
|
||||
// dispatched from that old InterfaceConnection.
|
||||
base::RunLoop run_loop;
|
||||
test_interface.set_connection_error_handler(run_loop.QuitWhenIdleClosure());
|
||||
run_loop.Run();
|
||||
|
||||
EXPECT_TRUE(test_interface.encountered_error());
|
||||
}
|
||||
|
||||
// Test the edge case where the `window` global object asssociated with the
|
||||
// initial empty document is re-used for document corresponding to the first
|
||||
// real committed load. This happens when the security origins of the two
|
||||
// documents are the same. We do not want to recalculate this in the browser
|
||||
// process, however, so for the first commit we leave it up to the renderer
|
||||
// whether it wants to replace the InterfaceProvider connection or not.
|
||||
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
|
||||
InterfaceProviderRequestIsOptionalForFirstCommit) {
|
||||
const GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
|
||||
const GURL subframe_url(embedded_test_server()->GetURL("/title2.html"));
|
||||
|
||||
service_manager::mojom::InterfaceProviderPtr interface_provider;
|
||||
auto stub_interface_provider_request = mojo::MakeRequest(&interface_provider);
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
null_interface_provider_request(nullptr);
|
||||
|
||||
for (auto* interface_provider_request :
|
||||
{&stub_interface_provider_request, &null_interface_provider_request}) {
|
||||
SCOPED_TRACE(interface_provider_request->is_pending());
|
||||
|
||||
ASSERT_TRUE(NavigateToURL(shell(), main_frame_url));
|
||||
|
||||
ScopedFakeInterfaceProviderRequestInjector injector(
|
||||
shell()->web_contents());
|
||||
injector.set_fake_request_for_next_commit(
|
||||
std::move(*interface_provider_request));
|
||||
|
||||
// Must set 'src` before adding the iframe element to the DOM, otherwise it
|
||||
// will load `about:blank` as the first real load instead of |subframe_url|.
|
||||
// See: https://crbug.com/778318.
|
||||
//
|
||||
// Note that the child frame will first cycle through loading the initial
|
||||
// empty document regardless of when/how/if the `src` attribute is set.
|
||||
const auto script = base::StringPrintf(
|
||||
"let f = document.createElement(\"iframe\");"
|
||||
"f.src=\"%s\"; "
|
||||
"document.body.append(f);",
|
||||
subframe_url.spec().c_str());
|
||||
ASSERT_TRUE(ExecuteScript(shell(), script));
|
||||
|
||||
WaitForLoadStop(shell()->web_contents());
|
||||
|
||||
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
|
||||
->GetFrameTree()
|
||||
->root();
|
||||
ASSERT_EQ(1u, root->child_count());
|
||||
FrameTreeNode* child = root->child_at(0u);
|
||||
|
||||
EXPECT_FALSE(injector.original_request_of_last_commit().is_pending());
|
||||
EXPECT_TRUE(child->has_committed_real_load());
|
||||
EXPECT_EQ(subframe_url, child->current_url());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace content
|
||||
|
@ -14,6 +14,8 @@
|
||||
namespace content {
|
||||
namespace {
|
||||
|
||||
bool g_bypass_interface_filtering_for_testing = false;
|
||||
|
||||
void FilterInterfacesImpl(
|
||||
const char* spec,
|
||||
int process_id,
|
||||
@ -40,6 +42,9 @@ FilterRendererExposedInterfaces(
|
||||
const char* spec,
|
||||
int process_id,
|
||||
service_manager::mojom::InterfaceProviderRequest request) {
|
||||
if (g_bypass_interface_filtering_for_testing)
|
||||
return request;
|
||||
|
||||
service_manager::mojom::InterfaceProviderPtr provider;
|
||||
auto filtered_request = mojo::MakeRequest(&provider);
|
||||
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
|
||||
@ -54,4 +59,18 @@ FilterRendererExposedInterfaces(
|
||||
return filtered_request;
|
||||
}
|
||||
|
||||
namespace test {
|
||||
|
||||
ScopedInterfaceFilterBypass::ScopedInterfaceFilterBypass() {
|
||||
// Nesting not supported.
|
||||
DCHECK(!g_bypass_interface_filtering_for_testing);
|
||||
g_bypass_interface_filtering_for_testing = true;
|
||||
}
|
||||
|
||||
ScopedInterfaceFilterBypass::~ScopedInterfaceFilterBypass() {
|
||||
g_bypass_interface_filtering_for_testing = false;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
} // namespace content
|
||||
|
@ -5,7 +5,11 @@
|
||||
#ifndef CONTENT_BROWSER_INTERFACE_PROVIDER_FILTERING_H_
|
||||
#define CONTENT_BROWSER_INTERFACE_PROVIDER_FILTERING_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "content/common/content_export.h"
|
||||
#include "services/service_manager/public/interfaces/interface_provider.mojom.h"
|
||||
|
||||
namespace content {
|
||||
@ -25,5 +29,23 @@ FilterRendererExposedInterfaces(
|
||||
int process_id,
|
||||
service_manager::mojom::InterfaceProviderRequest request);
|
||||
|
||||
namespace test {
|
||||
|
||||
// Allows through all interface requests while in scope. For testing only.
|
||||
//
|
||||
// TODO(https://crbug.com/792407): See if browser tests can just set up the
|
||||
// service_Manager::Connector properly instead of this heavy-handed solution.
|
||||
class CONTENT_EXPORT ScopedInterfaceFilterBypass {
|
||||
public:
|
||||
ScopedInterfaceFilterBypass();
|
||||
~ScopedInterfaceFilterBypass();
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedInterfaceFilterBypass);
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
} // namespace content
|
||||
|
||||
#endif // CONTENT_BROWSER_INTERFACE_PROVIDER_FILTERING_H_
|
||||
|
@ -219,7 +219,9 @@ class CommitBeforeSwapAckSentHelper
|
||||
// DidCommitProvisionalLoadInterceptor:
|
||||
void WillDispatchDidCommitProvisionalLoad(
|
||||
RenderFrameHost* render_frame_host,
|
||||
::FrameHostMsg_DidCommitProvisionalLoad_Params*) override {
|
||||
::FrameHostMsg_DidCommitProvisionalLoad_Params* params,
|
||||
service_manager::mojom::InterfaceProviderRequest*
|
||||
interface_provider_request) override {
|
||||
base::MessageLoop::ScopedNestableTaskAllower allow(
|
||||
base::MessageLoop::current());
|
||||
FrameWatcher(web_contents()).WaitFrames(1);
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "content/public/test/test_utils.h"
|
||||
#include "content/shell/browser/shell.h"
|
||||
#include "content/test/content_browser_test_utils_internal.h"
|
||||
#include "content/test/did_commit_provisional_load_interceptor.h"
|
||||
#include "content/test/mock_widget_impl.h"
|
||||
#include "content/test/test_content_browser_client.h"
|
||||
#include "ipc/ipc_security_test_util.h"
|
||||
@ -563,7 +564,7 @@ IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, MismatchedOriginOnCommit) {
|
||||
params->origin = url::Origin::Create(GURL("http://bar.com/"));
|
||||
|
||||
static_cast<mojom::FrameHost*>(root->current_frame_host())
|
||||
->DidCommitProvisionalLoad(std::move(params));
|
||||
->DidCommitProvisionalLoad(std::move(params), nullptr);
|
||||
|
||||
// When the IPC message is received and validation fails, the process is
|
||||
// terminated. However, the notification for that should be processed in a
|
||||
@ -576,6 +577,90 @@ IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, MismatchedOriginOnCommit) {
|
||||
ResourceDispatcherHost::Get()->SetDelegate(nullptr);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Interceptor that replaces |interface_provider_request| with the specified
|
||||
// value for the first DidCommitProvisionalLoad message it observes in the given
|
||||
// |web_contents| while in scope.
|
||||
class ScopedInterfaceProviderRequestReplacer
|
||||
: public DidCommitProvisionalLoadInterceptor {
|
||||
public:
|
||||
ScopedInterfaceProviderRequestReplacer(
|
||||
WebContents* web_contents,
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
interface_provider_request_override)
|
||||
: DidCommitProvisionalLoadInterceptor(web_contents),
|
||||
interface_provider_request_override_(
|
||||
std::move(interface_provider_request_override)) {}
|
||||
~ScopedInterfaceProviderRequestReplacer() override = default;
|
||||
|
||||
protected:
|
||||
void WillDispatchDidCommitProvisionalLoad(
|
||||
RenderFrameHost* render_frame_host,
|
||||
::FrameHostMsg_DidCommitProvisionalLoad_Params* params,
|
||||
service_manager::mojom::InterfaceProviderRequest*
|
||||
interface_provider_request) override {
|
||||
ASSERT_TRUE(interface_provider_request_override_.has_value());
|
||||
*interface_provider_request =
|
||||
std::move(interface_provider_request_override_).value();
|
||||
}
|
||||
|
||||
private:
|
||||
base::Optional<service_manager::mojom::InterfaceProviderRequest>
|
||||
interface_provider_request_override_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedInterfaceProviderRequestReplacer);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Test that, as a general rule, not receiving a new InterfaceProviderRequest
|
||||
// for a cross-document navigation properly terminates the renderer process.
|
||||
// There is one exception to this rule, see: RenderFrameHostImplBrowserTest.
|
||||
// InterfaceProviderRequestIsOptionalForFirstCommit.
|
||||
IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest,
|
||||
MissingInterfaceProviderOnNonSameDocumentCommit) {
|
||||
const GURL start_url(embedded_test_server()->GetURL("/title1.html"));
|
||||
const GURL non_same_document_url(
|
||||
embedded_test_server()->GetURL("/title2.html"));
|
||||
|
||||
EXPECT_TRUE(NavigateToURL(shell(), start_url));
|
||||
|
||||
RenderProcessHostWatcher exit_observer(
|
||||
shell()->web_contents()->GetMainFrame()->GetProcess(),
|
||||
RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
|
||||
|
||||
ScopedInterfaceProviderRequestReplacer replacer(shell()->web_contents(),
|
||||
nullptr);
|
||||
NavigateToURLAndExpectNoCommit(shell(), non_same_document_url);
|
||||
exit_observer.Wait();
|
||||
|
||||
EXPECT_FALSE(exit_observer.did_exit_normally());
|
||||
}
|
||||
|
||||
// Test that receiving a new InterfaceProviderRequest for a same-document
|
||||
// navigation properly terminates the renderer process.
|
||||
IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest,
|
||||
SuperfluousInterfaceProviderOnSameDocumentCommit) {
|
||||
const GURL start_url(embedded_test_server()->GetURL("/title1.html"));
|
||||
const GURL same_document_url(
|
||||
embedded_test_server()->GetURL("/title1.html#ref"));
|
||||
|
||||
EXPECT_TRUE(NavigateToURL(shell(), start_url));
|
||||
|
||||
RenderProcessHostWatcher exit_observer(
|
||||
shell()->web_contents()->GetMainFrame()->GetProcess(),
|
||||
RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
|
||||
|
||||
service_manager::mojom::InterfaceProviderPtr interface_provider;
|
||||
ScopedInterfaceProviderRequestReplacer replacer(
|
||||
shell()->web_contents(), mojo::MakeRequest(&interface_provider));
|
||||
NavigateToURLAndExpectNoCommit(shell(), same_document_url);
|
||||
exit_observer.Wait();
|
||||
|
||||
EXPECT_FALSE(exit_observer.did_exit_normally());
|
||||
}
|
||||
|
||||
// Test that a compromised renderer cannot ask to upload an arbitrary file in
|
||||
// OpenURL. This is a regression test for https://crbug.com/726067.
|
||||
IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest,
|
||||
@ -680,8 +765,10 @@ IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, PageStateToWrongEntry) {
|
||||
params->page_state = PageState::CreateFromURL(GURL("data:text/html,foo"));
|
||||
params->origin = url::Origin::Create(GURL("about:blank"));
|
||||
|
||||
service_manager::mojom::InterfaceProviderPtr isolated_interface_provider;
|
||||
static_cast<mojom::FrameHost*>(child0_0->current_frame_host())
|
||||
->DidCommitProvisionalLoad(std::move(params));
|
||||
->DidCommitProvisionalLoad(
|
||||
std::move(params), mojo::MakeRequest(&isolated_interface_provider));
|
||||
|
||||
// Make sure we haven't changed the FrameNavigationEntry. An attack would
|
||||
// modify the PageState but leave the SiteInstance as it was.
|
||||
|
@ -2150,6 +2150,10 @@ TEST_F(WebContentsImplTest, CreateInterstitialForClosingTab) {
|
||||
interstitial->Show();
|
||||
TestRenderFrameHost* interstitial_rfh =
|
||||
static_cast<TestRenderFrameHost*>(interstitial->GetMainFrame());
|
||||
|
||||
// Ensure the InterfaceProvider for the initial empty document is bound.
|
||||
interstitial_rfh->InitializeRenderFrameIfNeeded();
|
||||
|
||||
// The interstitial should not show until its navigation has committed.
|
||||
EXPECT_FALSE(interstitial->is_showing());
|
||||
EXPECT_FALSE(contents()->ShowingInterstitialPage());
|
||||
|
@ -170,8 +170,22 @@ interface FrameHost {
|
||||
IssueKeepAliveHandle(KeepAliveHandle& keep_alive_handle);
|
||||
|
||||
// Sent by the renderer when a navigation commits in the frame.
|
||||
//
|
||||
// If |interface_provider_request| is non-empty, the FrameHost implementation
|
||||
// must unbind the old InterfaceProvider connection, and drop any interface
|
||||
// requests pending on it. Then it should bind |interface_provider_request|
|
||||
// and start servicing GetInterface messages coming in on this new connection
|
||||
// in a security context that is appropriate for the committed navigation.
|
||||
//
|
||||
// The FrameHost implementation must enforce that |interface_provider_request|
|
||||
// is set for cross-document navigations. This prevents origin confusion by
|
||||
// ensuring that interface requests racing with navigation commit will be
|
||||
// either ignored, or serviced correctly in the security context of the
|
||||
// document they originated from (based on which InterfaceProvider connection
|
||||
// the GetInterface messages arrive on).
|
||||
DidCommitProvisionalLoad(
|
||||
DidCommitProvisionalLoadParams params);
|
||||
DidCommitProvisionalLoadParams params,
|
||||
service_manager.mojom.InterfaceProvider&? interface_provider_request);
|
||||
|
||||
// Sent by the renderer to request a navigation.
|
||||
BeginNavigation(
|
||||
|
@ -94,21 +94,12 @@ class MockRenderMessageFilterImpl : public mojom::RenderMessageFilter {
|
||||
MockRenderThread* const thread_;
|
||||
};
|
||||
|
||||
// Returns an InterfaceProvider that is safe to call into, but will not actually
|
||||
// service any interface requests.
|
||||
service_manager::mojom::InterfaceProviderPtrInfo CreateStubInterfaceProvider() {
|
||||
::service_manager::mojom::InterfaceProviderPtrInfo info;
|
||||
mojo::MakeRequest(&info);
|
||||
return info;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MockRenderThread::MockRenderThread()
|
||||
: routing_id_(0),
|
||||
opener_id_(0),
|
||||
new_window_routing_id_(0),
|
||||
new_window_main_frame_routing_id_(0),
|
||||
new_window_main_frame_widget_routing_id_(0),
|
||||
new_frame_routing_id_(0),
|
||||
mock_render_message_filter_(new MockRenderMessageFilterImpl(this)) {
|
||||
@ -301,6 +292,29 @@ void MockRenderThread::OnCreateWidget(int opener_id,
|
||||
*route_id = routing_id_;
|
||||
}
|
||||
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
MockRenderThread::TakeInitialInterfaceProviderRequestForFrame(
|
||||
int32_t routing_id) {
|
||||
auto it =
|
||||
frame_routing_id_to_initial_interface_provider_requests_.find(routing_id);
|
||||
if (it == frame_routing_id_to_initial_interface_provider_requests_.end())
|
||||
return nullptr;
|
||||
auto interface_provider_request = std::move(it->second);
|
||||
frame_routing_id_to_initial_interface_provider_requests_.erase(it);
|
||||
return interface_provider_request;
|
||||
}
|
||||
|
||||
void MockRenderThread::PassInitialInterfaceProviderRequestForFrame(
|
||||
int32_t routing_id,
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
interface_provider_request) {
|
||||
bool did_insertion = false;
|
||||
std::tie(std::ignore, did_insertion) =
|
||||
frame_routing_id_to_initial_interface_provider_requests_.emplace(
|
||||
routing_id, std::move(interface_provider_request));
|
||||
DCHECK(did_insertion);
|
||||
}
|
||||
|
||||
// The Frame expects to be returned a valid route_id different from its own.
|
||||
void MockRenderThread::OnCreateChildFrame(
|
||||
const FrameHostMsg_CreateChildFrame_Params& params,
|
||||
@ -308,8 +322,11 @@ void MockRenderThread::OnCreateChildFrame(
|
||||
mojo::MessagePipeHandle* new_interface_provider,
|
||||
base::UnguessableToken* devtools_frame_token) {
|
||||
*new_render_frame_id = new_frame_routing_id_++;
|
||||
service_manager::mojom::InterfaceProviderPtr interface_provider;
|
||||
frame_routing_id_to_initial_interface_provider_requests_.emplace(
|
||||
*new_render_frame_id, mojo::MakeRequest(&interface_provider));
|
||||
*new_interface_provider =
|
||||
CreateStubInterfaceProvider().PassHandle().release();
|
||||
interface_provider.PassInterface().PassHandle().release();
|
||||
*devtools_frame_token = base::UnguessableToken::Create();
|
||||
}
|
||||
|
||||
@ -347,10 +364,13 @@ void MockRenderThread::OnDuplicateSection(
|
||||
void MockRenderThread::OnCreateWindow(
|
||||
const mojom::CreateNewWindowParams& params,
|
||||
mojom::CreateNewWindowReply* reply) {
|
||||
reply->route_id = new_window_routing_id_;
|
||||
reply->main_frame_route_id = new_window_main_frame_routing_id_;
|
||||
reply->main_frame_interface_provider = CreateStubInterfaceProvider();
|
||||
reply->main_frame_widget_route_id = new_window_main_frame_widget_routing_id_;
|
||||
reply->route_id = new_window_routing_id_++;
|
||||
reply->main_frame_route_id = new_frame_routing_id_++;
|
||||
frame_routing_id_to_initial_interface_provider_requests_.emplace(
|
||||
reply->main_frame_route_id,
|
||||
mojo::MakeRequest(&reply->main_frame_interface_provider));
|
||||
reply->main_frame_widget_route_id =
|
||||
new_window_main_frame_widget_routing_id_++;
|
||||
reply->cloned_session_storage_namespace_id = 0;
|
||||
}
|
||||
|
||||
|
@ -128,6 +128,22 @@ class MockRenderThread : public RenderThread {
|
||||
void OnCreateWidget(int opener_id,
|
||||
blink::WebPopupType popup_type,
|
||||
int* route_id);
|
||||
|
||||
// Returns the request end of the InterfaceProvider interface whose client end
|
||||
// was passed in to construct RenderFrame with |routing_id|; if any. The
|
||||
// client end will be used by the RenderFrame to service interface requests
|
||||
// originating from the original the initial empty document.
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
TakeInitialInterfaceProviderRequestForFrame(int32_t routing_id);
|
||||
|
||||
// Called from the RenderViewTest harness to supply the request end of the
|
||||
// InterfaceProvider interface connection that the harness used to service the
|
||||
// initial empty document in the RenderFrame with |routing_id|.
|
||||
void PassInitialInterfaceProviderRequestForFrame(
|
||||
int32_t routing_id,
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
interface_provider_request);
|
||||
|
||||
protected:
|
||||
// This function operates as a regular IPC listener. Subclasses
|
||||
// overriding this should first delegate to this implementation.
|
||||
@ -152,12 +168,15 @@ class MockRenderThread : public RenderThread {
|
||||
// Opener id reported by the Widget.
|
||||
int32_t opener_id_;
|
||||
|
||||
// Routing id that will be assigned to a CreateWindow Widget.
|
||||
// Routing id that will be assigned to a CreateWindow Widget and/or child
|
||||
// frames.
|
||||
int32_t new_window_routing_id_;
|
||||
int32_t new_window_main_frame_routing_id_;
|
||||
int32_t new_window_main_frame_widget_routing_id_;
|
||||
int32_t new_frame_routing_id_;
|
||||
|
||||
std::map<int32_t, service_manager::mojom::InterfaceProviderRequest>
|
||||
frame_routing_id_to_initial_interface_provider_requests_;
|
||||
|
||||
// The last known good deserializer for sync messages.
|
||||
std::unique_ptr<IPC::MessageReplyDeserializer> reply_deserializer_;
|
||||
|
||||
|
@ -249,6 +249,9 @@ NavigationSimulator::NavigationSimulator(const GURL& original_url,
|
||||
else
|
||||
transition_ = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
|
||||
}
|
||||
|
||||
service_manager::mojom::InterfaceProviderPtr stub_interface_provider;
|
||||
interface_provider_request_ = mojo::MakeRequest(&stub_interface_provider);
|
||||
}
|
||||
|
||||
NavigationSimulator::~NavigationSimulator() {}
|
||||
@ -484,7 +487,11 @@ void NavigationSimulator::Commit() {
|
||||
navigation_url_, params.item_sequence_number,
|
||||
params.document_sequence_number);
|
||||
|
||||
render_frame_host_->SendNavigateWithParams(¶ms);
|
||||
if (params.was_within_same_document)
|
||||
interface_provider_request_ = nullptr;
|
||||
|
||||
render_frame_host_->SendNavigateWithParamsAndInterfaceProvider(
|
||||
¶ms, std::move(interface_provider_request_));
|
||||
|
||||
// Simulate the UnloadACK in the old RenderFrameHost if it was swapped out at
|
||||
// commit time.
|
||||
@ -596,7 +603,8 @@ void NavigationSimulator::CommitErrorPage() {
|
||||
navigation_url_, params.item_sequence_number,
|
||||
params.document_sequence_number);
|
||||
|
||||
render_frame_host_->SendNavigateWithParams(¶ms);
|
||||
render_frame_host_->SendNavigateWithParamsAndInterfaceProvider(
|
||||
¶ms, std::move(interface_provider_request_));
|
||||
|
||||
// Simulate the UnloadACK in the old RenderFrameHost if it was swapped out at
|
||||
// commit time.
|
||||
@ -642,7 +650,9 @@ void NavigationSimulator::CommitSameDocument() {
|
||||
params.page_state =
|
||||
PageState::CreateForTesting(navigation_url_, false, nullptr, nullptr);
|
||||
|
||||
render_frame_host_->SendNavigateWithParams(¶ms);
|
||||
interface_provider_request_ = nullptr;
|
||||
render_frame_host_->SendNavigateWithParamsAndInterfaceProvider(
|
||||
¶ms, nullptr /* interface_provider_request */);
|
||||
|
||||
state_ = FINISHED;
|
||||
|
||||
@ -695,6 +705,14 @@ void NavigationSimulator::SetSocketAddress(
|
||||
socket_address_ = socket_address;
|
||||
}
|
||||
|
||||
void NavigationSimulator::SetInterfaceProviderRequest(
|
||||
service_manager::mojom::InterfaceProviderRequest request) {
|
||||
CHECK_LE(state_, STARTED) << "The InterfaceProviderRequest cannot be set "
|
||||
"after the navigation has committed or failed";
|
||||
CHECK(request.is_pending());
|
||||
interface_provider_request_ = std::move(request);
|
||||
}
|
||||
|
||||
NavigationThrottle::ThrottleCheckResult
|
||||
NavigationSimulator::GetLastThrottleCheckResult() {
|
||||
return last_throttle_check_result_.value();
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "content/public/common/referrer.h"
|
||||
#include "net/base/host_port_pair.h"
|
||||
#include "services/service_manager/public/cpp/interface_provider.h"
|
||||
#include "ui/base/page_transition_types.h"
|
||||
|
||||
class GURL;
|
||||
@ -222,6 +223,16 @@ class NavigationSimulator : public WebContentsObserver {
|
||||
// commits. They should be specified before calling |Fail| or |Commit|.
|
||||
virtual void SetSocketAddress(const net::HostPortPair& socket_address);
|
||||
|
||||
// Sets the InterfaceProvider interface request to pass in as an argument to
|
||||
// DidCommitProvisionalLoad for cross-document navigations. If not called,
|
||||
// a stub will be passed in (which will never receive any interface requests).
|
||||
//
|
||||
// This interface connection would normally be created by the RenderFrame,
|
||||
// with the client end bound to |remote_interfaces_| to allow the new document
|
||||
// to access services exposed by the RenderFrameHost.
|
||||
virtual void SetInterfaceProviderRequest(
|
||||
service_manager::mojom::InterfaceProviderRequest request);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
// Gets the last throttle check result computed by the navigation throttles.
|
||||
@ -340,6 +351,7 @@ class NavigationSimulator : public WebContentsObserver {
|
||||
ReloadType reload_type_ = ReloadType::NONE;
|
||||
int session_history_offset_ = 0;
|
||||
bool has_user_gesture_ = true;
|
||||
service_manager::mojom::InterfaceProviderRequest interface_provider_request_;
|
||||
|
||||
// These are used to sanity check the content/public/ API calls emitted as
|
||||
// part of the navigation.
|
||||
|
@ -75,15 +75,16 @@ using blink::WebURLRequest;
|
||||
|
||||
namespace {
|
||||
|
||||
const int32_t kRouteId = 5;
|
||||
const int32_t kMainFrameRouteId = 6;
|
||||
const int32_t kRouteId = 101;
|
||||
const int32_t kMainFrameRouteId = 201;
|
||||
// TODO(avi): Widget routing IDs should be distinct from the view routing IDs,
|
||||
// once RenderWidgetHost is distilled from RenderViewHostImpl.
|
||||
// https://crbug.com/545684
|
||||
const int32_t kMainFrameWidgetRouteId = 5;
|
||||
const int32_t kNewWindowRouteId = 7;
|
||||
const int32_t kNewFrameRouteId = 10;
|
||||
const int32_t kNewFrameWidgetRouteId = 7;
|
||||
const int32_t kMainFrameWidgetRouteId = 101;
|
||||
|
||||
const int32_t kNewWindowRouteId = 211;
|
||||
const int32_t kNewFrameRouteId = 111;
|
||||
const int32_t kNewFrameWidgetRouteId = 211;
|
||||
|
||||
// Converts |ascii_character| into |key_code| and returns true on success.
|
||||
// Handles only the characters needed by tests.
|
||||
@ -309,8 +310,10 @@ void RenderViewTest::SetUp() {
|
||||
view_params->renderer_preferences = RendererPreferences();
|
||||
view_params->web_preferences = WebPreferences();
|
||||
view_params->view_id = kRouteId;
|
||||
render_thread_->PassInitialInterfaceProviderRequestForFrame(
|
||||
kMainFrameRouteId,
|
||||
mojo::MakeRequest(&view_params->main_frame_interface_provider));
|
||||
view_params->main_frame_routing_id = kMainFrameRouteId;
|
||||
mojo::MakeRequest(&view_params->main_frame_interface_provider);
|
||||
view_params->main_frame_widget_routing_id = kMainFrameWidgetRouteId;
|
||||
view_params->session_storage_namespace_id = kInvalidSessionStorageNamespaceId;
|
||||
view_params->swapped_out = false;
|
||||
|
@ -97,7 +97,7 @@ void AudioIPCFactory::RegisterRemoteFactoryOnIOThread(
|
||||
// Unretained is safe because |this| owns the binding, so a connection error
|
||||
// cannot trigger after destruction.
|
||||
emplaced_factory.set_connection_error_handler(
|
||||
base::BindOnce(&AudioIPCFactory::MaybeDeregisterRemoteFactory,
|
||||
base::BindOnce(&AudioIPCFactory::MaybeDeregisterRemoteFactoryOnIOThread,
|
||||
base::Unretained(this), frame_id));
|
||||
}
|
||||
|
||||
|
@ -374,8 +374,7 @@ MediaFactory::CreateRendererFactorySelector(
|
||||
// level uses the MediaPlayerRenderer as its underlying media::Renderer.
|
||||
auto mojo_media_player_renderer_factory =
|
||||
std::make_unique<media::MojoRendererFactory>(
|
||||
media::MojoRendererFactory::GetGpuFactoriesCB(),
|
||||
remote_interfaces_->get());
|
||||
media::MojoRendererFactory::GetGpuFactoriesCB(), remote_interfaces_);
|
||||
|
||||
// Always give |factory_selector| a MediaPlayerRendererClient factory. WMPI
|
||||
// might fallback to it if the final redirected URL is an HLS url.
|
||||
@ -567,8 +566,7 @@ media::mojom::VideoDecodeStatsRecorderPtr
|
||||
MediaFactory::CreateVideoDecodeStatsRecorder() {
|
||||
DCHECK(remote_interfaces_);
|
||||
media::mojom::VideoDecodeStatsRecorderPtr recorder_ptr;
|
||||
service_manager::GetInterface(remote_interfaces_->get(), &recorder_ptr);
|
||||
|
||||
remote_interfaces_->GetInterface(&recorder_ptr);
|
||||
return recorder_ptr;
|
||||
}
|
||||
|
||||
|
@ -3869,7 +3869,8 @@ void RenderFrameImpl::DidFailProvisionalLoad(
|
||||
|
||||
void RenderFrameImpl::DidCommitProvisionalLoad(
|
||||
const blink::WebHistoryItem& item,
|
||||
blink::WebHistoryCommitType commit_type) {
|
||||
blink::WebHistoryCommitType commit_type,
|
||||
blink::WebGlobalObjectReusePolicy global_object_reuse_policy) {
|
||||
TRACE_EVENT2("navigation,rail", "RenderFrameImpl::didCommitProvisionalLoad",
|
||||
"id", routing_id_,
|
||||
"url", GetLoadingUrl().possibly_invalid_spec());
|
||||
@ -4000,6 +4001,43 @@ void RenderFrameImpl::DidCommitProvisionalLoad(
|
||||
}
|
||||
}
|
||||
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
remote_interface_provider_request;
|
||||
if (!navigation_state->WasWithinSameDocument() &&
|
||||
global_object_reuse_policy !=
|
||||
blink::WebGlobalObjectReusePolicy::kUseExisting) {
|
||||
// If we're navigating to a new document, bind |remote_interfaces_| to a new
|
||||
// message pipe. The request end of the new InterfaceProvider interface will
|
||||
// be sent over as part of DidCommitProvisionalLoad. After the RFHI receives
|
||||
// the commit confirmation, it will immediately close the old message pipe
|
||||
// to avoid GetInterface calls racing with navigation commit, and bind the
|
||||
// request end of the message pipe created here.
|
||||
service_manager::mojom::InterfaceProviderPtr interfaces_provider;
|
||||
remote_interface_provider_request = mojo::MakeRequest(&interfaces_provider);
|
||||
|
||||
// Must initialize |remote_interfaces_| with a new working pipe *before*
|
||||
// observers receive DidCommitProvisionalLoad, so they can already request
|
||||
// remote interfaces. The interface requests will be serviced once the
|
||||
// InterfaceProvider interface request is bound by the RenderFrameHostImpl.
|
||||
remote_interfaces_.Close();
|
||||
remote_interfaces_.Bind(std::move(interfaces_provider));
|
||||
|
||||
// AudioIPCFactory may be null in tests.
|
||||
if (auto* factory = AudioIPCFactory::get()) {
|
||||
// The RendererAudioOutputStreamFactory must be readily accessible on the
|
||||
// IO thread when it's needed, because the main thread may block while
|
||||
// waiting for the factory call to finish on the IO thread, so if we tried
|
||||
// to lazily initialize it, we could deadlock.
|
||||
//
|
||||
// TODO(https://crbug.com/668275): Still, it is odd for one specific
|
||||
// factory to be registered here, make this a RenderFrameObserver.
|
||||
// code.
|
||||
factory->MaybeDeregisterRemoteFactory(GetRoutingID());
|
||||
factory->MaybeRegisterRemoteFactory(GetRoutingID(),
|
||||
GetRemoteInterfaces());
|
||||
}
|
||||
}
|
||||
|
||||
if (commit_type == blink::WebHistoryCommitType::kWebBackForwardCommit)
|
||||
render_view_->DidCommitProvisionalHistoryLoad();
|
||||
|
||||
@ -4036,7 +4074,8 @@ void RenderFrameImpl::DidCommitProvisionalLoad(
|
||||
// new navigation.
|
||||
navigation_state->set_request_committed(true);
|
||||
|
||||
SendDidCommitProvisionalLoad(frame_, commit_type);
|
||||
SendDidCommitProvisionalLoad(frame_, commit_type,
|
||||
std::move(remote_interface_provider_request));
|
||||
|
||||
// Check whether we have new encoding name.
|
||||
UpdateEncoding(frame_, frame_->View()->PageEncoding().Utf8());
|
||||
@ -4282,7 +4321,8 @@ void RenderFrameImpl::DidNavigateWithinPage(
|
||||
static_cast<NavigationStateImpl*>(document_state->navigation_state())
|
||||
->set_was_within_same_document(true);
|
||||
|
||||
DidCommitProvisionalLoad(item, commit_type);
|
||||
DidCommitProvisionalLoad(item, commit_type,
|
||||
blink::WebGlobalObjectReusePolicy::kUseExisting);
|
||||
}
|
||||
|
||||
void RenderFrameImpl::DidUpdateCurrentHistoryItem() {
|
||||
@ -5121,7 +5161,9 @@ const RenderFrameImpl* RenderFrameImpl::GetLocalRoot() const {
|
||||
// Tell the embedding application that the URL of the active page has changed.
|
||||
void RenderFrameImpl::SendDidCommitProvisionalLoad(
|
||||
blink::WebLocalFrame* frame,
|
||||
blink::WebHistoryCommitType commit_type) {
|
||||
blink::WebHistoryCommitType commit_type,
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
remote_interface_provider_request) {
|
||||
DCHECK_EQ(frame_, frame);
|
||||
WebDocumentLoader* document_loader = frame->GetDocumentLoader();
|
||||
DCHECK(document_loader);
|
||||
@ -5342,7 +5384,8 @@ void RenderFrameImpl::SendDidCommitProvisionalLoad(
|
||||
// allowPlugins() for the new page. This ensures that when these functions
|
||||
// send ViewHostMsg_ContentBlocked messages, those arrive after the browser
|
||||
// process has already been informed of the provisional load committing.
|
||||
GetFrameHost()->DidCommitProvisionalLoad(std::move(params));
|
||||
GetFrameHost()->DidCommitProvisionalLoad(
|
||||
std::move(params), std::move(remote_interface_provider_request));
|
||||
|
||||
// If we end up reusing this WebRequest (for example, due to a #ref click),
|
||||
// we don't want the transition type to persist. Just clear it.
|
||||
|
@ -609,7 +609,8 @@ class CONTENT_EXPORT RenderFrameImpl
|
||||
blink::WebHistoryCommitType commit_type) override;
|
||||
void DidCommitProvisionalLoad(
|
||||
const blink::WebHistoryItem& item,
|
||||
blink::WebHistoryCommitType commit_type) override;
|
||||
blink::WebHistoryCommitType commit_type,
|
||||
blink::WebGlobalObjectReusePolicy global_object_reuse_policy) override;
|
||||
void DidCreateNewDocument() override;
|
||||
void DidClearWindowObject() override;
|
||||
void DidCreateDocumentElement() override;
|
||||
@ -941,8 +942,11 @@ class CONTENT_EXPORT RenderFrameImpl
|
||||
const RenderFrameImpl* GetLocalRoot() const;
|
||||
|
||||
// Builds and sends DidCommitProvisionalLoad to the host.
|
||||
void SendDidCommitProvisionalLoad(blink::WebLocalFrame* frame,
|
||||
blink::WebHistoryCommitType commit_type);
|
||||
void SendDidCommitProvisionalLoad(
|
||||
blink::WebLocalFrame* frame,
|
||||
blink::WebHistoryCommitType commit_type,
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
remote_interface_provider_request);
|
||||
|
||||
// Swaps the current frame into the frame tree, replacing the
|
||||
// RenderFrameProxy it is associated with. Return value indicates whether
|
||||
|
@ -3,17 +3,23 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/debug/leak_annotations.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/test/bind_test_util.h"
|
||||
#include "build/build_config.h"
|
||||
#include "content/common/frame_messages.h"
|
||||
#include "content/common/frame_owner_properties.h"
|
||||
#include "content/common/renderer.mojom.h"
|
||||
#include "content/common/view_messages.h"
|
||||
#include "content/public/common/previews_state.h"
|
||||
#include "content/public/renderer/content_renderer_client.h"
|
||||
#include "content/public/renderer/document_state.h"
|
||||
#include "content/public/test/frame_load_waiter.h"
|
||||
#include "content/public/test/render_view_test.h"
|
||||
@ -24,6 +30,11 @@
|
||||
#include "content/renderer/render_frame_impl.h"
|
||||
#include "content/renderer/render_view_impl.h"
|
||||
#include "content/test/fake_compositor_dependencies.h"
|
||||
#include "content/test/frame_host_test_interface.mojom.h"
|
||||
#include "content/test/test_render_frame.h"
|
||||
#include "services/service_manager/public/cpp/interface_provider.h"
|
||||
#include "services/service_manager/public/interfaces/interface_provider.mojom.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "third_party/WebKit/public/platform/WebEffectiveConnectionType.h"
|
||||
#include "third_party/WebKit/public/platform/WebRuntimeFeatures.h"
|
||||
@ -38,15 +49,17 @@
|
||||
using blink::WebString;
|
||||
using blink::WebURLRequest;
|
||||
|
||||
namespace {
|
||||
const int32_t kSubframeRouteId = 20;
|
||||
const int32_t kSubframeWidgetRouteId = 21;
|
||||
const int32_t kFrameProxyRouteId = 22;
|
||||
const int32_t kEmbeddedSubframeRouteId = 23;
|
||||
} // namespace
|
||||
|
||||
namespace content {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int32_t kSubframeRouteId = 20;
|
||||
constexpr int32_t kSubframeWidgetRouteId = 21;
|
||||
constexpr int32_t kFrameProxyRouteId = 22;
|
||||
constexpr int32_t kEmbeddedSubframeRouteId = 23;
|
||||
|
||||
} // namespace
|
||||
|
||||
// RenderFrameImplTest creates a RenderFrameImpl that is a child of the
|
||||
// main frame, and has its own RenderWidget. This behaves like an out
|
||||
// of process frame even though it is in the same process as its parent.
|
||||
@ -84,7 +97,8 @@ class RenderFrameImplTest : public RenderViewTest {
|
||||
base::UnguessableToken::Create(), frame_replication_state,
|
||||
&compositor_deps_, widget_params, FrameOwnerProperties());
|
||||
|
||||
frame_ = RenderFrameImpl::FromRoutingID(kSubframeRouteId);
|
||||
frame_ = static_cast<TestRenderFrame*>(
|
||||
RenderFrameImpl::FromRoutingID(kSubframeRouteId));
|
||||
EXPECT_FALSE(frame_->is_main_frame_);
|
||||
}
|
||||
|
||||
@ -106,11 +120,11 @@ class RenderFrameImplTest : public RenderViewTest {
|
||||
frame->effective_connection_type_ = type;
|
||||
}
|
||||
|
||||
RenderFrameImpl* GetMainRenderFrame() {
|
||||
return static_cast<RenderFrameImpl*>(view_->GetMainRenderFrame());
|
||||
TestRenderFrame* GetMainRenderFrame() {
|
||||
return static_cast<TestRenderFrame*>(view_->GetMainRenderFrame());
|
||||
}
|
||||
|
||||
RenderFrameImpl* frame() { return frame_; }
|
||||
TestRenderFrame* frame() { return frame_; }
|
||||
|
||||
content::RenderWidget* frame_widget() const {
|
||||
return frame_->render_widget_.get();
|
||||
@ -125,7 +139,7 @@ class RenderFrameImplTest : public RenderViewTest {
|
||||
#endif
|
||||
|
||||
private:
|
||||
RenderFrameImpl* frame_;
|
||||
TestRenderFrame* frame_;
|
||||
FakeCompositorDependencies compositor_deps_;
|
||||
};
|
||||
|
||||
@ -251,7 +265,9 @@ TEST_F(RenderFrameImplTest, LoFiNotUpdatedOnSubframeCommits) {
|
||||
static_cast<NavigationStateImpl*>(document_state->navigation_state())
|
||||
->set_was_within_same_document(false);
|
||||
|
||||
frame()->DidCommitProvisionalLoad(item, blink::kWebStandardCommit);
|
||||
frame()->DidCommitProvisionalLoad(
|
||||
item, blink::kWebStandardCommit,
|
||||
blink::WebGlobalObjectReusePolicy::kCreateNew);
|
||||
EXPECT_EQ(SERVER_LOFI_ON, frame()->GetPreviewsState());
|
||||
|
||||
// The main frame's LoFi state should be reset to off on commit.
|
||||
@ -262,8 +278,9 @@ TEST_F(RenderFrameImplTest, LoFiNotUpdatedOnSubframeCommits) {
|
||||
|
||||
// Calling didCommitProvisionalLoad is not representative of a full navigation
|
||||
// but serves the purpose of testing the LoFi state logic.
|
||||
GetMainRenderFrame()->DidCommitProvisionalLoad(item,
|
||||
blink::kWebStandardCommit);
|
||||
GetMainRenderFrame()->DidCommitProvisionalLoad(
|
||||
item, blink::kWebStandardCommit,
|
||||
blink::WebGlobalObjectReusePolicy::kCreateNew);
|
||||
EXPECT_EQ(PREVIEWS_OFF, GetMainRenderFrame()->GetPreviewsState());
|
||||
// The subframe would be deleted here after a cross-document navigation. It
|
||||
// happens to be left around in this test because this does not simulate the
|
||||
@ -309,7 +326,9 @@ TEST_F(RenderFrameImplTest, EffectiveConnectionType) {
|
||||
static_cast<NavigationStateImpl*>(document_state->navigation_state())
|
||||
->set_was_within_same_document(false);
|
||||
|
||||
frame()->DidCommitProvisionalLoad(item, blink::kWebStandardCommit);
|
||||
frame()->DidCommitProvisionalLoad(
|
||||
item, blink::kWebStandardCommit,
|
||||
blink::WebGlobalObjectReusePolicy::kCreateNew);
|
||||
EXPECT_EQ(tests[i].type, frame()->GetEffectiveConnectionType());
|
||||
|
||||
// The main frame's effective connection type should be reset on commit.
|
||||
@ -318,8 +337,9 @@ TEST_F(RenderFrameImplTest, EffectiveConnectionType) {
|
||||
static_cast<NavigationStateImpl*>(document_state->navigation_state())
|
||||
->set_was_within_same_document(false);
|
||||
|
||||
GetMainRenderFrame()->DidCommitProvisionalLoad(item,
|
||||
blink::kWebStandardCommit);
|
||||
GetMainRenderFrame()->DidCommitProvisionalLoad(
|
||||
item, blink::kWebStandardCommit,
|
||||
blink::WebGlobalObjectReusePolicy::kCreateNew);
|
||||
EXPECT_EQ(blink::WebEffectiveConnectionType::kTypeUnknown,
|
||||
GetMainRenderFrame()->GetEffectiveConnectionType());
|
||||
|
||||
@ -617,4 +637,569 @@ TEST_F(RenderFrameImplTest, ShouldUseClientLoFiForRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
// RenderFrameRemoteInterfacesTest ------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kTestFirstURL[] = "http://foo.com/1";
|
||||
constexpr char kTestSecondURL[] = "http://foo.com/2";
|
||||
// constexpr char kTestCrossOriginURL[] = "http://bar.com/";
|
||||
constexpr char kAboutBlankURL[] = "about:blank";
|
||||
|
||||
constexpr char kFrameEventDidCreateNewFrame[] = "did-create-new-frame";
|
||||
constexpr char kFrameEventDidCreateNewDocument[] = "did-create-new-document";
|
||||
constexpr char kFrameEventDidCreateDocumentElement[] =
|
||||
"did-create-document-element";
|
||||
constexpr char kFrameEventWillCommitProvisionalLoad[] =
|
||||
"will-commit-provisional-load";
|
||||
constexpr char kFrameEventDidCommitProvisionalLoad[] =
|
||||
"did-commit-provisional-load";
|
||||
constexpr char kFrameEventDidCommitSameDocumentLoad[] =
|
||||
"did-commit-same-document-load";
|
||||
constexpr char kFrameEventAfterCommit[] = "after-commit";
|
||||
|
||||
constexpr char kNoDocumentMarkerURL[] = "data:,No document.";
|
||||
|
||||
// A simple testing implementation of mojom::InterfaceProvider that binds
|
||||
// interface requests only for one hard-coded kind of interface.
|
||||
class TestSimpleInterfaceProviderImpl
|
||||
: public service_manager::mojom::InterfaceProvider {
|
||||
public:
|
||||
using BinderCallback =
|
||||
base::RepeatingCallback<void(mojo::ScopedMessagePipeHandle)>;
|
||||
|
||||
// Incoming interface requests for |interface_name| will invoke |binder|.
|
||||
// Everything else is ignored.
|
||||
TestSimpleInterfaceProviderImpl(const std::string& interface_name,
|
||||
BinderCallback binder_callback)
|
||||
: binding_(this),
|
||||
interface_name_(interface_name),
|
||||
binder_callback_(binder_callback) {}
|
||||
|
||||
void BindAndFlush(service_manager::mojom::InterfaceProviderRequest request) {
|
||||
ASSERT_FALSE(binding_.is_bound());
|
||||
binding_.Bind(std::move(request));
|
||||
binding_.FlushForTesting();
|
||||
}
|
||||
|
||||
private:
|
||||
// mojom::InterfaceProvider:
|
||||
void GetInterface(const std::string& interface_name,
|
||||
mojo::ScopedMessagePipeHandle handle) override {
|
||||
if (interface_name == interface_name_)
|
||||
binder_callback_.Run(std::move(handle));
|
||||
}
|
||||
|
||||
mojo::Binding<service_manager::mojom::InterfaceProvider> binding_;
|
||||
|
||||
std::string interface_name_;
|
||||
BinderCallback binder_callback_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestSimpleInterfaceProviderImpl);
|
||||
};
|
||||
|
||||
// Used to annotate the source of an interface request.
|
||||
struct SourceAnnotation {
|
||||
// The URL of the active document in the frame, at the time the interface was
|
||||
// requested by the RenderFrame.
|
||||
GURL document_url;
|
||||
|
||||
// The RenderFrameObserver event in response to which the interface is
|
||||
// requested by the RenderFrame.
|
||||
std::string render_frame_event;
|
||||
|
||||
bool operator==(const SourceAnnotation& rhs) const {
|
||||
return document_url == rhs.document_url &&
|
||||
render_frame_event == rhs.render_frame_event;
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const SourceAnnotation& a) {
|
||||
return os << "[" << a.document_url << ", " << a.render_frame_event << "]";
|
||||
}
|
||||
|
||||
class FrameHostTestInterfaceImpl : public mojom::FrameHostTestInterface {
|
||||
public:
|
||||
FrameHostTestInterfaceImpl() : binding_(this) {}
|
||||
~FrameHostTestInterfaceImpl() override {}
|
||||
|
||||
void BindAndFlush(mojom::FrameHostTestInterfaceRequest request) {
|
||||
binding_.Bind(std::move(request));
|
||||
binding_.WaitForIncomingMethodCall();
|
||||
}
|
||||
|
||||
const base::Optional<SourceAnnotation>& ping_source() const {
|
||||
return ping_source_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void Ping(const GURL& url, const std::string& event) override {
|
||||
ping_source_ = SourceAnnotation{url, event};
|
||||
}
|
||||
|
||||
private:
|
||||
mojo::Binding<mojom::FrameHostTestInterface> binding_;
|
||||
base::Optional<SourceAnnotation> ping_source_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FrameHostTestInterfaceImpl);
|
||||
};
|
||||
|
||||
// RenderFrameObserver that issues FrameHostTestInterface interface requests
|
||||
// through the RenderFrame's |remote_interfaces_| in response to observing
|
||||
// important milestones in a frame's lifecycle.
|
||||
class FrameHostTestInterfaceRequestIssuer : public RenderFrameObserver {
|
||||
public:
|
||||
explicit FrameHostTestInterfaceRequestIssuer(RenderFrame* render_frame)
|
||||
: RenderFrameObserver(render_frame) {}
|
||||
|
||||
void RequestTestInterfaceOnFrameEvent(const std::string& event) {
|
||||
mojom::FrameHostTestInterfacePtr ptr;
|
||||
render_frame()->GetRemoteInterfaces()->GetInterface(
|
||||
mojo::MakeRequest(&ptr));
|
||||
|
||||
blink::WebDocument document = render_frame()->GetWebFrame()->GetDocument();
|
||||
ptr->Ping(
|
||||
!document.IsNull() ? GURL(document.Url()) : GURL(kNoDocumentMarkerURL),
|
||||
event);
|
||||
}
|
||||
|
||||
private:
|
||||
// RenderFrameObserver:
|
||||
void OnDestruct() override {}
|
||||
|
||||
void DidCreateDocumentElement() override {
|
||||
RequestTestInterfaceOnFrameEvent(kFrameEventDidCreateDocumentElement);
|
||||
}
|
||||
|
||||
void DidCreateNewDocument() override {
|
||||
RequestTestInterfaceOnFrameEvent(kFrameEventDidCreateNewDocument);
|
||||
}
|
||||
|
||||
void WillCommitProvisionalLoad() override {
|
||||
RequestTestInterfaceOnFrameEvent(kFrameEventWillCommitProvisionalLoad);
|
||||
}
|
||||
|
||||
void DidStartProvisionalLoad(
|
||||
blink::WebDocumentLoader* document_loader) override {}
|
||||
|
||||
void DidFailProvisionalLoad(const blink::WebURLError& error) override {}
|
||||
|
||||
void DidCommitProvisionalLoad(bool is_new_navigation,
|
||||
bool is_same_document_navigation) override {
|
||||
RequestTestInterfaceOnFrameEvent(is_same_document_navigation
|
||||
? kFrameEventDidCommitSameDocumentLoad
|
||||
: kFrameEventDidCommitProvisionalLoad);
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FrameHostTestInterfaceRequestIssuer);
|
||||
};
|
||||
|
||||
// RenderFrameObserver that can be used to wait for the next commit in a frame.
|
||||
class FrameCommitWaiter : public RenderFrameObserver {
|
||||
public:
|
||||
explicit FrameCommitWaiter(RenderFrame* render_frame)
|
||||
: RenderFrameObserver(render_frame) {}
|
||||
|
||||
void Wait() {
|
||||
if (did_commit_)
|
||||
return;
|
||||
run_loop_.Run();
|
||||
}
|
||||
|
||||
private:
|
||||
// RenderFrameObserver:
|
||||
void OnDestruct() override {}
|
||||
|
||||
void DidCommitProvisionalLoad(bool is_new_navigation,
|
||||
bool is_same_document_navigation) override {
|
||||
did_commit_ = true;
|
||||
run_loop_.Quit();
|
||||
}
|
||||
|
||||
base::RunLoop run_loop_;
|
||||
bool did_commit_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FrameCommitWaiter);
|
||||
};
|
||||
|
||||
// Testing ContentRendererClient implementation that fires the |callback|
|
||||
// whenever a new frame is created.
|
||||
class FrameCreationObservingRendererClient : public ContentRendererClient {
|
||||
public:
|
||||
using FrameCreatedCallback = base::RepeatingCallback<void(TestRenderFrame*)>;
|
||||
|
||||
FrameCreationObservingRendererClient() {}
|
||||
~FrameCreationObservingRendererClient() override {}
|
||||
|
||||
void set_callback(FrameCreatedCallback callback) {
|
||||
callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
void reset_callback() { callback_.Reset(); }
|
||||
|
||||
protected:
|
||||
void RenderFrameCreated(RenderFrame* render_frame) override {
|
||||
ContentRendererClient::RenderFrameCreated(render_frame);
|
||||
if (callback_)
|
||||
callback_.Run(static_cast<TestRenderFrame*>(render_frame));
|
||||
}
|
||||
|
||||
private:
|
||||
FrameCreatedCallback callback_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FrameCreationObservingRendererClient);
|
||||
};
|
||||
|
||||
// Expects observing the creation of a new frame, and creates an instance of
|
||||
// FrameHostTestInterfaceRequestIssuerRenderFrame for that new frame to exercise
|
||||
// its RemoteInterfaceProvider interface.
|
||||
class ScopedNewFrameInterfaceProviderExerciser {
|
||||
public:
|
||||
explicit ScopedNewFrameInterfaceProviderExerciser(
|
||||
FrameCreationObservingRendererClient* frame_creation_observer,
|
||||
const base::Optional<GURL>& url_override_for_first_load = base::nullopt)
|
||||
: frame_creation_observer_(frame_creation_observer),
|
||||
url_override_for_first_load_(url_override_for_first_load) {
|
||||
frame_creation_observer_->set_callback(base::BindRepeating(
|
||||
&ScopedNewFrameInterfaceProviderExerciser::OnFrameCreated,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
~ScopedNewFrameInterfaceProviderExerciser() {
|
||||
frame_creation_observer_->reset_callback();
|
||||
}
|
||||
|
||||
void ExpectNewFrameAndWaitForLoad(const GURL& expected_loaded_url) {
|
||||
ASSERT_NE(nullptr, frame_);
|
||||
frame_commit_waiter_->Wait();
|
||||
|
||||
ASSERT_FALSE(frame_->current_history_item().IsNull());
|
||||
ASSERT_FALSE(frame_->GetWebFrame()->GetDocument().IsNull());
|
||||
EXPECT_EQ(expected_loaded_url,
|
||||
GURL(frame_->GetWebFrame()->GetDocument().Url()));
|
||||
|
||||
interface_request_for_first_document_ =
|
||||
frame_->TakeLastInterfaceProviderRequest();
|
||||
}
|
||||
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
interface_request_for_initial_empty_document() {
|
||||
return std::move(interface_request_for_initial_empty_document_);
|
||||
};
|
||||
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
interface_request_for_first_document() {
|
||||
return std::move(interface_request_for_first_document_);
|
||||
}
|
||||
|
||||
private:
|
||||
void OnFrameCreated(TestRenderFrame* frame) {
|
||||
ASSERT_EQ(nullptr, frame_);
|
||||
frame_ = frame;
|
||||
frame_commit_waiter_.emplace(frame);
|
||||
|
||||
if (url_override_for_first_load_.has_value()) {
|
||||
frame_->SetURLOverrideForNextWebURLRequest(
|
||||
std::move(url_override_for_first_load_).value());
|
||||
}
|
||||
|
||||
// The FrameHostTestInterfaceRequestIssuer needs to stay alive even after
|
||||
// this method returns, so that it continues to observe RenderFrame
|
||||
// lifecycle events and request test interfaces in response.
|
||||
test_request_issuer_.emplace(frame);
|
||||
test_request_issuer_->RequestTestInterfaceOnFrameEvent(
|
||||
kFrameEventDidCreateNewFrame);
|
||||
|
||||
interface_request_for_initial_empty_document_ =
|
||||
frame->TakeLastInterfaceProviderRequest();
|
||||
EXPECT_TRUE(frame->current_history_item().IsNull());
|
||||
}
|
||||
|
||||
FrameCreationObservingRendererClient* frame_creation_observer_;
|
||||
TestRenderFrame* frame_ = nullptr;
|
||||
base::Optional<GURL> url_override_for_first_load_;
|
||||
GURL first_committed_url_;
|
||||
|
||||
base::Optional<FrameCommitWaiter> frame_commit_waiter_;
|
||||
base::Optional<FrameHostTestInterfaceRequestIssuer> test_request_issuer_;
|
||||
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
interface_request_for_initial_empty_document_;
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
interface_request_for_first_document_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedNewFrameInterfaceProviderExerciser);
|
||||
};
|
||||
|
||||
// Extracts all interface requests for FrameHostTestInterface pending on the
|
||||
// specified |interface_provider_request|, and returns a list of the source
|
||||
// annotations that are provided in the pending Ping() call for each of these
|
||||
// FrameHostTestInterface requests.
|
||||
void ExpectPendingInterfaceRequestsFromSources(
|
||||
service_manager::mojom::InterfaceProviderRequest interface_provider_request,
|
||||
std::vector<SourceAnnotation> expected_sources) {
|
||||
std::vector<SourceAnnotation> sources;
|
||||
ASSERT_TRUE(interface_provider_request.is_pending());
|
||||
TestSimpleInterfaceProviderImpl provider(
|
||||
mojom::FrameHostTestInterface::Name_,
|
||||
base::BindLambdaForTesting(
|
||||
[&sources](mojo::ScopedMessagePipeHandle handle) {
|
||||
FrameHostTestInterfaceImpl impl;
|
||||
impl.BindAndFlush(
|
||||
mojom::FrameHostTestInterfaceRequest(std::move(handle)));
|
||||
ASSERT_TRUE(impl.ping_source().has_value());
|
||||
sources.push_back(impl.ping_source().value());
|
||||
}));
|
||||
provider.BindAndFlush(std::move(interface_provider_request));
|
||||
EXPECT_THAT(sources, ::testing::ElementsAreArray(expected_sources));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class RenderFrameRemoteInterfacesTest : public RenderViewTest {
|
||||
public:
|
||||
RenderFrameRemoteInterfacesTest() {}
|
||||
~RenderFrameRemoteInterfacesTest() override {}
|
||||
|
||||
protected:
|
||||
void SetUp() override {
|
||||
RenderViewTest::SetUp();
|
||||
LoadHTML("Nothing to see here.");
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
#if defined(LEAK_SANITIZER)
|
||||
// Do this before shutting down V8 in RenderViewTest::TearDown().
|
||||
// http://crbug.com/328552
|
||||
__lsan_do_leak_check();
|
||||
#endif
|
||||
RenderViewTest::TearDown();
|
||||
}
|
||||
|
||||
FrameCreationObservingRendererClient* frame_creation_observer() {
|
||||
DCHECK(frame_creation_observer_);
|
||||
return frame_creation_observer_;
|
||||
}
|
||||
|
||||
TestRenderFrame* GetMainRenderFrame() {
|
||||
return static_cast<TestRenderFrame*>(view_->GetMainRenderFrame());
|
||||
}
|
||||
|
||||
ContentRendererClient* CreateContentRendererClient() override {
|
||||
frame_creation_observer_ = new FrameCreationObservingRendererClient();
|
||||
return frame_creation_observer_;
|
||||
}
|
||||
|
||||
private:
|
||||
// Owned by RenderViewTest.
|
||||
FrameCreationObservingRendererClient* frame_creation_observer_ = nullptr;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RenderFrameRemoteInterfacesTest);
|
||||
};
|
||||
|
||||
// Expect that |remote_interfaces_| is bound before the first committed load in
|
||||
// a child frame, and then re-bound on the first commit.
|
||||
TEST_F(RenderFrameRemoteInterfacesTest, ChildFrameAtFirstCommittedLoad) {
|
||||
constexpr struct {
|
||||
const char* main_frame_url_override;
|
||||
const char* child_frame_url;
|
||||
} kTestCases[] = {
|
||||
{kTestFirstURL, kAboutBlankURL},
|
||||
{kTestSecondURL, "data:text/html,Child"},
|
||||
{kAboutBlankURL, kAboutBlankURL},
|
||||
};
|
||||
|
||||
for (const auto& test_case : kTestCases) {
|
||||
SCOPED_TRACE(::testing::Message()
|
||||
<< "main_frame_url = " << test_case.main_frame_url_override
|
||||
<< ", child_frame_url = " << test_case.child_frame_url);
|
||||
|
||||
ScopedNewFrameInterfaceProviderExerciser child_frame_exerciser(
|
||||
frame_creation_observer());
|
||||
const std::string html = base::StringPrintf("<iframe src=\"%s\"></iframe>",
|
||||
test_case.child_frame_url);
|
||||
LoadHTMLWithUrlOverride(html.c_str(), test_case.main_frame_url_override);
|
||||
|
||||
const GURL child_frame_url(test_case.child_frame_url);
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
child_frame_exerciser.ExpectNewFrameAndWaitForLoad(child_frame_url));
|
||||
|
||||
// TODO(https://crbug.com/792410): It is unfortunate how many internal
|
||||
// details of frame/document creation this encodes. Need to decouple.
|
||||
const GURL initial_empty_url(kAboutBlankURL);
|
||||
ExpectPendingInterfaceRequestsFromSources(
|
||||
child_frame_exerciser.interface_request_for_initial_empty_document(),
|
||||
{{GURL(kNoDocumentMarkerURL), kFrameEventDidCreateNewFrame},
|
||||
{initial_empty_url, kFrameEventDidCreateNewDocument},
|
||||
{initial_empty_url, kFrameEventDidCreateDocumentElement},
|
||||
{initial_empty_url, kFrameEventWillCommitProvisionalLoad},
|
||||
// TODO(https://crbug.com/555773): It seems strange that the new
|
||||
// document is created and DidCreateNewDocument is invoked *before* the
|
||||
// provisional load would have even committed.
|
||||
{child_frame_url, kFrameEventDidCreateNewDocument}});
|
||||
ExpectPendingInterfaceRequestsFromSources(
|
||||
child_frame_exerciser.interface_request_for_first_document(),
|
||||
{{child_frame_url, kFrameEventDidCommitProvisionalLoad},
|
||||
{child_frame_url, kFrameEventDidCreateDocumentElement}});
|
||||
}
|
||||
}
|
||||
|
||||
// Expect that |remote_interfaces_| is bound before the first committed load in
|
||||
// the main frame of an opened window, and then re-bound on the first commit.
|
||||
TEST_F(RenderFrameRemoteInterfacesTest,
|
||||
MainFrameOfOpenedWindowAtFirstCommittedLoad) {
|
||||
constexpr struct {
|
||||
const char* main_frame_url_override;
|
||||
const char* new_window_url;
|
||||
} kTestCases[] = {
|
||||
{kTestFirstURL, kAboutBlankURL},
|
||||
{kTestSecondURL, "data:text/html,NewWindow"},
|
||||
{kAboutBlankURL, kAboutBlankURL},
|
||||
};
|
||||
|
||||
for (const auto& test_case : kTestCases) {
|
||||
SCOPED_TRACE(::testing::Message()
|
||||
<< "main_frame_url = " << test_case.main_frame_url_override
|
||||
<< ", new_window_url = " << test_case.new_window_url);
|
||||
|
||||
const GURL new_window_url(test_case.new_window_url);
|
||||
ScopedNewFrameInterfaceProviderExerciser main_frame_exerciser(
|
||||
frame_creation_observer(), new_window_url);
|
||||
const std::string html =
|
||||
base::StringPrintf("<script>window.open(\"%s\", \"_blank\")</script>",
|
||||
test_case.new_window_url);
|
||||
LoadHTMLWithUrlOverride(html.c_str(), test_case.main_frame_url_override);
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
main_frame_exerciser.ExpectNewFrameAndWaitForLoad(new_window_url));
|
||||
|
||||
// The URL of the initial empty document is "" for opened windows, in
|
||||
// contrast to child frames, where it is "about:blank". See
|
||||
// Document::Document and Document::SetURL for more details.
|
||||
//
|
||||
// Furthermore, for main frames, InitializeCoreFrame is invoked first, and
|
||||
// RenderFrameImpl::Initialize is invoked second, in contrast to child
|
||||
// frames where it is vice versa. ContentRendererClient::RenderFrameCreated
|
||||
// is invoked from RenderFrameImpl::Initialize, so we miss the events
|
||||
// related to initial empty document that is created from
|
||||
// InitializeCoreFrame, and there is already a document when
|
||||
// RenderFrameCreated is invoked.
|
||||
//
|
||||
// TODO(https://crbug.com/792410): It is unfortunate how many internal
|
||||
// details of frame/document creation this encodes. Need to decouple.
|
||||
const GURL initial_empty_url;
|
||||
ExpectPendingInterfaceRequestsFromSources(
|
||||
main_frame_exerciser.interface_request_for_initial_empty_document(),
|
||||
{{initial_empty_url, kFrameEventDidCreateNewFrame},
|
||||
{initial_empty_url, kFrameEventWillCommitProvisionalLoad},
|
||||
{new_window_url, kFrameEventDidCreateNewDocument}});
|
||||
ExpectPendingInterfaceRequestsFromSources(
|
||||
main_frame_exerciser.interface_request_for_first_document(),
|
||||
{{new_window_url, kFrameEventDidCommitProvisionalLoad},
|
||||
{new_window_url, kFrameEventDidCreateDocumentElement}});
|
||||
}
|
||||
}
|
||||
|
||||
// Expect that |remote_interfaces_| is not bound to a new pipe if the first
|
||||
// committed load in the child frame has the same security origin as that of the
|
||||
// initial empty document.
|
||||
//
|
||||
// In this case, the LocalDOMWindow object associated with the initial empty
|
||||
// document will be re-used for the newly committed document. Here, we must
|
||||
// continue using the InterfaceProvider connection created for the initial empty
|
||||
// document to support the following use-case:
|
||||
// 1) Parent frame dynamically injects an <iframe>.
|
||||
// 2) The parent frame calls `child.contentDocument.write(...)` to inject
|
||||
// Javascript that may stash objects on the child frame's global object
|
||||
// (LocalDOMWindow). Internally, these objects may be using Mojo services
|
||||
// exposed by the RenderFrameHost. The InterfaceRequests for these services
|
||||
// might still be en-route to the RemnderFrameHost's InterfaceProvider.
|
||||
// 3) The `child` frame commits the first real load, and it is same-origin.
|
||||
// 4) The global object in the child frame's browsing context is re-used.
|
||||
// 5) Javascript objects stashed on the global object should continue to work.
|
||||
//
|
||||
// TODO(https://crbug.com/778318): Once the Window object inheritance is fixed,
|
||||
// add a similar test for: <iframe src="javascript:'html'"></iframe>.
|
||||
TEST_F(RenderFrameRemoteInterfacesTest,
|
||||
ChildFrameReusingWindowOfInitialDocument) {
|
||||
const GURL main_frame_url(kTestFirstURL);
|
||||
const GURL initial_empty_url(kAboutBlankURL);
|
||||
const GURL child_frame_url(kTestSecondURL);
|
||||
|
||||
// Override the URL for the first navigation in the newly created frame to
|
||||
// |child_frame_url|.
|
||||
ScopedNewFrameInterfaceProviderExerciser child_frame_exerciser(
|
||||
frame_creation_observer(), child_frame_url);
|
||||
|
||||
constexpr char kHTML[] = "<iframe srcdoc=\"Foo\"></iframe>";
|
||||
LoadHTMLWithUrlOverride(kHTML, main_frame_url.spec().c_str());
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
child_frame_exerciser.ExpectNewFrameAndWaitForLoad(child_frame_url));
|
||||
|
||||
ExpectPendingInterfaceRequestsFromSources(
|
||||
child_frame_exerciser.interface_request_for_initial_empty_document(),
|
||||
{{GURL(kNoDocumentMarkerURL), kFrameEventDidCreateNewFrame},
|
||||
{initial_empty_url, kFrameEventDidCreateNewDocument},
|
||||
{initial_empty_url, kFrameEventDidCreateDocumentElement},
|
||||
{initial_empty_url, kFrameEventWillCommitProvisionalLoad},
|
||||
{child_frame_url, kFrameEventDidCreateNewDocument},
|
||||
{child_frame_url, kFrameEventDidCommitProvisionalLoad},
|
||||
{child_frame_url, kFrameEventDidCreateDocumentElement}});
|
||||
|
||||
auto request = child_frame_exerciser.interface_request_for_first_document();
|
||||
ASSERT_FALSE(request.is_pending());
|
||||
}
|
||||
|
||||
// Expect that |remote_interfaces_| is bound to a new pipe on cross-document
|
||||
// navigations.
|
||||
TEST_F(RenderFrameRemoteInterfacesTest, ReplacedOnNonSameDocumentNavigation) {
|
||||
LoadHTMLWithUrlOverride("", kTestFirstURL);
|
||||
|
||||
auto interface_provider_request_for_first_document =
|
||||
GetMainRenderFrame()->TakeLastInterfaceProviderRequest();
|
||||
|
||||
FrameHostTestInterfaceRequestIssuer requester(GetMainRenderFrame());
|
||||
requester.RequestTestInterfaceOnFrameEvent(kFrameEventAfterCommit);
|
||||
|
||||
LoadHTMLWithUrlOverride("", kTestSecondURL);
|
||||
|
||||
auto interface_provider_request_for_second_document =
|
||||
GetMainRenderFrame()->TakeLastInterfaceProviderRequest();
|
||||
|
||||
ASSERT_TRUE(interface_provider_request_for_first_document.is_pending());
|
||||
ExpectPendingInterfaceRequestsFromSources(
|
||||
std::move(interface_provider_request_for_first_document),
|
||||
{{GURL(kTestFirstURL), kFrameEventAfterCommit},
|
||||
{GURL(kTestFirstURL), kFrameEventWillCommitProvisionalLoad},
|
||||
{GURL(kTestSecondURL), kFrameEventDidCreateNewDocument}});
|
||||
|
||||
ASSERT_TRUE(interface_provider_request_for_second_document.is_pending());
|
||||
ExpectPendingInterfaceRequestsFromSources(
|
||||
std::move(interface_provider_request_for_second_document),
|
||||
{{GURL(kTestSecondURL), kFrameEventDidCommitProvisionalLoad},
|
||||
{GURL(kTestSecondURL), kFrameEventDidCreateDocumentElement}});
|
||||
}
|
||||
|
||||
// Expect that |remote_interfaces_| is not bound to a new pipe on same-document
|
||||
// navigations, i.e. the existing InterfaceProvider connection is continued to
|
||||
// be used.
|
||||
TEST_F(RenderFrameRemoteInterfacesTest, ReusedOnSameDocumentNavigation) {
|
||||
LoadHTMLWithUrlOverride("", kTestFirstURL);
|
||||
|
||||
auto interface_provider_request =
|
||||
GetMainRenderFrame()->TakeLastInterfaceProviderRequest();
|
||||
|
||||
FrameHostTestInterfaceRequestIssuer requester(GetMainRenderFrame());
|
||||
OnSameDocumentNavigation(GetMainFrame(), true /* is_new_navigation */,
|
||||
true /* is_contenet_initiated */);
|
||||
|
||||
EXPECT_FALSE(
|
||||
GetMainRenderFrame()->TakeLastInterfaceProviderRequest().is_pending());
|
||||
|
||||
ASSERT_TRUE(interface_provider_request.is_pending());
|
||||
ExpectPendingInterfaceRequestsFromSources(
|
||||
std::move(interface_provider_request),
|
||||
{{GURL(kTestFirstURL), kFrameEventDidCommitSameDocumentLoad}});
|
||||
}
|
||||
|
||||
} // namespace content
|
||||
|
@ -443,7 +443,8 @@ void WebFrameTestClient::DidFailProvisionalLoad(
|
||||
|
||||
void WebFrameTestClient::DidCommitProvisionalLoad(
|
||||
const blink::WebHistoryItem& history_item,
|
||||
blink::WebHistoryCommitType history_type) {
|
||||
blink::WebHistoryCommitType history_type,
|
||||
blink::WebGlobalObjectReusePolicy) {
|
||||
if (test_runner()->shouldDumpFrameLoadCallbacks()) {
|
||||
PrintFrameDescription(delegate_, web_frame_test_proxy_base_->web_frame());
|
||||
delegate_->PrintMessage(" - didCommitLoadForFrame\n");
|
||||
|
@ -62,9 +62,9 @@ class WebFrameTestClient : public blink::WebFrameClient {
|
||||
void DidReceiveServerRedirectForProvisionalLoad() override;
|
||||
void DidFailProvisionalLoad(const blink::WebURLError& error,
|
||||
blink::WebHistoryCommitType commit_type) override;
|
||||
void DidCommitProvisionalLoad(
|
||||
const blink::WebHistoryItem& history_item,
|
||||
blink::WebHistoryCommitType history_type) override;
|
||||
void DidCommitProvisionalLoad(const blink::WebHistoryItem& history_item,
|
||||
blink::WebHistoryCommitType history_type,
|
||||
blink::WebGlobalObjectReusePolicy) override;
|
||||
void DidReceiveTitle(const blink::WebString& title,
|
||||
blink::WebTextDirection direction) override;
|
||||
void DidChangeIcon(blink::WebIconURL::Type icon_type) override;
|
||||
|
@ -118,9 +118,12 @@ class WebFrameTestProxy : public Base, public WebFrameTestProxyBase {
|
||||
|
||||
void DidCommitProvisionalLoad(
|
||||
const blink::WebHistoryItem& item,
|
||||
blink::WebHistoryCommitType commit_type) override {
|
||||
test_client()->DidCommitProvisionalLoad(item, commit_type);
|
||||
Base::DidCommitProvisionalLoad(item, commit_type);
|
||||
blink::WebHistoryCommitType commit_type,
|
||||
blink::WebGlobalObjectReusePolicy global_object_reuse_policy) override {
|
||||
test_client()->DidCommitProvisionalLoad(item, commit_type,
|
||||
global_object_reuse_policy);
|
||||
Base::DidCommitProvisionalLoad(item, commit_type,
|
||||
global_object_reuse_policy);
|
||||
}
|
||||
|
||||
void DidReceiveTitle(const blink::WebString& title,
|
||||
|
@ -530,8 +530,12 @@ static_library("browsertest_support") {
|
||||
mojom("content_test_mojo_bindings") {
|
||||
sources = [
|
||||
"../public/test/test_service.mojom",
|
||||
"frame_host_test_interface.mojom",
|
||||
"test_browser_associated_interfaces.mojom",
|
||||
]
|
||||
public_deps = [
|
||||
"//url/mojo:url_mojom_gurl",
|
||||
]
|
||||
}
|
||||
|
||||
mojom("web_ui_test_mojo_bindings") {
|
||||
|
@ -38,10 +38,13 @@ class DidCommitProvisionalLoadInterceptor::FrameAgent
|
||||
// mojom::FrameHostInterceptorForTesting:
|
||||
FrameHost* GetForwardingInterface() override { return impl_; }
|
||||
void DidCommitProvisionalLoad(
|
||||
std::unique_ptr<::FrameHostMsg_DidCommitProvisionalLoad_Params> params)
|
||||
override {
|
||||
interceptor_->WillDispatchDidCommitProvisionalLoad(rfhi_, params.get());
|
||||
GetForwardingInterface()->DidCommitProvisionalLoad(std::move(params));
|
||||
std::unique_ptr<::FrameHostMsg_DidCommitProvisionalLoad_Params> params,
|
||||
::service_manager::mojom::InterfaceProviderRequest
|
||||
interface_provider_request) override {
|
||||
interceptor_->WillDispatchDidCommitProvisionalLoad(
|
||||
rfhi_, params.get(), &interface_provider_request);
|
||||
GetForwardingInterface()->DidCommitProvisionalLoad(
|
||||
std::move(params), std::move(interface_provider_request));
|
||||
}
|
||||
|
||||
private:
|
||||
@ -56,8 +59,10 @@ class DidCommitProvisionalLoadInterceptor::FrameAgent
|
||||
DidCommitProvisionalLoadInterceptor::DidCommitProvisionalLoadInterceptor(
|
||||
WebContents* web_contents)
|
||||
: WebContentsObserver(web_contents) {
|
||||
for (auto* rfh : web_contents->GetAllFrames())
|
||||
RenderFrameCreated(rfh);
|
||||
for (auto* rfh : web_contents->GetAllFrames()) {
|
||||
if (rfh->IsRenderFrameLive())
|
||||
RenderFrameCreated(rfh);
|
||||
}
|
||||
}
|
||||
|
||||
DidCommitProvisionalLoadInterceptor::~DidCommitProvisionalLoadInterceptor() =
|
||||
|
@ -27,11 +27,13 @@ class DidCommitProvisionalLoadInterceptor : public WebContentsObserver {
|
||||
explicit DidCommitProvisionalLoadInterceptor(WebContents* web_contents);
|
||||
~DidCommitProvisionalLoadInterceptor() override;
|
||||
|
||||
// Called just before DidCommitProvisionalLoad with |params| would be
|
||||
// dispatched to |render_frame_host|.
|
||||
// Called just before DidCommitProvisionalLoad with |params| and
|
||||
// |interface_provider_request| would be dispatched to |render_frame_host|.
|
||||
virtual void WillDispatchDidCommitProvisionalLoad(
|
||||
RenderFrameHost* render_frame_host,
|
||||
::FrameHostMsg_DidCommitProvisionalLoad_Params* params) = 0;
|
||||
::FrameHostMsg_DidCommitProvisionalLoad_Params* params,
|
||||
service_manager::mojom::InterfaceProviderRequest*
|
||||
interface_provider_request) = 0;
|
||||
|
||||
private:
|
||||
class FrameAgent;
|
||||
|
19
content/test/frame_host_test_interface.mojom
Normal file
19
content/test/frame_host_test_interface.mojom
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
module content.mojom;
|
||||
|
||||
import "url/mojo/url.mojom";
|
||||
|
||||
// Test interface used in RenderFrame and RenderFrameHost tests to exercise
|
||||
// requesting document-scoped interfaces from the RenderFrameHost through
|
||||
// the InterfaceProvider interface.
|
||||
//
|
||||
// The `Ping` method is invoked by clients immediately after making the
|
||||
// FrameHostTestInterfaceRequest, so as to annotate where the request
|
||||
// originates from. This allows verification that the request was delivered /
|
||||
// not delivered to a certain InterfaceProvider implementation.
|
||||
interface FrameHostTestInterface {
|
||||
Ping(url.mojom.Url source_url, string source_event);
|
||||
};
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "content/test/test_render_frame.h"
|
||||
|
||||
#include "base/debug/stack_trace.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "content/common/frame_messages.h"
|
||||
@ -28,6 +29,21 @@ class MockFrameHost : public mojom::FrameHost {
|
||||
return std::move(last_commit_params_);
|
||||
}
|
||||
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
TakeLastInterfaceProviderRequest() {
|
||||
return std::move(last_interface_provider_request_);
|
||||
}
|
||||
|
||||
// Holds on to the request end of the InterfaceProvider interface whose client
|
||||
// end is bound to the corresponding RenderFrame's |remote_interfaces_| to
|
||||
// facilitate retrieving the most recent |interface_provider_request| in
|
||||
// tests.
|
||||
void PassLastInterfaceProviderRequest(
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
interface_provider_request) {
|
||||
last_interface_provider_request_ = std::move(interface_provider_request);
|
||||
}
|
||||
|
||||
protected:
|
||||
// mojom::FrameHost:
|
||||
void CreateNewWindow(mojom::CreateNewWindowParamsPtr,
|
||||
@ -49,9 +65,10 @@ class MockFrameHost : public mojom::FrameHost {
|
||||
void IssueKeepAliveHandle(mojom::KeepAliveHandleRequest request) override {}
|
||||
|
||||
void DidCommitProvisionalLoad(
|
||||
std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params)
|
||||
override {
|
||||
std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params,
|
||||
service_manager::mojom::InterfaceProviderRequest request) override {
|
||||
last_commit_params_ = std::move(params);
|
||||
last_interface_provider_request_ = std::move(request);
|
||||
}
|
||||
|
||||
void BeginNavigation(const CommonNavigationParams& common_params,
|
||||
@ -67,6 +84,8 @@ class MockFrameHost : public mojom::FrameHost {
|
||||
private:
|
||||
std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params>
|
||||
last_commit_params_;
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
last_interface_provider_request_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MockFrameHost);
|
||||
};
|
||||
@ -79,10 +98,26 @@ RenderFrameImpl* TestRenderFrame::CreateTestRenderFrame(
|
||||
|
||||
TestRenderFrame::TestRenderFrame(RenderFrameImpl::CreateParams params)
|
||||
: RenderFrameImpl(std::move(params)),
|
||||
mock_frame_host_(std::make_unique<MockFrameHost>()) {}
|
||||
mock_frame_host_(std::make_unique<MockFrameHost>()) {
|
||||
MockRenderThread* mock_render_thread =
|
||||
static_cast<MockRenderThread*>(RenderThread::Get());
|
||||
mock_frame_host_->PassLastInterfaceProviderRequest(
|
||||
mock_render_thread->TakeInitialInterfaceProviderRequestForFrame(
|
||||
params.routing_id));
|
||||
}
|
||||
|
||||
TestRenderFrame::~TestRenderFrame() {}
|
||||
|
||||
void TestRenderFrame::SetURLOverrideForNextWebURLRequest(const GURL& url) {
|
||||
next_request_url_override_ = url;
|
||||
}
|
||||
|
||||
void TestRenderFrame::WillSendRequest(blink::WebURLRequest& request) {
|
||||
if (next_request_url_override_.has_value())
|
||||
request.SetURL(std::move(next_request_url_override_).value());
|
||||
RenderFrameImpl::WillSendRequest(request);
|
||||
}
|
||||
|
||||
void TestRenderFrame::Navigate(const CommonNavigationParams& common_params,
|
||||
const StartNavigationParams& start_params,
|
||||
const RequestNavigationParams& request_params) {
|
||||
@ -139,7 +174,8 @@ blink::WebNavigationPolicy TestRenderFrame::DecidePolicyForNavigation(
|
||||
const blink::WebFrameClient::NavigationPolicyInfo& info) {
|
||||
if (IsBrowserSideNavigationEnabled() &&
|
||||
info.url_request.CheckForBrowserSideNavigation() &&
|
||||
GetWebFrame()->Parent() && info.form.IsNull()) {
|
||||
((GetWebFrame()->Parent() && info.form.IsNull()) ||
|
||||
next_request_url_override_.has_value())) {
|
||||
// RenderViewTest::LoadHTML already disables PlzNavigate for the main frame
|
||||
// requests. However if the loaded html has a subframe, the WebURLRequest
|
||||
// will be created inside Blink and it won't have this flag set.
|
||||
@ -153,6 +189,11 @@ TestRenderFrame::TakeLastCommitParams() {
|
||||
return mock_frame_host_->TakeLastCommitParams();
|
||||
}
|
||||
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
TestRenderFrame::TakeLastInterfaceProviderRequest() {
|
||||
return mock_frame_host_->TakeLastInterfaceProviderRequest();
|
||||
}
|
||||
|
||||
mojom::FrameHost* TestRenderFrame::GetFrameHost() {
|
||||
// Need to mock this interface directly without going through a binding,
|
||||
// otherwise calling its sync methods could lead to a deadlock.
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/optional.h"
|
||||
#include "content/common/frame.mojom.h"
|
||||
#include "content/renderer/render_frame_impl.h"
|
||||
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
|
||||
@ -34,6 +35,12 @@ class TestRenderFrame : public RenderFrameImpl {
|
||||
return current_history_item_;
|
||||
}
|
||||
|
||||
// Overrides the URL in the next WebURLRequest originating from the frame.
|
||||
// This will also short-circuit browser-side navigation for main resource
|
||||
// loads, FrameLoader will always carry out the load renderer-side.
|
||||
void SetURLOverrideForNextWebURLRequest(const GURL& url);
|
||||
|
||||
void WillSendRequest(blink::WebURLRequest& request) override;
|
||||
void Navigate(const CommonNavigationParams& common_params,
|
||||
const StartNavigationParams& start_params,
|
||||
const RequestNavigationParams& request_params);
|
||||
@ -57,12 +64,16 @@ class TestRenderFrame : public RenderFrameImpl {
|
||||
std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params>
|
||||
TakeLastCommitParams();
|
||||
|
||||
service_manager::mojom::InterfaceProviderRequest
|
||||
TakeLastInterfaceProviderRequest();
|
||||
|
||||
private:
|
||||
explicit TestRenderFrame(RenderFrameImpl::CreateParams params);
|
||||
|
||||
mojom::FrameHost* GetFrameHost() override;
|
||||
|
||||
std::unique_ptr<MockFrameHost> mock_frame_host_;
|
||||
base::Optional<GURL> next_request_url_override_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestRenderFrame);
|
||||
};
|
||||
|
@ -450,15 +450,28 @@ void TestRenderFrameHost::SendNavigateWithParameters(
|
||||
|
||||
void TestRenderFrameHost::SendNavigateWithParams(
|
||||
FrameHostMsg_DidCommitProvisionalLoad_Params* params) {
|
||||
service_manager::mojom::InterfaceProviderPtr interface_provider;
|
||||
service_manager::mojom::InterfaceProviderRequest interface_provider_request;
|
||||
if (!params->was_within_same_document)
|
||||
interface_provider_request = mojo::MakeRequest(&interface_provider);
|
||||
|
||||
SendNavigateWithParamsAndInterfaceProvider(
|
||||
params, std::move(interface_provider_request));
|
||||
}
|
||||
|
||||
void TestRenderFrameHost::SendNavigateWithParamsAndInterfaceProvider(
|
||||
FrameHostMsg_DidCommitProvisionalLoad_Params* params,
|
||||
service_manager::mojom::InterfaceProviderRequest request) {
|
||||
if (navigation_handle()) {
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers =
|
||||
new net::HttpResponseHeaders(std::string());
|
||||
response_headers->AddHeader(
|
||||
std::string("Content-Type: ") + contents_mime_type_);
|
||||
response_headers->AddHeader(std::string("Content-Type: ") +
|
||||
contents_mime_type_);
|
||||
navigation_handle()->set_response_headers_for_testing(response_headers);
|
||||
}
|
||||
DidCommitProvisionalLoad(
|
||||
std::make_unique<FrameHostMsg_DidCommitProvisionalLoad_Params>(*params));
|
||||
std::make_unique<FrameHostMsg_DidCommitProvisionalLoad_Params>(*params),
|
||||
std::move(request));
|
||||
last_commit_was_error_page_ = params->url_is_unreachable;
|
||||
}
|
||||
|
||||
|
@ -100,6 +100,9 @@ class TestRenderFrameHost : public RenderFrameHostImpl,
|
||||
const ModificationCallback& callback);
|
||||
void SendNavigateWithParams(
|
||||
FrameHostMsg_DidCommitProvisionalLoad_Params* params);
|
||||
void SendNavigateWithParamsAndInterfaceProvider(
|
||||
FrameHostMsg_DidCommitProvisionalLoad_Params* params,
|
||||
service_manager::mojom::InterfaceProviderRequest request);
|
||||
|
||||
// Simulates a navigation to |url| failing with the error code |error_code|.
|
||||
// DEPRECATED: use NavigationSimulator instead.
|
||||
@ -181,6 +184,9 @@ class TestRenderFrameHost : public RenderFrameHostImpl,
|
||||
return last_commit_was_error_page_;
|
||||
}
|
||||
|
||||
// Exposes the interface registry to be manipulated for testing.
|
||||
service_manager::BinderRegistry& binder_registry() { return *registry_; }
|
||||
|
||||
// Returns a pending InterfaceProvider request that is safe to bind to an
|
||||
// implementation, but will never receive any interface requests.
|
||||
static service_manager::mojom::InterfaceProviderRequest
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "media/renderers/video_overlay_factory.h"
|
||||
#include "mojo/public/cpp/bindings/interface_request.h"
|
||||
#include "services/service_manager/public/cpp/connect.h"
|
||||
#include "services/service_manager/public/interfaces/interface_provider.mojom.h"
|
||||
#include "services/service_manager/public/cpp/interface_provider.h"
|
||||
|
||||
namespace media {
|
||||
|
||||
@ -26,7 +26,7 @@ MojoRendererFactory::MojoRendererFactory(
|
||||
|
||||
MojoRendererFactory::MojoRendererFactory(
|
||||
const GetGpuFactoriesCB& get_gpu_factories_cb,
|
||||
service_manager::mojom::InterfaceProvider* interface_provider)
|
||||
service_manager::InterfaceProvider* interface_provider)
|
||||
: get_gpu_factories_cb_(get_gpu_factories_cb),
|
||||
interface_provider_(interface_provider) {
|
||||
DCHECK(interface_provider_);
|
||||
@ -63,8 +63,7 @@ mojom::RendererPtr MojoRendererFactory::GetRendererPtr() {
|
||||
interface_factory_->CreateRenderer(std::string(),
|
||||
mojo::MakeRequest(&renderer_ptr));
|
||||
} else if (interface_provider_) {
|
||||
service_manager::GetInterface<mojom::Renderer>(interface_provider_,
|
||||
&renderer_ptr);
|
||||
interface_provider_->GetInterface(&renderer_ptr);
|
||||
} else {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
@ -12,10 +12,8 @@
|
||||
#include "media/mojo/interfaces/renderer.mojom.h"
|
||||
|
||||
namespace service_manager {
|
||||
namespace mojom {
|
||||
class InterfaceProvider;
|
||||
}
|
||||
}
|
||||
|
||||
namespace media {
|
||||
|
||||
@ -32,9 +30,8 @@ class MojoRendererFactory : public RendererFactory {
|
||||
|
||||
MojoRendererFactory(const GetGpuFactoriesCB& get_gpu_factories_cb,
|
||||
media::mojom::InterfaceFactory* interface_factory);
|
||||
MojoRendererFactory(
|
||||
const GetGpuFactoriesCB& get_gpu_factories_cb,
|
||||
service_manager::mojom::InterfaceProvider* interface_provider);
|
||||
MojoRendererFactory(const GetGpuFactoriesCB& get_gpu_factories_cb,
|
||||
service_manager::InterfaceProvider* interface_provider);
|
||||
|
||||
~MojoRendererFactory() final;
|
||||
|
||||
@ -54,7 +51,7 @@ class MojoRendererFactory : public RendererFactory {
|
||||
// InterfaceFactory or InterfaceProvider used to create or connect to remote
|
||||
// renderer.
|
||||
media::mojom::InterfaceFactory* interface_factory_ = nullptr;
|
||||
service_manager::mojom::InterfaceProvider* interface_provider_ = nullptr;
|
||||
service_manager::InterfaceProvider* interface_provider_ = nullptr;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MojoRendererFactory);
|
||||
};
|
||||
|
@ -76,9 +76,6 @@ class SERVICE_MANAGER_PUBLIC_CPP_EXPORT InterfaceProvider {
|
||||
// be called before any calls to GetInterface() are made.
|
||||
void Forward(const ForwardCallback& callback);
|
||||
|
||||
// Returns a raw pointer to the remote InterfaceProvider.
|
||||
mojom::InterfaceProvider* get() { return interface_provider_.get(); }
|
||||
|
||||
// Sets a closure to be run when the remote InterfaceProvider pipe is closed.
|
||||
void SetConnectionLostClosure(const base::Closure& connection_lost_closure);
|
||||
|
||||
|
@ -434,7 +434,8 @@ void LocalFrameClientImpl::DispatchDidChangeIcons(IconType type) {
|
||||
|
||||
void LocalFrameClientImpl::DispatchDidCommitLoad(
|
||||
HistoryItem* item,
|
||||
HistoryCommitType commit_type) {
|
||||
HistoryCommitType commit_type,
|
||||
WebGlobalObjectReusePolicy global_object_reuse_policy) {
|
||||
if (!web_frame_->Parent()) {
|
||||
web_frame_->ViewImpl()->DidCommitLoad(commit_type == kStandardCommit,
|
||||
false);
|
||||
@ -442,7 +443,8 @@ void LocalFrameClientImpl::DispatchDidCommitLoad(
|
||||
|
||||
if (web_frame_->Client()) {
|
||||
web_frame_->Client()->DidCommitProvisionalLoad(
|
||||
WebHistoryItem(item), static_cast<WebHistoryCommitType>(commit_type));
|
||||
WebHistoryItem(item), static_cast<WebHistoryCommitType>(commit_type),
|
||||
global_object_reuse_policy);
|
||||
}
|
||||
if (WebDevToolsAgentImpl* dev_tools = DevToolsAgent())
|
||||
dev_tools->DidCommitLoadForLocalFrame(web_frame_->GetFrame());
|
||||
|
@ -102,7 +102,9 @@ class LocalFrameClientImpl final : public LocalFrameClient {
|
||||
ResourceRequest&) override;
|
||||
void DispatchDidReceiveTitle(const String&) override;
|
||||
void DispatchDidChangeIcons(IconType) override;
|
||||
void DispatchDidCommitLoad(HistoryItem*, HistoryCommitType) override;
|
||||
void DispatchDidCommitLoad(HistoryItem*,
|
||||
HistoryCommitType,
|
||||
WebGlobalObjectReusePolicy) override;
|
||||
void DispatchDidFailProvisionalLoad(const ResourceError&,
|
||||
HistoryCommitType) override;
|
||||
void DispatchDidFailLoad(const ResourceError&, HistoryCommitType) override;
|
||||
|
@ -4248,7 +4248,8 @@ class ClearScrollStateOnCommitWebFrameClient
|
||||
|
||||
// FrameTestHelpers::TestWebFrameClient:
|
||||
void DidCommitProvisionalLoad(const WebHistoryItem&,
|
||||
WebHistoryCommitType) override {
|
||||
WebHistoryCommitType,
|
||||
WebGlobalObjectReusePolicy) override {
|
||||
Frame()->View()->ResetScrollAndScaleState();
|
||||
}
|
||||
};
|
||||
@ -6539,7 +6540,8 @@ class TestSubstituteDataWebFrameClient
|
||||
error.url(), true);
|
||||
}
|
||||
void DidCommitProvisionalLoad(const WebHistoryItem&,
|
||||
WebHistoryCommitType) override {
|
||||
WebHistoryCommitType,
|
||||
WebGlobalObjectReusePolicy) override {
|
||||
if (Frame()->GetDocumentLoader()->GetResponse().Url() !=
|
||||
WebURL(URLTestHelpers::ToKURL("about:blank")))
|
||||
commit_called_ = true;
|
||||
@ -6593,7 +6595,8 @@ class TestWillInsertBodyWebFrameClient
|
||||
|
||||
// FrameTestHelpers::TestWebFrameClient:
|
||||
void DidCommitProvisionalLoad(const WebHistoryItem&,
|
||||
WebHistoryCommitType) override {
|
||||
WebHistoryCommitType,
|
||||
WebGlobalObjectReusePolicy) override {
|
||||
num_bodies_ = 0;
|
||||
did_load_ = true;
|
||||
}
|
||||
@ -9429,9 +9432,9 @@ class RemoteToLocalSwapWebFrameClient
|
||||
~RemoteToLocalSwapWebFrameClient() override {}
|
||||
|
||||
// FrameTestHelpers::TestWebFrameClient:
|
||||
void DidCommitProvisionalLoad(
|
||||
const WebHistoryItem&,
|
||||
WebHistoryCommitType history_commit_type) override {
|
||||
void DidCommitProvisionalLoad(const WebHistoryItem&,
|
||||
WebHistoryCommitType history_commit_type,
|
||||
WebGlobalObjectReusePolicy) override {
|
||||
history_commit_type_ = history_commit_type;
|
||||
remote_frame_->Swap(Frame());
|
||||
}
|
||||
@ -9643,9 +9646,9 @@ class CommitTypeWebFrameClient : public FrameTestHelpers::TestWebFrameClient {
|
||||
~CommitTypeWebFrameClient() override {}
|
||||
|
||||
// FrameTestHelpers::TestWebFrameClient:
|
||||
void DidCommitProvisionalLoad(
|
||||
const WebHistoryItem&,
|
||||
WebHistoryCommitType history_commit_type) override {
|
||||
void DidCommitProvisionalLoad(const WebHistoryItem&,
|
||||
WebHistoryCommitType history_commit_type,
|
||||
WebGlobalObjectReusePolicy) override {
|
||||
history_commit_type_ = history_commit_type;
|
||||
}
|
||||
|
||||
@ -10584,7 +10587,8 @@ class CallbackOrderingWebFrameClient
|
||||
EXPECT_EQ(1, callback_count_++);
|
||||
}
|
||||
void DidCommitProvisionalLoad(const WebHistoryItem&,
|
||||
WebHistoryCommitType) override {
|
||||
WebHistoryCommitType,
|
||||
WebGlobalObjectReusePolicy) override {
|
||||
EXPECT_EQ(2, callback_count_++);
|
||||
}
|
||||
void DidFinishDocumentLoad() override { EXPECT_EQ(3, callback_count_++); }
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "public/platform/WebScopedVirtualTimePauser.h"
|
||||
#include "public/platform/WebSuddenTerminationDisablerType.h"
|
||||
#include "public/platform/WebURLRequest.h"
|
||||
#include "public/web/WebGlobalObjectReusePolicy.h"
|
||||
#include "public/web/WebTriggeringEventInfo.h"
|
||||
#include "third_party/WebKit/common/feature_policy/feature_policy.h"
|
||||
#include "v8/include/v8.h"
|
||||
@ -125,7 +126,9 @@ class CORE_EXPORT LocalFrameClient : public FrameClient {
|
||||
ResourceRequest&) = 0;
|
||||
virtual void DispatchDidReceiveTitle(const String&) = 0;
|
||||
virtual void DispatchDidChangeIcons(IconType) = 0;
|
||||
virtual void DispatchDidCommitLoad(HistoryItem*, HistoryCommitType) = 0;
|
||||
virtual void DispatchDidCommitLoad(HistoryItem*,
|
||||
HistoryCommitType,
|
||||
WebGlobalObjectReusePolicy) = 0;
|
||||
virtual void DispatchDidFailProvisionalLoad(const ResourceError&,
|
||||
HistoryCommitType) = 0;
|
||||
virtual void DispatchDidFailLoad(const ResourceError&, HistoryCommitType) = 0;
|
||||
|
@ -677,14 +677,16 @@ void DocumentLoader::CommitNavigation(const AtomicString& mime_type,
|
||||
if (owner_frame && owner_frame->IsLocalFrame())
|
||||
owner_document = ToLocalFrame(owner_frame)->GetDocument();
|
||||
}
|
||||
bool should_reuse_default_view = frame_->ShouldReuseDefaultView(Url());
|
||||
DCHECK(frame_->GetPage());
|
||||
|
||||
ParserSynchronizationPolicy parsing_policy = kAllowAsynchronousParsing;
|
||||
if (!Document::ThreadedParsingEnabledForTesting())
|
||||
parsing_policy = kForceSynchronousParsing;
|
||||
|
||||
InstallNewDocument(Url(), owner_document, should_reuse_default_view,
|
||||
InstallNewDocument(Url(), owner_document,
|
||||
frame_->ShouldReuseDefaultView(Url())
|
||||
? WebGlobalObjectReusePolicy::kUseExisting
|
||||
: WebGlobalObjectReusePolicy::kCreateNew,
|
||||
mime_type, encoding, InstallNewDocumentReason::kNavigation,
|
||||
parsing_policy, overriding_url);
|
||||
parser_->SetDocumentWasLoadedAsPartOfNavigation();
|
||||
@ -950,7 +952,8 @@ void DocumentLoader::WillCommitNavigation() {
|
||||
frame_->GetIdlenessDetector()->WillCommitLoad();
|
||||
}
|
||||
|
||||
void DocumentLoader::DidCommitNavigation() {
|
||||
void DocumentLoader::DidCommitNavigation(
|
||||
WebGlobalObjectReusePolicy global_object_reuse_policy) {
|
||||
if (GetFrameLoader().StateMachine()->CreatingInitialEmptyDocument())
|
||||
return;
|
||||
|
||||
@ -964,7 +967,8 @@ void DocumentLoader::DidCommitNavigation() {
|
||||
frame_->FrameScheduler()->DidCommitProvisionalLoad(
|
||||
commit_type == kHistoryInertCommit, load_type_ == kFrameLoadTypeReload,
|
||||
frame_->IsLocalRoot());
|
||||
GetLocalFrameClient().DispatchDidCommitLoad(history_item_.Get(), commit_type);
|
||||
GetLocalFrameClient().DispatchDidCommitLoad(history_item_.Get(), commit_type,
|
||||
global_object_reuse_policy);
|
||||
|
||||
// When the embedder gets notified (above) that the new navigation has
|
||||
// committed, the embedder will drop the old Content Security Policy and
|
||||
@ -1050,7 +1054,7 @@ bool DocumentLoader::ShouldPersistUserGestureValue(
|
||||
void DocumentLoader::InstallNewDocument(
|
||||
const KURL& url,
|
||||
Document* owner_document,
|
||||
bool should_reuse_default_view,
|
||||
WebGlobalObjectReusePolicy global_object_reuse_policy,
|
||||
const AtomicString& mime_type,
|
||||
const AtomicString& encoding,
|
||||
InstallNewDocumentReason reason,
|
||||
@ -1058,7 +1062,6 @@ void DocumentLoader::InstallNewDocument(
|
||||
const KURL& overriding_url) {
|
||||
DCHECK(!frame_->GetDocument() || !frame_->GetDocument()->IsActive());
|
||||
DCHECK_EQ(frame_->Tree().ChildCount(), 0u);
|
||||
|
||||
if (GetFrameLoader().StateMachine()->IsDisplayingInitialEmptyDocument()) {
|
||||
GetFrameLoader().StateMachine()->AdvanceTo(
|
||||
FrameLoaderStateMachine::kCommittedFirstRealLoad);
|
||||
@ -1076,7 +1079,7 @@ void DocumentLoader::InstallNewDocument(
|
||||
// commits. To make that happen, we "securely transition" the existing
|
||||
// LocalDOMWindow to the Document that results from the network load. See also
|
||||
// Document::IsSecureTransitionTo.
|
||||
if (!should_reuse_default_view)
|
||||
if (global_object_reuse_policy != WebGlobalObjectReusePolicy::kUseExisting)
|
||||
frame_->SetDOMWindow(LocalDOMWindow::Create(*frame_));
|
||||
|
||||
bool user_gesture_bit_set = frame_->HasBeenActivated() ||
|
||||
@ -1122,7 +1125,7 @@ void DocumentLoader::InstallNewDocument(
|
||||
// This must be called before the document is opened, otherwise HTML parser
|
||||
// will use stale values from HTMLParserOption.
|
||||
if (reason == InstallNewDocumentReason::kNavigation)
|
||||
DidCommitNavigation();
|
||||
DidCommitNavigation(global_object_reuse_policy);
|
||||
|
||||
if (document->GetSettings()
|
||||
->GetForceTouchEventFeatureDetectionForInspector()) {
|
||||
@ -1156,10 +1159,10 @@ const AtomicString& DocumentLoader::MimeType() const {
|
||||
void DocumentLoader::ReplaceDocumentWhileExecutingJavaScriptURL(
|
||||
const KURL& url,
|
||||
Document* owner_document,
|
||||
bool should_reuse_default_view,
|
||||
WebGlobalObjectReusePolicy global_object_reuse_policy,
|
||||
const String& source) {
|
||||
InstallNewDocument(url, owner_document, should_reuse_default_view, MimeType(),
|
||||
response_.TextEncodingName(),
|
||||
InstallNewDocument(url, owner_document, global_object_reuse_policy,
|
||||
MimeType(), response_.TextEncodingName(),
|
||||
InstallNewDocumentReason::kJavascriptURL,
|
||||
kForceSynchronousParsing, NullURL());
|
||||
|
||||
|
@ -53,6 +53,7 @@
|
||||
#include "platform/loader/fetch/SubstituteData.h"
|
||||
#include "platform/wtf/HashSet.h"
|
||||
#include "public/platform/WebLoadingBehaviorFlag.h"
|
||||
#include "public/web/WebGlobalObjectReusePolicy.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@ -100,11 +101,10 @@ class CORE_EXPORT DocumentLoader
|
||||
|
||||
unsigned long MainResourceIdentifier() const;
|
||||
|
||||
void ReplaceDocumentWhileExecutingJavaScriptURL(
|
||||
const KURL&,
|
||||
Document* owner_document,
|
||||
bool should_reuse_default_view,
|
||||
const String& source);
|
||||
void ReplaceDocumentWhileExecutingJavaScriptURL(const KURL&,
|
||||
Document* owner_document,
|
||||
WebGlobalObjectReusePolicy,
|
||||
const String& source);
|
||||
|
||||
const AtomicString& MimeType() const;
|
||||
|
||||
@ -257,7 +257,7 @@ class CORE_EXPORT DocumentLoader
|
||||
enum class InstallNewDocumentReason { kNavigation, kJavascriptURL };
|
||||
void InstallNewDocument(const KURL&,
|
||||
Document* owner_document,
|
||||
bool should_reuse_default_view,
|
||||
WebGlobalObjectReusePolicy,
|
||||
const AtomicString& mime_type,
|
||||
const AtomicString& encoding,
|
||||
InstallNewDocumentReason,
|
||||
@ -265,7 +265,7 @@ class CORE_EXPORT DocumentLoader
|
||||
const KURL& overriding_url);
|
||||
void DidInstallNewDocument(Document*);
|
||||
void WillCommitNavigation();
|
||||
void DidCommitNavigation();
|
||||
void DidCommitNavigation(WebGlobalObjectReusePolicy);
|
||||
|
||||
void CommitNavigation(const AtomicString& mime_type,
|
||||
const KURL& overriding_url = KURL());
|
||||
|
@ -261,7 +261,9 @@ class CORE_EXPORT EmptyLocalFrameClient : public LocalFrameClient {
|
||||
ResourceRequest&) override {}
|
||||
void DispatchDidReceiveTitle(const String&) override {}
|
||||
void DispatchDidChangeIcons(IconType) override {}
|
||||
void DispatchDidCommitLoad(HistoryItem*, HistoryCommitType) override {}
|
||||
void DispatchDidCommitLoad(HistoryItem*,
|
||||
HistoryCommitType,
|
||||
WebGlobalObjectReusePolicy) override {}
|
||||
void DispatchDidFailProvisionalLoad(const ResourceError&,
|
||||
HistoryCommitType) override {}
|
||||
void DispatchDidFailLoad(const ResourceError&, HistoryCommitType) override {}
|
||||
|
@ -429,7 +429,10 @@ void FrameLoader::ReplaceDocumentWhileExecutingJavaScriptURL(
|
||||
|
||||
// Compute this before clearing the frame, because it may need to inherit an
|
||||
// aliased security context.
|
||||
bool should_reuse_default_view = frame_->ShouldReuseDefaultView(url);
|
||||
WebGlobalObjectReusePolicy global_object_reuse_policy =
|
||||
frame_->ShouldReuseDefaultView(url)
|
||||
? WebGlobalObjectReusePolicy::kUseExisting
|
||||
: WebGlobalObjectReusePolicy::kCreateNew;
|
||||
|
||||
StopAllLoaders();
|
||||
// Don't allow any new child frames to load in this frame: attaching a new
|
||||
@ -446,7 +449,7 @@ void FrameLoader::ReplaceDocumentWhileExecutingJavaScriptURL(
|
||||
frame_->GetDocument()->Shutdown();
|
||||
Client()->TransitionToCommittedForNewPage();
|
||||
document_loader_->ReplaceDocumentWhileExecutingJavaScriptURL(
|
||||
url, owner_document, should_reuse_default_view, source);
|
||||
url, owner_document, global_object_reuse_policy, source);
|
||||
}
|
||||
|
||||
void FrameLoader::FinishedParsing() {
|
||||
|
1
third_party/WebKit/public/BUILD.gn
vendored
1
third_party/WebKit/public/BUILD.gn
vendored
@ -516,6 +516,7 @@ source_set("blink_headers") {
|
||||
"web/WebFrameSerializerCacheControlPolicy.h",
|
||||
"web/WebFrameSerializerClient.h",
|
||||
"web/WebFrameWidget.h",
|
||||
"web/WebGlobalObjectReusePolicy.h",
|
||||
"web/WebHeap.h",
|
||||
"web/WebHelperPlugin.h",
|
||||
"web/WebHistoryCommitType.h",
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "WebFormElement.h"
|
||||
#include "WebFrame.h"
|
||||
#include "WebFrameOwnerProperties.h"
|
||||
#include "WebGlobalObjectReusePolicy.h"
|
||||
#include "WebHistoryCommitType.h"
|
||||
#include "WebHistoryItem.h"
|
||||
#include "WebIconURL.h"
|
||||
@ -417,7 +418,8 @@ class BLINK_EXPORT WebFrameClient {
|
||||
// response body has been received, and the encoding of the response
|
||||
// body is known.
|
||||
virtual void DidCommitProvisionalLoad(const WebHistoryItem&,
|
||||
WebHistoryCommitType) {}
|
||||
WebHistoryCommitType,
|
||||
WebGlobalObjectReusePolicy) {}
|
||||
|
||||
// The frame's document has just been initialized.
|
||||
virtual void DidCreateNewDocument() {}
|
||||
|
22
third_party/WebKit/public/web/WebGlobalObjectReusePolicy.h
vendored
Normal file
22
third_party/WebKit/public/web/WebGlobalObjectReusePolicy.h
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef WebGlobalObjectReusePolicy_h
|
||||
#define WebGlobalObjectReusePolicy_h
|
||||
|
||||
namespace blink {
|
||||
|
||||
// Indicates whether the global object (i.e. Window instance) associated with
|
||||
// the previous document in a browsing context was replaced or reused for the
|
||||
// new Document corresponding to the just-committed navigation; effective in the
|
||||
// main world and all isolated worlds. WindowProxies are not affected.
|
||||
//
|
||||
// TODO(dcheng): Investigate removing the need for this by moving the
|
||||
// InterfaceProvider plumbing from DidCommitProvisionalLoad to
|
||||
// RenderFrameImpl::CommitNavigation.
|
||||
enum class WebGlobalObjectReusePolicy { kCreateNew, kUseExisting };
|
||||
|
||||
} // namespace blink
|
||||
|
||||
#endif
|
@ -2830,6 +2830,8 @@ uploading your change for review. These are checked by presubmit scripts.
|
||||
<int value="185" label="WEBUI_BAD_SCHEME_ACCESS"/>
|
||||
<int value="186" label="CSDH_UNEXPECTED_OPERATION"/>
|
||||
<int value="187" label="RMF_BAD_URL_CACHEABLE_METADATA"/>
|
||||
<int value="188" label="RFH_INTERFACE_PROVIDER_MISSING"/>
|
||||
<int value="189" label="RFH_INTERFACE_PROVIDER_SUPERFLUOUS"/>
|
||||
</enum>
|
||||
|
||||
<enum name="BadMessageReasonExtensions">
|
||||
|
Reference in New Issue
Block a user