| // Copyright (c) 2014 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 <cstdlib> |
| #include <set> |
| #include <sstream> |
| #include <vector> |
| |
| #include "include/base/cef_bind.h" |
| #include "include/base/cef_weak_ptr.h" |
| #include "include/cef_v8.h" |
| #include "include/wrapper/cef_closure_task.h" |
| #include "tests/ceftests/routing_test_handler.h" |
| #include "tests/gtest/include/gtest/gtest.h" |
| #include "tests/shared/renderer/client_app_renderer.h" |
| |
| using client::ClientAppRenderer; |
| |
| namespace { |
| |
| const char kTestDomainRoot[] = "http://tests-mr"; |
| const char kTestDomain1[] = "http://tests-mr1.com/"; |
| const char kTestDomain2[] = "http://tests-mr2.com/"; |
| const char kTestDomain3[] = "http://tests-mr3.com/"; |
| |
| const char kDoneMessageName[] = "mrtNotifyMsg"; |
| |
| const char kJSNotifyFunc[] = "mrtNotify"; |
| const char kJSAssertTotalCountFunc[] = "mrtAssertTotalCount"; |
| const char kJSAssertBrowserCountFunc[] = "mrtAssertBrowserCount"; |
| const char kJSAssertContextCountFunc[] = "mrtAssertContextCount"; |
| |
| void SetRouterConfig(CefMessageRouterConfig& config) { |
| config.js_query_function = "mrtQuery"; |
| config.js_cancel_function = "mrtQueryCancel"; |
| } |
| |
| // Handle the renderer side of the routing implementation. |
| class MRRenderDelegate : public ClientAppRenderer::Delegate { |
| public: |
| class V8HandlerImpl : public CefV8Handler { |
| public: |
| explicit V8HandlerImpl(CefRefPtr<MRRenderDelegate> delegate) |
| : delegate_(delegate) {} |
| |
| bool Execute(const CefString& name, |
| CefRefPtr<CefV8Value> object, |
| const CefV8ValueList& arguments, |
| CefRefPtr<CefV8Value>& retval, |
| CefString& exception) override { |
| const std::string& message_name = name; |
| if (message_name == kJSNotifyFunc) { |
| EXPECT_EQ(1U, arguments.size()); |
| EXPECT_TRUE(arguments[0]->IsString()); |
| |
| const CefString& msg = arguments[0]->GetStringValue(); |
| CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext(); |
| CefRefPtr<CefFrame> frame = context->GetFrame(); |
| |
| CefRefPtr<CefProcessMessage> message = |
| CefProcessMessage::Create(kDoneMessageName); |
| CefRefPtr<CefListValue> args = message->GetArgumentList(); |
| args->SetString(0, msg); |
| frame->SendProcessMessage(PID_BROWSER, message); |
| return true; |
| } else { |
| EXPECT_EQ(1U, arguments.size()); |
| EXPECT_TRUE(arguments[0]->IsInt()); |
| |
| const int expected_count = arguments[0]->GetIntValue(); |
| int actual_count = -1; |
| |
| CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext(); |
| CefRefPtr<CefBrowser> browser = context->GetBrowser(); |
| |
| if (name == kJSAssertTotalCountFunc) { |
| actual_count = |
| delegate_->message_router_->GetPendingCount(nullptr, nullptr); |
| } else if (name == kJSAssertBrowserCountFunc) { |
| actual_count = |
| delegate_->message_router_->GetPendingCount(browser, nullptr); |
| } else if (name == kJSAssertContextCountFunc) { |
| actual_count = |
| delegate_->message_router_->GetPendingCount(browser, context); |
| } |
| |
| if (expected_count != actual_count) { |
| std::stringstream ss; |
| ss << message_name << " failed; expected " << expected_count |
| << ", got " << actual_count; |
| exception = ss.str(); |
| } |
| } |
| |
| return true; |
| } |
| |
| private: |
| CefRefPtr<MRRenderDelegate> delegate_; |
| |
| IMPLEMENT_REFCOUNTING(V8HandlerImpl); |
| }; |
| |
| MRRenderDelegate() {} |
| |
| void OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) override { |
| // Create the renderer-side router for query handling. |
| CefMessageRouterConfig config; |
| SetRouterConfig(config); |
| message_router_ = CefMessageRouterRendererSide::Create(config); |
| } |
| |
| void OnContextCreated(CefRefPtr<ClientAppRenderer> app, |
| CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| CefRefPtr<CefV8Context> context) override { |
| const std::string& url = frame->GetURL(); |
| if (url.find(kTestDomainRoot) != 0) |
| return; |
| |
| message_router_->OnContextCreated(browser, frame, context); |
| |
| // Register function handlers with the 'window' object. |
| CefRefPtr<CefV8Value> window = context->GetGlobal(); |
| |
| CefRefPtr<V8HandlerImpl> handler = new V8HandlerImpl(this); |
| CefV8Value::PropertyAttribute attributes = |
| static_cast<CefV8Value::PropertyAttribute>( |
| V8_PROPERTY_ATTRIBUTE_READONLY | V8_PROPERTY_ATTRIBUTE_DONTENUM | |
| V8_PROPERTY_ATTRIBUTE_DONTDELETE); |
| |
| CefRefPtr<CefV8Value> notify_func = |
| CefV8Value::CreateFunction(kJSNotifyFunc, handler.get()); |
| window->SetValue(kJSNotifyFunc, notify_func, attributes); |
| |
| CefRefPtr<CefV8Value> total_count_func = |
| CefV8Value::CreateFunction(kJSAssertTotalCountFunc, handler.get()); |
| window->SetValue(kJSAssertTotalCountFunc, total_count_func, attributes); |
| |
| CefRefPtr<CefV8Value> browser_count_func = |
| CefV8Value::CreateFunction(kJSAssertBrowserCountFunc, handler.get()); |
| window->SetValue(kJSAssertBrowserCountFunc, browser_count_func, attributes); |
| |
| CefRefPtr<CefV8Value> context_count_func = |
| CefV8Value::CreateFunction(kJSAssertContextCountFunc, handler.get()); |
| window->SetValue(kJSAssertContextCountFunc, context_count_func, attributes); |
| } |
| |
| void OnContextReleased(CefRefPtr<ClientAppRenderer> app, |
| CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| CefRefPtr<CefV8Context> context) override { |
| const std::string& url = frame->GetURL(); |
| if (url.find(kTestDomainRoot) != 0) |
| return; |
| |
| message_router_->OnContextReleased(browser, frame, context); |
| } |
| |
| bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app, |
| CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| CefProcessId source_process, |
| CefRefPtr<CefProcessMessage> message) override { |
| const std::string& url = browser->GetMainFrame()->GetURL(); |
| if (url.find(kTestDomainRoot) != 0) |
| return false; |
| |
| return message_router_->OnProcessMessageReceived(browser, frame, |
| source_process, message); |
| } |
| |
| private: |
| CefRefPtr<CefMessageRouterRendererSide> message_router_; |
| |
| IMPLEMENT_REFCOUNTING(MRRenderDelegate); |
| }; |
| |
| } // namespace |
| |
| // Entry point for creating the test delegate. |
| // Called from client_app_delegates.cc. |
| void CreateMessageRouterRendererTests( |
| ClientAppRenderer::DelegateSet& delegates) { |
| delegates.insert(new MRRenderDelegate); |
| } |
| |
| namespace { |
| |
| class MRTestHandler : public TestHandler { |
| public: |
| MRTestHandler() {} |
| |
| void RunTest() override { |
| RunMRTest(); |
| |
| // Time out the test after a reasonable period of time. |
| SetTestTimeout(10000); |
| } |
| |
| void OnAfterCreated(CefRefPtr<CefBrowser> browser) override { |
| if (!message_router_.get()) { |
| // Create the browser-side router for query handling. |
| CefMessageRouterConfig config; |
| SetRouterConfig(config); |
| message_router_ = CefMessageRouterBrowserSide::Create(config); |
| AddHandlers(message_router_); |
| } |
| TestHandler::OnAfterCreated(browser); |
| } |
| |
| void OnBeforeClose(CefRefPtr<CefBrowser> browser) override { |
| message_router_->OnBeforeClose(browser); |
| TestHandler::OnBeforeClose(browser); |
| } |
| |
| void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser, |
| TerminationStatus status) override { |
| message_router_->OnRenderProcessTerminated(browser); |
| } |
| |
| // Only call this method if the navigation isn't canceled. |
| bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| CefRefPtr<CefRequest> request, |
| bool user_gesture, |
| bool is_redirect) override { |
| message_router_->OnBeforeBrowse(browser, frame); |
| return false; |
| } |
| |
| // Returns true if the router handled the navigation. |
| bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| CefProcessId source_process, |
| CefRefPtr<CefProcessMessage> message) override { |
| const std::string& message_name = message->GetName(); |
| if (message_name == kDoneMessageName) { |
| CefRefPtr<CefListValue> args = message->GetArgumentList(); |
| EXPECT_EQ(1U, args->GetSize()); |
| EXPECT_EQ(VTYPE_STRING, args->GetType(0)); |
| OnNotify(browser, frame, args->GetString(0)); |
| return true; |
| } |
| |
| return message_router_->OnProcessMessageReceived(browser, frame, |
| source_process, message); |
| } |
| |
| CefRefPtr<CefMessageRouterBrowserSide> GetRouter() const { |
| return message_router_; |
| } |
| |
| protected: |
| virtual void RunMRTest() = 0; |
| |
| virtual void AddHandlers( |
| CefRefPtr<CefMessageRouterBrowserSide> message_router) = 0; |
| |
| virtual void OnNotify(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| const std::string& message) = 0; |
| |
| bool AssertQueryCount(CefRefPtr<CefBrowser> browser, |
| CefMessageRouterBrowserSide::Handler* handler, |
| int expected_count) { |
| int actual_count = message_router_->GetPendingCount(browser, handler); |
| EXPECT_EQ(expected_count, actual_count); |
| return (expected_count == actual_count); |
| } |
| |
| void AssertMainBrowser(CefRefPtr<CefBrowser> browser) { |
| EXPECT_TRUE(browser.get()); |
| EXPECT_EQ(GetBrowserId(), browser->GetIdentifier()); |
| } |
| |
| private: |
| CefRefPtr<CefMessageRouterBrowserSide> message_router_; |
| |
| IMPLEMENT_REFCOUNTING(MRTestHandler); |
| }; |
| |
| // Implementation of MRTestHandler that loads a single page. |
| class SingleLoadTestHandler : public MRTestHandler, |
| public CefMessageRouterBrowserSide::Handler { |
| public: |
| SingleLoadTestHandler() |
| : main_url_(std::string(kTestDomain1) + "main.html") {} |
| |
| const std::string& GetMainURL() { return main_url_; } |
| |
| protected: |
| void RunMRTest() override { |
| AddOtherResources(); |
| AddResource(main_url_, GetMainHTML(), "text/html"); |
| |
| CreateBrowser(main_url_, nullptr); |
| } |
| |
| void AddHandlers( |
| CefRefPtr<CefMessageRouterBrowserSide> message_router) override { |
| message_router->AddHandler(this, false); |
| } |
| |
| virtual void AddOtherResources() {} |
| |
| virtual std::string GetMainHTML() = 0; |
| |
| void AssertMainFrame(CefRefPtr<CefFrame> frame) { |
| EXPECT_TRUE(frame.get()); |
| EXPECT_TRUE(frame->IsMain()); |
| EXPECT_STREQ(main_url_.c_str(), frame->GetURL().ToString().c_str()); |
| } |
| |
| private: |
| const std::string main_url_; |
| }; |
| |
| // Used to verify that the test harness (bound functions) behave correctly. |
| class HarnessTestHandler : public SingleLoadTestHandler { |
| public: |
| HarnessTestHandler(bool test_success) : test_success_(test_success) {} |
| |
| std::string GetMainHTML() override { |
| std::string html; |
| if (test_success_) { |
| // All assertions should pass. |
| html = |
| "<html><body><script>\n" |
| "var fail_ct = 0;\n" |
| "try { window.mrtAssertTotalCount(0); } catch (e) { fail_ct++; }\n" |
| "try { window.mrtAssertBrowserCount(0); } catch (e) { fail_ct++; }\n" |
| "try { window.mrtAssertContextCount(0); } catch (e) { fail_ct++; }\n" |
| "window.mrtNotify('' + (fail_ct == 0));" |
| "</script></body></html>"; |
| } else { |
| // All assertions should fail. |
| html = |
| "<html><body><script>\n" |
| "var fail_ct = 0;\n" |
| "try { window.mrtAssertTotalCount(1); } catch (e) { fail_ct++; }\n" |
| "try { window.mrtAssertBrowserCount(1); } catch (e) { fail_ct++; }\n" |
| "try { window.mrtAssertContextCount(1); } catch (e) { fail_ct++; }\n" |
| "window.mrtNotify('' + (fail_ct == 3));" |
| "</script></body></html>"; |
| } |
| return html; |
| } |
| |
| void OnNotify(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| const std::string& message) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| |
| got_done_.yes(); |
| EXPECT_STREQ("true", message.c_str()); |
| DestroyTest(); |
| } |
| |
| void DestroyTest() override { |
| EXPECT_TRUE(got_done_); |
| TestHandler::DestroyTest(); |
| } |
| |
| private: |
| const bool test_success_; |
| TrackCallback got_done_; |
| }; |
| |
| } // namespace |
| |
| // Verify that the test harness works with successful assertions. |
| TEST(MessageRouterTest, HarnessSuccess) { |
| CefRefPtr<HarnessTestHandler> handler = new HarnessTestHandler(true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Verify that the test harness works with failed assertions. |
| TEST(MessageRouterTest, HarnessFailure) { |
| CefRefPtr<HarnessTestHandler> handler = new HarnessTestHandler(false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| namespace { |
| |
| const char kSingleQueryRequest[] = "request_context"; |
| const char kSingleQueryResponse[] = "success_response"; |
| const int kSingleQueryErrorCode = 5; |
| const char kSingleQueryErrorMessage[] = "error_message"; |
| |
| // Test a single query in a single page load. |
| class SingleQueryTestHandler : public SingleLoadTestHandler { |
| public: |
| enum TestType { |
| SUCCESS, |
| FAILURE, |
| CANCEL, |
| }; |
| |
| SingleQueryTestHandler(TestType type, bool sync_callback) |
| : test_type_(type), sync_callback_(sync_callback), query_id_(0) {} |
| |
| std::string GetMainHTML() override { |
| std::string html; |
| |
| std::stringstream ss; |
| ss << kSingleQueryErrorCode; |
| const std::string& errorCodeStr = ss.str(); |
| |
| html = |
| "<html><body><script>\n" |
| // No requests should exist. |
| "window.mrtAssertTotalCount(0);\n" |
| "window.mrtAssertBrowserCount(0);\n" |
| "window.mrtAssertContextCount(0);\n" |
| // Send the query. |
| "var request_id = window.mrtQuery({\n" |
| " request: '" + |
| std::string(kSingleQueryRequest) + |
| "',\n" |
| " persistent: false,\n" |
| " onSuccess: function(response) {\n" |
| // Request should be removed before callback is executed. |
| " window.mrtAssertTotalCount(0);\n" |
| " window.mrtAssertBrowserCount(0);\n" |
| " window.mrtAssertContextCount(0);\n" |
| " if (response == '" + |
| std::string(kSingleQueryResponse) + |
| "')\n" |
| " window.mrtNotify('success');\n" |
| " else\n" |
| " window.mrtNotify('error-onSuccess');\n" |
| " },\n" |
| " onFailure: function(error_code, error_message) {\n" |
| // Request should be removed before callback is executed. |
| " window.mrtAssertTotalCount(0);\n" |
| " window.mrtAssertBrowserCount(0);\n" |
| " window.mrtAssertContextCount(0);\n" |
| " if (error_code == " + |
| errorCodeStr + " && error_message == '" + |
| std::string(kSingleQueryErrorMessage) + |
| "')\n" |
| " window.mrtNotify('failure');\n" |
| " else\n" |
| " window.mrtNotify('error-onFailure');\n" |
| " }\n" |
| "});\n" |
| // Request should exist. |
| "window.mrtAssertTotalCount(1);\n" |
| "window.mrtAssertBrowserCount(1);\n" |
| "window.mrtAssertContextCount(1);\n"; |
| |
| if (test_type_ == CANCEL) { |
| html += |
| "window.mrtQueryCancel(request_id);\n" |
| // Request should be removed immediately. |
| "window.mrtAssertTotalCount(0);\n" |
| "window.mrtAssertBrowserCount(0);\n" |
| "window.mrtAssertContextCount(0);\n" |
| "window.mrtNotify('cancel');\n"; |
| } |
| |
| html += "</script></body></html>"; |
| return html; |
| } |
| |
| void OnNotify(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| const std::string& message) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| |
| // OnNotify only be called once. |
| EXPECT_FALSE(got_notify_); |
| got_notify_.yes(); |
| |
| if (test_type_ == SUCCESS) { |
| EXPECT_STREQ("success", message.c_str()); |
| } else if (test_type_ == FAILURE) { |
| EXPECT_STREQ("failure", message.c_str()); |
| } else if (test_type_ == CANCEL) { |
| EXPECT_STREQ("cancel", message.c_str()); |
| } |
| |
| DestroyTestIfDone(); |
| } |
| |
| void ExecuteCallback() { |
| EXPECT_TRUE(callback_.get()); |
| if (test_type_ == SUCCESS) { |
| callback_->Success(kSingleQueryResponse); |
| } else if (test_type_ == FAILURE) { |
| callback_->Failure(kSingleQueryErrorCode, kSingleQueryErrorMessage); |
| } else { |
| EXPECT_TRUE(false); // Not reached. |
| } |
| callback_ = nullptr; |
| } |
| |
| bool OnQuery(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id, |
| const CefString& request, |
| bool persistent, |
| CefRefPtr<Callback> callback) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| EXPECT_NE(0, query_id); |
| EXPECT_FALSE(persistent); |
| EXPECT_STREQ(kSingleQueryRequest, request.ToString().c_str()); |
| |
| got_on_query_.yes(); |
| |
| query_id_ = query_id; |
| callback_ = callback; |
| |
| if (test_type_ == SUCCESS || test_type_ == FAILURE) { |
| if (sync_callback_) { |
| ExecuteCallback(); |
| } else { |
| CefPostTask(TID_UI, |
| base::Bind(&SingleQueryTestHandler::ExecuteCallback, this)); |
| } |
| } |
| |
| return true; |
| } |
| |
| void OnQueryCanceled(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| EXPECT_EQ(test_type_, CANCEL); |
| EXPECT_EQ(query_id_, query_id); |
| EXPECT_TRUE(got_on_query_); |
| EXPECT_TRUE(callback_.get()); |
| |
| got_on_query_canceled_.yes(); |
| callback_ = nullptr; |
| |
| DestroyTestIfDone(); |
| } |
| |
| void DestroyTestIfDone() { |
| bool destroy_test = false; |
| if (test_type_ == CANCEL) |
| destroy_test = got_notify_ && got_on_query_canceled_; |
| else |
| destroy_test = got_notify_; |
| if (destroy_test) |
| DestroyTest(); |
| } |
| |
| void DestroyTest() override { |
| EXPECT_TRUE(got_notify_); |
| EXPECT_TRUE(got_on_query_); |
| EXPECT_FALSE(callback_.get()); |
| |
| if (test_type_ == CANCEL) |
| EXPECT_TRUE(got_on_query_canceled_); |
| else |
| EXPECT_FALSE(got_on_query_canceled_); |
| |
| TestHandler::DestroyTest(); |
| } |
| |
| private: |
| const TestType test_type_; |
| const bool sync_callback_; |
| |
| int64 query_id_; |
| CefRefPtr<Callback> callback_; |
| |
| TrackCallback got_on_query_; |
| TrackCallback got_on_query_canceled_; |
| TrackCallback got_notify_; |
| }; |
| |
| } // namespace |
| |
| // Test that a single query with successful result delivered synchronously. |
| TEST(MessageRouterTest, SingleQuerySuccessSyncCallback) { |
| CefRefPtr<SingleQueryTestHandler> handler = |
| new SingleQueryTestHandler(SingleQueryTestHandler::SUCCESS, true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that a single query with successful result delivered asynchronously. |
| TEST(MessageRouterTest, SingleQuerySuccessAsyncCallback) { |
| CefRefPtr<SingleQueryTestHandler> handler = |
| new SingleQueryTestHandler(SingleQueryTestHandler::SUCCESS, false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that a single query with failure result delivered synchronously. |
| TEST(MessageRouterTest, SingleQueryFailureSyncCallback) { |
| CefRefPtr<SingleQueryTestHandler> handler = |
| new SingleQueryTestHandler(SingleQueryTestHandler::FAILURE, true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that a single query with failure result delivered asynchronously. |
| TEST(MessageRouterTest, SingleQueryFailureAsyncCallback) { |
| CefRefPtr<SingleQueryTestHandler> handler = |
| new SingleQueryTestHandler(SingleQueryTestHandler::FAILURE, false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that a single query with cancellation. |
| TEST(MessageRouterTest, SingleQueryCancel) { |
| CefRefPtr<SingleQueryTestHandler> handler = |
| new SingleQueryTestHandler(SingleQueryTestHandler::CANCEL, true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| namespace { |
| |
| const int kSinglePersistentQueryResponseCount = 10; |
| |
| // Test a single persistent query in a single page load. |
| class SinglePersistentQueryTestHandler : public SingleLoadTestHandler { |
| public: |
| enum TestType { |
| SUCCESS, |
| FAILURE, |
| }; |
| |
| SinglePersistentQueryTestHandler(TestType test_type, bool sync_callback) |
| : test_type_(test_type), sync_callback_(sync_callback), query_id_(0) {} |
| |
| std::string GetMainHTML() override { |
| std::string html; |
| |
| std::stringstream ss; |
| ss << kSinglePersistentQueryResponseCount; |
| const std::string& responseCountStr = ss.str(); |
| ss.str(""); |
| ss << kSingleQueryErrorCode; |
| const std::string& errorCodeStr = ss.str(); |
| |
| html = |
| "<html><body><script>\n" |
| // No requests should exist. |
| "window.mrtAssertTotalCount(0);\n" |
| "window.mrtAssertBrowserCount(0);\n" |
| "window.mrtAssertContextCount(0);\n" |
| // Keep track of the number of responses. |
| "var count = 0;\n" |
| // Send the query. |
| "var request_id = window.mrtQuery({\n" |
| " request: '" + |
| std::string(kSingleQueryRequest) + |
| "',\n" |
| " persistent: true,\n" |
| " onSuccess: function(response) {\n" |
| // Request should not be removed. |
| " window.mrtAssertTotalCount(1);\n" |
| " window.mrtAssertBrowserCount(1);\n" |
| " window.mrtAssertContextCount(1);\n" |
| " if (response == '" + |
| std::string(kSingleQueryResponse) + |
| "') {\n" |
| " if (++count == " + |
| responseCountStr + |
| ") {\n" |
| " window.mrtNotify('success');\n" |
| " window.mrtQueryCancel(request_id);\n" |
| // Request should be removed immediately. |
| " window.mrtAssertTotalCount(0);\n" |
| " window.mrtAssertBrowserCount(0);\n" |
| " window.mrtAssertContextCount(0);\n" |
| " }\n" |
| " } else {\n" |
| " window.mrtNotify('error-onSuccess');\n" |
| " }\n" |
| " },\n" |
| " onFailure: function(error_code, error_message) {\n" |
| // Request should be removed before callback is executed. |
| " window.mrtAssertTotalCount(0);\n" |
| " window.mrtAssertBrowserCount(0);\n" |
| " window.mrtAssertContextCount(0);\n" |
| " if (error_code == " + |
| errorCodeStr + " && error_message == '" + |
| std::string(kSingleQueryErrorMessage) + |
| "') {\n" |
| " window.mrtNotify('failure');\n" |
| " } else {\n" |
| " window.mrtNotify('error-onFailure');\n" |
| " }\n" |
| " }\n" |
| "});\n" |
| // Request should exist. |
| "window.mrtAssertTotalCount(1);\n" |
| "window.mrtAssertBrowserCount(1);\n" |
| "window.mrtAssertContextCount(1);\n"; |
| |
| html += "</script></body></html>"; |
| return html; |
| } |
| |
| void OnNotify(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| const std::string& message) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| |
| if (test_type_ == SUCCESS) { |
| EXPECT_STREQ("success", message.c_str()); |
| } else if (test_type_ == FAILURE) { |
| EXPECT_STREQ("failure", message.c_str()); |
| } |
| |
| got_notify_.yes(); |
| |
| DestroyTestIfDone(); |
| } |
| |
| void ExecuteCallback() { |
| EXPECT_TRUE(callback_.get()); |
| if (test_type_ == SUCCESS) { |
| callback_->Success(kSingleQueryResponse); |
| } else { |
| callback_->Failure(kSingleQueryErrorCode, kSingleQueryErrorMessage); |
| callback_ = nullptr; |
| } |
| } |
| |
| bool OnQuery(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id, |
| const CefString& request, |
| bool persistent, |
| CefRefPtr<Callback> callback) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| EXPECT_NE(0, query_id); |
| EXPECT_TRUE(persistent); |
| EXPECT_STREQ(kSingleQueryRequest, request.ToString().c_str()); |
| |
| got_on_query_.yes(); |
| |
| query_id_ = query_id; |
| callback_ = callback; |
| |
| int repeat = |
| (test_type_ == SUCCESS ? kSinglePersistentQueryResponseCount : 1); |
| |
| for (int i = 0; i < repeat; ++i) { |
| if (sync_callback_) { |
| ExecuteCallback(); |
| } else { |
| CefPostTask( |
| TID_UI, |
| base::Bind(&SinglePersistentQueryTestHandler::ExecuteCallback, |
| this)); |
| } |
| } |
| |
| return true; |
| } |
| |
| void OnQueryCanceled(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| EXPECT_EQ(query_id_, query_id); |
| EXPECT_TRUE(got_on_query_); |
| EXPECT_TRUE(callback_.get()); |
| |
| got_on_query_canceled_.yes(); |
| callback_ = nullptr; |
| |
| DestroyTestIfDone(); |
| } |
| |
| void DestroyTestIfDone() { |
| bool destroy_test = false; |
| if (test_type_ == SUCCESS) { |
| if (got_on_query_ && got_on_query_canceled_ && got_notify_) |
| destroy_test = true; |
| } else if (got_on_query_ && got_notify_) { |
| destroy_test = true; |
| } |
| |
| if (destroy_test) |
| DestroyTest(); |
| } |
| |
| void DestroyTest() override { |
| EXPECT_TRUE(got_notify_); |
| EXPECT_TRUE(got_on_query_); |
| EXPECT_FALSE(callback_.get()); |
| |
| if (test_type_ == SUCCESS) |
| EXPECT_TRUE(got_on_query_canceled_); |
| else |
| EXPECT_FALSE(got_on_query_canceled_); |
| |
| TestHandler::DestroyTest(); |
| } |
| |
| private: |
| const TestType test_type_; |
| const bool sync_callback_; |
| |
| int64 query_id_; |
| CefRefPtr<Callback> callback_; |
| |
| TrackCallback got_on_query_; |
| TrackCallback got_on_query_canceled_; |
| TrackCallback got_notify_; |
| }; |
| |
| } // namespace |
| |
| // Test that a single query with successful result delivered synchronously. |
| TEST(MessageRouterTest, SinglePersistentQuerySuccessSyncCallback) { |
| CefRefPtr<SinglePersistentQueryTestHandler> handler = |
| new SinglePersistentQueryTestHandler( |
| SinglePersistentQueryTestHandler::SUCCESS, true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that a single query with successful result delivered asynchronously. |
| TEST(MessageRouterTest, SinglePersistentQuerySuccessAsyncCallback) { |
| CefRefPtr<SinglePersistentQueryTestHandler> handler = |
| new SinglePersistentQueryTestHandler( |
| SinglePersistentQueryTestHandler::SUCCESS, false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that a single query with failure result delivered synchronously. |
| TEST(MessageRouterTest, SinglePersistentQueryFailureSyncCallback) { |
| CefRefPtr<SinglePersistentQueryTestHandler> handler = |
| new SinglePersistentQueryTestHandler( |
| SinglePersistentQueryTestHandler::FAILURE, true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that a single query with failure result delivered asynchronously. |
| TEST(MessageRouterTest, SinglePersistentQueryFailureAsyncCallback) { |
| CefRefPtr<SinglePersistentQueryTestHandler> handler = |
| new SinglePersistentQueryTestHandler( |
| SinglePersistentQueryTestHandler::FAILURE, false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| namespace { |
| |
| // Test a single unhandled query in a single page load. |
| class SingleUnhandledQueryTestHandler : public SingleLoadTestHandler { |
| public: |
| SingleUnhandledQueryTestHandler() {} |
| |
| std::string GetMainHTML() override { |
| std::string html; |
| |
| html = |
| "<html><body><script>\n" |
| // No requests should exist. |
| "window.mrtAssertTotalCount(0);\n" |
| "window.mrtAssertBrowserCount(0);\n" |
| "window.mrtAssertContextCount(0);\n" |
| // Keep track of the number of responses. |
| "var count = 0;\n" |
| // Send the query. |
| "var request_id = window.mrtQuery({\n" |
| " request: '" + |
| std::string(kSingleQueryRequest) + |
| "',\n" |
| " persistent: false,\n" |
| " onSuccess: function(response) {\n" |
| " window.mrtNotify('error-onSuccess');\n" |
| " },\n" |
| " onFailure: function(error_code, error_message) {\n" |
| // Request should be removed before callback is executed. |
| " window.mrtAssertTotalCount(0);\n" |
| " window.mrtAssertBrowserCount(0);\n" |
| " window.mrtAssertContextCount(0);\n" |
| " if (error_code == -1 && " |
| "error_message == 'The query has been canceled') {\n" |
| " window.mrtNotify('failure');\n" |
| " } else {\n" |
| " window.mrtNotify('error-onFailure');\n" |
| " }\n" |
| " }\n" |
| "});\n" |
| // Request should exist. |
| "window.mrtAssertTotalCount(1);\n" |
| "window.mrtAssertBrowserCount(1);\n" |
| "window.mrtAssertContextCount(1);\n"; |
| |
| html += "</script></body></html>"; |
| return html; |
| } |
| |
| void OnNotify(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| const std::string& message) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| EXPECT_STREQ("failure", message.c_str()); |
| |
| got_notify_.yes(); |
| |
| DestroyTest(); |
| } |
| |
| bool OnQuery(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id, |
| const CefString& request, |
| bool persistent, |
| CefRefPtr<Callback> callback) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| EXPECT_NE(0, query_id); |
| EXPECT_FALSE(persistent); |
| EXPECT_STREQ(kSingleQueryRequest, request.ToString().c_str()); |
| |
| got_on_query_.yes(); |
| |
| return false; |
| } |
| |
| void OnQueryCanceled(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id) override { |
| EXPECT_FALSE(true); // Not reached. |
| } |
| |
| void DestroyTest() override { |
| EXPECT_TRUE(got_on_query_); |
| EXPECT_TRUE(got_notify_); |
| |
| TestHandler::DestroyTest(); |
| } |
| |
| private: |
| TrackCallback got_on_query_; |
| TrackCallback got_notify_; |
| }; |
| |
| } // namespace |
| |
| // Test that a single unhandled query results in a call to onFailure. |
| TEST(MessageRouterTest, SingleUnhandledQuery) { |
| CefRefPtr<SingleUnhandledQueryTestHandler> handler = |
| new SingleUnhandledQueryTestHandler(); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| namespace { |
| |
| const char kMultiQueryRequestId[] = "request_id"; |
| const char kMultiQueryRepeatCt[] = "repeat_ct"; |
| const char kMultiQueryRequest[] = "request"; |
| const char kMultiQueryResponse[] = "response"; |
| const char kMultiQuerySuccess[] = "success"; |
| const char kMultiQueryError[] = "error"; |
| const char kMultiQueryErrorMessage[] = "errormsg"; |
| const int kMultiQueryPersistentResponseCount = 5; |
| |
| // Generates HTML and verifies results for multiple simultanious queries. |
| class MultiQueryManager : public CefMessageRouterBrowserSide::Handler { |
| public: |
| enum TestType { |
| // Initiates a non-persistent query with a successful response. |
| // OnQuery and OnNotify will be called. |
| SUCCESS, |
| |
| // Initiates a non-persistent query with a failure response. |
| // OnQuery and OnNotify will be called. |
| FAILURE, |
| |
| // Initiates a persistent query with multiple successful responses. |
| // OnQuery, OnNotify and OnQueryCanceled will be called. |
| PERSISTENT_SUCCESS, |
| |
| // Initiates a persistent query with multiple successful responses and one |
| // failure response. |
| // OnQuery and OnNotify will be called. |
| PERSISTENT_FAILURE, |
| |
| // Initiates a non-persistent query that will be canceled via JavaScript. |
| // No JavaScript callbacks will be executed. |
| // OnQuery and OnQueryCanceled will be called. |
| CANCEL, |
| |
| // Initiates a non-persistent query that will not be manually canceled. |
| // No JavaScript callbacks will be executed. |
| // OnQuery and OnQueryCanceled will be called. |
| AUTOCANCEL, |
| |
| // Initiates a persistent query with multiple successful responses that will |
| // not be manually canceled. |
| // OnQuery, OnNotify and OnQueryCanceled will be called. |
| PERSISTENT_AUTOCANCEL, |
| }; |
| |
| class Observer { |
| public: |
| // Called when all manual queries are complete. |
| virtual void OnManualQueriesCompleted(MultiQueryManager* manager) {} |
| |
| // Called when all queries are complete. |
| virtual void OnAllQueriesCompleted(MultiQueryManager* manager) {} |
| |
| protected: |
| virtual ~Observer() {} |
| }; |
| |
| MultiQueryManager(const std::string& label, |
| bool synchronous, |
| int id_offset = 0) |
| : label_(label), |
| synchronous_(synchronous), |
| id_offset_(id_offset), |
| finalized_(false), |
| running_(false), |
| manual_total_(0), |
| received_count_(0), |
| manual_complete_count_(0), |
| auto_complete_count_(0), |
| will_cancel_by_removing_handler_(false), |
| weak_ptr_factory_(this) {} |
| |
| virtual ~MultiQueryManager() {} |
| |
| std::string label() const { return label_; } |
| |
| void AddObserver(Observer* observer) { |
| EXPECT_FALSE(running_); |
| observer_set_.insert(observer); |
| } |
| |
| void RemoveObserver(Observer* observer) { |
| EXPECT_FALSE(running_); |
| EXPECT_TRUE(observer_set_.erase(observer)); |
| } |
| |
| // Can be called from any thread, but should always be called from the same |
| // thread. |
| void AddTestQuery(TestType type) { |
| EXPECT_FALSE(finalized_); |
| test_query_vector_.push_back(TestQuery(type)); |
| if (!IsAuto(type)) |
| manual_total_++; |
| } |
| |
| // Must be called after AddTestQuery and before the manager is used. |
| void Finalize() { |
| EXPECT_FALSE(finalized_); |
| finalized_ = true; |
| } |
| |
| // Call after all manual queries have completed if you intend to cancel auto |
| // queries by removing the handler. |
| void WillCancelByRemovingHandler() { |
| EXPECT_TRUE(IsManualComplete()); |
| will_cancel_by_removing_handler_ = true; |
| } |
| |
| std::string GetHTML(bool assert_total, bool assert_browser) const { |
| EXPECT_TRUE(finalized_); |
| EXPECT_FALSE(running_); |
| |
| std::string html; |
| |
| html = "<html><body>" + label_ + "<script>\n"; |
| |
| // No requests should exist. |
| if (assert_total) |
| html += "window.mrtAssertTotalCount(0);\n"; |
| if (assert_browser) |
| html += "window.mrtAssertBrowserCount(0);\n"; |
| html += "window.mrtAssertContextCount(0);\n"; |
| |
| if (synchronous_) { |
| // Run all of the queries synchronously. None will complete before the |
| // last one begins. |
| for (size_t i = 0; i < test_query_vector_.size(); ++i) { |
| const TestQuery& query = test_query_vector_[i]; |
| html += GetQueryHTML(static_cast<int>(i), query); |
| } |
| |
| const int total_ct = static_cast<int>(test_query_vector_.size()); |
| |
| // Pending requests should match the total created. |
| const std::string& total_val = GetIntString(total_ct); |
| if (assert_total) |
| html += "window.mrtAssertTotalCount(" + total_val + ");\n"; |
| if (assert_browser) |
| html += "window.mrtAssertBrowserCount(" + total_val + ");\n"; |
| html += "window.mrtAssertContextCount(" + total_val + ");\n"; |
| |
| int cancel_ct = 0; |
| |
| // Cancel all of the queries with type CANCEL. |
| for (size_t i = 0; i < test_query_vector_.size(); ++i) { |
| const TestQuery& query = test_query_vector_[i]; |
| if (query.type == CANCEL) { |
| html += GetCancelHTML(static_cast<int>(i), query); |
| cancel_ct++; |
| } |
| } |
| |
| if (cancel_ct > 0) { |
| // Pending requests should match the total not canceled. |
| const std::string& cancel_val = GetIntString(total_ct - cancel_ct); |
| if (assert_total) |
| html += "window.mrtAssertTotalCount(" + cancel_val + ");\n"; |
| if (assert_browser) |
| html += "window.mrtAssertBrowserCount(" + cancel_val + ");\n"; |
| html += "window.mrtAssertContextCount(" + cancel_val + ");\n"; |
| } |
| } else { |
| // Run all of the queries asynchronously. Some may complete before |
| // others begin. |
| for (size_t i = 0; i < test_query_vector_.size(); ++i) { |
| const TestQuery& query = test_query_vector_[i]; |
| |
| const int index = static_cast<int>(i); |
| |
| // Each request is delayed by 10ms from the previous request. |
| const std::string& delay_val = GetIntString(index); |
| const std::string& query_html = GetQueryHTML(index, query); |
| |
| html += "window.setTimeout(function() {\n" + query_html; |
| |
| if (query.type == CANCEL) { |
| // Cancel the query asynchronously with a 10ms delay. |
| const std::string& request_id_var = |
| GetIDString(kMultiQueryRequestId, index); |
| html += |
| " window.setTimeout(function() {\n" |
| " window.mrtQueryCancel(" + |
| request_id_var + |
| ");\n" |
| " }, 1);\n"; |
| } |
| |
| html += "\n}, " + delay_val + ");\n"; |
| } |
| } |
| |
| html += "</script></body></html>"; |
| |
| return html; |
| } |
| |
| void OnNotify(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| const std::string& message) { |
| EXPECT_TRUE(finalized_); |
| EXPECT_UI_THREAD(); |
| |
| if (!running_) |
| running_ = true; |
| |
| EXPECT_TRUE(browser.get()); |
| EXPECT_TRUE(frame.get()); |
| |
| std::string value; |
| int index = 0; |
| EXPECT_TRUE(SplitIDString(message, &value, &index)); |
| |
| TestQuery& query = test_query_vector_[index]; |
| |
| // Verify that browser and frame are the same. |
| EXPECT_EQ(query.browser_id, browser->GetIdentifier()) << index; |
| EXPECT_EQ(query.frame_id, frame->GetIdentifier()) << index; |
| |
| // Verify a successful/expected result. |
| if (will_cancel_by_removing_handler_) { |
| // Auto queries receive an onFailure callback which will notify with error |
| // when the handler is removed. |
| EXPECT_STREQ(kMultiQueryError, value.c_str()) << index; |
| EXPECT_TRUE(IsAuto(query.type)) << index; |
| EXPECT_TRUE(query.got_query) << index; |
| if (query.type == PERSISTENT_AUTOCANCEL) |
| EXPECT_TRUE(query.got_success) << index; |
| else |
| EXPECT_FALSE(query.got_success) << index; |
| |
| query.got_error.yes(); |
| |
| // There's a race between OnQueryCanceled and OnNotification. Only call |
| // OnQueryCompleted a single time. |
| if (query.got_query_canceled) |
| OnQueryCompleted(query.type); |
| } else { |
| EXPECT_STREQ(kMultiQuerySuccess, value.c_str()) << index; |
| EXPECT_TRUE(WillNotify(query.type)) << index; |
| EXPECT_TRUE(query.got_query) << index; |
| EXPECT_FALSE(query.got_query_canceled) << index; |
| EXPECT_FALSE(query.got_success) << index; |
| |
| query.got_success.yes(); |
| |
| // PERSISTENT_AUTOCANCEL doesn't call OnReceiveCompleted from OnQuery. |
| if (query.type == PERSISTENT_AUTOCANCEL) |
| OnReceiveCompleted(query.type); |
| |
| // Call OnQueryCompleted for types that don't get OnQueryCanceled. |
| if (!WillCancel(query.type)) |
| OnQueryCompleted(query.type); |
| } |
| } |
| |
| bool OnQuery(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id, |
| const CefString& request, |
| bool persistent, |
| CefRefPtr<Callback> callback) override { |
| EXPECT_TRUE(finalized_); |
| EXPECT_UI_THREAD(); |
| |
| if (!running_) |
| running_ = true; |
| |
| EXPECT_TRUE(browser.get()); |
| EXPECT_TRUE(frame.get()); |
| EXPECT_NE(0, query_id); |
| |
| std::string value; |
| int index = 0; |
| EXPECT_TRUE(SplitIDString(request, &value, &index)); |
| |
| TestQuery& query = test_query_vector_[index]; |
| |
| if (IsPersistent(query.type)) |
| EXPECT_TRUE(persistent); |
| else |
| EXPECT_FALSE(persistent); |
| |
| // Verify expected request. |
| EXPECT_STREQ(kMultiQueryRequest, value.c_str()) << index; |
| |
| // Verify that call order is correct. |
| EXPECT_FALSE(query.got_query) << index; |
| EXPECT_FALSE(query.got_query_canceled) << index; |
| EXPECT_FALSE(query.got_success) << index; |
| EXPECT_FALSE(query.got_error) << index; |
| |
| query.got_query.yes(); |
| |
| query.browser_id = browser->GetIdentifier(); |
| query.frame_id = frame->GetIdentifier(); |
| query.is_main_frame = frame->IsMain(); |
| |
| if (query.type == SUCCESS) { |
| // Send the single success response. |
| callback->Success(GetIDString(kMultiQueryResponse, index)); |
| } else if (IsPersistent(query.type)) { |
| // Send the required number of successful responses. |
| const std::string& response = GetIDString(kMultiQueryResponse, index); |
| for (int i = 0; i < kMultiQueryPersistentResponseCount; ++i) |
| callback->Success(response); |
| } |
| |
| if (WillFail(query.type)) { |
| // Send the single failure response. |
| callback->Failure(index, GetIDString(kMultiQueryErrorMessage, index)); |
| } |
| |
| if (WillCancel(query.type)) { |
| // Hold onto the callback until the query is canceled. |
| query.query_id = query_id; |
| query.callback = callback; |
| } |
| |
| // PERSISTENT_AUTOCANCEL will call OnReceiveCompleted once the success |
| // notification is received. |
| if (query.type != PERSISTENT_AUTOCANCEL) |
| OnReceiveCompleted(query.type); |
| |
| return true; |
| } |
| |
| void OnQueryCanceled(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id) override { |
| EXPECT_TRUE(finalized_); |
| EXPECT_UI_THREAD(); |
| |
| if (!running_) |
| running_ = true; |
| |
| EXPECT_TRUE(browser.get()); |
| EXPECT_TRUE(frame.get()); |
| EXPECT_NE(0, query_id); |
| |
| bool found = false; |
| for (size_t i = 0; i < test_query_vector_.size(); ++i) { |
| TestQuery& query = test_query_vector_[i]; |
| if (query.query_id == query_id) { |
| // Verify that browser and frame are the same. |
| EXPECT_EQ(query.browser_id, browser->GetIdentifier()) << i; |
| if (query.is_main_frame) { |
| EXPECT_TRUE(frame->IsMain()) << i; |
| } else { |
| EXPECT_FALSE(frame->IsMain()) << i; |
| EXPECT_EQ(query.frame_id, frame->GetIdentifier()) << i; |
| } |
| |
| // Verify a successful/expected result. |
| EXPECT_TRUE(WillCancel(query.type)) << i; |
| EXPECT_TRUE(query.callback.get()) << i; |
| |
| // Release the callback. |
| query.callback = nullptr; |
| |
| // Verify that call order is correct. |
| EXPECT_TRUE(query.got_query) << i; |
| |
| if (query.type == CANCEL || query.type == AUTOCANCEL) { |
| // No JavaScript onSuccess callback executes. |
| EXPECT_FALSE(query.got_success) << i; |
| } else { |
| // JavaScript onSuccess does execute before cancellation. |
| EXPECT_TRUE(query.got_success) << i; |
| } |
| |
| query.got_query_canceled.yes(); |
| |
| if (will_cancel_by_removing_handler_) { |
| // There's a race between OnQueryCanceled and OnNotification. Only |
| // call OnQueryCompleted a single time. |
| if (query.got_error) |
| OnQueryCompleted(query.type); |
| } else { |
| EXPECT_FALSE(query.got_error) << i; |
| |
| // Cancellation is always completion. |
| OnQueryCompleted(query.type); |
| } |
| |
| found = true; |
| break; |
| } |
| } |
| EXPECT_TRUE(found); |
| } |
| |
| // Asserts that all queries have completed. |
| void AssertAllComplete() const { |
| EXPECT_TRUE(finalized_); |
| EXPECT_FALSE(running_); |
| EXPECT_UI_THREAD(); |
| |
| for (size_t i = 0; i < test_query_vector_.size(); ++i) { |
| const TestQuery& query = test_query_vector_[i]; |
| EXPECT_TRUE(query.got_query) << i; |
| |
| if (WillCancel(query.type)) |
| EXPECT_TRUE(query.got_query_canceled) << i; |
| else |
| EXPECT_FALSE(query.got_query_canceled) << i; |
| |
| if (WillNotify(query.type)) |
| EXPECT_TRUE(query.got_success) << i; |
| else |
| EXPECT_FALSE(query.got_success) << i; |
| |
| if (IsAuto(query.type) && will_cancel_by_removing_handler_) |
| EXPECT_TRUE(query.got_error); |
| else |
| EXPECT_FALSE(query.got_error); |
| |
| EXPECT_FALSE(query.callback.get()) << i; |
| } |
| } |
| |
| // Returns true if all manual queries have completed. |
| bool IsManualComplete() const { |
| EXPECT_TRUE(finalized_); |
| EXPECT_UI_THREAD(); |
| |
| return (manual_complete_count_ == manual_total_); |
| } |
| |
| // Returns true if all queries have completed. |
| bool IsAllComplete() const { |
| EXPECT_TRUE(finalized_); |
| EXPECT_UI_THREAD(); |
| |
| return (manual_complete_count_ + auto_complete_count_ == |
| static_cast<int>(test_query_vector_.size())); |
| } |
| |
| bool HasAutoQueries() const { |
| return (manual_total_ != static_cast<int>(test_query_vector_.size())); |
| } |
| |
| private: |
| struct TestQuery { |
| explicit TestQuery(TestType test_type) |
| : type(test_type), |
| browser_id(0), |
| frame_id(0), |
| is_main_frame(false), |
| query_id(0) {} |
| |
| TestType type; |
| |
| // Set in OnQuery and verified in OnNotify or OnQueryCanceled. |
| int browser_id; |
| int64 frame_id; |
| bool is_main_frame; |
| |
| // Used when a query is canceled. |
| int64 query_id; |
| CefRefPtr<Callback> callback; |
| |
| TrackCallback got_query; |
| TrackCallback got_query_canceled; |
| TrackCallback got_success; |
| TrackCallback got_error; |
| }; |
| |
| class NotifyTask : public CefTask { |
| public: |
| NotifyTask(base::WeakPtr<MultiQueryManager> weak_ptr, bool notify_all) |
| : weak_ptr_(weak_ptr), notify_all_(notify_all) {} |
| |
| void Execute() override { |
| if (weak_ptr_) { |
| if (notify_all_) |
| weak_ptr_->NotifyAllQueriesCompleted(); |
| else |
| weak_ptr_->NotifyManualQueriesCompleted(); |
| } |
| } |
| |
| private: |
| base::WeakPtr<MultiQueryManager> weak_ptr_; |
| const bool notify_all_; |
| |
| IMPLEMENT_REFCOUNTING(NotifyTask); |
| }; |
| |
| static bool IsAuto(TestType type) { |
| return (type == AUTOCANCEL || type == PERSISTENT_AUTOCANCEL); |
| } |
| |
| static bool IsPersistent(TestType type) { |
| return (type == PERSISTENT_SUCCESS || type == PERSISTENT_FAILURE || |
| type == PERSISTENT_AUTOCANCEL); |
| } |
| |
| static bool WillFail(TestType type) { |
| return (type == FAILURE || type == PERSISTENT_FAILURE); |
| } |
| |
| static bool WillCancel(TestType type) { |
| return (type == PERSISTENT_SUCCESS || type == CANCEL || |
| type == AUTOCANCEL || type == PERSISTENT_AUTOCANCEL); |
| } |
| |
| static bool WillNotify(TestType type) { |
| return (type == SUCCESS || type == PERSISTENT_SUCCESS || type == FAILURE || |
| type == PERSISTENT_FAILURE || type == PERSISTENT_AUTOCANCEL); |
| } |
| |
| void OnReceiveCompleted(TestType type) { |
| const int total_count = static_cast<int>(test_query_vector_.size()); |
| if (++received_count_ == total_count && manual_total_ == 0) { |
| // There aren't any manual queries so notify here. |
| CefPostTask(TID_UI, |
| new NotifyTask(weak_ptr_factory_.GetWeakPtr(), false)); |
| } |
| } |
| |
| void OnQueryCompleted(TestType type) { |
| const int total_count = static_cast<int>(test_query_vector_.size()); |
| EXPECT_LT(manual_complete_count_ + auto_complete_count_, total_count); |
| EXPECT_LE(manual_complete_count_, manual_total_); |
| |
| const bool is_auto = IsAuto(type); |
| if (is_auto) |
| auto_complete_count_++; |
| else if (++manual_complete_count_ == manual_total_) { |
| CefPostTask(TID_UI, |
| new NotifyTask(weak_ptr_factory_.GetWeakPtr(), false)); |
| } |
| |
| if (auto_complete_count_ + manual_complete_count_ == total_count) { |
| running_ = false; |
| CefPostTask(TID_UI, new NotifyTask(weak_ptr_factory_.GetWeakPtr(), true)); |
| } |
| } |
| |
| void NotifyManualQueriesCompleted() { |
| if (observer_set_.empty()) |
| return; |
| |
| // Use a copy of the set in case an Observer is removed while we're |
| // iterating. |
| ObserverSet observer_set = observer_set_; |
| |
| ObserverSet::const_iterator it = observer_set.begin(); |
| for (; it != observer_set.end(); ++it) { |
| (*it)->OnManualQueriesCompleted(this); |
| } |
| } |
| |
| void NotifyAllQueriesCompleted() { |
| if (observer_set_.empty()) |
| return; |
| |
| // Use a copy of the set in case an Observer is removed while we're |
| // iterating. |
| ObserverSet observer_set = observer_set_; |
| |
| ObserverSet::const_iterator it = observer_set.begin(); |
| for (; it != observer_set.end(); ++it) { |
| (*it)->OnAllQueriesCompleted(this); |
| } |
| } |
| |
| std::string GetQueryHTML(const int index, const TestQuery& query) const { |
| const std::string& request_id_var = |
| GetIDString(kMultiQueryRequestId, index); |
| const std::string& repeat_ct_var = GetIDString(kMultiQueryRepeatCt, index); |
| const std::string& request_val = |
| GetIDString(std::string(kMultiQueryRequest) + ":", index); |
| const std::string& success_val = |
| GetIDString(std::string(kMultiQuerySuccess) + ":", index); |
| const std::string& error_val = |
| GetIDString(std::string(kMultiQueryError) + ":", index); |
| |
| std::string html; |
| |
| const bool persistent = IsPersistent(query.type); |
| |
| if (persistent) |
| html += "var " + repeat_ct_var + " = 0;\n"; |
| |
| html += "var " + request_id_var + |
| " = window.mrtQuery({\n" |
| " request: '" + |
| request_val + |
| "',\n" |
| " persistent: " + |
| (persistent ? "true" : "false") + ",\n"; |
| |
| if (query.type == SUCCESS) { |
| const std::string& response_val = GetIDString(kMultiQueryResponse, index); |
| |
| html += |
| " onSuccess: function(response) {\n" |
| " if (response == '" + |
| response_val + |
| "')\n" |
| " window.mrtNotify('" + |
| success_val + |
| "');\n" |
| " else\n" |
| " window.mrtNotify('" + |
| error_val + |
| "');\n" |
| " },\n" |
| " onFailure: function(error_code, error_message) {\n" |
| " window.mrtNotify('" + |
| error_val + |
| "');\n" |
| " }\n"; |
| } else if (query.type == FAILURE) { |
| const std::string& error_code_val = GetIntString(index); |
| const std::string& error_message_val = |
| GetIDString(kMultiQueryErrorMessage, index); |
| |
| html += |
| " onSuccess: function(response) {\n" |
| " window.mrtNotify('" + |
| error_val + |
| "');\n" |
| " },\n" |
| " onFailure: function(error_code, error_message) {\n" |
| " if (error_code == " + |
| error_code_val + " && error_message == '" + error_message_val + |
| "')\n" |
| " window.mrtNotify('" + |
| success_val + |
| "');\n" |
| " else\n" |
| " window.mrtNotify('" + |
| error_val + |
| "');\n" |
| " }\n"; |
| } else if (query.type == PERSISTENT_SUCCESS || |
| query.type == PERSISTENT_AUTOCANCEL) { |
| const std::string& response_val = GetIDString(kMultiQueryResponse, index); |
| const std::string& repeat_ct = |
| GetIntString(kMultiQueryPersistentResponseCount); |
| |
| html += |
| " onSuccess: function(response) {\n" |
| " if (response == '" + |
| response_val + |
| "') {\n" |
| // Should get repeat_ct number of successful responses. |
| " if (++" + |
| repeat_ct_var + " == " + repeat_ct + |
| ") {\n" |
| " window.mrtNotify('" + |
| success_val + "');\n"; |
| |
| if (query.type == PERSISTENT_SUCCESS) { |
| // Manually cancel the request. |
| html += " window.mrtQueryCancel(" + request_id_var + ");\n"; |
| } |
| |
| html += |
| " }\n" |
| " } else {\n" |
| " window.mrtNotify('" + |
| error_val + |
| "');\n" |
| " }\n" |
| " },\n" |
| " onFailure: function(error_code, error_message) {\n" |
| " window.mrtNotify('" + |
| error_val + |
| "');\n" |
| " }\n"; |
| } else if (query.type == PERSISTENT_FAILURE) { |
| const std::string& error_code_val = GetIntString(index); |
| const std::string& error_message_val = |
| GetIDString(kMultiQueryErrorMessage, index); |
| const std::string& repeat_ct = |
| GetIntString(kMultiQueryPersistentResponseCount); |
| |
| html += |
| " onSuccess: function(response) {\n" |
| // Should get some successful responses before failure. |
| " if (++" + |
| repeat_ct_var + " > " + repeat_ct + |
| ") {\n" |
| " window.mrtNotify('" + |
| error_val + |
| "');\n" |
| " }\n" |
| " },\n" |
| " onFailure: function(error_code, error_message) {\n" |
| " if (error_code == " + |
| error_code_val + " && error_message == '" + error_message_val + |
| "'" |
| " && " + |
| repeat_ct_var + " == " + repeat_ct + |
| ")\n" |
| " window.mrtNotify('" + |
| success_val + |
| "');\n" |
| " else\n" |
| " window.mrtNotify('" + |
| error_val + |
| "');\n" |
| " }\n"; |
| } else if (query.type == CANCEL || query.type == AUTOCANCEL) { |
| html += |
| " onSuccess: function(response) {\n" |
| " window.mrtNotify('" + |
| error_val + |
| "');\n" |
| " },\n" |
| " onFailure: function(error_code, error_message) {\n" |
| " window.mrtNotify('" + |
| error_val + |
| "');\n" |
| " }\n"; |
| } |
| |
| html += "});\n"; |
| |
| return html; |
| } |
| |
| std::string GetCancelHTML(const int index, const TestQuery& query) const { |
| const std::string& request_id_var = |
| GetIDString(kMultiQueryRequestId, index); |
| return "window.mrtQueryCancel(" + request_id_var + ");\n"; |
| } |
| |
| std::string GetIDString(const std::string& prefix, int index) const { |
| EXPECT_TRUE(!prefix.empty()); |
| std::stringstream ss; |
| ss << prefix << GetIDFromIndex(index); |
| return ss.str(); |
| } |
| |
| bool SplitIDString(const std::string& str, |
| std::string* value, |
| int* index) const { |
| size_t pos = str.find(':'); |
| if (pos != std::string::npos) { |
| *value = str.substr(0, pos); |
| *index = GetIndexFromID(atoi(str.substr(pos + 1).c_str())); |
| return (*index >= 0 && |
| *index < static_cast<int>(test_query_vector_.size())); |
| } |
| |
| return false; |
| } |
| |
| std::string GetIntString(int val) const { |
| std::stringstream ss; |
| ss << val; |
| return ss.str(); |
| } |
| |
| int GetIDFromIndex(int index) const { return id_offset_ + index; } |
| int GetIndexFromID(int id) const { return id - id_offset_; } |
| |
| const std::string label_; |
| const bool synchronous_; |
| const int id_offset_; |
| |
| typedef std::vector<TestQuery> TestQueryVector; |
| TestQueryVector test_query_vector_; |
| |
| typedef std::set<Observer*> ObserverSet; |
| ObserverSet observer_set_; |
| |
| // Set to true after all queries have been added. |
| bool finalized_; |
| // Set to true while queries are pending. |
| bool running_; |
| |
| // Total number of queries that will manually complete. |
| int manual_total_; |
| |
| // Number of queries that have been received. |
| int received_count_; |
| |
| // Number of queries that have completed successfully. |
| int manual_complete_count_; |
| int auto_complete_count_; |
| |
| // If true any pending queries will receive an onFailure callback in addition |
| // to be canceled. |
| bool will_cancel_by_removing_handler_; |
| |
| // Should always be the last member. |
| base::WeakPtrFactory<MultiQueryManager> weak_ptr_factory_; |
| }; |
| |
| void MakeTestQueries(MultiQueryManager* manager, |
| bool some, |
| int many_count = 200) { |
| if (some) { |
| // Test some queries of arbitrary types. |
| // Use a hard-coded list so the behavior is deterministic across test runs. |
| MultiQueryManager::TestType types[] = { |
| MultiQueryManager::PERSISTENT_AUTOCANCEL, |
| MultiQueryManager::SUCCESS, |
| MultiQueryManager::AUTOCANCEL, |
| MultiQueryManager::PERSISTENT_FAILURE, |
| MultiQueryManager::CANCEL, |
| MultiQueryManager::FAILURE, |
| MultiQueryManager::AUTOCANCEL, |
| MultiQueryManager::SUCCESS, |
| MultiQueryManager::PERSISTENT_SUCCESS, |
| MultiQueryManager::SUCCESS, |
| MultiQueryManager::PERSISTENT_AUTOCANCEL, |
| MultiQueryManager::CANCEL, |
| MultiQueryManager::PERSISTENT_SUCCESS, |
| MultiQueryManager::FAILURE, |
| }; |
| for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) { |
| manager->AddTestQuery(types[i]); |
| } |
| } else { |
| // Test every type of query. |
| for (int i = 0; i < many_count; ++i) { |
| MultiQueryManager::TestType type = MultiQueryManager::SUCCESS; |
| switch (i % 7) { |
| case 0: |
| type = MultiQueryManager::SUCCESS; |
| break; |
| case 1: |
| type = MultiQueryManager::FAILURE; |
| break; |
| case 2: |
| type = MultiQueryManager::PERSISTENT_SUCCESS; |
| break; |
| case 3: |
| type = MultiQueryManager::PERSISTENT_FAILURE; |
| break; |
| case 4: |
| type = MultiQueryManager::CANCEL; |
| break; |
| case 5: |
| type = MultiQueryManager::AUTOCANCEL; |
| break; |
| case 6: |
| type = MultiQueryManager::PERSISTENT_AUTOCANCEL; |
| break; |
| } |
| manager->AddTestQuery(type); |
| } |
| } |
| manager->Finalize(); |
| } |
| |
| // Test multiple queries in a single page load with a single frame. |
| class MultiQuerySingleFrameTestHandler : public SingleLoadTestHandler, |
| public MultiQueryManager::Observer { |
| public: |
| enum CancelType { |
| CANCEL_BY_NAVIGATION, |
| CANCEL_BY_REMOVING_HANDLER, |
| CANCEL_BY_CLOSING_BROWSER, |
| }; |
| |
| MultiQuerySingleFrameTestHandler( |
| bool synchronous, |
| CancelType cancel_type = CANCEL_BY_NAVIGATION) |
| : manager_(std::string(), synchronous), cancel_type_(cancel_type) { |
| manager_.AddObserver(this); |
| } |
| |
| std::string GetMainHTML() override { return manager_.GetHTML(true, true); } |
| |
| void OnNotify(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| const std::string& message) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| |
| manager_.OnNotify(browser, frame, message); |
| } |
| |
| bool OnQuery(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id, |
| const CefString& request, |
| bool persistent, |
| CefRefPtr<Callback> callback) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| |
| return manager_.OnQuery(browser, frame, query_id, request, persistent, |
| callback); |
| } |
| |
| void OnQueryCanceled(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| |
| manager_.OnQueryCanceled(browser, frame, query_id); |
| } |
| |
| void OnManualQueriesCompleted(MultiQueryManager* manager) override { |
| EXPECT_EQ(manager, &manager_); |
| if (manager_.HasAutoQueries()) { |
| if (cancel_type_ == CANCEL_BY_NAVIGATION) { |
| // Navigate somewhere else to terminate the auto queries. |
| GetBrowser()->GetMainFrame()->LoadURL(std::string(kTestDomain1) + |
| "cancel.html"); |
| } else if (cancel_type_ == CANCEL_BY_REMOVING_HANDLER) { |
| // Change the expected behavior in the manager. |
| manager_.WillCancelByRemovingHandler(); |
| GetRouter()->RemoveHandler(this); |
| // All queries should be immediately canceled. |
| AssertQueryCount(nullptr, nullptr, 0); |
| } else if (cancel_type_ == CANCEL_BY_CLOSING_BROWSER) { |
| // Change the expected behavior in the handler. |
| SetSignalCompletionWhenAllBrowsersClose(false); |
| CloseBrowser(GetBrowser(), false); |
| } |
| } |
| } |
| |
| void OnAllQueriesCompleted(MultiQueryManager* manager) override { |
| EXPECT_EQ(manager, &manager_); |
| |
| // All queries should be canceled. |
| AssertQueryCount(nullptr, nullptr, 0); |
| |
| DestroyTest(); |
| |
| if (!SignalCompletionWhenAllBrowsersClose()) { |
| // Complete asynchronously so the call stack has a chance to unwind. |
| CefPostTask( |
| TID_UI, |
| base::Bind(&MultiQuerySingleFrameTestHandler::TestComplete, this)); |
| } |
| } |
| |
| void DestroyTest() override { |
| manager_.AssertAllComplete(); |
| TestHandler::DestroyTest(); |
| } |
| |
| MultiQueryManager* GetManager() { return &manager_; } |
| |
| private: |
| MultiQueryManager manager_; |
| const CancelType cancel_type_; |
| }; |
| |
| } // namespace |
| |
| #define MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(name, type, synchronous) \ |
| TEST(MessageRouterTest, name) { \ |
| CefRefPtr<MultiQuerySingleFrameTestHandler> handler = \ |
| new MultiQuerySingleFrameTestHandler(synchronous); \ |
| MultiQueryManager* manager = handler->GetManager(); \ |
| manager->AddTestQuery(MultiQueryManager::type); \ |
| manager->Finalize(); \ |
| handler->ExecuteTest(); \ |
| ReleaseAndWaitForDestructor(handler); \ |
| } |
| |
| // Test the query types individually. |
| MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncSuccess, |
| SUCCESS, |
| true) |
| MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncSuccess, |
| SUCCESS, |
| false) |
| MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncFailure, |
| FAILURE, |
| true) |
| MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncFailure, |
| FAILURE, |
| false) |
| MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncPersistentSuccess, |
| PERSISTENT_SUCCESS, |
| true) |
| MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncPersistentSuccess, |
| PERSISTENT_SUCCESS, |
| false) |
| MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncPersistentFailure, |
| PERSISTENT_FAILURE, |
| true) |
| MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncPersistentFailure, |
| PERSISTENT_FAILURE, |
| false) |
| MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameCancel, CANCEL, true) |
| MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAutoCancel, |
| AUTOCANCEL, |
| true) |
| MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFramePersistentAutoCancel, |
| PERSISTENT_AUTOCANCEL, |
| true) |
| |
| // Test that one frame can run some queries successfully in a synchronous |
| // manner. |
| TEST(MessageRouterTest, MultiQuerySingleFrameSyncSome) { |
| CefRefPtr<MultiQuerySingleFrameTestHandler> handler = |
| new MultiQuerySingleFrameTestHandler(true); |
| MakeTestQueries(handler->GetManager(), true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that one frame can run some queries successfully in an asynchronous |
| // manner. |
| TEST(MessageRouterTest, MultiQuerySingleFrameAsyncSome) { |
| CefRefPtr<MultiQuerySingleFrameTestHandler> handler = |
| new MultiQuerySingleFrameTestHandler(false); |
| MakeTestQueries(handler->GetManager(), true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that one frame can run many queries successfully in a synchronous |
| // manner. |
| TEST(MessageRouterTest, MultiQuerySingleFrameSyncMany) { |
| CefRefPtr<MultiQuerySingleFrameTestHandler> handler = |
| new MultiQuerySingleFrameTestHandler(true); |
| MakeTestQueries(handler->GetManager(), false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that one frame can run many queries successfully in an asynchronous |
| // manner. |
| TEST(MessageRouterTest, MultiQuerySingleFrameAsyncMany) { |
| CefRefPtr<MultiQuerySingleFrameTestHandler> handler = |
| new MultiQuerySingleFrameTestHandler(false); |
| MakeTestQueries(handler->GetManager(), false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that pending queries can be canceled by removing the handler. |
| TEST(MessageRouterTest, MultiQuerySingleFrameCancelByRemovingHandler) { |
| CefRefPtr<MultiQuerySingleFrameTestHandler> handler = |
| new MultiQuerySingleFrameTestHandler( |
| false, MultiQuerySingleFrameTestHandler::CANCEL_BY_REMOVING_HANDLER); |
| MakeTestQueries(handler->GetManager(), false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that pending queries can be canceled by closing the browser. |
| TEST(MessageRouterTest, MultiQuerySingleFrameCancelByClosingBrowser) { |
| CefRefPtr<MultiQuerySingleFrameTestHandler> handler = |
| new MultiQuerySingleFrameTestHandler( |
| false, MultiQuerySingleFrameTestHandler::CANCEL_BY_CLOSING_BROWSER); |
| MakeTestQueries(handler->GetManager(), false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| namespace { |
| |
| // Test multiple handlers. |
| class MultiQueryMultiHandlerTestHandler : public SingleLoadTestHandler, |
| public MultiQueryManager::Observer { |
| public: |
| class Handler : public CefMessageRouterBrowserSide::Handler { |
| public: |
| Handler(MultiQueryMultiHandlerTestHandler* test_handler, int index) |
| : test_handler_(test_handler), index_(index), query_id_(0) {} |
| |
| bool OnQuery(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id, |
| const CefString& request, |
| bool persistent, |
| CefRefPtr<Callback> callback) override { |
| // Each handler only handles a single request. |
| std::stringstream ss; |
| ss << kMultiQueryRequest << ":" << index_; |
| const std::string& handled_request = ss.str(); |
| if (request != handled_request) |
| return false; |
| |
| // Verify that handlers are called in the correct order. |
| if (index_ == 0) { |
| EXPECT_FALSE(test_handler_->got_query0_); |
| EXPECT_FALSE(test_handler_->got_query1_); |
| EXPECT_FALSE(test_handler_->got_query2_); |
| |
| test_handler_->got_query0_.yes(); |
| } else if (index_ == 1) { |
| EXPECT_TRUE(test_handler_->got_query0_); |
| EXPECT_FALSE(test_handler_->got_query1_); |
| EXPECT_FALSE(test_handler_->got_query2_); |
| |
| test_handler_->got_query1_.yes(); |
| } else if (index_ == 2) { |
| EXPECT_TRUE(test_handler_->got_query0_); |
| EXPECT_TRUE(test_handler_->got_query1_); |
| EXPECT_FALSE(test_handler_->got_query2_); |
| |
| test_handler_->got_query2_.yes(); |
| } |
| |
| query_id_ = query_id; |
| return test_handler_->OnQuery(browser, frame, query_id, request, |
| persistent, callback); |
| } |
| |
| void OnQueryCanceled(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id) override { |
| // Verify that the correct handler is called for cancellation. |
| EXPECT_EQ(query_id_, query_id); |
| |
| if (index_ == 0) { |
| EXPECT_FALSE(test_handler_->got_query_canceled0_); |
| test_handler_->got_query_canceled0_.yes(); |
| } else if (index_ == 1) { |
| EXPECT_FALSE(test_handler_->got_query_canceled1_); |
| test_handler_->got_query_canceled1_.yes(); |
| } else if (index_ == 2) { |
| EXPECT_FALSE(test_handler_->got_query_canceled2_); |
| test_handler_->got_query_canceled2_.yes(); |
| } |
| |
| test_handler_->OnQueryCanceled(browser, frame, query_id); |
| } |
| |
| private: |
| MultiQueryMultiHandlerTestHandler* test_handler_; |
| const int index_; |
| int query_id_; |
| }; |
| |
| MultiQueryMultiHandlerTestHandler(bool synchronous, |
| bool cancel_by_removing_handler) |
| : manager_(std::string(), synchronous, 0), |
| handler0_(this, 0), |
| handler1_(this, 1), |
| handler2_(this, 2), |
| cancel_by_removing_handler_(cancel_by_removing_handler) { |
| manager_.AddObserver(this); |
| |
| // Each handler will handle one of the queries. |
| manager_.AddTestQuery(MultiQueryManager::PERSISTENT_AUTOCANCEL); |
| manager_.AddTestQuery(MultiQueryManager::PERSISTENT_AUTOCANCEL); |
| manager_.AddTestQuery(MultiQueryManager::PERSISTENT_AUTOCANCEL); |
| manager_.Finalize(); |
| } |
| |
| std::string GetMainHTML() override { return manager_.GetHTML(true, true); } |
| |
| void OnNotify(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| const std::string& message) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| |
| manager_.OnNotify(browser, frame, message); |
| } |
| |
| bool OnQuery(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id, |
| const CefString& request, |
| bool persistent, |
| CefRefPtr<Callback> callback) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| |
| return manager_.OnQuery(browser, frame, query_id, request, persistent, |
| callback); |
| } |
| |
| void OnQueryCanceled(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id) override { |
| AssertMainBrowser(browser); |
| AssertMainFrame(frame); |
| |
| manager_.OnQueryCanceled(browser, frame, query_id); |
| } |
| |
| void OnManualQueriesCompleted(MultiQueryManager* manager) override { |
| EXPECT_EQ(manager, &manager_); |
| |
| EXPECT_TRUE(got_query0_); |
| EXPECT_TRUE(got_query1_); |
| EXPECT_TRUE(got_query2_); |
| EXPECT_FALSE(got_query_canceled0_); |
| EXPECT_FALSE(got_query_canceled1_); |
| EXPECT_FALSE(got_query_canceled2_); |
| |
| EXPECT_TRUE(manager_.HasAutoQueries()); |
| |
| CefRefPtr<CefMessageRouterBrowserSide> router = GetRouter(); |
| |
| // Remove one handler to cancel a query. |
| |
| if (cancel_by_removing_handler_) { |
| manager_.WillCancelByRemovingHandler(); |
| |
| // Each query should be canceled as the handler is removed. |
| EXPECT_TRUE(router->RemoveHandler(&handler1_)); |
| EXPECT_FALSE(got_query_canceled0_); |
| EXPECT_TRUE(got_query_canceled1_); |
| EXPECT_FALSE(got_query_canceled2_); |
| |
| EXPECT_TRUE(router->RemoveHandler(&handler2_)); |
| EXPECT_FALSE(got_query_canceled0_); |
| EXPECT_TRUE(got_query_canceled2_); |
| |
| EXPECT_TRUE(router->RemoveHandler(&handler0_)); |
| EXPECT_TRUE(got_query_canceled0_); |
| } else { |
| GetBrowser()->GetMainFrame()->LoadURL(std::string(kTestDomain1) + |
| "cancel.html"); |
| } |
| } |
| |
| void OnAllQueriesCompleted(MultiQueryManager* manager) override { |
| EXPECT_EQ(manager, &manager_); |
| |
| // All queries should be canceled. |
| AssertQueryCount(nullptr, nullptr, 0); |
| |
| DestroyTest(); |
| } |
| |
| void DestroyTest() override { |
| EXPECT_TRUE(got_query0_); |
| EXPECT_TRUE(got_query1_); |
| EXPECT_TRUE(got_query2_); |
| EXPECT_TRUE(got_query_canceled0_); |
| EXPECT_TRUE(got_query_canceled1_); |
| EXPECT_TRUE(got_query_canceled2_); |
| |
| manager_.AssertAllComplete(); |
| TestHandler::DestroyTest(); |
| } |
| |
| protected: |
| void AddHandlers( |
| CefRefPtr<CefMessageRouterBrowserSide> message_router) override { |
| // OnQuery call order will verify that the first/last ordering works as |
| // expected. |
| EXPECT_TRUE(message_router->AddHandler(&handler1_, true)); |
| EXPECT_TRUE(message_router->AddHandler(&handler0_, true)); |
| EXPECT_TRUE(message_router->AddHandler(&handler2_, false)); |
| |
| // Can't add the same handler multiple times. |
| EXPECT_FALSE(message_router->AddHandler(&handler1_, true)); |
| } |
| |
| private: |
| MultiQueryManager manager_; |
| Handler handler0_; |
| Handler handler1_; |
| Handler handler2_; |
| |
| const bool cancel_by_removing_handler_; |
| |
| TrackCallback got_query0_; |
| TrackCallback got_query1_; |
| TrackCallback got_query2_; |
| |
| TrackCallback got_query_canceled0_; |
| TrackCallback got_query_canceled1_; |
| TrackCallback got_query_canceled2_; |
| }; |
| |
| } // namespace |
| |
| // Test that multiple handlers behave correctly. |
| TEST(MessageRouterTest, MultiQueryMultiHandler) { |
| CefRefPtr<MultiQueryMultiHandlerTestHandler> handler = |
| new MultiQueryMultiHandlerTestHandler(false, false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that multiple handlers behave correctly. Cancel by removing the |
| // handlers. |
| TEST(MessageRouterTest, MultiQueryMultiHandlerCancelByRemovingHandler) { |
| CefRefPtr<MultiQueryMultiHandlerTestHandler> handler = |
| new MultiQueryMultiHandlerTestHandler(false, true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| namespace { |
| |
| // Map of managers on a per-URL basis. |
| class MultiQueryManagerMap : public CefMessageRouterBrowserSide::Handler, |
| public MultiQueryManager::Observer { |
| public: |
| class Observer { |
| public: |
| // Called when all manual queries are complete. |
| virtual void OnMapManualQueriesCompleted(MultiQueryManagerMap* map) {} |
| |
| // Called when all queries are complete. |
| virtual void OnMapAllQueriesCompleted(MultiQueryManagerMap* map) {} |
| |
| protected: |
| virtual ~Observer() {} |
| }; |
| |
| MultiQueryManagerMap() |
| : finalized_(false), |
| running_(false), |
| manual_complete_count_(0), |
| total_complete_count_(0) {} |
| |
| virtual ~MultiQueryManagerMap() { RemoveAllManagers(); } |
| |
| void AddObserver(Observer* observer) { |
| EXPECT_FALSE(running_); |
| observer_set_.insert(observer); |
| } |
| |
| void RemoveObserver(Observer* observer) { |
| EXPECT_FALSE(running_); |
| EXPECT_TRUE(observer_set_.erase(observer)); |
| } |
| |
| MultiQueryManager* CreateManager(const std::string& url, bool synchronous) { |
| EXPECT_FALSE(finalized_); |
| |
| MultiQueryManager* manager = new MultiQueryManager( |
| url, synchronous, static_cast<int>(manager_map_.size()) * 1000); |
| manager->AddObserver(this); |
| all_managers_.push_back(manager); |
| pending_managers_.push_back(manager); |
| |
| return manager; |
| } |
| |
| void Finalize() { |
| EXPECT_FALSE(finalized_); |
| finalized_ = true; |
| } |
| |
| std::string GetMainHTML() const { |
| EXPECT_TRUE(finalized_); |
| EXPECT_FALSE(running_); |
| |
| std::string html = "<html><body>\n"; |
| |
| for (size_t i = 0; i < all_managers_.size(); ++i) { |
| const std::string& url = all_managers_[i]->label(); |
| const std::string& name = GetNameForURL(url); |
| html += "<iframe id=\"" + name + "\" src=\"" + url + "\"></iframe>\n"; |
| } |
| |
| html += "</body></html>"; |
| return html; |
| } |
| |
| void OnNotify(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| const std::string& message) { |
| EXPECT_TRUE(finalized_); |
| if (!running_) |
| running_ = true; |
| |
| MultiQueryManager* manager = GetManager(browser, frame); |
| manager->OnNotify(browser, frame, message); |
| } |
| |
| bool OnQuery(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id, |
| const CefString& request, |
| bool persistent, |
| CefRefPtr<Callback> callback) override { |
| EXPECT_TRUE(finalized_); |
| if (!running_) |
| running_ = true; |
| |
| MultiQueryManager* manager = GetManager(browser, frame); |
| return manager->OnQuery(browser, frame, query_id, request, persistent, |
| callback); |
| } |
| |
| void OnQueryCanceled(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id) override { |
| EXPECT_TRUE(finalized_); |
| if (!running_) |
| running_ = true; |
| |
| MultiQueryManager* manager = GetManager(browser, frame); |
| manager->OnQueryCanceled(browser, frame, query_id); |
| } |
| |
| void OnManualQueriesCompleted(MultiQueryManager* manager) override { |
| const int size = static_cast<int>(all_managers_.size()); |
| EXPECT_LT(manual_complete_count_, size); |
| if (++manual_complete_count_ == size) { |
| running_ = false; |
| |
| // Notify observers. |
| if (!observer_set_.empty()) { |
| // Use a copy of the set in case an Observer is removed while we're |
| // iterating. |
| ObserverSet observer_set = observer_set_; |
| |
| ObserverSet::const_iterator it = observer_set.begin(); |
| for (; it != observer_set.end(); ++it) { |
| (*it)->OnMapManualQueriesCompleted(this); |
| } |
| } |
| } |
| } |
| |
| void OnAllQueriesCompleted(MultiQueryManager* manager) override { |
| const int size = static_cast<int>(all_managers_.size()); |
| EXPECT_LT(total_complete_count_, size); |
| if (++total_complete_count_ == size) { |
| running_ = false; |
| |
| // Notify observers. |
| if (!observer_set_.empty()) { |
| // Use a copy of the set in case an Observer is removed while we're |
| // iterating. |
| ObserverSet observer_set = observer_set_; |
| |
| ObserverSet::const_iterator it = observer_set.begin(); |
| for (; it != observer_set.end(); ++it) { |
| (*it)->OnMapAllQueriesCompleted(this); |
| } |
| } |
| } |
| } |
| |
| bool AllComplete() const { |
| EXPECT_TRUE(finalized_); |
| |
| for (size_t i = 0; i < all_managers_.size(); ++i) { |
| if (!all_managers_[i]->IsAllComplete()) |
| return false; |
| } |
| return true; |
| } |
| |
| void AssertAllComplete() const { |
| EXPECT_TRUE(finalized_); |
| EXPECT_TRUE(pending_managers_.empty()); |
| EXPECT_FALSE(running_); |
| |
| for (size_t i = 0; i < all_managers_.size(); ++i) { |
| all_managers_[i]->AssertAllComplete(); |
| } |
| } |
| |
| bool HasAutoQueries() const { |
| for (size_t i = 0; i < all_managers_.size(); ++i) { |
| if (all_managers_[i]->HasAutoQueries()) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void OnLoadStart(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame) { |
| if (pending_managers_.empty()) |
| return; |
| |
| const std::string& expected_url = frame->GetURL(); |
| MultiQueryManager* next_manager = nullptr; |
| |
| // Find the pending manager that matches the expected URL. |
| ManagerList::iterator it = pending_managers_.begin(); |
| for (; it != pending_managers_.end(); ++it) { |
| if ((*it)->label() == expected_url) { |
| next_manager = *it; |
| pending_managers_.erase(it); |
| break; |
| } |
| } |
| |
| EXPECT_TRUE(next_manager); |
| |
| const int browser_id = browser->GetIdentifier(); |
| // Always use the same ID for the main frame. |
| const int64 frame_id = frame->IsMain() ? -1 : frame->GetIdentifier(); |
| |
| const std::pair<int, int64>& id = std::make_pair(browser_id, frame_id); |
| |
| // Remove the currently active manager, if any. |
| ManagerMap::iterator it2 = manager_map_.find(id); |
| if (it2 != manager_map_.end()) |
| manager_map_.erase(it2); |
| |
| // Add the next manager to the active map. |
| manager_map_.insert(std::make_pair(id, next_manager)); |
| } |
| |
| MultiQueryManager* GetManager(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame) const { |
| const int browser_id = browser->GetIdentifier(); |
| // Always use the same ID for the main frame. |
| const int64 frame_id = frame->IsMain() ? -1 : frame->GetIdentifier(); |
| |
| // Find the manager in the active map. |
| ManagerMap::const_iterator it = |
| manager_map_.find(std::make_pair(browser_id, frame_id)); |
| EXPECT_NE(it, manager_map_.end()) |
| << "browser_id = " << browser_id << ", frame_id = " << frame_id; |
| return it->second; |
| } |
| |
| void RemoveAllManagers() { |
| EXPECT_TRUE(pending_managers_.empty()); |
| if (all_managers_.empty()) |
| return; |
| |
| for (size_t i = 0; i < all_managers_.size(); ++i) { |
| delete all_managers_[i]; |
| } |
| all_managers_.clear(); |
| manager_map_.clear(); |
| } |
| |
| static std::string GetNameForURL(const std::string& url) { |
| // Extract the file name without extension. |
| int pos1 = static_cast<int>(url.rfind("/")); |
| int pos2 = static_cast<int>(url.rfind(".")); |
| EXPECT_TRUE(pos1 >= 0 && pos2 >= 0 && pos1 < pos2); |
| return url.substr(pos1 + 1, pos2 - pos1 - 1); |
| } |
| |
| private: |
| typedef std::vector<MultiQueryManager*> ManagerList; |
| // Map of (browser ID, frame ID) to manager. |
| typedef std::map<std::pair<int, int64>, MultiQueryManager*> ManagerMap; |
| |
| // All managers that have been created. |
| ManagerList all_managers_; |
| // Managers that have not yet associated with a frame. |
| ManagerList pending_managers_; |
| // Managers that are currently active. |
| ManagerMap manager_map_; |
| |
| typedef std::set<Observer*> ObserverSet; |
| ObserverSet observer_set_; |
| |
| // Set to true after all query managers have been added. |
| bool finalized_; |
| // Set to true while queries are pending. |
| bool running_; |
| |
| // Number of managers that have completed. |
| int manual_complete_count_; |
| int total_complete_count_; |
| }; |
| |
| // Test multiple queries in a single page load with multiple frames. |
| class MultiQueryMultiFrameTestHandler : public SingleLoadTestHandler, |
| public MultiQueryManagerMap::Observer { |
| public: |
| MultiQueryMultiFrameTestHandler(bool synchronous, bool cancel_with_subnav) |
| : synchronous_(synchronous), cancel_with_subnav_(cancel_with_subnav) { |
| manager_map_.AddObserver(this); |
| } |
| |
| void AddOtherResources() override { |
| AddSubFrameResource("sub1"); |
| AddSubFrameResource("sub2"); |
| AddSubFrameResource("sub3"); |
| manager_map_.Finalize(); |
| |
| if (manager_map_.HasAutoQueries()) { |
| cancel_url_ = std::string(kTestDomain1) + "cancel.html"; |
| AddResource(cancel_url_, "<html><body>cancel</body></html>", "text/html"); |
| } |
| } |
| |
| std::string GetMainHTML() override { return manager_map_.GetMainHTML(); } |
| |
| void OnLoadStart(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| TransitionType transition_type) override { |
| AssertMainBrowser(browser); |
| if (!frame->IsMain()) |
| manager_map_.OnLoadStart(browser, frame); |
| } |
| |
| void OnNotify(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| const std::string& message) override { |
| AssertMainBrowser(browser); |
| EXPECT_FALSE(frame->IsMain()); |
| |
| manager_map_.OnNotify(browser, frame, message); |
| } |
| |
| bool OnQuery(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id, |
| const CefString& request, |
| bool persistent, |
| CefRefPtr<Callback> callback) override { |
| AssertMainBrowser(browser); |
| EXPECT_FALSE(frame->IsMain()); |
| |
| return manager_map_.OnQuery(browser, frame, query_id, request, persistent, |
| callback); |
| } |
| |
| void OnQueryCanceled(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id) override { |
| AssertMainBrowser(browser); |
| EXPECT_FALSE(frame->IsMain()); |
| |
| manager_map_.OnQueryCanceled(browser, frame, query_id); |
| } |
| |
| void OnMapManualQueriesCompleted(MultiQueryManagerMap* map) override { |
| EXPECT_EQ(map, &manager_map_); |
| if (manager_map_.HasAutoQueries()) { |
| CefRefPtr<CefFrame> frame = GetBrowser()->GetMainFrame(); |
| |
| // Navigate somewhere else to terminate the auto queries. |
| if (cancel_with_subnav_) { |
| // Navigate each subframe individually. |
| const std::string js = "document.getElementById('sub1').src = '" + |
| cancel_url_ + |
| "';" |
| "document.getElementById('sub2').src = '" + |
| cancel_url_ + |
| "';" |
| "document.getElementById('sub3').src = '" + |
| cancel_url_ + "';"; |
| |
| frame->ExecuteJavaScript(js, frame->GetURL(), 0); |
| } else { |
| // Navigate the main frame. |
| frame->LoadURL(cancel_url_); |
| } |
| } |
| } |
| |
| void OnMapAllQueriesCompleted(MultiQueryManagerMap* map) override { |
| EXPECT_EQ(map, &manager_map_); |
| DestroyTest(); |
| } |
| |
| void DestroyTest() override { |
| manager_map_.AssertAllComplete(); |
| TestHandler::DestroyTest(); |
| } |
| |
| private: |
| void AddSubFrameResource(const std::string& name) { |
| const std::string& url = std::string(kTestDomain1) + name + ".html"; |
| |
| MultiQueryManager* manager = manager_map_.CreateManager(url, synchronous_); |
| MakeTestQueries(manager, false, 100); |
| |
| const std::string& html = manager->GetHTML(false, false); |
| AddResource(url, html, "text/html"); |
| } |
| |
| const bool synchronous_; |
| const bool cancel_with_subnav_; |
| |
| MultiQueryManagerMap manager_map_; |
| |
| std::string cancel_url_; |
| }; |
| |
| } // namespace |
| |
| // Test that multiple frames can run many queries successfully in a synchronous |
| // manner. |
| TEST(MessageRouterTest, MultiQueryMultiFrameSync) { |
| CefRefPtr<MultiQueryMultiFrameTestHandler> handler = |
| new MultiQueryMultiFrameTestHandler(true, false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that multiple frames can run many queries successfully in an |
| // asynchronous manner. |
| TEST(MessageRouterTest, MultiQueryMultiFrameAsync) { |
| CefRefPtr<MultiQueryMultiFrameTestHandler> handler = |
| new MultiQueryMultiFrameTestHandler(false, false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that multiple frames can run many queries successfully in a synchronous |
| // manner. Cancel auto queries with sub-frame navigation. |
| TEST(MessageRouterTest, MultiQueryMultiFrameSyncSubnavCancel) { |
| CefRefPtr<MultiQueryMultiFrameTestHandler> handler = |
| new MultiQueryMultiFrameTestHandler(true, true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that multiple frames can run many queries successfully in an |
| // asynchronous manner. Cancel auto queries with sub-frame navigation. |
| TEST(MessageRouterTest, MultiQueryMultiFrameAsyncSubnavCancel) { |
| CefRefPtr<MultiQueryMultiFrameTestHandler> handler = |
| new MultiQueryMultiFrameTestHandler(false, true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| namespace { |
| |
| // Implementation of MRTestHandler that loads multiple pages and/or browsers and |
| // executes multiple queries. |
| class MultiQueryMultiLoadTestHandler |
| : public MRTestHandler, |
| public CefMessageRouterBrowserSide::Handler, |
| public MultiQueryManagerMap::Observer, |
| public MultiQueryManager::Observer { |
| public: |
| MultiQueryMultiLoadTestHandler(bool some, bool synchronous) |
| : some_(some), synchronous_(synchronous) { |
| manager_map_.AddObserver(this); |
| } |
| |
| void OnLoadStart(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| TransitionType transition_type) override { |
| manager_map_.OnLoadStart(browser, frame); |
| } |
| |
| void OnNotify(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| const std::string& message) override { |
| manager_map_.OnNotify(browser, frame, message); |
| } |
| |
| bool OnQuery(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id, |
| const CefString& request, |
| bool persistent, |
| CefRefPtr<Callback> callback) override { |
| return manager_map_.OnQuery(browser, frame, query_id, request, persistent, |
| callback); |
| } |
| |
| void OnQueryCanceled(CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| int64 query_id) override { |
| manager_map_.OnQueryCanceled(browser, frame, query_id); |
| } |
| |
| void OnMapManualQueriesCompleted(MultiQueryManagerMap* map) override { |
| EXPECT_EQ(map, &manager_map_); |
| if (manager_map_.HasAutoQueries()) { |
| // Navigate all browsers somewhere else to terminate the auto queries. |
| BrowserMap browser_map; |
| GetAllBrowsers(&browser_map); |
| |
| BrowserMap::const_iterator it = browser_map.begin(); |
| for (; it != browser_map.end(); ++it) { |
| it->second->GetMainFrame()->LoadURL(cancel_url_); |
| } |
| } |
| } |
| |
| void OnMapAllQueriesCompleted(MultiQueryManagerMap* map) override { |
| EXPECT_EQ(map, &manager_map_); |
| DestroyTest(); |
| } |
| |
| void DestroyTest() override { |
| manager_map_.AssertAllComplete(); |
| TestHandler::DestroyTest(); |
| } |
| |
| protected: |
| void AddHandlers( |
| CefRefPtr<CefMessageRouterBrowserSide> message_router) override { |
| message_router->AddHandler(this, false); |
| } |
| |
| void AddManagedResource(const std::string& url, |
| bool assert_total, |
| bool assert_browser) { |
| MultiQueryManager* manager = manager_map_.CreateManager(url, synchronous_); |
| manager->AddObserver(this); |
| MakeTestQueries(manager, some_, 75); |
| |
| const std::string& html = manager->GetHTML(assert_total, assert_browser); |
| AddResource(url, html, "text/html"); |
| } |
| |
| void Finalize() { |
| manager_map_.Finalize(); |
| |
| if (manager_map_.HasAutoQueries()) { |
| cancel_url_ = std::string(kTestDomain1) + "cancel.html"; |
| AddResource(cancel_url_, "<html><body>cancel</body></html>", "text/html"); |
| } |
| } |
| |
| MultiQueryManagerMap manager_map_; |
| |
| private: |
| const bool some_; |
| const bool synchronous_; |
| |
| std::string cancel_url_; |
| }; |
| |
| // Test multiple browsers that send queries at the same time. |
| class MultiQueryMultiBrowserTestHandler |
| : public MultiQueryMultiLoadTestHandler { |
| public: |
| MultiQueryMultiBrowserTestHandler(bool synchronous, bool same_origin) |
| : MultiQueryMultiLoadTestHandler(false, synchronous), |
| same_origin_(same_origin) {} |
| |
| protected: |
| void RunMRTest() override { |
| const std::string& url1 = std::string(kTestDomain1) + "browser1.html"; |
| const std::string& url2 = |
| std::string(same_origin_ ? kTestDomain1 : kTestDomain2) + |
| "browser2.html"; |
| const std::string& url3 = |
| std::string(same_origin_ ? kTestDomain1 : kTestDomain3) + |
| "browser3.html"; |
| |
| AddManagedResource(url1, false, true); |
| AddManagedResource(url2, false, true); |
| AddManagedResource(url3, false, true); |
| Finalize(); |
| |
| // Create 2 browsers simultaniously. |
| CreateBrowser(url1, nullptr); |
| CreateBrowser(url2, nullptr); |
| CreateBrowser(url3, nullptr); |
| } |
| |
| private: |
| bool same_origin_; |
| }; |
| |
| } // namespace |
| |
| // Test that multiple browsers can query simultaniously from the same origin. |
| TEST(MessageRouterTest, MultiQueryMultiBrowserSameOriginSync) { |
| CefRefPtr<MultiQueryMultiBrowserTestHandler> handler = |
| new MultiQueryMultiBrowserTestHandler(true, true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that multiple browsers can query simultaniously from the same origin. |
| TEST(MessageRouterTest, MultiQueryMultiBrowserSameOriginAsync) { |
| CefRefPtr<MultiQueryMultiBrowserTestHandler> handler = |
| new MultiQueryMultiBrowserTestHandler(false, true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that multiple browsers can query simultaniously from different origins. |
| TEST(MessageRouterTest, MultiQueryMultiBrowserDifferentOriginSync) { |
| CefRefPtr<MultiQueryMultiBrowserTestHandler> handler = |
| new MultiQueryMultiBrowserTestHandler(true, false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that multiple browsers can query simultaniously from different origins. |
| TEST(MessageRouterTest, MultiQueryMultiBrowserDifferentOriginAsync) { |
| CefRefPtr<MultiQueryMultiBrowserTestHandler> handler = |
| new MultiQueryMultiBrowserTestHandler(false, false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| namespace { |
| |
| // Test multiple navigations that send queries sequentially. |
| class MultiQueryMultiNavigateTestHandler |
| : public MultiQueryMultiLoadTestHandler { |
| public: |
| MultiQueryMultiNavigateTestHandler(bool synchronous, bool same_origin) |
| : MultiQueryMultiLoadTestHandler(false, synchronous), |
| same_origin_(same_origin) {} |
| |
| void OnManualQueriesCompleted(MultiQueryManager* manager) override { |
| const std::string& url = manager->label(); |
| if (url == url1_) // 2. Load the 2nd url. |
| GetBrowser()->GetMainFrame()->LoadURL(url2_); |
| else if (url == url2_) // 3. Load the 3rd url. |
| GetBrowser()->GetMainFrame()->LoadURL(url3_); |
| } |
| |
| protected: |
| void RunMRTest() override { |
| url1_ = std::string(kTestDomain1) + "browser1.html"; |
| url2_ = std::string(same_origin_ ? kTestDomain1 : kTestDomain2) + |
| "browser2.html"; |
| url3_ = std::string(same_origin_ ? kTestDomain1 : kTestDomain3) + |
| "browser3.html"; |
| |
| AddManagedResource(url1_, true, true); |
| AddManagedResource(url2_, true, true); |
| AddManagedResource(url3_, true, true); |
| Finalize(); |
| |
| // 1. Load the 1st url. |
| CreateBrowser(url1_, nullptr); |
| } |
| |
| private: |
| bool same_origin_; |
| |
| std::string url1_; |
| std::string url2_; |
| std::string url3_; |
| }; |
| |
| } // namespace |
| |
| // Test that multiple navigations can query from the same origin. |
| TEST(MessageRouterTest, MultiQueryMultiNavigateSameOriginSync) { |
| CefRefPtr<MultiQueryMultiNavigateTestHandler> handler = |
| new MultiQueryMultiNavigateTestHandler(true, true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that multiple navigations can query from the same origin. |
| TEST(MessageRouterTest, MultiQueryMultiNavigateSameOriginAsync) { |
| CefRefPtr<MultiQueryMultiNavigateTestHandler> handler = |
| new MultiQueryMultiNavigateTestHandler(false, true); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that multiple navigations can query from different origins. |
| TEST(MessageRouterTest, MultiQueryMultiNavigateDifferentOriginSync) { |
| CefRefPtr<MultiQueryMultiNavigateTestHandler> handler = |
| new MultiQueryMultiNavigateTestHandler(true, false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |
| |
| // Test that multiple navigations can query from different origins. |
| TEST(MessageRouterTest, MultiQueryMultiNavigateDifferentOriginAsync) { |
| CefRefPtr<MultiQueryMultiNavigateTestHandler> handler = |
| new MultiQueryMultiNavigateTestHandler(false, false); |
| handler->ExecuteTest(); |
| ReleaseAndWaitForDestructor(handler); |
| } |