mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	Add support and enable out-of-Blink CORS (fixes issue #2716)
It can still be disabled for a short time by passing `--disable-features=OutOfBlinkCors` on the command-line.
This commit is contained in:
		
							
								
								
									
										983
									
								
								tests/ceftests/cors_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										983
									
								
								tests/ceftests/cors_unittest.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,983 @@
 | 
			
		||||
// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
 | 
			
		||||
// reserved. Use of this source code is governed by a BSD-style license that
 | 
			
		||||
// can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "include/base/cef_bind.h"
 | 
			
		||||
#include "include/cef_callback.h"
 | 
			
		||||
#include "include/cef_origin_whitelist.h"
 | 
			
		||||
#include "include/cef_scheme.h"
 | 
			
		||||
#include "include/wrapper/cef_closure_task.h"
 | 
			
		||||
#include "tests/ceftests/routing_test_handler.h"
 | 
			
		||||
#include "tests/ceftests/test_request.h"
 | 
			
		||||
#include "tests/ceftests/test_server.h"
 | 
			
		||||
#include "tests/ceftests/test_util.h"
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
const char kMimeTypeHtml[] = "text/html";
 | 
			
		||||
const char kMimeTypeText[] = "text/plain";
 | 
			
		||||
 | 
			
		||||
const char kDefaultHtml[] = "<html><body>TEST</body></html>";
 | 
			
		||||
const char kDefaultText[] = "TEST";
 | 
			
		||||
 | 
			
		||||
const char kSuccessMsg[] = "CorsTestHandler.Success";
 | 
			
		||||
const char kFailureMsg[] = "CorsTestHandler.Failure";
 | 
			
		||||
 | 
			
		||||
// Source that will handle the request.
 | 
			
		||||
enum class HandlerType {
 | 
			
		||||
  SERVER,
 | 
			
		||||
  HTTP_SCHEME,
 | 
			
		||||
  CUSTOM_STANDARD_SCHEME,
 | 
			
		||||
  CUSTOM_NONSTANDARD_SCHEME,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::string GetOrigin(HandlerType handler) {
 | 
			
		||||
  switch (handler) {
 | 
			
		||||
    case HandlerType::SERVER:
 | 
			
		||||
      return test_server::kServerOrigin;
 | 
			
		||||
    case HandlerType::HTTP_SCHEME:
 | 
			
		||||
      return "http://corstest.com";
 | 
			
		||||
    case HandlerType::CUSTOM_STANDARD_SCHEME:
 | 
			
		||||
      // Standard scheme that is CORS and fetch enabled.
 | 
			
		||||
      // Registered in scheme_handler_unittest.cc.
 | 
			
		||||
      return "customstdfetch://corstest";
 | 
			
		||||
    case HandlerType::CUSTOM_NONSTANDARD_SCHEME:
 | 
			
		||||
      // Non-sandard scheme that is not CORS or fetch enabled.
 | 
			
		||||
      // Registered in scheme_handler_unittest.cc.
 | 
			
		||||
      return "customnonstd:corstest";
 | 
			
		||||
  }
 | 
			
		||||
  NOTREACHED();
 | 
			
		||||
  return std::string();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetPathURL(HandlerType handler, const std::string& path) {
 | 
			
		||||
  return GetOrigin(handler) + path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Resource {
 | 
			
		||||
  // Uniquely identifies the resource.
 | 
			
		||||
  HandlerType handler = HandlerType::SERVER;
 | 
			
		||||
  std::string path;
 | 
			
		||||
 | 
			
		||||
  // Response information that will be returned.
 | 
			
		||||
  CefRefPtr<CefResponse> response;
 | 
			
		||||
  std::string response_data;
 | 
			
		||||
 | 
			
		||||
  // Expected error code in OnLoadError.
 | 
			
		||||
  cef_errorcode_t expected_error_code = ERR_NONE;
 | 
			
		||||
 | 
			
		||||
  // Expected number of responses.
 | 
			
		||||
  int expected_response_ct = 1;
 | 
			
		||||
 | 
			
		||||
  // Expected number of OnQuery calls.
 | 
			
		||||
  int expected_success_query_ct = 0;
 | 
			
		||||
  int expected_failure_query_ct = 0;
 | 
			
		||||
 | 
			
		||||
  // Actual number of responses.
 | 
			
		||||
  int response_ct = 0;
 | 
			
		||||
 | 
			
		||||
  // Actual number of OnQuery calls.
 | 
			
		||||
  int success_query_ct = 0;
 | 
			
		||||
  int failure_query_ct = 0;
 | 
			
		||||
 | 
			
		||||
  Resource() {}
 | 
			
		||||
  Resource(HandlerType request_handler,
 | 
			
		||||
           const std::string& request_path,
 | 
			
		||||
           const std::string& mime_type = kMimeTypeHtml,
 | 
			
		||||
           const std::string& data = kDefaultHtml,
 | 
			
		||||
           int status = 200) {
 | 
			
		||||
    Init(request_handler, request_path, mime_type, data, status);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Perform basic initialization.
 | 
			
		||||
  void Init(HandlerType request_handler,
 | 
			
		||||
            const std::string& request_path,
 | 
			
		||||
            const std::string& mime_type = kMimeTypeHtml,
 | 
			
		||||
            const std::string& data = kDefaultHtml,
 | 
			
		||||
            int status = 200) {
 | 
			
		||||
    handler = request_handler;
 | 
			
		||||
    path = request_path;
 | 
			
		||||
    response_data = data;
 | 
			
		||||
    response = CefResponse::Create();
 | 
			
		||||
    response->SetMimeType(mime_type);
 | 
			
		||||
    response->SetStatus(status);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Validate expected initial state.
 | 
			
		||||
  void Validate() const {
 | 
			
		||||
    DCHECK(!path.empty());
 | 
			
		||||
    DCHECK(response);
 | 
			
		||||
    DCHECK(!response->GetMimeType().empty());
 | 
			
		||||
    DCHECK_EQ(0, response_ct);
 | 
			
		||||
    DCHECK_GE(expected_response_ct, 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::string GetPathURL() const { return ::GetPathURL(handler, path); }
 | 
			
		||||
 | 
			
		||||
  // Returns true if all expectations have been met.
 | 
			
		||||
  bool IsDone() const {
 | 
			
		||||
    return response_ct == expected_response_ct &&
 | 
			
		||||
           success_query_ct == expected_success_query_ct &&
 | 
			
		||||
           failure_query_ct == expected_failure_query_ct;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void AssertDone() const {
 | 
			
		||||
    EXPECT_EQ(expected_response_ct, response_ct) << GetPathURL();
 | 
			
		||||
    EXPECT_EQ(expected_success_query_ct, success_query_ct) << GetPathURL();
 | 
			
		||||
    EXPECT_EQ(expected_failure_query_ct, failure_query_ct) << GetPathURL();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Optionally override to verify request contents.
 | 
			
		||||
  virtual bool VerifyRequest(CefRefPtr<CefRequest> request) const {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct TestSetup {
 | 
			
		||||
  // Available resources.
 | 
			
		||||
  typedef std::vector<Resource*> ResourceList;
 | 
			
		||||
  ResourceList resources;
 | 
			
		||||
 | 
			
		||||
  // Used for testing received console messages.
 | 
			
		||||
  std::vector<std::string> console_messages;
 | 
			
		||||
 | 
			
		||||
  void AddResource(Resource* resource) {
 | 
			
		||||
    DCHECK(resource);
 | 
			
		||||
    resource->Validate();
 | 
			
		||||
    resources.push_back(resource);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void AddConsoleMessage(const std::string& message) {
 | 
			
		||||
    DCHECK(!message.empty());
 | 
			
		||||
    console_messages.push_back(message);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Resource* GetResource(const std::string& url) const {
 | 
			
		||||
    if (resources.empty())
 | 
			
		||||
      return nullptr;
 | 
			
		||||
 | 
			
		||||
    const std::string& path_url = test_request::GetPathURL(url);
 | 
			
		||||
    ResourceList::const_iterator it = resources.begin();
 | 
			
		||||
    for (; it != resources.end(); ++it) {
 | 
			
		||||
      Resource* resource = *it;
 | 
			
		||||
      if (resource->GetPathURL() == path_url)
 | 
			
		||||
        return resource;
 | 
			
		||||
    }
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Resource* GetResource(CefRefPtr<CefRequest> request) const {
 | 
			
		||||
    return GetResource(request->GetURL());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Validate expected initial state.
 | 
			
		||||
  void Validate() const { DCHECK(!resources.empty()); }
 | 
			
		||||
 | 
			
		||||
  std::string GetMainURL() const { return resources.front()->GetPathURL(); }
 | 
			
		||||
 | 
			
		||||
  // Returns true if the server will be used.
 | 
			
		||||
  bool NeedsServer() const {
 | 
			
		||||
    ResourceList::const_iterator it = resources.begin();
 | 
			
		||||
    for (; it != resources.end(); ++it) {
 | 
			
		||||
      Resource* resource = *it;
 | 
			
		||||
      if (resource->handler == HandlerType::SERVER)
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Returns true if all expectations have been met.
 | 
			
		||||
  bool IsDone() const {
 | 
			
		||||
    ResourceList::const_iterator it = resources.begin();
 | 
			
		||||
    for (; it != resources.end(); ++it) {
 | 
			
		||||
      Resource* resource = *it;
 | 
			
		||||
      if (!resource->IsDone())
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void AssertDone() const {
 | 
			
		||||
    ResourceList::const_iterator it = resources.begin();
 | 
			
		||||
    for (; it != resources.end(); ++it) {
 | 
			
		||||
      (*it)->AssertDone();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class TestServerObserver : public test_server::ObserverHelper {
 | 
			
		||||
 public:
 | 
			
		||||
  typedef base::Callback<bool()> CheckDoneCallback;
 | 
			
		||||
 | 
			
		||||
  TestServerObserver(TestSetup* setup,
 | 
			
		||||
                     const base::Closure& ready_callback,
 | 
			
		||||
                     const base::Closure& done_callback)
 | 
			
		||||
      : setup_(setup),
 | 
			
		||||
        ready_callback_(ready_callback),
 | 
			
		||||
        done_callback_(done_callback),
 | 
			
		||||
        weak_ptr_factory_(this) {
 | 
			
		||||
    DCHECK(setup);
 | 
			
		||||
    Initialize();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ~TestServerObserver() override { done_callback_.Run(); }
 | 
			
		||||
 | 
			
		||||
  void OnInitialized(const std::string& server_origin) override {
 | 
			
		||||
    CEF_REQUIRE_UI_THREAD();
 | 
			
		||||
    ready_callback_.Run();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool OnHttpRequest(CefRefPtr<CefServer> server,
 | 
			
		||||
                     int connection_id,
 | 
			
		||||
                     const CefString& client_address,
 | 
			
		||||
                     CefRefPtr<CefRequest> request) override {
 | 
			
		||||
    CEF_REQUIRE_UI_THREAD();
 | 
			
		||||
    Resource* resource = setup_->GetResource(request);
 | 
			
		||||
    if (!resource) {
 | 
			
		||||
      // Not a request we handle.
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    resource->response_ct++;
 | 
			
		||||
    EXPECT_TRUE(resource->VerifyRequest(request))
 | 
			
		||||
        << request->GetURL().ToString();
 | 
			
		||||
    test_server::SendResponse(server, connection_id, resource->response,
 | 
			
		||||
                              resource->response_data);
 | 
			
		||||
 | 
			
		||||
    // Stop propagating the callback.
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void OnShutdown() override {
 | 
			
		||||
    CEF_REQUIRE_UI_THREAD();
 | 
			
		||||
    delete this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  TestSetup* const setup_;
 | 
			
		||||
  const base::Closure ready_callback_;
 | 
			
		||||
  const base::Closure done_callback_;
 | 
			
		||||
 | 
			
		||||
  base::WeakPtrFactory<TestServerObserver> weak_ptr_factory_;
 | 
			
		||||
 | 
			
		||||
  DISALLOW_COPY_AND_ASSIGN(TestServerObserver);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class CorsTestHandler : public RoutingTestHandler {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit CorsTestHandler(TestSetup* setup) : setup_(setup) {
 | 
			
		||||
    setup_->Validate();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void RunTest() override {
 | 
			
		||||
    StartServer(base::Bind(&CorsTestHandler::TriggerCreateBrowser, this));
 | 
			
		||||
 | 
			
		||||
    // Time out the test after a reasonable period of time.
 | 
			
		||||
    SetTestTimeout();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Necessary to make the method public in order to destroy the test from
 | 
			
		||||
  // ClientSchemeHandlerType::ProcessRequest().
 | 
			
		||||
  void DestroyTest() override {
 | 
			
		||||
    EXPECT_TRUE(shutting_down_);
 | 
			
		||||
 | 
			
		||||
    setup_->AssertDone();
 | 
			
		||||
    EXPECT_TRUE(setup_->console_messages.empty())
 | 
			
		||||
        << "Did not receive expected console message: "
 | 
			
		||||
        << setup_->console_messages.front();
 | 
			
		||||
 | 
			
		||||
    RoutingTestHandler::DestroyTest();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void DestroyTestIfDone() {
 | 
			
		||||
    CEF_REQUIRE_UI_THREAD();
 | 
			
		||||
    if (shutting_down_)
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    if (setup_->IsDone()) {
 | 
			
		||||
      shutting_down_ = true;
 | 
			
		||||
      StopServer();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  CefRefPtr<CefResourceHandler> GetResourceHandler(
 | 
			
		||||
      CefRefPtr<CefBrowser> browser,
 | 
			
		||||
      CefRefPtr<CefFrame> frame,
 | 
			
		||||
      CefRefPtr<CefRequest> request) override {
 | 
			
		||||
    CEF_REQUIRE_IO_THREAD();
 | 
			
		||||
    Resource* resource = setup_->GetResource(request);
 | 
			
		||||
    if (resource && resource->handler != HandlerType::SERVER) {
 | 
			
		||||
      resource->response_ct++;
 | 
			
		||||
      EXPECT_TRUE(resource->VerifyRequest(request))
 | 
			
		||||
          << request->GetURL().ToString();
 | 
			
		||||
      return test_request::CreateResourceHandler(resource->response,
 | 
			
		||||
                                                 resource->response_data);
 | 
			
		||||
    }
 | 
			
		||||
    return RoutingTestHandler::GetResourceHandler(browser, frame, request);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
 | 
			
		||||
                 CefRefPtr<CefFrame> frame,
 | 
			
		||||
                 int httpStatusCode) override {
 | 
			
		||||
    const std::string& url = frame->GetURL();
 | 
			
		||||
    Resource* resource = GetResource(url);
 | 
			
		||||
    if (!resource)
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    const int expected_status = resource->response->GetStatus();
 | 
			
		||||
    if (url == main_url_ || expected_status != 200) {
 | 
			
		||||
      // Test that the status code is correct.
 | 
			
		||||
      EXPECT_EQ(expected_status, httpStatusCode) << url;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TriggerDestroyTestIfDone();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void OnLoadError(CefRefPtr<CefBrowser> browser,
 | 
			
		||||
                   CefRefPtr<CefFrame> frame,
 | 
			
		||||
                   ErrorCode errorCode,
 | 
			
		||||
                   const CefString& errorText,
 | 
			
		||||
                   const CefString& failedUrl) override {
 | 
			
		||||
    Resource* resource = GetResource(failedUrl);
 | 
			
		||||
    if (!resource)
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    const cef_errorcode_t expected_error = resource->response->GetError();
 | 
			
		||||
 | 
			
		||||
    // Tests sometimes also fail with ERR_ABORTED.
 | 
			
		||||
    if (!(expected_error == ERR_NONE && errorCode == ERR_ABORTED)) {
 | 
			
		||||
      EXPECT_EQ(expected_error, errorCode) << failedUrl.ToString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TriggerDestroyTestIfDone();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool OnQuery(CefRefPtr<CefBrowser> browser,
 | 
			
		||||
               CefRefPtr<CefFrame> frame,
 | 
			
		||||
               int64 query_id,
 | 
			
		||||
               const CefString& request,
 | 
			
		||||
               bool persistent,
 | 
			
		||||
               CefRefPtr<Callback> callback) override {
 | 
			
		||||
    Resource* resource = GetResource(frame->GetURL());
 | 
			
		||||
    if (!resource)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    if (request.ToString() == kSuccessMsg ||
 | 
			
		||||
        request.ToString() == kFailureMsg) {
 | 
			
		||||
      callback->Success("");
 | 
			
		||||
      if (request.ToString() == kSuccessMsg)
 | 
			
		||||
        resource->success_query_ct++;
 | 
			
		||||
      else
 | 
			
		||||
        resource->failure_query_ct++;
 | 
			
		||||
      TriggerDestroyTestIfDone();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool OnConsoleMessage(CefRefPtr<CefBrowser> browser,
 | 
			
		||||
                        cef_log_severity_t level,
 | 
			
		||||
                        const CefString& message,
 | 
			
		||||
                        const CefString& source,
 | 
			
		||||
                        int line) override {
 | 
			
		||||
    bool expected = false;
 | 
			
		||||
    if (!setup_->console_messages.empty()) {
 | 
			
		||||
      std::vector<std::string>::iterator it = setup_->console_messages.begin();
 | 
			
		||||
      for (; it != setup_->console_messages.end(); ++it) {
 | 
			
		||||
        const std::string& possible = *it;
 | 
			
		||||
        const std::string& actual = message.ToString();
 | 
			
		||||
        if (actual.find(possible) == 0U) {
 | 
			
		||||
          expected = true;
 | 
			
		||||
          setup_->console_messages.erase(it);
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    EXPECT_TRUE(expected) << "Unexpected console message: "
 | 
			
		||||
                          << message.ToString();
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void TriggerCreateBrowser() {
 | 
			
		||||
    main_url_ = setup_->GetMainURL();
 | 
			
		||||
    CreateBrowser(main_url_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void TriggerDestroyTestIfDone() {
 | 
			
		||||
    CefPostTask(TID_UI, base::Bind(&CorsTestHandler::DestroyTestIfDone, this));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void StartServer(const base::Closure& next_step) {
 | 
			
		||||
    if (!CefCurrentlyOn(TID_UI)) {
 | 
			
		||||
      CefPostTask(TID_UI,
 | 
			
		||||
                  base::Bind(&CorsTestHandler::StartServer, this, next_step));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!setup_->NeedsServer()) {
 | 
			
		||||
      next_step.Run();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Will delete itself after the server stops.
 | 
			
		||||
    server_ = new TestServerObserver(
 | 
			
		||||
        setup_, next_step, base::Bind(&CorsTestHandler::StoppedServer, this));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void StopServer() {
 | 
			
		||||
    CEF_REQUIRE_UI_THREAD();
 | 
			
		||||
    if (!server_) {
 | 
			
		||||
      DCHECK(!setup_->NeedsServer());
 | 
			
		||||
      DestroyTest();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Results in a call to StoppedServer().
 | 
			
		||||
    server_->Shutdown();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void StoppedServer() {
 | 
			
		||||
    CEF_REQUIRE_UI_THREAD();
 | 
			
		||||
    server_ = nullptr;
 | 
			
		||||
    DestroyTest();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Resource* GetResource(const std::string& url) const {
 | 
			
		||||
    Resource* resource = setup_->GetResource(url);
 | 
			
		||||
    EXPECT_TRUE(resource) << url;
 | 
			
		||||
    return resource;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  TestSetup* setup_;
 | 
			
		||||
  std::string main_url_;
 | 
			
		||||
  TestServerObserver* server_ = nullptr;
 | 
			
		||||
  bool shutting_down_ = false;
 | 
			
		||||
 | 
			
		||||
  IMPLEMENT_REFCOUNTING(CorsTestHandler);
 | 
			
		||||
  DISALLOW_COPY_AND_ASSIGN(CorsTestHandler);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// JS that results in a call to CorsTestHandler::OnQuery.
 | 
			
		||||
std::string GetMsgJS(const std::string& msg) {
 | 
			
		||||
  return "window.testQuery({request:'" + msg + "'});";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetSuccessMsgJS() {
 | 
			
		||||
  return GetMsgJS(kSuccessMsg);
 | 
			
		||||
}
 | 
			
		||||
std::string GetFailureMsgJS() {
 | 
			
		||||
  return GetMsgJS(kFailureMsg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetDefaultSuccessMsgHtml() {
 | 
			
		||||
  return "<html><body>TEST<script>" + GetSuccessMsgJS() +
 | 
			
		||||
         "</script></body></html>";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
// Verify the test harness for server requests.
 | 
			
		||||
TEST(CorsTest, BasicServer) {
 | 
			
		||||
  TestSetup setup;
 | 
			
		||||
  Resource resource(HandlerType::SERVER, "/CorsTest.BasicServer");
 | 
			
		||||
  setup.AddResource(&resource);
 | 
			
		||||
 | 
			
		||||
  CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
 | 
			
		||||
  handler->ExecuteTest();
 | 
			
		||||
  ReleaseAndWaitForDestructor(handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Like above, but also send a query JS message.
 | 
			
		||||
TEST(CorsTest, BasicServerWithQuery) {
 | 
			
		||||
  TestSetup setup;
 | 
			
		||||
  Resource resource(HandlerType::SERVER, "/CorsTest.BasicServerWithQuery",
 | 
			
		||||
                    kMimeTypeHtml, GetDefaultSuccessMsgHtml());
 | 
			
		||||
  resource.expected_success_query_ct = 1;
 | 
			
		||||
  setup.AddResource(&resource);
 | 
			
		||||
 | 
			
		||||
  CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
 | 
			
		||||
  handler->ExecuteTest();
 | 
			
		||||
  ReleaseAndWaitForDestructor(handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Verify the test harness for http scheme requests.
 | 
			
		||||
TEST(CorsTest, BasicHttpScheme) {
 | 
			
		||||
  TestSetup setup;
 | 
			
		||||
  Resource resource(HandlerType::HTTP_SCHEME, "/CorsTest.BasicHttpScheme");
 | 
			
		||||
  setup.AddResource(&resource);
 | 
			
		||||
 | 
			
		||||
  CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
 | 
			
		||||
  handler->ExecuteTest();
 | 
			
		||||
  ReleaseAndWaitForDestructor(handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Like above, but also send a query JS message.
 | 
			
		||||
TEST(CorsTest, BasicHttpSchemeWithQuery) {
 | 
			
		||||
  TestSetup setup;
 | 
			
		||||
  Resource resource(HandlerType::HTTP_SCHEME,
 | 
			
		||||
                    "/CorsTest.BasicHttpSchemeWithQuery", kMimeTypeHtml,
 | 
			
		||||
                    GetDefaultSuccessMsgHtml());
 | 
			
		||||
  resource.expected_success_query_ct = 1;
 | 
			
		||||
  setup.AddResource(&resource);
 | 
			
		||||
 | 
			
		||||
  CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
 | 
			
		||||
  handler->ExecuteTest();
 | 
			
		||||
  ReleaseAndWaitForDestructor(handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Verify the test harness for custom standard scheme requests.
 | 
			
		||||
TEST(CorsTest, BasicCustomStandardScheme) {
 | 
			
		||||
  TestSetup setup;
 | 
			
		||||
  Resource resource(HandlerType::CUSTOM_STANDARD_SCHEME,
 | 
			
		||||
                    "/CorsTest.BasicCustomStandardScheme");
 | 
			
		||||
  setup.AddResource(&resource);
 | 
			
		||||
 | 
			
		||||
  CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
 | 
			
		||||
  handler->ExecuteTest();
 | 
			
		||||
  ReleaseAndWaitForDestructor(handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Like above, but also send a query JS message.
 | 
			
		||||
TEST(CorsTest, BasicCustomStandardSchemeWithQuery) {
 | 
			
		||||
  TestSetup setup;
 | 
			
		||||
  Resource resource(HandlerType::CUSTOM_STANDARD_SCHEME,
 | 
			
		||||
                    "/CorsTest.BasicCustomStandardSchemeWithQuery",
 | 
			
		||||
                    kMimeTypeHtml, GetDefaultSuccessMsgHtml());
 | 
			
		||||
  resource.expected_success_query_ct = 1;
 | 
			
		||||
  setup.AddResource(&resource);
 | 
			
		||||
 | 
			
		||||
  CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
 | 
			
		||||
  handler->ExecuteTest();
 | 
			
		||||
  ReleaseAndWaitForDestructor(handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
std::string GetIframeMainHtml(const std::string& iframe_url,
 | 
			
		||||
                              const std::string& sandbox_attribs) {
 | 
			
		||||
  return "<html><body>TEST<iframe src=\"" + iframe_url + "\" sandbox=\"" +
 | 
			
		||||
         sandbox_attribs + "\"></iframe></body></html>";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetIframeSubHtml() {
 | 
			
		||||
  // Try to script the parent frame, then send the SuccessMsg.
 | 
			
		||||
  return "<html><body>TEST<script>try { parent.document.body; } catch "
 | 
			
		||||
         "(exception) { console.log(exception.toString()); }" +
 | 
			
		||||
         GetSuccessMsgJS() + "</script></body></html>";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool HasSandboxAttrib(const std::string& sandbox_attribs,
 | 
			
		||||
                      const std::string& attrib) {
 | 
			
		||||
  return sandbox_attribs.find(attrib) != std::string::npos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetupIframeRequest(TestSetup* setup,
 | 
			
		||||
                        const std::string& test_name,
 | 
			
		||||
                        HandlerType main_handler,
 | 
			
		||||
                        Resource* main_resource,
 | 
			
		||||
                        HandlerType iframe_handler,
 | 
			
		||||
                        Resource* iframe_resource,
 | 
			
		||||
                        const std::string& sandbox_attribs) {
 | 
			
		||||
  const std::string& base_path = "/" + test_name;
 | 
			
		||||
 | 
			
		||||
  // Expect a single iframe request.
 | 
			
		||||
  iframe_resource->Init(iframe_handler, base_path + ".iframe.html",
 | 
			
		||||
                        kMimeTypeHtml, GetIframeSubHtml());
 | 
			
		||||
 | 
			
		||||
  // Expect a single main frame request.
 | 
			
		||||
  const std::string& iframe_url = iframe_resource->GetPathURL();
 | 
			
		||||
  main_resource->Init(main_handler, base_path, kMimeTypeHtml,
 | 
			
		||||
                      GetIframeMainHtml(iframe_url, sandbox_attribs));
 | 
			
		||||
 | 
			
		||||
  if (HasSandboxAttrib(sandbox_attribs, "allow-scripts")) {
 | 
			
		||||
    // Expect the iframe to load successfully and send the SuccessMsg.
 | 
			
		||||
    iframe_resource->expected_success_query_ct = 1;
 | 
			
		||||
 | 
			
		||||
    const bool has_same_origin =
 | 
			
		||||
        HasSandboxAttrib(sandbox_attribs, "allow-same-origin");
 | 
			
		||||
    if (!has_same_origin ||
 | 
			
		||||
        (has_same_origin &&
 | 
			
		||||
         (main_handler == HandlerType::CUSTOM_NONSTANDARD_SCHEME ||
 | 
			
		||||
          main_handler != iframe_handler))) {
 | 
			
		||||
      // Expect parent frame scripting to fail if:
 | 
			
		||||
      // - "allow-same-origin" is not specified;
 | 
			
		||||
      // - the main frame is a non-standard scheme (e.g. CORS disabled);
 | 
			
		||||
      // - the main frame and iframe origins don't match.
 | 
			
		||||
      // The reported origin will be "null" if "allow-same-origin" is not
 | 
			
		||||
      // specified, or if the iframe is hosted on a non-standard scheme.
 | 
			
		||||
      const std::string& origin =
 | 
			
		||||
          !has_same_origin ||
 | 
			
		||||
                  iframe_handler == HandlerType::CUSTOM_NONSTANDARD_SCHEME
 | 
			
		||||
              ? "null"
 | 
			
		||||
              : GetOrigin(iframe_handler);
 | 
			
		||||
      setup->AddConsoleMessage("SecurityError: Blocked a frame with origin \"" +
 | 
			
		||||
                               origin +
 | 
			
		||||
                               "\" from accessing a cross-origin frame.");
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    // Expect JavaScript execution to fail.
 | 
			
		||||
    setup->AddConsoleMessage("Blocked script execution in '" + iframe_url +
 | 
			
		||||
                             "' because the document's frame is sandboxed and "
 | 
			
		||||
                             "the 'allow-scripts' permission is not set.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setup->AddResource(main_resource);
 | 
			
		||||
  setup->AddResource(iframe_resource);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
// Test iframe sandbox attributes with different origin combinations.
 | 
			
		||||
#define CORS_TEST_IFRAME(test_name, handler_main, handler_iframe,     \
 | 
			
		||||
                         sandbox_attribs)                             \
 | 
			
		||||
  TEST(CorsTest, Iframe##test_name) {                                 \
 | 
			
		||||
    TestSetup setup;                                                  \
 | 
			
		||||
    Resource resource_main, resource_iframe;                          \
 | 
			
		||||
    SetupIframeRequest(&setup, "CorsTest.Iframe" #test_name,          \
 | 
			
		||||
                       HandlerType::handler_main, &resource_main,     \
 | 
			
		||||
                       HandlerType::handler_iframe, &resource_iframe, \
 | 
			
		||||
                       sandbox_attribs);                              \
 | 
			
		||||
    CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup); \
 | 
			
		||||
    handler->ExecuteTest();                                           \
 | 
			
		||||
    ReleaseAndWaitForDestructor(handler);                             \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
// Test all origin combinations (same and cross-origin).
 | 
			
		||||
#define CORS_TEST_IFRAME_ALL(name, sandbox_attribs)                            \
 | 
			
		||||
  CORS_TEST_IFRAME(name##ServerToServer, SERVER, SERVER, sandbox_attribs)      \
 | 
			
		||||
  CORS_TEST_IFRAME(name##ServerToHttpScheme, SERVER, HTTP_SCHEME,              \
 | 
			
		||||
                   sandbox_attribs)                                            \
 | 
			
		||||
  CORS_TEST_IFRAME(name##ServerToCustomStandardScheme, SERVER,                 \
 | 
			
		||||
                   CUSTOM_STANDARD_SCHEME, sandbox_attribs)                    \
 | 
			
		||||
  CORS_TEST_IFRAME(name##ServerToCustomNonStandardScheme, SERVER,              \
 | 
			
		||||
                   CUSTOM_NONSTANDARD_SCHEME, sandbox_attribs)                 \
 | 
			
		||||
  CORS_TEST_IFRAME(name##HttpSchemeToServer, HTTP_SCHEME, SERVER,              \
 | 
			
		||||
                   sandbox_attribs)                                            \
 | 
			
		||||
  CORS_TEST_IFRAME(name##HttpSchemeToHttpScheme, HTTP_SCHEME, HTTP_SCHEME,     \
 | 
			
		||||
                   sandbox_attribs)                                            \
 | 
			
		||||
  CORS_TEST_IFRAME(name##HttpSchemeToCustomStandardScheme, HTTP_SCHEME,        \
 | 
			
		||||
                   CUSTOM_STANDARD_SCHEME, sandbox_attribs)                    \
 | 
			
		||||
  CORS_TEST_IFRAME(name##HttpSchemeToCustomNonStandardScheme, HTTP_SCHEME,     \
 | 
			
		||||
                   CUSTOM_NONSTANDARD_SCHEME, sandbox_attribs)                 \
 | 
			
		||||
  CORS_TEST_IFRAME(name##CustomStandardSchemeToServer, CUSTOM_STANDARD_SCHEME, \
 | 
			
		||||
                   SERVER, sandbox_attribs)                                    \
 | 
			
		||||
  CORS_TEST_IFRAME(name##CustomStandardSchemeToHttpScheme,                     \
 | 
			
		||||
                   CUSTOM_STANDARD_SCHEME, HTTP_SCHEME, sandbox_attribs)       \
 | 
			
		||||
  CORS_TEST_IFRAME(name##CustomStandardSchemeToCustomStandardScheme,           \
 | 
			
		||||
                   CUSTOM_STANDARD_SCHEME, CUSTOM_STANDARD_SCHEME,             \
 | 
			
		||||
                   sandbox_attribs)                                            \
 | 
			
		||||
  CORS_TEST_IFRAME(name##CustomStandardSchemeToCustomNonStandardScheme,        \
 | 
			
		||||
                   CUSTOM_STANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME,          \
 | 
			
		||||
                   sandbox_attribs)                                            \
 | 
			
		||||
  CORS_TEST_IFRAME(name##CustomNonStandardSchemeToServer,                      \
 | 
			
		||||
                   CUSTOM_NONSTANDARD_SCHEME, SERVER, sandbox_attribs)         \
 | 
			
		||||
  CORS_TEST_IFRAME(name##CustomNonStandardSchemeToHttpScheme,                  \
 | 
			
		||||
                   CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME, sandbox_attribs)    \
 | 
			
		||||
  CORS_TEST_IFRAME(name##CustomNonStandardSchemeToCustomStandardScheme,        \
 | 
			
		||||
                   CUSTOM_NONSTANDARD_SCHEME, CUSTOM_STANDARD_SCHEME,          \
 | 
			
		||||
                   sandbox_attribs)                                            \
 | 
			
		||||
  CORS_TEST_IFRAME(name##CustomNonStandardSchemeToCustomNonStandardScheme,     \
 | 
			
		||||
                   CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME,       \
 | 
			
		||||
                   sandbox_attribs)
 | 
			
		||||
 | 
			
		||||
// Everything is blocked.
 | 
			
		||||
CORS_TEST_IFRAME_ALL(None, "")
 | 
			
		||||
 | 
			
		||||
// JavaScript execution is allowed.
 | 
			
		||||
CORS_TEST_IFRAME_ALL(AllowScripts, "allow-scripts")
 | 
			
		||||
 | 
			
		||||
// JavaScript execution is allowed and scripting the parent is allowed for
 | 
			
		||||
// same-origin only.
 | 
			
		||||
CORS_TEST_IFRAME_ALL(AllowScriptsAndSameOrigin,
 | 
			
		||||
                     "allow-scripts allow-same-origin")
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
struct SubResource : Resource {
 | 
			
		||||
  SubResource() {}
 | 
			
		||||
 | 
			
		||||
  std::string main_origin;
 | 
			
		||||
  bool supports_cors = false;
 | 
			
		||||
  bool is_cross_origin = false;
 | 
			
		||||
 | 
			
		||||
  void InitCors(HandlerType main_handler, bool add_header) {
 | 
			
		||||
    // Origin is always "null" for non-standard schemes.
 | 
			
		||||
    main_origin = main_handler == HandlerType::CUSTOM_NONSTANDARD_SCHEME
 | 
			
		||||
                      ? "null"
 | 
			
		||||
                      : GetOrigin(main_handler);
 | 
			
		||||
 | 
			
		||||
    // True if cross-origin requests are allowed. XHR requests to non-standard
 | 
			
		||||
    // schemes are not allowed (due to the "null" origin).
 | 
			
		||||
    supports_cors = handler != HandlerType::CUSTOM_NONSTANDARD_SCHEME;
 | 
			
		||||
    if (!supports_cors) {
 | 
			
		||||
      // Don't expect the xhr request.
 | 
			
		||||
      expected_response_ct = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // True if the request is considered cross-origin. Any requests between
 | 
			
		||||
    // non-standard schemes are considered cross-origin (due to the "null"
 | 
			
		||||
    // origin).
 | 
			
		||||
    is_cross_origin = main_handler != handler ||
 | 
			
		||||
                      (main_handler == HandlerType::CUSTOM_NONSTANDARD_SCHEME &&
 | 
			
		||||
                       handler == main_handler);
 | 
			
		||||
 | 
			
		||||
    if (is_cross_origin && add_header) {
 | 
			
		||||
      response->SetHeaderByName("Access-Control-Allow-Origin", main_origin,
 | 
			
		||||
                                false);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool VerifyRequest(CefRefPtr<CefRequest> request) const override {
 | 
			
		||||
    // Verify that the "Origin" header contains the expected value.
 | 
			
		||||
    const std::string& origin = request->GetHeaderByName("Origin");
 | 
			
		||||
    if (is_cross_origin) {
 | 
			
		||||
      EXPECT_STREQ(main_origin.c_str(), origin.c_str());
 | 
			
		||||
      return main_origin == origin;
 | 
			
		||||
    }
 | 
			
		||||
    EXPECT_TRUE(origin.empty());
 | 
			
		||||
    return origin.empty();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class ExecMode {
 | 
			
		||||
  XHR,
 | 
			
		||||
  FETCH,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::string GetXhrExecJS(const std::string& sub_url) {
 | 
			
		||||
  return "xhr = new XMLHttpRequest();\n"
 | 
			
		||||
         "xhr.open(\"GET\", \"" +
 | 
			
		||||
         sub_url +
 | 
			
		||||
         "\", true)\n;"
 | 
			
		||||
         "xhr.onload = function(e) {\n"
 | 
			
		||||
         "  if (xhr.readyState === 4) {\n"
 | 
			
		||||
         "    if (xhr.status === 200) {\n"
 | 
			
		||||
         "      onResult(xhr.responseText);\n"
 | 
			
		||||
         "    } else {\n"
 | 
			
		||||
         "      console.log('XMLHttpRequest failed with status ' + "
 | 
			
		||||
         "xhr.status);\n"
 | 
			
		||||
         "      onResult('FAILURE');\n"
 | 
			
		||||
         "    }\n"
 | 
			
		||||
         "  }\n"
 | 
			
		||||
         "};\n"
 | 
			
		||||
         "xhr.onerror = function(e) {\n"
 | 
			
		||||
         "  onResult('FAILURE');\n"
 | 
			
		||||
         "};\n"
 | 
			
		||||
         "xhr.send();\n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetFetchExecJS(const std::string& sub_url) {
 | 
			
		||||
  return "fetch('" + sub_url +
 | 
			
		||||
         "')\n"
 | 
			
		||||
         ".then(function(response) {\n"
 | 
			
		||||
         "  if (response.status === 200) {\n"
 | 
			
		||||
         "      response.text().then(function(text) {\n"
 | 
			
		||||
         "          onResult(text);\n"
 | 
			
		||||
         "      }).catch(function(e) {\n"
 | 
			
		||||
         "          onResult('FAILURE')\n;        "
 | 
			
		||||
         "      })\n;"
 | 
			
		||||
         "  } else {\n"
 | 
			
		||||
         "      onResult('FAILURE');\n"
 | 
			
		||||
         "  }\n"
 | 
			
		||||
         "}).catch(function(e) {\n"
 | 
			
		||||
         "  onResult('FAILURE');\n"
 | 
			
		||||
         "});\n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetExecMainHtml(ExecMode mode, const std::string& sub_url) {
 | 
			
		||||
  return std::string() +
 | 
			
		||||
         "<html><head>\n"
 | 
			
		||||
         "<script language=\"JavaScript\">\n" +
 | 
			
		||||
         "function onResult(val) {\n"
 | 
			
		||||
         "  if (val === '" +
 | 
			
		||||
         kDefaultText + "') {" + GetSuccessMsgJS() + "} else {" +
 | 
			
		||||
         GetFailureMsgJS() +
 | 
			
		||||
         "}\n}\n"
 | 
			
		||||
         "function execRequest() {\n" +
 | 
			
		||||
         (mode == ExecMode::XHR ? GetXhrExecJS(sub_url)
 | 
			
		||||
                                : GetFetchExecJS(sub_url)) +
 | 
			
		||||
         "}\n</script>\n"
 | 
			
		||||
         "</head><body onload=\"execRequest();\">"
 | 
			
		||||
         "Running execRequest..."
 | 
			
		||||
         "</body></html>";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// XHR and fetch requests behave the same, except for console message contents.
 | 
			
		||||
void SetupExecRequest(ExecMode mode,
 | 
			
		||||
                      TestSetup* setup,
 | 
			
		||||
                      const std::string& test_name,
 | 
			
		||||
                      HandlerType main_handler,
 | 
			
		||||
                      Resource* main_resource,
 | 
			
		||||
                      HandlerType sub_handler,
 | 
			
		||||
                      SubResource* sub_resource,
 | 
			
		||||
                      bool add_header) {
 | 
			
		||||
  const std::string& base_path = "/" + test_name;
 | 
			
		||||
 | 
			
		||||
  // Expect a single xhr request.
 | 
			
		||||
  sub_resource->Init(sub_handler, base_path + ".sub.txt", kMimeTypeText,
 | 
			
		||||
                     kDefaultText);
 | 
			
		||||
  sub_resource->InitCors(main_handler, add_header);
 | 
			
		||||
 | 
			
		||||
  // Expect a single main frame request.
 | 
			
		||||
  const std::string& sub_url = sub_resource->GetPathURL();
 | 
			
		||||
  main_resource->Init(main_handler, base_path, kMimeTypeHtml,
 | 
			
		||||
                      GetExecMainHtml(mode, sub_url));
 | 
			
		||||
 | 
			
		||||
  if (sub_resource->is_cross_origin &&
 | 
			
		||||
      (!sub_resource->supports_cors || !add_header)) {
 | 
			
		||||
    // Expect the cross-origin XHR to be blocked.
 | 
			
		||||
    main_resource->expected_failure_query_ct = 1;
 | 
			
		||||
 | 
			
		||||
    if (sub_resource->supports_cors && !add_header) {
 | 
			
		||||
      // The request supports CORS, but we didn't add the header.
 | 
			
		||||
      if (mode == ExecMode::XHR) {
 | 
			
		||||
        setup->AddConsoleMessage(
 | 
			
		||||
            "Access to XMLHttpRequest at '" + sub_url + "' from origin '" +
 | 
			
		||||
            sub_resource->main_origin +
 | 
			
		||||
            "' has been blocked by CORS policy: No "
 | 
			
		||||
            "'Access-Control-Allow-Origin' "
 | 
			
		||||
            "header is present on the requested resource.");
 | 
			
		||||
      } else {
 | 
			
		||||
        setup->AddConsoleMessage(
 | 
			
		||||
            "Access to fetch at '" + sub_url + "' from origin '" +
 | 
			
		||||
            sub_resource->main_origin +
 | 
			
		||||
            "' has been blocked by CORS policy: No "
 | 
			
		||||
            "'Access-Control-Allow-Origin' header is present on the requested "
 | 
			
		||||
            "resource. If an opaque response serves your needs, set the "
 | 
			
		||||
            "request's mode to 'no-cors' to fetch the resource with CORS "
 | 
			
		||||
            "disabled.");
 | 
			
		||||
      }
 | 
			
		||||
    } else if (mode == ExecMode::XHR) {
 | 
			
		||||
      setup->AddConsoleMessage(
 | 
			
		||||
          "Access to XMLHttpRequest at '" + sub_url + "' from origin '" +
 | 
			
		||||
          sub_resource->main_origin +
 | 
			
		||||
          "' has been blocked by CORS policy: Cross origin requests are only "
 | 
			
		||||
          "supported for protocol schemes:");
 | 
			
		||||
    } else {
 | 
			
		||||
      setup->AddConsoleMessage(
 | 
			
		||||
          "Fetch API cannot load " + sub_url +
 | 
			
		||||
          ". URL scheme must be \"http\" or \"https\" for CORS request.");
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    // Expect the (possibly cross-origin) XHR to be allowed.
 | 
			
		||||
    main_resource->expected_success_query_ct = 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setup->AddResource(main_resource);
 | 
			
		||||
  setup->AddResource(sub_resource);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
// Test XHR requests with different origin combinations.
 | 
			
		||||
#define CORS_TEST_XHR(test_name, handler_main, handler_sub, add_header)    \
 | 
			
		||||
  TEST(CorsTest, Xhr##test_name) {                                         \
 | 
			
		||||
    TestSetup setup;                                                       \
 | 
			
		||||
    Resource resource_main;                                                \
 | 
			
		||||
    SubResource resource_sub;                                              \
 | 
			
		||||
    SetupExecRequest(ExecMode::XHR, &setup, "CorsTest.Xhr" #test_name,     \
 | 
			
		||||
                     HandlerType::handler_main, &resource_main,            \
 | 
			
		||||
                     HandlerType::handler_sub, &resource_sub, add_header); \
 | 
			
		||||
    CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);      \
 | 
			
		||||
    handler->ExecuteTest();                                                \
 | 
			
		||||
    ReleaseAndWaitForDestructor(handler);                                  \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
// Test all origin combinations (same and cross-origin).
 | 
			
		||||
#define CORS_TEST_XHR_ALL(name, add_header)                                    \
 | 
			
		||||
  CORS_TEST_XHR(name##ServerToServer, SERVER, SERVER, add_header)              \
 | 
			
		||||
  CORS_TEST_XHR(name##ServerToHttpScheme, SERVER, HTTP_SCHEME, add_header)     \
 | 
			
		||||
  CORS_TEST_XHR(name##ServerToCustomStandardScheme, SERVER,                    \
 | 
			
		||||
                CUSTOM_STANDARD_SCHEME, add_header)                            \
 | 
			
		||||
  CORS_TEST_XHR(name##ServerToCustomNonStandardScheme, SERVER,                 \
 | 
			
		||||
                CUSTOM_NONSTANDARD_SCHEME, add_header)                         \
 | 
			
		||||
  CORS_TEST_XHR(name##HttpSchemeToServer, HTTP_SCHEME, SERVER, add_header)     \
 | 
			
		||||
  CORS_TEST_XHR(name##HttpSchemeToHttpScheme, HTTP_SCHEME, HTTP_SCHEME,        \
 | 
			
		||||
                add_header)                                                    \
 | 
			
		||||
  CORS_TEST_XHR(name##HttpSchemeToCustomStandardScheme, HTTP_SCHEME,           \
 | 
			
		||||
                CUSTOM_STANDARD_SCHEME, add_header)                            \
 | 
			
		||||
  CORS_TEST_XHR(name##HttpSchemeToCustomNonStandardScheme, HTTP_SCHEME,        \
 | 
			
		||||
                CUSTOM_NONSTANDARD_SCHEME, add_header)                         \
 | 
			
		||||
  CORS_TEST_XHR(name##CustomStandardSchemeToServer, CUSTOM_STANDARD_SCHEME,    \
 | 
			
		||||
                SERVER, add_header)                                            \
 | 
			
		||||
  CORS_TEST_XHR(name##CustomStandardSchemeToHttpScheme,                        \
 | 
			
		||||
                CUSTOM_STANDARD_SCHEME, HTTP_SCHEME, add_header)               \
 | 
			
		||||
  CORS_TEST_XHR(name##CustomStandardSchemeToCustomStandardScheme,              \
 | 
			
		||||
                CUSTOM_STANDARD_SCHEME, CUSTOM_STANDARD_SCHEME, add_header)    \
 | 
			
		||||
  CORS_TEST_XHR(name##CustomStandardSchemeToCustomNonStandardScheme,           \
 | 
			
		||||
                CUSTOM_STANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME, add_header) \
 | 
			
		||||
  CORS_TEST_XHR(name##CustomNonStandardSchemeToServer,                         \
 | 
			
		||||
                CUSTOM_NONSTANDARD_SCHEME, SERVER, add_header)                 \
 | 
			
		||||
  CORS_TEST_XHR(name##CustomNonStandardSchemeToHttpScheme,                     \
 | 
			
		||||
                CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME, add_header)            \
 | 
			
		||||
  CORS_TEST_XHR(name##CustomNonStandardSchemeToCustomStandardScheme,           \
 | 
			
		||||
                CUSTOM_NONSTANDARD_SCHEME, CUSTOM_STANDARD_SCHEME, add_header) \
 | 
			
		||||
  CORS_TEST_XHR(name##CustomNonStandardSchemeToCustomNonStandardScheme,        \
 | 
			
		||||
                CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME,          \
 | 
			
		||||
                add_header)
 | 
			
		||||
 | 
			
		||||
// XHR requests without the "Access-Control-Allow-Origin" header.
 | 
			
		||||
CORS_TEST_XHR_ALL(NoHeader, false)
 | 
			
		||||
 | 
			
		||||
// XHR requests with the "Access-Control-Allow-Origin" header.
 | 
			
		||||
CORS_TEST_XHR_ALL(WithHeader, true)
 | 
			
		||||
 | 
			
		||||
// Test fetch requests with different origin combinations.
 | 
			
		||||
#define CORS_TEST_FETCH(test_name, handler_main, handler_sub, add_header)  \
 | 
			
		||||
  TEST(CorsTest, Fetch##test_name) {                                       \
 | 
			
		||||
    TestSetup setup;                                                       \
 | 
			
		||||
    Resource resource_main;                                                \
 | 
			
		||||
    SubResource resource_sub;                                              \
 | 
			
		||||
    SetupExecRequest(ExecMode::FETCH, &setup, "CorsTest.Fetch" #test_name, \
 | 
			
		||||
                     HandlerType::handler_main, &resource_main,            \
 | 
			
		||||
                     HandlerType::handler_sub, &resource_sub, add_header); \
 | 
			
		||||
    CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);      \
 | 
			
		||||
    handler->ExecuteTest();                                                \
 | 
			
		||||
    ReleaseAndWaitForDestructor(handler);                                  \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
// Test all origin combinations (same and cross-origin).
 | 
			
		||||
#define CORS_TEST_FETCH_ALL(name, add_header)                                 \
 | 
			
		||||
  CORS_TEST_FETCH(name##ServerToServer, SERVER, SERVER, add_header)           \
 | 
			
		||||
  CORS_TEST_FETCH(name##ServerToHttpScheme, SERVER, HTTP_SCHEME, add_header)  \
 | 
			
		||||
  CORS_TEST_FETCH(name##ServerToCustomStandardScheme, SERVER,                 \
 | 
			
		||||
                  CUSTOM_STANDARD_SCHEME, add_header)                         \
 | 
			
		||||
  CORS_TEST_FETCH(name##ServerToCustomNonStandardScheme, SERVER,              \
 | 
			
		||||
                  CUSTOM_NONSTANDARD_SCHEME, add_header)                      \
 | 
			
		||||
  CORS_TEST_FETCH(name##HttpSchemeToServer, HTTP_SCHEME, SERVER, add_header)  \
 | 
			
		||||
  CORS_TEST_FETCH(name##HttpSchemeToHttpScheme, HTTP_SCHEME, HTTP_SCHEME,     \
 | 
			
		||||
                  add_header)                                                 \
 | 
			
		||||
  CORS_TEST_FETCH(name##HttpSchemeToCustomStandardScheme, HTTP_SCHEME,        \
 | 
			
		||||
                  CUSTOM_STANDARD_SCHEME, add_header)                         \
 | 
			
		||||
  CORS_TEST_FETCH(name##HttpSchemeToCustomNonStandardScheme, HTTP_SCHEME,     \
 | 
			
		||||
                  CUSTOM_NONSTANDARD_SCHEME, add_header)                      \
 | 
			
		||||
  CORS_TEST_FETCH(name##CustomStandardSchemeToServer, CUSTOM_STANDARD_SCHEME, \
 | 
			
		||||
                  SERVER, add_header)                                         \
 | 
			
		||||
  CORS_TEST_FETCH(name##CustomStandardSchemeToHttpScheme,                     \
 | 
			
		||||
                  CUSTOM_STANDARD_SCHEME, HTTP_SCHEME, add_header)            \
 | 
			
		||||
  CORS_TEST_FETCH(name##CustomStandardSchemeToCustomStandardScheme,           \
 | 
			
		||||
                  CUSTOM_STANDARD_SCHEME, CUSTOM_STANDARD_SCHEME, add_header) \
 | 
			
		||||
  CORS_TEST_FETCH(name##CustomStandardSchemeToCustomNonStandardScheme,        \
 | 
			
		||||
                  CUSTOM_STANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME,          \
 | 
			
		||||
                  add_header)                                                 \
 | 
			
		||||
  CORS_TEST_FETCH(name##CustomNonStandardSchemeToServer,                      \
 | 
			
		||||
                  CUSTOM_NONSTANDARD_SCHEME, SERVER, add_header)              \
 | 
			
		||||
  CORS_TEST_FETCH(name##CustomNonStandardSchemeToHttpScheme,                  \
 | 
			
		||||
                  CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME, add_header)         \
 | 
			
		||||
  CORS_TEST_FETCH(name##CustomNonStandardSchemeToCustomStandardScheme,        \
 | 
			
		||||
                  CUSTOM_NONSTANDARD_SCHEME, CUSTOM_STANDARD_SCHEME,          \
 | 
			
		||||
                  add_header)                                                 \
 | 
			
		||||
  CORS_TEST_FETCH(name##CustomNonStandardSchemeToCustomNonStandardScheme,     \
 | 
			
		||||
                  CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME,       \
 | 
			
		||||
                  add_header)
 | 
			
		||||
 | 
			
		||||
// Fetch requests without the "Access-Control-Allow-Origin" header.
 | 
			
		||||
CORS_TEST_FETCH_ALL(NoHeader, false)
 | 
			
		||||
 | 
			
		||||
// Fetch requests with the "Access-Control-Allow-Origin" header.
 | 
			
		||||
CORS_TEST_FETCH_ALL(WithHeader, true)
 | 
			
		||||
		Reference in New Issue
	
	Block a user