mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-02-21 14:40:49 +01:00
This restores the default site isolation mode for Chromium on desktop platforms. Unit tests have been updated to reflect the new behavior expectations. Known behavior changes in CEF are as follows: - A spare renderer process may be created on initial browser creation or cross- origin navigation. This spare process may be used with a future cross-origin navigation or discarded on application shutdown. As a result CefRenderProcessHandler::OnRenderThreadCreated, which is called shortly after renderer process creation, can no longer be used to reliably transfer state for the currently in-progress navigation. Unit tests have been updated to use the CreateBrowser/OnBeforePopup |extra_info| value for transferring test state to CefRenderProcessHandler::OnBrowserCreated which will be called in the correct/expected renderer process. - Cross-origin navigations will again receive a new renderer process, as expected. This behavior had briefly regressed in M78 due to the ProcessSharingWithDefaultSiteInstances feature becoming enabled by default. - Cross-origin navigations initiated by calling LoadURL in the renderer process will now crash that process with "bad IPC message" reason INVALID_INITIATOR_ORIGIN (213). This is a security feature implemented in Chromium. - A DevTools browser created using CefBrowserHost::ShowDevTools will receive the same CefRenderProcessHandler::OnBrowserCreated |extra_info| value that was set via CreateBrowser/OnBeforePopup for the parent browser.
522 lines
18 KiB
C++
522 lines
18 KiB
C++
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
|
|
// reserved. Use of this source code is governed by a BSD-style license that
|
|
// can be found in the LICENSE file.
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "include/base/cef_bind.h"
|
|
#include "include/base/cef_scoped_ptr.h"
|
|
#include "include/cef_cookie.h"
|
|
#include "include/cef_request_context_handler.h"
|
|
#include "include/wrapper/cef_closure_task.h"
|
|
#include "include/wrapper/cef_stream_resource_handler.h"
|
|
#include "tests/ceftests/test_handler.h"
|
|
#include "tests/ceftests/test_util.h"
|
|
#include "tests/gtest/include/gtest/gtest.h"
|
|
#include "tests/shared/browser/client_app_browser.h"
|
|
#include "tests/shared/renderer/client_app_renderer.h"
|
|
|
|
using client::ClientAppBrowser;
|
|
using client::ClientAppRenderer;
|
|
|
|
namespace {
|
|
|
|
enum NetNotifyTestType {
|
|
NNTT_NONE = 0,
|
|
NNTT_NORMAL,
|
|
NNTT_DELAYED_RENDERER,
|
|
NNTT_DELAYED_BROWSER,
|
|
};
|
|
|
|
const char kNetNotifyOrigin1[] = "http://tests-netnotify1/";
|
|
const char kNetNotifyOrigin2[] = "http://tests-netnotify2/";
|
|
const char kNetNotifyMsg[] = "RequestHandlerTest.NetNotify";
|
|
const char kNetNotifyTestCmdKey[] = "rh-net-notify-test";
|
|
|
|
// Browser side.
|
|
class NetNotifyTestHandler : public TestHandler {
|
|
public:
|
|
NetNotifyTestHandler(CompletionState* completion_state,
|
|
NetNotifyTestType test_type,
|
|
bool same_origin)
|
|
: TestHandler(completion_state),
|
|
test_type_(test_type),
|
|
same_origin_(same_origin) {}
|
|
|
|
void SetupTest() override {
|
|
std::stringstream ss;
|
|
ss << kNetNotifyOrigin1 << "nav1.html?t=" << test_type_;
|
|
url1_ = ss.str();
|
|
ss.str("");
|
|
ss << (same_origin_ ? kNetNotifyOrigin1 : kNetNotifyOrigin2)
|
|
<< "nav2.html?t=" << test_type_;
|
|
url2_ = ss.str();
|
|
|
|
const std::string& resource1 =
|
|
"<html>"
|
|
"<head><script>document.cookie='name1=value1';</script></head>"
|
|
"<body>Nav1</body>"
|
|
"</html>";
|
|
response_length1_ = static_cast<int64>(resource1.size());
|
|
AddResource(url1_, resource1, "text/html");
|
|
|
|
const std::string& resource2 =
|
|
"<html>"
|
|
"<head><script>document.cookie='name2=value2';</script></head>"
|
|
"<body>Nav2</body>"
|
|
"</html>";
|
|
response_length2_ = static_cast<int64>(resource2.size());
|
|
AddResource(url2_, resource2, "text/html");
|
|
|
|
// Create the request context that will use an in-memory cache.
|
|
CefRequestContextSettings settings;
|
|
CefRefPtr<CefRequestContext> request_context =
|
|
CefRequestContext::CreateContext(settings, NULL);
|
|
cookie_manager_ = request_context->GetCookieManager(NULL);
|
|
|
|
CefRefPtr<CefDictionaryValue> extra_info = CefDictionaryValue::Create();
|
|
extra_info->SetBool(kNetNotifyTestCmdKey, true);
|
|
|
|
// Create browser that loads the 1st URL.
|
|
CreateBrowser(url1_, request_context, extra_info);
|
|
}
|
|
|
|
void RunTest() override {
|
|
// Navigate to the 2nd URL.
|
|
GetBrowser()->GetMainFrame()->LoadURL(url2_);
|
|
|
|
// Time out the test after a reasonable period of time.
|
|
SetTestTimeout();
|
|
}
|
|
|
|
cef_return_value_t OnBeforeResourceLoad(
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefRefPtr<CefRequest> request,
|
|
CefRefPtr<CefRequestCallback> callback) override {
|
|
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
|
|
|
|
const std::string& url = request->GetURL();
|
|
if (url.find(url1_) == 0)
|
|
got_before_resource_load1_.yes();
|
|
else if (url.find(url2_) == 0)
|
|
got_before_resource_load2_.yes();
|
|
else
|
|
EXPECT_TRUE(false); // Not reached
|
|
|
|
return RV_CONTINUE;
|
|
}
|
|
|
|
CefRefPtr<CefResourceHandler> GetResourceHandler(
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefRefPtr<CefRequest> request) override {
|
|
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
|
|
|
|
const std::string& url = request->GetURL();
|
|
if (url.find(url1_) == 0)
|
|
got_get_resource_handler1_.yes();
|
|
else if (url.find(url2_) == 0)
|
|
got_get_resource_handler2_.yes();
|
|
else
|
|
EXPECT_TRUE(false); // Not reached
|
|
|
|
return TestHandler::GetResourceHandler(browser, frame, request);
|
|
}
|
|
|
|
void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefRefPtr<CefRequest> request,
|
|
CefRefPtr<CefResponse> response,
|
|
URLRequestStatus status,
|
|
int64 received_content_length) override {
|
|
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
|
|
EXPECT_EQ(UR_SUCCESS, status);
|
|
|
|
const std::string& url = request->GetURL();
|
|
if (url.find(url1_) == 0) {
|
|
got_resource_load_complete1_.yes();
|
|
EXPECT_EQ(response_length1_, received_content_length);
|
|
} else if (url.find(url2_) == 0) {
|
|
got_resource_load_complete2_.yes();
|
|
EXPECT_EQ(response_length2_, received_content_length);
|
|
} else {
|
|
EXPECT_TRUE(false); // Not reached
|
|
}
|
|
}
|
|
|
|
bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefRefPtr<CefRequest> request,
|
|
bool user_gesture,
|
|
bool is_redirect) override {
|
|
std::string url = request->GetURL();
|
|
|
|
// Check if the load has already been delayed.
|
|
bool delay_loaded = (url.find("delayed=true") != std::string::npos);
|
|
|
|
if (url.find(url1_) == 0) {
|
|
got_before_browse1_.yes();
|
|
EXPECT_FALSE(delay_loaded);
|
|
} else if (url.find(url2_) == 0) {
|
|
got_before_browse2_.yes();
|
|
if (delay_loaded) {
|
|
got_before_browse2_delayed_.yes();
|
|
} else if (test_type_ == NNTT_DELAYED_RENDERER ||
|
|
test_type_ == NNTT_DELAYED_BROWSER) {
|
|
got_before_browse2_will_delay_.yes();
|
|
|
|
// Navigating cross-origin from the browser process will cause a new
|
|
// render process to be created. We therefore need some information in
|
|
// the request itself to tell us that the navigation has already been
|
|
// delayed.
|
|
// Navigating cross-origin from the renderer process will cause the
|
|
// process to be terminated with "bad IPC message" reason
|
|
// INVALID_INITIATOR_ORIGIN (213).
|
|
url += "&delayed=true";
|
|
|
|
if (test_type_ == NNTT_DELAYED_RENDERER) {
|
|
// Load the URL from the render process.
|
|
CefRefPtr<CefProcessMessage> message =
|
|
CefProcessMessage::Create(kNetNotifyMsg);
|
|
CefRefPtr<CefListValue> args = message->GetArgumentList();
|
|
args->SetInt(0, test_type_);
|
|
args->SetString(1, url);
|
|
frame->SendProcessMessage(PID_RENDERER, message);
|
|
} else {
|
|
// Load the URL from the browser process.
|
|
frame->LoadURL(url);
|
|
}
|
|
|
|
// Cancel the load.
|
|
return true;
|
|
}
|
|
} else {
|
|
EXPECT_TRUE(false); // Not reached
|
|
}
|
|
|
|
// Allow the load to continue.
|
|
return false;
|
|
}
|
|
|
|
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int httpStatusCode) override {
|
|
const std::string& url = frame->GetURL();
|
|
if (url.find(url1_) == 0) {
|
|
got_load_end1_.yes();
|
|
SetupCompleteIfDone();
|
|
} else if (url.find(url2_) == 0) {
|
|
got_load_end2_.yes();
|
|
FinishTestIfDone();
|
|
} else {
|
|
EXPECT_TRUE(false); // Not reached
|
|
}
|
|
}
|
|
|
|
bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefProcessId source_process,
|
|
CefRefPtr<CefProcessMessage> message) override {
|
|
if (message->GetName().ToString() == kNetNotifyMsg) {
|
|
CefRefPtr<CefListValue> args = message->GetArgumentList();
|
|
EXPECT_TRUE(args.get());
|
|
|
|
std::string url = args->GetString(0);
|
|
if (url.find(url1_) == 0) {
|
|
got_process_message1_.yes();
|
|
SetupCompleteIfDone();
|
|
} else if (url.find(url2_) == 0) {
|
|
got_process_message2_.yes();
|
|
FinishTestIfDone();
|
|
} else {
|
|
EXPECT_TRUE(false); // Not reached
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Message not handled.
|
|
return false;
|
|
}
|
|
|
|
void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
|
|
TerminationStatus status) override {
|
|
got_process_terminated_ct_++;
|
|
|
|
// Termination is expected for cross-origin requests initiated from the
|
|
// renderer process.
|
|
if (!(test_type_ == NNTT_DELAYED_RENDERER && !same_origin_)) {
|
|
TestHandler::OnRenderProcessTerminated(browser, status);
|
|
}
|
|
|
|
FinishTest();
|
|
}
|
|
|
|
protected:
|
|
void SetupCompleteIfDone() {
|
|
if (got_load_end1_ && got_process_message1_)
|
|
SetupComplete();
|
|
}
|
|
|
|
void FinishTestIfDone() {
|
|
if (got_load_end2_ && got_process_message2_)
|
|
FinishTest();
|
|
}
|
|
|
|
void FinishTest() {
|
|
// Verify that cookies were set correctly.
|
|
class TestVisitor : public CefCookieVisitor {
|
|
public:
|
|
explicit TestVisitor(NetNotifyTestHandler* handler) : handler_(handler) {}
|
|
~TestVisitor() override {
|
|
// Destroy the test.
|
|
CefPostTask(TID_UI,
|
|
base::Bind(&NetNotifyTestHandler::DestroyTest, handler_));
|
|
}
|
|
|
|
bool Visit(const CefCookie& cookie,
|
|
int count,
|
|
int total,
|
|
bool& deleteCookie) override {
|
|
const std::string& name = CefString(&cookie.name);
|
|
const std::string& value = CefString(&cookie.value);
|
|
if (name == "name1" && value == "value1") {
|
|
handler_->got_cookie1_.yes();
|
|
deleteCookie = true;
|
|
} else if (name == "name2" && value == "value2") {
|
|
handler_->got_cookie2_.yes();
|
|
deleteCookie = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
NetNotifyTestHandler* handler_;
|
|
IMPLEMENT_REFCOUNTING(TestVisitor);
|
|
};
|
|
|
|
cookie_manager_->VisitAllCookies(new TestVisitor(this));
|
|
}
|
|
|
|
void DestroyTest() override {
|
|
int browser_id = GetBrowser()->GetIdentifier();
|
|
|
|
// Verify test expectations.
|
|
EXPECT_TRUE(got_before_browse1_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_load_end1_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_before_resource_load1_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_get_resource_handler1_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_resource_load_complete1_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_cookie1_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_process_message1_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_before_browse2_) << " browser " << browser_id;
|
|
|
|
if (test_type_ == NNTT_DELAYED_RENDERER && !same_origin_) {
|
|
EXPECT_EQ(1, got_process_terminated_ct_) << " browser " << browser_id;
|
|
EXPECT_FALSE(got_load_end2_) << " browser " << browser_id;
|
|
EXPECT_FALSE(got_before_resource_load2_) << " browser " << browser_id;
|
|
EXPECT_FALSE(got_get_resource_handler2_) << " browser " << browser_id;
|
|
EXPECT_FALSE(got_resource_load_complete2_) << " browser " << browser_id;
|
|
EXPECT_FALSE(got_cookie2_) << " browser " << browser_id;
|
|
EXPECT_FALSE(got_process_message2_) << " browser " << browser_id;
|
|
} else {
|
|
EXPECT_EQ(0, got_process_terminated_ct_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_load_end2_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_before_resource_load2_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_get_resource_handler2_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_resource_load_complete2_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_cookie2_) << " browser " << browser_id;
|
|
EXPECT_TRUE(got_process_message2_) << " browser " << browser_id;
|
|
}
|
|
|
|
if (test_type_ == NNTT_DELAYED_RENDERER ||
|
|
test_type_ == NNTT_DELAYED_BROWSER) {
|
|
EXPECT_TRUE(got_before_browse2_will_delay_) << " browser " << browser_id;
|
|
if (test_type_ == NNTT_DELAYED_RENDERER && !same_origin_) {
|
|
EXPECT_FALSE(got_before_browse2_delayed_) << " browser " << browser_id;
|
|
} else {
|
|
EXPECT_TRUE(got_before_browse2_delayed_) << " browser " << browser_id;
|
|
}
|
|
} else {
|
|
EXPECT_FALSE(got_before_browse2_will_delay_) << " browser " << browser_id;
|
|
EXPECT_FALSE(got_before_browse2_delayed_) << " browser " << browser_id;
|
|
}
|
|
|
|
cookie_manager_ = NULL;
|
|
|
|
TestHandler::DestroyTest();
|
|
}
|
|
|
|
NetNotifyTestType test_type_;
|
|
bool same_origin_;
|
|
std::string url1_;
|
|
std::string url2_;
|
|
|
|
CefRefPtr<CefCookieManager> cookie_manager_;
|
|
|
|
TrackCallback got_before_browse1_;
|
|
TrackCallback got_load_end1_;
|
|
TrackCallback got_before_resource_load1_;
|
|
TrackCallback got_get_resource_handler1_;
|
|
TrackCallback got_resource_load_complete1_;
|
|
TrackCallback got_cookie1_;
|
|
TrackCallback got_process_message1_;
|
|
TrackCallback got_before_browse2_;
|
|
TrackCallback got_load_end2_;
|
|
TrackCallback got_before_resource_load2_;
|
|
TrackCallback got_get_resource_handler2_;
|
|
TrackCallback got_resource_load_complete2_;
|
|
TrackCallback got_cookie2_;
|
|
TrackCallback got_process_message2_;
|
|
TrackCallback got_before_browse2_will_delay_;
|
|
TrackCallback got_before_browse2_delayed_;
|
|
int got_process_terminated_ct_ = 0;
|
|
|
|
int64 response_length1_;
|
|
int64 response_length2_;
|
|
|
|
IMPLEMENT_REFCOUNTING(NetNotifyTestHandler);
|
|
};
|
|
|
|
// Renderer side.
|
|
class NetNotifyRendererTest : public ClientAppRenderer::Delegate,
|
|
public CefLoadHandler {
|
|
public:
|
|
NetNotifyRendererTest() : run_test_(false) {}
|
|
|
|
void OnBrowserCreated(CefRefPtr<ClientAppRenderer> app,
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefDictionaryValue> extra_info) override {
|
|
run_test_ = extra_info->HasKey(kNetNotifyTestCmdKey);
|
|
}
|
|
|
|
CefRefPtr<CefLoadHandler> GetLoadHandler(
|
|
CefRefPtr<ClientAppRenderer> app) override {
|
|
if (run_test_)
|
|
return this;
|
|
return NULL;
|
|
}
|
|
|
|
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int httpStatusCode) override {
|
|
if (!run_test_)
|
|
return;
|
|
|
|
const std::string& url = frame->GetURL();
|
|
|
|
// Continue in the browser process.
|
|
CefRefPtr<CefProcessMessage> message =
|
|
CefProcessMessage::Create(kNetNotifyMsg);
|
|
CefRefPtr<CefListValue> args = message->GetArgumentList();
|
|
args->SetString(0, url);
|
|
frame->SendProcessMessage(PID_BROWSER, message);
|
|
}
|
|
|
|
bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefProcessId source_process,
|
|
CefRefPtr<CefProcessMessage> message) override {
|
|
if (message->GetName().ToString() == kNetNotifyMsg) {
|
|
CefRefPtr<CefListValue> args = message->GetArgumentList();
|
|
EXPECT_TRUE(args.get());
|
|
|
|
NetNotifyTestType test_type =
|
|
static_cast<NetNotifyTestType>(args->GetInt(0));
|
|
EXPECT_EQ(test_type, NNTT_DELAYED_RENDERER);
|
|
|
|
const std::string& url = args->GetString(1);
|
|
|
|
// Load the URL from the render process.
|
|
frame->LoadURL(url);
|
|
return true;
|
|
}
|
|
|
|
// Message not handled.
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
bool run_test_;
|
|
|
|
IMPLEMENT_REFCOUNTING(NetNotifyRendererTest);
|
|
};
|
|
|
|
void RunNetNotifyTest(NetNotifyTestType test_type,
|
|
bool same_origin,
|
|
size_t count = 3U) {
|
|
TestHandler::CompletionState completion_state(count);
|
|
TestHandler::Collection collection(&completion_state);
|
|
|
|
std::vector<CefRefPtr<NetNotifyTestHandler>> handlers;
|
|
for (size_t i = 0U; i < count; ++i) {
|
|
CefRefPtr<NetNotifyTestHandler> handler =
|
|
new NetNotifyTestHandler(&completion_state, test_type, same_origin);
|
|
collection.AddTestHandler(handler);
|
|
handlers.push_back(handler);
|
|
}
|
|
|
|
collection.ExecuteTests();
|
|
|
|
while (!handlers.empty()) {
|
|
auto handler = handlers.front();
|
|
handlers.erase(handlers.begin());
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Verify network notifications for multiple browsers existing simultaniously.
|
|
// URL loading is from the same origin and is not delayed.
|
|
TEST(RequestHandlerTest, NotificationsSameOriginDirect) {
|
|
RunNetNotifyTest(NNTT_NORMAL, true);
|
|
}
|
|
|
|
// Verify network notifications for multiple browsers existing simultaniously.
|
|
// URL loading is from the same origin and is continued asynchronously from the
|
|
// render process.
|
|
TEST(RequestHandlerTest, NotificationsSameOriginDelayedRenderer) {
|
|
RunNetNotifyTest(NNTT_DELAYED_RENDERER, true);
|
|
}
|
|
|
|
// Verify network notifications for multiple browsers existing simultaniously.
|
|
// URL loading is from the same origin and is continued asynchronously from the
|
|
// browser process.
|
|
TEST(RequestHandlerTest, NotificationsSameOriginDelayedBrowser) {
|
|
RunNetNotifyTest(NNTT_DELAYED_BROWSER, true);
|
|
}
|
|
|
|
// Verify network notifications for multiple browsers existing simultaniously.
|
|
// URL loading is from a different origin and is not delayed.
|
|
TEST(RequestHandlerTest, NotificationsCrossOriginDirect) {
|
|
RunNetNotifyTest(NNTT_NORMAL, false);
|
|
}
|
|
|
|
// Verify network notifications for multiple browsers existing simultaniously.
|
|
// URL loading is from a different origin and is continued asynchronously from
|
|
// the render process.
|
|
TEST(RequestHandlerTest, NotificationsCrossOriginDelayedRenderer) {
|
|
RunNetNotifyTest(NNTT_DELAYED_RENDERER, false);
|
|
}
|
|
|
|
// Verify network notifications for multiple browsers existing simultaniously.
|
|
// URL loading is from a different origin and is continued asynchronously from
|
|
// the browser process.
|
|
TEST(RequestHandlerTest, NotificationsCrossOriginDelayedBrowser) {
|
|
RunNetNotifyTest(NNTT_DELAYED_BROWSER, false);
|
|
}
|
|
|
|
// Entry point for creating request handler renderer test objects.
|
|
// Called from client_app_delegates.cc.
|
|
void CreateRequestHandlerRendererTests(
|
|
ClientAppRenderer::DelegateSet& delegates) {
|
|
delegates.insert(new NetNotifyRendererTest);
|
|
}
|