mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	This change moves the SendProcessMessage method from CefBrowser to CefFrame and adds CefBrowser parameters to OnProcessMessageReceived and OnDraggableRegionsChanged. The internal implementation has changed as follows: - Frame IDs are now a 64-bit combination of the 32-bit render_process_id and render_routing_id values that uniquely identify a RenderFrameHost (RFH). - CefFrameHostImpl objects are now managed by CefBrowserInfo with life span tied to RFH expectations. Specifically, a CefFrameHostImpl object representing a sub-frame will be created when a RenderFrame is created in the renderer process and detached when the associated RenderFrame is deleted or the renderer process in which it runs has died. - The CefFrameHostImpl object representing the main frame will always be valid but the underlying RFH (and associated frame ID) may change over time as a result of cross-origin navigations. Despite these changes calling LoadURL on the main frame object in the browser process will always navigate as expected. - Speculative RFHs, which may be created as a result of a cross-origin navigation and discarded if that navigation is not committed, are now handled correctly (e.g. ignored in most cases until they're committed). - It is less likely, but still possible, to receive a CefFrame object with an invalid frame ID (ID < 0). This can happen in cases where a RFH has not yet been created for a sub-frame. For example, when OnBeforeBrowse is called before initiating navigation in a previously nonexisting sub-frame. To test: All tests pass with NetworkService enabled and disabled.
		
			
				
	
	
		
			439 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			439 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2016 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 "include/base/cef_bind.h"
 | |
| #include "include/cef_task.h"
 | |
| #include "include/cef_thread.h"
 | |
| #include "include/wrapper/cef_closure_task.h"
 | |
| #include "tests/ceftests/test_handler.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 {
 | |
| 
 | |
| // Base class for creating and testing threads.
 | |
| class ThreadTest : public base::RefCountedThreadSafe<ThreadTest> {
 | |
|  public:
 | |
|   ThreadTest() {}
 | |
|   virtual ~ThreadTest() {}
 | |
| 
 | |
|   // Create the test thread. Should only be called one time.
 | |
|   void CreateTestThread() {
 | |
|     EXPECT_TRUE(!thread_.get());
 | |
| 
 | |
|     owner_task_runner_ = CefTaskRunner::GetForCurrentThread();
 | |
|     EXPECT_TRUE(owner_task_runner_.get());
 | |
|     EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
 | |
| 
 | |
|     thread_ = CefThread::CreateThread("test_thread");
 | |
|     EXPECT_TRUE(thread_.get());
 | |
|     EXPECT_TRUE(thread_->IsRunning());
 | |
| 
 | |
|     thread_id_ = thread_->GetPlatformThreadId();
 | |
|     EXPECT_NE(thread_id_, kInvalidPlatformThreadId);
 | |
| 
 | |
|     thread_task_runner_ = thread_->GetTaskRunner();
 | |
|     EXPECT_TRUE(thread_task_runner_.get());
 | |
| 
 | |
|     AssertOwnerThread();
 | |
|   }
 | |
| 
 | |
|   // Destroy the test thread. Should only be called one time.
 | |
|   void DestroyTestThread() {
 | |
|     EXPECT_TRUE(thread_.get());
 | |
|     AssertOwnerThread();
 | |
| 
 | |
|     EXPECT_TRUE(thread_->IsRunning());
 | |
|     thread_->Stop();
 | |
|     EXPECT_FALSE(thread_->IsRunning());
 | |
| 
 | |
|     AssertOwnerThread();
 | |
| 
 | |
|     thread_ = nullptr;
 | |
|   }
 | |
| 
 | |
|   // Execute |test_task| on the test thread. After execution |callback| will be
 | |
|   // posted to |callback_task_runner|.
 | |
|   void PostOnTestThreadAndCallback(
 | |
|       const base::Closure& test_task,
 | |
|       CefRefPtr<CefTaskRunner> callback_task_runner,
 | |
|       const base::Closure& callback) {
 | |
|     EXPECT_TRUE(thread_.get());
 | |
|     thread_task_runner_->PostTask(CefCreateClosureTask(
 | |
|         base::Bind(&ThreadTest::ExecuteOnTestThread, this, test_task,
 | |
|                    callback_task_runner, callback)));
 | |
|   }
 | |
| 
 | |
|   CefRefPtr<CefTaskRunner> owner_task_runner() const {
 | |
|     return owner_task_runner_;
 | |
|   }
 | |
|   CefRefPtr<CefTaskRunner> thread_task_runner() const {
 | |
|     return thread_task_runner_;
 | |
|   }
 | |
| 
 | |
|   // Assert that we're running on the owner thread.
 | |
|   void AssertOwnerThread() {
 | |
|     EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
 | |
|     EXPECT_FALSE(thread_task_runner_->BelongsToCurrentThread());
 | |
|     EXPECT_TRUE(thread_task_runner_->IsSame(thread_->GetTaskRunner()));
 | |
|     EXPECT_EQ(thread_id_, thread_->GetPlatformThreadId());
 | |
|   }
 | |
| 
 | |
|   // Assert that we're running on the test thread.
 | |
|   void AssertTestThread() {
 | |
|     EXPECT_FALSE(owner_task_runner_->BelongsToCurrentThread());
 | |
|     EXPECT_TRUE(thread_task_runner_->BelongsToCurrentThread());
 | |
|     EXPECT_TRUE(thread_task_runner_->IsSame(thread_->GetTaskRunner()));
 | |
|     EXPECT_EQ(thread_id_, thread_->GetPlatformThreadId());
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   // Helper for PostOnTestThreadAndCallback().
 | |
|   void ExecuteOnTestThread(const base::Closure& test_task,
 | |
|                            CefRefPtr<CefTaskRunner> callback_task_runner,
 | |
|                            const base::Closure& callback) {
 | |
|     AssertTestThread();
 | |
| 
 | |
|     test_task.Run();
 | |
| 
 | |
|     callback_task_runner->PostTask(CefCreateClosureTask(callback));
 | |
|   }
 | |
| 
 | |
|   CefRefPtr<CefTaskRunner> owner_task_runner_;
 | |
| 
 | |
|   CefRefPtr<CefThread> thread_;
 | |
|   cef_platform_thread_id_t thread_id_;
 | |
|   CefRefPtr<CefTaskRunner> thread_task_runner_;
 | |
| 
 | |
|   DISALLOW_COPY_AND_ASSIGN(ThreadTest);
 | |
| };
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| // Test thread creation and destruction without any task execution.
 | |
| TEST(ThreadTest, Create) {
 | |
|   scoped_refptr<ThreadTest> thread_test = new ThreadTest();
 | |
|   thread_test->CreateTestThread();
 | |
|   thread_test->DestroyTestThread();
 | |
|   thread_test = nullptr;
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| // Simple implementation of ThreadTest that creates a thread, executes tasks
 | |
| // on the thread, then destroys the thread after all tasks have completed.
 | |
| class SimpleThreadTest : public ThreadTest {
 | |
|  public:
 | |
|   SimpleThreadTest(size_t expected_task_count,
 | |
|                    const base::Closure& task_callback,
 | |
|                    const base::Closure& done_callback)
 | |
|       : expected_task_count_(expected_task_count),
 | |
|         task_callback_(task_callback),
 | |
|         done_callback_(done_callback),
 | |
|         got_task_count_(0U),
 | |
|         got_done_count_(0U) {}
 | |
| 
 | |
|   void RunTest() {
 | |
|     // Create the test thread.
 | |
|     CreateTestThread();
 | |
| 
 | |
|     for (size_t i = 0U; i < expected_task_count_; ++i) {
 | |
|       // Execute Task() on the test thread and then call Done() on this thread.
 | |
|       PostOnTestThreadAndCallback(base::Bind(&SimpleThreadTest::Task, this),
 | |
|                                   owner_task_runner(),
 | |
|                                   base::Bind(&SimpleThreadTest::Done, this));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void DestroyTest() {
 | |
|     EXPECT_EQ(expected_task_count_, got_task_count_);
 | |
|     EXPECT_EQ(expected_task_count_, got_done_count_);
 | |
| 
 | |
|     // Destroy the test thread.
 | |
|     DestroyTestThread();
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   void Task() {
 | |
|     AssertTestThread();
 | |
|     got_task_count_++;
 | |
|     if (!task_callback_.is_null())
 | |
|       task_callback_.Run();
 | |
|   }
 | |
| 
 | |
|   void Done() {
 | |
|     AssertOwnerThread();
 | |
|     if (++got_done_count_ == expected_task_count_ && !done_callback_.is_null())
 | |
|       done_callback_.Run();
 | |
|   }
 | |
| 
 | |
|   const size_t expected_task_count_;
 | |
|   base::Closure task_callback_;
 | |
|   base::Closure done_callback_;
 | |
| 
 | |
|   size_t got_task_count_;
 | |
|   size_t got_done_count_;
 | |
| 
 | |
|   DISALLOW_COPY_AND_ASSIGN(SimpleThreadTest);
 | |
| };
 | |
| 
 | |
| // Test creation/execution of threads in the browser process.
 | |
| 
 | |
| const char kBrowserThreadTestHtml[] = "http://test.com/browserthread.html";
 | |
| 
 | |
| // Browser side.
 | |
| class BrowserThreadTestHandler : public TestHandler {
 | |
|  public:
 | |
|   explicit BrowserThreadTestHandler(CefThreadId owner_thread_id)
 | |
|       : owner_thread_id_(owner_thread_id) {}
 | |
| 
 | |
|   void RunTest() override {
 | |
|     AddResource(kBrowserThreadTestHtml, "<html><body>Test</body></html>",
 | |
|                 "text/html");
 | |
| 
 | |
|     CreateBrowser(kBrowserThreadTestHtml);
 | |
| 
 | |
|     // Time out the test after a reasonable period of time.
 | |
|     SetTestTimeout();
 | |
|   }
 | |
| 
 | |
|   void RunThreadTestOnOwnerThread() {
 | |
|     if (!CefCurrentlyOn(owner_thread_id_)) {
 | |
|       // Run the test on the desired owner thread.
 | |
|       CefPostTask(
 | |
|           owner_thread_id_,
 | |
|           base::Bind(&BrowserThreadTestHandler::RunThreadTestOnOwnerThread,
 | |
|                      this));
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     EXPECT_FALSE(thread_test_.get());
 | |
|     thread_test_ = new SimpleThreadTest(
 | |
|         3, base::Closure(),
 | |
|         base::Bind(&BrowserThreadTestHandler::DoneOnOwnerThread, this));
 | |
|     thread_test_->RunTest();
 | |
|   }
 | |
| 
 | |
|   void DoneOnOwnerThread() {
 | |
|     // Let the call stack unwind before destroying |thread_test_|.
 | |
|     CefPostTask(
 | |
|         owner_thread_id_,
 | |
|         base::Bind(&BrowserThreadTestHandler::DestroyTestOnOwnerThread, this));
 | |
|   }
 | |
| 
 | |
|   void DestroyTestOnOwnerThread() {
 | |
|     EXPECT_TRUE(CefCurrentlyOn(owner_thread_id_));
 | |
| 
 | |
|     EXPECT_TRUE(thread_test_.get());
 | |
|     if (thread_test_) {
 | |
|       thread_test_->DestroyTest();
 | |
|       thread_test_ = nullptr;
 | |
|     }
 | |
| 
 | |
|     got_test_done_.yes();
 | |
| 
 | |
|     // Call DestroyTest() on the UI thread.
 | |
|     CefPostTask(TID_UI,
 | |
|                 base::Bind(&BrowserThreadTestHandler::DestroyTest, this));
 | |
|   }
 | |
| 
 | |
|   void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
 | |
|                             bool isLoading,
 | |
|                             bool canGoBack,
 | |
|                             bool canGoForward) override {
 | |
|     if (!isLoading)
 | |
|       RunThreadTestOnOwnerThread();
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   void DestroyTest() override {
 | |
|     EXPECT_FALSE(thread_test_.get());
 | |
|     EXPECT_TRUE(got_test_done_);
 | |
| 
 | |
|     TestHandler::DestroyTest();
 | |
|   }
 | |
| 
 | |
|   const CefThreadId owner_thread_id_;
 | |
| 
 | |
|   scoped_refptr<SimpleThreadTest> thread_test_;
 | |
|   TrackCallback got_test_done_;
 | |
| 
 | |
|   IMPLEMENT_REFCOUNTING(BrowserThreadTestHandler);
 | |
|   DISALLOW_COPY_AND_ASSIGN(BrowserThreadTestHandler);
 | |
| };
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| // Test creation of new threads from the browser UI thread.
 | |
| TEST(ThreadTest, CreateFromBrowserUIThread) {
 | |
|   CefRefPtr<BrowserThreadTestHandler> handler =
 | |
|       new BrowserThreadTestHandler(TID_UI);
 | |
|   handler->ExecuteTest();
 | |
|   ReleaseAndWaitForDestructor(handler);
 | |
| }
 | |
| 
 | |
| // Test creation of new threads from the browser IO thread.
 | |
| TEST(ThreadTest, CreateFromBrowserIOThread) {
 | |
|   CefRefPtr<BrowserThreadTestHandler> handler =
 | |
|       new BrowserThreadTestHandler(TID_IO);
 | |
|   handler->ExecuteTest();
 | |
|   ReleaseAndWaitForDestructor(handler);
 | |
| }
 | |
| 
 | |
| // Test creation of new threads from the browser FILE thread.
 | |
| TEST(ThreadTest, CreateFromBrowserFILEThread) {
 | |
|   CefRefPtr<BrowserThreadTestHandler> handler =
 | |
|       new BrowserThreadTestHandler(TID_FILE);
 | |
|   handler->ExecuteTest();
 | |
|   ReleaseAndWaitForDestructor(handler);
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| // Test creation/execution of threads in the render process.
 | |
| 
 | |
| const char kRenderThreadTestHtml[] = "http://test.com/renderthread.html";
 | |
| const char kRenderThreadTestMsg[] = "ThreadTest.RenderThreadTest";
 | |
| 
 | |
| // Browser side.
 | |
| class RenderThreadTestHandler : public TestHandler {
 | |
|  public:
 | |
|   RenderThreadTestHandler() {}
 | |
| 
 | |
|   void RunTest() override {
 | |
|     AddResource(kRenderThreadTestHtml, "<html><body>Test</body></html>",
 | |
|                 "text/html");
 | |
| 
 | |
|     CreateBrowser(kRenderThreadTestHtml);
 | |
| 
 | |
|     // Time out the test after a reasonable period of time.
 | |
|     SetTestTimeout();
 | |
|   }
 | |
| 
 | |
|   void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
 | |
|                             bool isLoading,
 | |
|                             bool canGoBack,
 | |
|                             bool canGoForward) override {
 | |
|     if (!isLoading) {
 | |
|       // Return the test in the render process.
 | |
|       CefRefPtr<CefProcessMessage> msg =
 | |
|           CefProcessMessage::Create(kRenderThreadTestMsg);
 | |
|       browser->GetMainFrame()->SendProcessMessage(PID_RENDERER, msg);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
 | |
|                                 CefRefPtr<CefFrame> frame,
 | |
|                                 CefProcessId source_process,
 | |
|                                 CefRefPtr<CefProcessMessage> message) override {
 | |
|     EXPECT_TRUE(browser.get());
 | |
|     EXPECT_TRUE(frame.get());
 | |
|     EXPECT_EQ(PID_RENDERER, source_process);
 | |
|     EXPECT_TRUE(message.get());
 | |
|     EXPECT_TRUE(message->IsReadOnly());
 | |
| 
 | |
|     const std::string& message_name = message->GetName();
 | |
|     EXPECT_STREQ(kRenderThreadTestMsg, message_name.c_str());
 | |
| 
 | |
|     got_message_.yes();
 | |
| 
 | |
|     if (message->GetArgumentList()->GetBool(0))
 | |
|       got_success_.yes();
 | |
| 
 | |
|     // Test is complete.
 | |
|     DestroyTest();
 | |
| 
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|  protected:
 | |
|   void DestroyTest() override {
 | |
|     EXPECT_TRUE(got_message_);
 | |
|     EXPECT_TRUE(got_success_);
 | |
| 
 | |
|     TestHandler::DestroyTest();
 | |
|   }
 | |
| 
 | |
|   TrackCallback got_message_;
 | |
|   TrackCallback got_success_;
 | |
| 
 | |
|   IMPLEMENT_REFCOUNTING(RenderThreadTestHandler);
 | |
|   DISALLOW_COPY_AND_ASSIGN(RenderThreadTestHandler);
 | |
| };
 | |
| 
 | |
| // Renderer side.
 | |
| class RenderThreadRendererTest : public ClientAppRenderer::Delegate {
 | |
|  public:
 | |
|   RenderThreadRendererTest() {}
 | |
| 
 | |
|   bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
 | |
|                                 CefRefPtr<CefBrowser> browser,
 | |
|                                 CefRefPtr<CefFrame> frame,
 | |
|                                 CefProcessId source_process,
 | |
|                                 CefRefPtr<CefProcessMessage> message) override {
 | |
|     if (message->GetName().ToString() == kRenderThreadTestMsg) {
 | |
|       browser_ = browser;
 | |
|       EXPECT_FALSE(thread_test_.get());
 | |
|       thread_test_ = new SimpleThreadTest(
 | |
|           3, base::Closure(),
 | |
|           base::Bind(&RenderThreadRendererTest::Done, this));
 | |
|       thread_test_->RunTest();
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     // Message not handled.
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   void Done() {
 | |
|     // Let the call stack unwind before destroying |thread_test_|.
 | |
|     CefPostTask(TID_RENDERER,
 | |
|                 base::Bind(&RenderThreadRendererTest::DestroyTest, this));
 | |
|   }
 | |
| 
 | |
|   void DestroyTest() {
 | |
|     EXPECT_TRUE(thread_test_.get());
 | |
|     if (thread_test_) {
 | |
|       thread_test_->DestroyTest();
 | |
|       thread_test_ = nullptr;
 | |
|     }
 | |
| 
 | |
|     // Check if the test has failed.
 | |
|     bool result = !TestFailed();
 | |
| 
 | |
|     // Return the result to the browser process.
 | |
|     CefRefPtr<CefProcessMessage> return_msg =
 | |
|         CefProcessMessage::Create(kRenderThreadTestMsg);
 | |
|     EXPECT_TRUE(return_msg->GetArgumentList()->SetBool(0, result));
 | |
|     browser_->GetMainFrame()->SendProcessMessage(PID_BROWSER, return_msg);
 | |
| 
 | |
|     browser_ = nullptr;
 | |
|   }
 | |
| 
 | |
|   CefRefPtr<CefBrowser> browser_;
 | |
|   scoped_refptr<SimpleThreadTest> thread_test_;
 | |
| 
 | |
|   IMPLEMENT_REFCOUNTING(RenderThreadRendererTest);
 | |
|   DISALLOW_COPY_AND_ASSIGN(RenderThreadRendererTest);
 | |
| };
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| TEST(ThreadTest, CreateFromRenderThread) {
 | |
|   CefRefPtr<RenderThreadTestHandler> handler = new RenderThreadTestHandler();
 | |
|   handler->ExecuteTest();
 | |
|   ReleaseAndWaitForDestructor(handler);
 | |
| }
 | |
| 
 | |
| // Entry point for creating request handler renderer test objects.
 | |
| // Called from client_app_delegates.cc.
 | |
| void CreateThreadRendererTests(ClientAppRenderer::DelegateSet& delegates) {
 | |
|   delegates.insert(new RenderThreadRendererTest);
 | |
| }
 |