1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198

content / renderer / browser_render_view_browsertest.cc [blame]

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Browser tests targeted at the `blink::WebView` that run in browser context.
// Note that these tests rely on single-process mode, and hence may be
// disabled in some configurations (check gyp files).

#include <utility>

#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_browser_context.h"
#include "content/shell/browser/shell_content_browser_client.h"
#include "content/shell/common/shell_content_client.h"
#include "content/shell/renderer/shell_content_renderer_client.h"
#include "net/base/net_errors.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/clear_data_filter.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_url_error.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/web/web_frame.h"

namespace content {

namespace {

class TestShellContentRendererClient : public ShellContentRendererClient {
 public:
  TestShellContentRendererClient()
      : latest_error_valid_(false),
        latest_error_reason_(0),
        latest_error_stale_copy_in_cache_(false) {}

  void PrepareErrorPage(
      content::RenderFrame* render_frame,
      const blink::WebURLError& error,
      const std::string& http_method,
      mojom::AlternativeErrorPageOverrideInfoPtr alternative_error_page_info,
      std::string* error_html) override {
    if (error_html)
      *error_html = "A suffusion of yellow.";
    latest_error_valid_ = true;
    latest_error_reason_ = error.reason();
    latest_error_stale_copy_in_cache_ = error.has_copy_in_cache();
  }

  bool GetLatestError(int* error_code, bool* stale_cache_entry_present) {
    if (latest_error_valid_) {
      *error_code = latest_error_reason_;
      *stale_cache_entry_present = latest_error_stale_copy_in_cache_;
    }
    return latest_error_valid_;
  }

 private:
  bool latest_error_valid_;
  int latest_error_reason_;
  bool latest_error_stale_copy_in_cache_;
};

}  // namespace

class RenderViewBrowserTest : public ContentBrowserTest {
 public:
  RenderViewBrowserTest() {}

  void SetUpCommandLine(base::CommandLine* command_line) override {
    // This method is needed to allow interaction with in-process renderer
    // and use of a test ContentRendererClient.
    command_line->AppendSwitch(switches::kSingleProcess);
  }

  void SetUpOnMainThread() override {
    // Override setting of renderer client.
    renderer_client_ = new TestShellContentRendererClient();
    // Explicitly leaks ownership; this object will remain alive
    // until process death.  We don't deleted the returned value,
    // since some contexts set the pointer to a non-heap address.
    SetRendererClientForTesting(renderer_client_);
  }

  // Navigates to the given URL and waits for |num_navigations| to occur, and
  // the title to change to |expected_title|.
  void NavigateToURLAndWaitForTitle(const GURL& url,
                                    const std::string& expected_title,
                                    int num_navigations) {
    content::TitleWatcher title_watcher(
        shell()->web_contents(), base::ASCIIToUTF16(expected_title));

    content::NavigateToURLBlockUntilNavigationsComplete(
        shell(), url, num_navigations);

    EXPECT_EQ(base::ASCIIToUTF16(expected_title),
              title_watcher.WaitAndGetTitle());
  }

  // Returns true if there is a valid error stored; in this case
  // |*error_code| and |*stale_cache_entry_present| will be updated
  // appropriately.
  // Must be called after the renderer thread is created.
  bool GetLatestErrorFromRendererClient(
      int* error_code, bool* stale_cache_entry_present) {
    bool result = false;

    PostTaskToInProcessRendererAndWait(base::BindOnce(
        &RenderViewBrowserTest::GetLatestErrorFromRendererClient0,
        renderer_client_, &result, error_code, stale_cache_entry_present));
    return result;
  }

 private:
  // Must be run on renderer thread.
  static void GetLatestErrorFromRendererClient0(
      TestShellContentRendererClient* renderer_client,
      bool* result, int* error_code, bool* stale_cache_entry_present) {
    *result = renderer_client->GetLatestError(
        error_code, stale_cache_entry_present);
  }

  raw_ptr<TestShellContentRendererClient> renderer_client_;
};

// https://crbug.com/788788
#if (BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)) || \
    (BUILDFLAG(IS_LINUX) && defined(MEMORY_SANITIZER))
#define MAYBE_ConfirmCacheInformationPlumbed \
  DISABLED_ConfirmCacheInformationPlumbed
#else
#define MAYBE_ConfirmCacheInformationPlumbed ConfirmCacheInformationPlumbed
#endif  // BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)
IN_PROC_BROWSER_TEST_F(RenderViewBrowserTest,
                       MAYBE_ConfirmCacheInformationPlumbed) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Load URL with "nocache" set, to create stale cache.
  GURL test_url(embedded_test_server()->GetURL("/nocache-with-etag.html"));
  NavigateToURLAndWaitForTitle(test_url, "Nocache Test Page", 1);

  // Shut down the server to force a network error.
  ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());

  // An error results in one completed navigation.
  NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1);
  int error_code = net::OK;
  bool stale_cache_entry_present = false;
  ASSERT_TRUE(GetLatestErrorFromRendererClient(
      &error_code, &stale_cache_entry_present));
  EXPECT_EQ(net::ERR_CONNECTION_REFUSED, error_code);
  EXPECT_TRUE(stale_cache_entry_present);

  // Clear the cache and repeat; confirm lack of entry in cache reported.
  {
    base::RunLoop run_loop;
    content::StoragePartition* partition = shell()
                                               ->web_contents()
                                               ->GetPrimaryMainFrame()
                                               ->GetProcess()
                                               ->GetStoragePartition();
    partition->GetNetworkContext()->ClearHttpCache(
        base::Time(), base::Time(), nullptr /* filter */,
        base::BindOnce(run_loop.QuitClosure()));
    run_loop.Run();
  }

  content::NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1);

  error_code = net::OK;
  stale_cache_entry_present = true;
  ASSERT_TRUE(GetLatestErrorFromRendererClient(
      &error_code, &stale_cache_entry_present));
  EXPECT_EQ(net::ERR_CONNECTION_REFUSED, error_code);
  EXPECT_FALSE(stale_cache_entry_present);
}

}  // namespace content