text: // Copyright (c) 2016 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef CHROME_BROWSER_PREFETCH_NO_STATE_PREFETCH_PRERENDER_TEST_UTILS_H_
  #define CHROME_BROWSER_PREFETCH_NO_STATE_PREFETCH_PRERENDER_TEST_UTILS_H_
  
  #include <functional>
  #include <map>
  #include <memory>
  #include <set>
  #include <string>
  #include <vector>
  
  #include "base/containers/circular_deque.h"
  #include "base/memory/weak_ptr.h"
  #include "base/run_loop.h"
  #include "base/scoped_multi_source_observation.h"
  #include "base/synchronization/lock.h"
  #include "base/test/metrics/histogram_tester.h"
  #include "chrome/browser/external_protocol/external_protocol_handler.h"
  #include "chrome/browser/prefetch/no_state_prefetch/chrome_no_state_prefetch_contents_delegate.h"
  #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
  #include "chrome/test/base/in_process_browser_test.h"
  #include "components/no_state_prefetch/browser/no_state_prefetch_contents.h"
  #include "components/no_state_prefetch/browser/no_state_prefetch_contents_delegate.h"
  #include "components/no_state_prefetch/browser/no_state_prefetch_manager.h"
  #include "components/safe_browsing/core/browser/db/fake_database_manager.h"
  #include "content/public/browser/browser_thread.h"
  #include "content/public/browser/render_widget_host_observer.h"
  #include "url/gurl.h"
  
  namespace prerender {
  
  namespace test_utils {
  
  class TestNoStatePrefetchContents : public NoStatePrefetchContents,
                                      public content::RenderWidgetHostObserver {
   public:
    TestNoStatePrefetchContents(
        NoStatePrefetchManager* no_state_prefetch_manager,
        content::BrowserContext* browser_context,
        const GURL& url,
        const content::Referrer& referrer,
        const absl::optional<url::Origin>& initiator_origin,
        Origin origin,
        FinalStatus expected_final_status,
        bool ignore_final_status);
  
    TestNoStatePrefetchContents(const TestNoStatePrefetchContents&) = delete;
    TestNoStatePrefetchContents& operator=(const TestNoStatePrefetchContents&) =
        delete;
  
    ~TestNoStatePrefetchContents() override;
  
    bool CheckURL(const GURL& url) override;
  
    // For tests which do not know whether the no-state prefetcher will be used.
    void set_skip_final_checks(bool value) { skip_final_checks_ = value; }
  
    FinalStatus expected_final_status() const { return expected_final_status_; }
  
   private:
    // WebContentsObserver overrides.
    void RenderFrameHostChanged(
        content::RenderFrameHost* old_frame_host,
        content::RenderFrameHost* new_frame_host) override;
  
    // RenderWidgetHostObserver overrides.
    void RenderWidgetHostVisibilityChanged(content::RenderWidgetHost* widget_host,
                                           bool became_visible) override;
    void RenderWidgetHostDestroyed(
        content::RenderWidgetHost* widget_host) override;
  
    FinalStatus expected_final_status_;
  
    base::ScopedMultiSourceObservation<content::RenderWidgetHost,
                                       content::RenderWidgetHostObserver>
        observations_{this};
  
    // The main frame created for the prerender, if any.
    content::RenderFrameHost* new_main_frame_ = nullptr;
  
    // If true, |expected_final_status_| and other shutdown checks are skipped.
    bool skip_final_checks_;
  };
  
  // A handle to a TestNoStatePrefetchContents whose lifetime is under the
  // caller's control. A NoStatePrefetchContents may be destroyed at any point.
  // This allows tracking the FinalStatus.
  class TestPrerender : public NoStatePrefetchContents::Observer,
                        public base::SupportsWeakPtr<TestPrerender> {
   public:
    TestPrerender();
  
    TestPrerender(const TestPrerender&) = delete;
    TestPrerender& operator=(const TestPrerender&) = delete;
  
    ~TestPrerender() override;
  
    TestNoStatePrefetchContents* contents() const { return contents_; }
    int number_of_loads() const { return number_of_loads_; }
    FinalStatus GetFinalStatus() const;
  
    void WaitForCreate();
    void WaitForStart();
    void WaitForStop();
  
    // Waits for |number_of_loads()| to be at least |expected_number_of_loads| OR
    // for the prerender to stop running (just to avoid a timeout if the prerender
    // dies). Note: this does not assert equality on the number of loads; the
    // caller must do it instead.
    void WaitForLoads(int expected_number_of_loads);
  
    void OnPrefetchContentsCreated(TestNoStatePrefetchContents* contents);
  
    // NoStatePrefetchContents::Observer implementation:
    void OnPrefetchStart(NoStatePrefetchContents* contents) override;
  
    void OnPrefetchStopLoading(NoStatePrefetchContents* contents) override;
  
    void OnPrefetchStop(NoStatePrefetchContents* contents) override;
  
   private:
    TestNoStatePrefetchContents* contents_;
    FinalStatus final_status_;
    int number_of_loads_;
  
    int expected_number_of_loads_;
    std::unique_ptr<base::RunLoop> load_waiter_;
  
    bool started_;
    bool stopped_;
  
    base::RunLoop create_loop_;
    base::RunLoop start_loop_;
    base::RunLoop stop_loop_;
  };
  
  // Blocks until a TestNoStatePrefetchContents has been destroyed with the given
  // final status. Should be created with a TestNoStatePrefetchContents, and then
  // WaitForDestroy should be called and its return value checked.
  class DestructionWaiter {
   public:
    // Does not own the no_state_prefetch_contents, which must outlive any call to
    // WaitForDestroy().
    DestructionWaiter(TestNoStatePrefetchContents* no_state_prefetch_contents,
                      FinalStatus expected_final_status);
  
    DestructionWaiter(const DestructionWaiter&) = delete;
    DestructionWaiter& operator=(const DestructionWaiter&) = delete;
  
    ~DestructionWaiter();
  
    // Returns true if the TestNoStatePrefetchContents was destroyed with the
    // correct final status, or false otherwise. Note this also may hang if the
    // contents is never destroyed (which will presumably cause the test to time
    // out).
    bool WaitForDestroy();
  
   private:
    class DestructionMarker : public NoStatePrefetchContents::Observer {
     public:
      // Does not own the waiter which must outlive the
      // TestNoStatePrefetchContents.
      explicit DestructionMarker(DestructionWaiter* waiter);
  
      DestructionMarker(const DestructionMarker&) = delete;
      DestructionMarker& operator=(const DestructionMarker&) = delete;
  
      ~DestructionMarker() override;
  
      void OnPrefetchStop(NoStatePrefetchContents* contents) override;
  
     private:
      DestructionWaiter* waiter_;
    };
  
    // To be called by a DestructionMarker.
    void MarkDestruction(FinalStatus reason);
  
    base::RunLoop wait_loop_;
    FinalStatus expected_final_status_;
    bool saw_correct_status_;
    std::unique_ptr<DestructionMarker> marker_;
  };
  
  // Wait until a NoStatePrefetchManager has seen a first contentful paint.
  class FirstContentfulPaintManagerWaiter
      : public NoStatePrefetchManagerObserver {
   public:
    // Create and return a pointer to a |FirstContentfulPaintManagerWaiter|. The
    // instance is owned by the |NoStatePrefetchManager|.
    static FirstContentfulPaintManagerWaiter* Create(
        NoStatePrefetchManager* manager);
  
    FirstContentfulPaintManagerWaiter(const FirstContentfulPaintManagerWaiter&) =
        delete;
    FirstContentfulPaintManagerWaiter& operator=(
        const FirstContentfulPaintManagerWaiter&) = delete;
  
    ~FirstContentfulPaintManagerWaiter() override;
  
    void OnFirstContentfulPaint() override;
  
    // Wait for a first contentful paint to be seen by our NoStatePrefetchManager.
    void Wait();
  
   private:
    FirstContentfulPaintManagerWaiter();
  
    std::unique_ptr<base::RunLoop> waiter_;
    bool saw_fcp_;
  };
  
  // NoStatePrefetchContentsFactory that uses TestNoStatePrefetchContents.
  class TestNoStatePrefetchContentsFactory
      : public NoStatePrefetchContents::Factory {
   public:
    TestNoStatePrefetchContentsFactory();
  
    TestNoStatePrefetchContentsFactory(
        const TestNoStatePrefetchContentsFactory&) = delete;
    TestNoStatePrefetchContentsFactory& operator=(
        const TestNoStatePrefetchContentsFactory&) = delete;
  
    ~TestNoStatePrefetchContentsFactory() override;
  
    std::unique_ptr<TestPrerender> ExpectNoStatePrefetchContents(
        FinalStatus final_status);
  
    void IgnoreNoStatePrefetchContents();
  
    NoStatePrefetchContents* CreateNoStatePrefetchContents(
        std::unique_ptr<NoStatePrefetchContentsDelegate> delegate,
        NoStatePrefetchManager* no_state_prefetch_manager,
        content::BrowserContext* browser_context,
        const GURL& url,
        const content::Referrer& referrer,
        const absl::optional<url::Origin>& initiator_origin,
        Origin origin) override;
  
   private:
    struct ExpectedContents {
      ExpectedContents();
      ExpectedContents(const ExpectedContents& other);
      ExpectedContents(FinalStatus final_status,
                       const base::WeakPtr<TestPrerender>& handle);
      explicit ExpectedContents(bool ignore);
      ~ExpectedContents();
  
      FinalStatus final_status = FINAL_STATUS_UNKNOWN;
      bool ignore = false;
      base::WeakPtr<TestPrerender> handle = nullptr;
    };
  
    base::circular_deque<ExpectedContents> expected_contents_queue_;
  };
  
  class PrerenderInProcessBrowserTest : virtual public InProcessBrowserTest {
   public:
    PrerenderInProcessBrowserTest();
  
    PrerenderInProcessBrowserTest(const PrerenderInProcessBrowserTest&) = delete;
    PrerenderInProcessBrowserTest& operator=(
        const PrerenderInProcessBrowserTest&) = delete;
  
    ~PrerenderInProcessBrowserTest() override;
  
    void CreatedBrowserMainParts(
        content::BrowserMainParts* browser_main_parts) override;
    void TearDownInProcessBrowserTestFixture() override;
    void SetUpOnMainThread() override;
    content::SessionStorageNamespace* GetSessionStorageNamespace() const;
  
    // Many of the file and server manipulation commands are fussy about paths
    // being relative or absolute. This makes path absolute if it is not
    // already. The path must not be empty.
    std::string MakeAbsolute(const std::string& path);
  
    bool UrlIsInNoStatePrefetchManager(const std::string& html_file) const;
    bool UrlIsInNoStatePrefetchManager(const GURL& url) const;
  
    // Convenience function to get the currently active WebContents in
    // current_browser().
    content::WebContents* GetActiveWebContents() const;
  
    NoStatePrefetchManager* GetNoStatePrefetchManager() const;
  
    TestNoStatePrefetchContents* GetNoStatePrefetchContentsFor(
        const GURL& url) const;
  
    // Set up an HTTPS server.
    void UseHttpsSrcServer();
  
    // Returns the currently active server. See |UseHttpsSrcServer|.
    net::EmbeddedTestServer* src_server();
  
    safe_browsing::TestSafeBrowsingServiceFactory* safe_browsing_factory() const {
      return safe_browsing_factory_.get();
    }
  
    safe_browsing::FakeSafeBrowsingDatabaseManager*
    GetFakeSafeBrowsingDatabaseManager();
  
    TestNoStatePrefetchContentsFactory* no_state_prefetch_contents_factory()
        const {
      return no_state_prefetch_contents_factory_;
    }
  
    void set_autostart_test_server(bool value) { autostart_test_server_ = value; }
  
    void set_browser(Browser* browser) { explicitly_set_browser_ = browser; }
  
    Browser* current_browser() const {
      return explicitly_set_browser_ ? explicitly_set_browser_ : browser();
    }
  
    const base::HistogramTester& histogram_tester() { return histogram_tester_; }
  
    // Returns a string for pattern-matching TaskManager tab entries.
    std::u16string MatchTaskManagerTab(const char* page_title);
  
    // Returns a string for pattern-matching TaskManager prerender entries.
    std::u16string MatchTaskManagerPrerender(const char* page_title);
  
    // Returns a GURL for an EmbeddedTestServer that will serves the file
    // |url_file| with |replacement_text| replacing |replacement_variable|.
    GURL GetURLWithReplacement(const std::string& url_file,
                               const std::string& replacement_variable,
                               const std::string& replacement_text);
  
   protected:
    // For each FinalStatus in |expected_final_status_queue| creates a prerender
    // that is going to verify the correctness of its FinalStatus upon
    // destruction. Waits for creation of the first NoStatePrefetchContents.
    std::vector<std::unique_ptr<TestPrerender>> NavigateWithPrerenders(
        const GURL& loader_url,
        const std::vector<FinalStatus>& expected_final_status_queue);
  
    // Creates the URL that instructs the test server to substitute the text
    // |replacement_variable| in the contents of the file pointed to by
    // |loader_path| with |url_to_prerender|. Also appends the |loader_query| to
    // the URL.
    GURL ServeLoaderURL(const std::string& loader_path,
                        const std::string& replacement_variable,
                        const GURL& url_to_prerender,
                        const std::string& loader_query,
                        const std::string& hostname_alternative = std::string());
  
    // A variation of the above that allows for overriding the hostname.
    GURL ServeLoaderURLWithHostname(const std::string& loader_path,
                                    const std::string& replacement_variable,
                                    const GURL& url_to_prerender,
                                    const std::string& loader_query,
                                    const std::string& hostname);
  
    uint32_t GetRequestCount(const GURL& url);
    void WaitForRequestCount(const GURL& url, uint32_t expected_count);
  
   private:
    void MonitorResourceRequest(const net::test_server::HttpRequest& request);
    std::unique_ptr<ExternalProtocolHandler::Delegate>
        external_protocol_handler_delegate_;
    std::unique_ptr<safe_browsing::TestSafeBrowsingServiceFactory>
        safe_browsing_factory_;
    TestNoStatePrefetchContentsFactory* no_state_prefetch_contents_factory_;
    Browser* explicitly_set_browser_;
    bool autostart_test_server_;
    base::HistogramTester histogram_tester_;
    std::unique_ptr<net::EmbeddedTestServer> https_src_server_;
  
    // The following are guarded by |lock_| as they're used on multiple threads.
    std::map<GURL, uint32_t> requests_;
    GURL waiting_url_;
    uint32_t waiting_count_ = 0;
    base::OnceClosure waiting_closure_;
    base::Lock lock_;
  };
  
  }  // namespace test_utils
  
  }  // namespace prerender
  
  #endif  // CHROME_BROWSER_PREFETCH_NO_STATE_PREFETCH_PRERENDER_TEST_UTILS_H_
  