1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_TAB_HELPER_H_
6 #define CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_TAB_HELPER_H_
7 
8 #include <memory>
9 #include <string>
10 #include <vector>
11 
12 #include "base/macros.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/offline_pages/offline_page_utils.h"
16 #include "chrome/common/mhtml_page_notifier.mojom.h"
17 #include "components/offline_pages/core/request_header/offline_page_header.h"
18 #include "content/public/browser/web_contents_observer.h"
19 #include "content/public/browser/web_contents_receiver_set.h"
20 #include "content/public/browser/web_contents_user_data.h"
21 #include "services/service_manager/public/cpp/binder_registry.h"
22 #include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-forward.h"
23 #include "url/gurl.h"
24 
25 namespace content {
26 class WebContents;
27 }
28 
29 namespace offline_pages {
30 
31 struct OfflinePageItem;
32 class PrefetchService;
33 
34 // This enum is used for UMA reporting. It contains all possible trusted states
35 // of the offline page.
36 // NOTE: because this is used for UMA reporting, these values should not be
37 // changed or reused; new values should be ended immediately before the MAX
38 // value. Make sure to update the histogram enum (OfflinePageTrustedState in
39 // enums.xml) accordingly.
40 enum class OfflinePageTrustedState {
41   // Trusted because the archive file is in internal directory.
42   TRUSTED_AS_IN_INTERNAL_DIR,
43   // Trusted because the archive file is in public directory without
44   // modification.
45   TRUSTED_AS_UNMODIFIED_AND_IN_PUBLIC_DIR,
46   // No trusted because the archive file is in public directory and it is
47   // modified.
48   UNTRUSTED,
49   TRUSTED_STATE_MAX
50 };
51 
52 // Per-tab class that monitors the navigations and stores the necessary info
53 // to facilitate the synchronous access to offline information.
54 class OfflinePageTabHelper
55     : public content::WebContentsObserver,
56       public content::WebContentsUserData<OfflinePageTabHelper>,
57       public offline_pages::mojom::MhtmlPageNotifier {
58  public:
59   ~OfflinePageTabHelper() override;
60 
61   // MhtmlPageNotifier overrides.
62   void NotifyMhtmlPageLoadAttempted(blink::mojom::MHTMLLoadResult result,
63                                     const GURL& main_frame_url,
64                                     base::Time date) override;
65 
66   void SetOfflinePage(const OfflinePageItem& offline_page,
67                       const OfflinePageHeader& offline_header,
68                       OfflinePageTrustedState trusted_state,
69                       bool is_offline_preview);
70 
71   void ClearOfflinePage();
72 
offline_page()73   OfflinePageItem* offline_page() { return offline_info_.offline_page.get(); }
74 
offline_header()75   const OfflinePageHeader& offline_header() const {
76     return offline_info_.offline_header;
77   }
78 
trusted_state()79   OfflinePageTrustedState trusted_state() const {
80     return offline_info_.trusted_state;
81   }
82 
83   // Returns whether a trusted offline page is being displayed.
84   bool IsShowingTrustedOfflinePage() const;
85 
86   // Returns nullptr if the page is not an offline preview. Returns the
87   // OfflinePageItem related to the page if the page is an offline preview.
88   const OfflinePageItem* GetOfflinePreviewItem() const;
89 
90   // Returns provisional offline page since actual navigation does not happen
91   // during unit tests.
92   const OfflinePageItem* GetOfflinePageForTest() const;
93 
94   // True if an offline page is loading, but has not committed.
95   bool IsLoadingOfflinePage() const;
96 
97   // Returns trusted state of provisional offline page.
98   OfflinePageTrustedState GetTrustedStateForTest() const;
99 
100   // Sets the target frame, useful for unit testing the MhtmlPageNotifier
101   // interface.
102   void SetCurrentTargetFrameForTest(
103       content::RenderFrameHost* render_frame_host);
104 
105   // Helper function which normally should only be called by
106   // OfflinePageUtils::ScheduleDownload to do the work. This is because we need
107   // to ensure |web_contents| is still valid after returning from the
108   // asynchronous call of duplicate checking function. The lifetime of
109   // OfflinePageTabHelper instance is tied with the associated |web_contents|
110   // and thus the callback will be automatically invalidated if |web_contents|
111   // is gone.
112   void ScheduleDownloadHelper(content::WebContents* web_contents,
113                               const std::string& name_space,
114                               const GURL& url,
115                               OfflinePageUtils::DownloadUIActionFlags ui_action,
116                               const std::string& request_origin);
117 
118  private:
119   friend class content::WebContentsUserData<OfflinePageTabHelper>;
120 
121   // Contains the info about the offline page being loaded.
122   struct LoadedOfflinePageInfo {
123     LoadedOfflinePageInfo();
124     ~LoadedOfflinePageInfo();
125 
126     // Constructs a valid but untrusted LoadedOfflinePageInfo with |url| as the
127     // online URL.
128     static LoadedOfflinePageInfo MakeUntrusted();
129 
130     LoadedOfflinePageInfo& operator=(LoadedOfflinePageInfo&& other);
131     LoadedOfflinePageInfo(LoadedOfflinePageInfo&& other);
132 
133     // The cached copy of OfflinePageItem. Note that if |is_trusted| is false,
134     // offline_page may contain information derived from the MHTML itself and
135     // should be exposed to the user as untrusted.
136     std::unique_ptr<OfflinePageItem> offline_page;
137 
138     // The offline header that is provided when offline page is loaded.
139     OfflinePageHeader offline_header;
140 
141     // The trusted state of the page.
142     OfflinePageTrustedState trusted_state;
143 
144     // Whether the page is an offline preview. Offline page previews are shown
145     // when a user's effective connection type is prohibitively slow.
146     bool is_showing_offline_preview = false;
147 
148     // Returns true if this contains an offline page.  When constructed,
149     // LoadedOfflinePageInfo objects are invalid until filled with an offline
150     // page.
151     bool IsValid() const;
152 
153     void Clear();
154   };
155 
156   explicit OfflinePageTabHelper(content::WebContents* web_contents);
157 
158   // Overridden from content::WebContentsObserver:
159   void DidStartNavigation(
160       content::NavigationHandle* navigation_handle) override;
161   void DidFinishNavigation(
162       content::NavigationHandle* navigation_handle) override;
163 
164   // Finalize the offline info when the navigation is done.
165   void FinalizeOfflineInfo(content::NavigationHandle* navigation_handle);
166 
167   void ReportOfflinePageMetrics();
168 
169   // Report the metrics essential to PrefetchService.
170   void ReportPrefetchMetrics(content::NavigationHandle* navigation_handle);
171 
172   // Reload the URL in order to fetch the offline page on certain net errors.
173   void TryLoadingOfflinePageOnNetError(
174       content::NavigationHandle* navigation_handle);
175 
176   // Creates an offline info with an invalid offline ID and the given URL.
177   LoadedOfflinePageInfo MakeUntrustedOfflineInfo(const GURL& url);
178 
179   void SelectPagesForURLDone(const std::vector<OfflinePageItem>& offline_pages);
180 
181   void DuplicateCheckDoneForScheduleDownload(
182       content::WebContents* web_contents,
183       const std::string& name_space,
184       const GURL& url,
185       OfflinePageUtils::DownloadUIActionFlags ui_action,
186       const std::string& request_origin,
187       OfflinePageUtils::DuplicateCheckResult result);
188   void DoDownloadPageLater(content::WebContents* web_contents,
189                            const std::string& name_space,
190                            const GURL& url,
191                            OfflinePageUtils::DownloadUIActionFlags ui_action,
192                            const std::string& request_origin);
193 
194   // The provisional info about the offline page being loaded. This is set when
195   // the offline interceptor decides to serve the offline page and it will be
196   // moved to |offline_info_| once the navigation is committed without error.
197   LoadedOfflinePageInfo provisional_offline_info_;
198 
199   // The info about offline page being loaded. This is set from
200   // |provisional_offline_info_| when the navigation is committed without error.
201   // This can be used to by the Tab to synchronously ask about the offline
202   // info.
203   LoadedOfflinePageInfo offline_info_;
204 
205   bool reloading_url_on_net_error_ = false;
206 
207   // Service, outlives this object.
208   PrefetchService* prefetch_service_ = nullptr;
209 
210   // TODO(crbug.com/827215): We only really want interface messages for the main
211   // frame but this is not easily done with the current helper classes.
212   content::WebContentsFrameReceiverSet<mojom::MhtmlPageNotifier>
213       mhtml_page_notifier_receivers_;
214 
215   base::WeakPtrFactory<OfflinePageTabHelper> weak_ptr_factory_{this};
216 
217   WEB_CONTENTS_USER_DATA_KEY_DECL();
218 
219   DISALLOW_COPY_AND_ASSIGN(OfflinePageTabHelper);
220 };
221 
222 }  // namespace offline_pages
223 
224 #endif  // CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_TAB_HELPER_H_
225