1 // Copyright 2013 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_RENDERER_NET_NET_ERROR_HELPER_CORE_H_
6 #define CHROME_RENDERER_NET_NET_ERROR_HELPER_CORE_H_
7 
8 #include <memory>
9 #include <string>
10 
11 #include "base/callback.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/timer/timer.h"
14 #include "build/build_config.h"
15 #include "components/error_page/common/error.h"
16 #include "components/error_page/common/localized_error.h"
17 #include "components/error_page/common/net_error_info.h"
18 #include "net/base/net_errors.h"
19 #include "url/gurl.h"
20 
21 #if defined(OS_ANDROID)
22 #include "chrome/renderer/net/available_offline_content_helper.h"
23 #include "chrome/renderer/net/page_auto_fetcher_helper_android.h"
24 #endif
25 
26 namespace content {
27 class RenderFrame;
28 }
29 namespace error_page {
30 struct ErrorPageParams;
31 }
32 
33 // Class that contains the logic for how the NetErrorHelper.  This allows for
34 // testing the logic without a RenderView or WebFrame, which are difficult to
35 // mock, and for testing races which are impossible to reliably reproduce
36 // with real RenderViews or WebFrames.
37 class NetErrorHelperCore {
38  public:
39   enum FrameType {
40     MAIN_FRAME,
41     SUB_FRAME,
42   };
43 
44   enum PageType {
45     NON_ERROR_PAGE,
46     ERROR_PAGE,
47   };
48 
49   enum Button {
50     NO_BUTTON,
51     RELOAD_BUTTON,
52     MORE_BUTTON,
53     EASTER_EGG,
54     SHOW_CACHED_COPY_BUTTON,  // "Google cached copy" button label experiment.
55     DIAGNOSE_ERROR,
56     DOWNLOAD_BUTTON,  // "Download page later" experiment.
57   };
58 
59   // The Delegate handles all interaction with the RenderView, WebFrame, and
60   // the network, as well as the generation of error pages.
61   class Delegate {
62    public:
63     // Generates an error page's HTML for the given error.
64     virtual error_page::LocalizedError::PageState GenerateLocalizedErrorPage(
65         const error_page::Error& error,
66         bool is_failed_post,
67         bool can_show_network_diagnostics_dialog,
68         std::unique_ptr<error_page::ErrorPageParams> params,
69         std::string* html) const = 0;
70 
71     // Loads the given HTML in the frame for use as an error page.
72     virtual void LoadErrorPage(const std::string& html,
73                                const GURL& failed_url) = 0;
74 
75     // Create extra Javascript bindings in the error page. Will only be invoked
76     // after an error page has finished loading.
77     virtual void EnablePageHelperFunctions() = 0;
78 
79     // Updates the currently displayed error page with a new error code.  The
80     // currently displayed error page must have finished loading, and must have
81     // been generated by a call to GenerateLocalizedErrorPage.
82     virtual error_page::LocalizedError::PageState UpdateErrorPage(
83         const error_page::Error& error,
84         bool is_failed_post,
85         bool can_show_network_diagnostics_dialog) = 0;
86 
87     // Tell the currently displayed error page about the user's current easter
88     // egg game high score (from the user's synced preferences).  The currently
89     // displayed error page must have finished loading, and must have been
90     // generated by a call to GenerateLocalizedErrorPage.
91     virtual void InitializeErrorPageEasterEggHighScore(int high_score) = 0;
92 
93     // Request the current easter egg high score stored in the user's synced
94     // preferences from the browser.  The delegate should call
95     // OnEasterEggHighScoreReceived() with the response.
96     virtual void RequestEasterEggHighScore() = 0;
97 
98     // Fetches an error page and calls into OnErrorPageFetched when done.  Any
99     // previous fetch must either be canceled or finished before calling.  Can't
100     // be called synchronously after a previous fetch completes.
101     virtual void FetchNavigationCorrections(
102         const GURL& navigation_correction_url,
103         const std::string& navigation_correction_request_body) = 0;
104 
105     // Cancels fetching navigation corrections.  Does nothing if no fetch is
106     // ongoing.
107     virtual void CancelFetchNavigationCorrections() = 0;
108 
109     // Sends an HTTP request used to track which link on the page was clicked to
110     // the navigation correction service.
111     virtual void SendTrackingRequest(
112         const GURL& tracking_url,
113         const std::string& tracking_request_body) = 0;
114 
115     // Starts a reload of the observed frame.
116     virtual void ReloadFrame() = 0;
117 
118     // Run the platform diagnostics too for the specified URL.
119     virtual void DiagnoseError(const GURL& page_url) = 0;
120 
121     // Schedule to download the page at a later time.
122     virtual void DownloadPageLater() = 0;
123 
124     // Inform that download button is being shown in the error page.
125     virtual void SetIsShowingDownloadButton(bool show) = 0;
126 
127     // Signals that offline content is available.
128     virtual void OfflineContentAvailable(
129         bool list_visible_by_prefs,
130         const std::string& offline_content_json) = 0;
131 
132     // Returns the render frame associated with NetErrorHelper.
133     virtual content::RenderFrame* GetRenderFrame() = 0;
134 
135 #if defined(OS_ANDROID)
136     // Called after an attempt to automatically schedule a background fetch for
137     // a page with a network error.
138     virtual void SetAutoFetchState(
139         chrome::mojom::OfflinePageAutoFetcherScheduleResult result) = 0;
140 #endif
141 
142    protected:
~Delegate()143     virtual ~Delegate() {}
144   };
145 
146   struct NavigationCorrectionParams {
147     NavigationCorrectionParams();
148     NavigationCorrectionParams(const NavigationCorrectionParams& other);
149     ~NavigationCorrectionParams();
150 
151     // URL used both for getting the suggestions and tracking clicks.
152     GURL url;
153 
154     std::string language;
155     std::string country_code;
156     std::string api_key;
157     GURL search_url;
158   };
159 
160   NetErrorHelperCore(Delegate* delegate,
161                      bool auto_reload_enabled,
162                      bool is_visible);
163   ~NetErrorHelperCore();
164 
165   // Sets values in |pending_error_page_info_|. If |error_html| is not null, it
166   // initializes |error_html| with the HTML of an error page in response to
167   // |error|.  Updates internals state with the assumption the page will be
168   // loaded immediately.
169   void PrepareErrorPage(FrameType frame_type,
170                         const error_page::Error& error,
171                         bool is_failed_post,
172                         std::string* error_html);
173 
174   // These methods handle tracking the actual state of the page.
175   void OnStartLoad(FrameType frame_type, PageType page_type);
176   void OnCommitLoad(FrameType frame_type, const GURL& url);
177   void OnFinishLoad(FrameType frame_type);
178   void OnStop();
179   void OnWasShown();
180   void OnWasHidden();
181 
182   void CancelPendingFetches();
183 
184   // Called when an error page have has been retrieved over the network.  |html|
185   // must be an empty string on error.
186   void OnNavigationCorrectionsFetched(const std::string& corrections,
187                                       bool is_rtl);
188 
189   // Notifies |this| that network error information from the browser process
190   // has been received.
191   void OnNetErrorInfo(error_page::DnsProbeStatus status);
192 
193   // Notifies |this| if it can use a local error diagnostics service through its
194   // delegate.
195   void OnSetCanShowNetworkDiagnosticsDialog(
196       bool can_show_network_diagnostics_dialog);
197 
198   void OnSetNavigationCorrectionInfo(const GURL& navigation_correction_url,
199                                      const std::string& language,
200                                      const std::string& country_code,
201                                      const std::string& api_key,
202                                      const GURL& search_url);
203 
204   // Notifies |this| about the current high score that's saved in the user's
205   // synced preferences.
206   void OnEasterEggHighScoreReceived(int high_score);
207 
208   // Notifies |this| that the network's online status changed.
209   // Handler for NetworkStateChanged notification from the browser process. If
210   // the network state changes to online, this method is responsible for
211   // starting the auto-reload process.
212   //
213   // Warning: if there are many tabs sitting at an error page, this handler will
214   // be run at the same time for each of their top-level renderframes, which can
215   // cause many requests to be started at the same time. There's no current
216   // protection against this kind of "reload storm".
217   //
218   // TODO(rdsmith): prevent the reload storm.
219   void NetworkStateChanged(bool online);
220 
auto_reload_count()221   int auto_reload_count() const { return auto_reload_count_; }
222 
223   bool ShouldSuppressErrorPage(FrameType frame_type, const GURL& url);
224 
set_timer_for_testing(std::unique_ptr<base::OneShotTimer> timer)225   void set_timer_for_testing(std::unique_ptr<base::OneShotTimer> timer) {
226     auto_reload_timer_ = std::move(timer);
227   }
228 
229 #if defined(OS_ANDROID)
230   void SetPageAutoFetcherHelperForTesting(
231       std::unique_ptr<PageAutoFetcherHelper> page_auto_fetcher_helper);
232 #endif
233 
234   // Execute the effect of pressing the specified button.
235   // Note that the visual effects of the 'MORE' button are taken
236   // care of in JavaScript.
237   void ExecuteButtonPress(Button button);
238 
239   // Reports to the correction service that the link with the given tracking
240   // ID was clicked.  Only pages generated with information from the service
241   // have links with tracking IDs.  Duplicate requests from the same page with
242   // the same tracking ID are ignored.
243   void TrackClick(int tracking_id);
244 
245   // Opens a suggested offline item.
246   void LaunchOfflineItem(const std::string& id, const std::string& name_space);
247 
248   // Shows all available offline content.
249   void LaunchDownloadsPage();
250 
251   void CancelSavePage();
252   void SavePageForLater();
253 
254   // Signals the user changed the visibility of the offline content list in the
255   // dino page.
256   void ListVisibilityChanged(bool is_visible);
257 
258  private:
259   struct ErrorPageInfo;
260 
261   // Sets values in |pending_error_page_info| for a main frame error page. If
262   // |error_html| is not null, it also fetches the string containing the error
263   // page HTML, and sets error_html to it. Depending on
264   // |pending_error_page_info|, may use the navigation correction service, or
265   // show a DNS probe error page.  May modify |pending_error_page_info|.
266   void PrepareErrorPageForMainFrame(ErrorPageInfo* pending_error_page_info,
267                                     std::string* error_html);
268 
269   // Updates the currently displayed error page with a new error based on the
270   // most recently received DNS probe result.  The page must have finished
271   // loading before this is called.
272   void UpdateErrorPage();
273 
274   // Called after the error page is loaded and is showing the final error code.
275   // This is either called on page load, or after a DNS probe finishes.
276   void ErrorPageLoadedWithFinalErrorCode();
277 
278   error_page::Error GetUpdatedError(const ErrorPageInfo& error_info) const;
279 
280   void Reload();
281   bool MaybeStartAutoReloadTimer();
282   void StartAutoReloadTimer();
283   void AutoReloadTimerFired();
284   void PauseAutoReloadTimer();
285 
286   static bool IsReloadableError(const ErrorPageInfo& info);
287 
288   Delegate* delegate_;
289 
290   // The last DnsProbeStatus received from the browser.
291   error_page::DnsProbeStatus last_probe_status_;
292 
293   // Information for the provisional / "pre-provisional" error page.  NULL when
294   // there's no page pending, or the pending page is not an error page.
295   std::unique_ptr<ErrorPageInfo> pending_error_page_info_;
296 
297   // Information for the committed error page.  NULL when the committed page is
298   // not an error page.
299   std::unique_ptr<ErrorPageInfo> committed_error_page_info_;
300 
301   bool can_show_network_diagnostics_dialog_;
302 
303   NavigationCorrectionParams navigation_correction_params_;
304 
305   // True if auto-reload is enabled at all.
306   const bool auto_reload_enabled_;
307 
308   // Timer used to wait for auto-reload attempts.
309   std::unique_ptr<base::OneShotTimer> auto_reload_timer_;
310 
311   // True if the auto-reload timer would be running but is waiting for an
312   // offline->online network transition.
313   bool auto_reload_paused_;
314 
315   // Whether an auto-reload-initiated Reload() attempt is in flight.
316   bool auto_reload_in_flight_;
317 
318   // True if there is an uncommitted-but-started load, error page or not. This
319   // is used to inhibit starting auto-reload when an error page finishes, in
320   // case this happens:
321   //   Error page starts
322   //   Error page commits
323   //   Non-error page starts
324   //   Error page finishes
325   bool uncommitted_load_started_;
326 
327   // Is the browser online?
328   bool online_;
329 
330   // Is the RenderFrame this object is observing visible?
331   bool visible_;
332 
333   int auto_reload_count_;
334 
335   // This value is set only when a navigation has been initiated from
336   // the error page.  It is used to detect when such navigations result
337   // in errors.
338   Button navigation_from_button_;
339 
340 #if defined(OS_ANDROID)
341   AvailableOfflineContentHelper available_content_helper_;
342   std::unique_ptr<PageAutoFetcherHelper> page_auto_fetcher_helper_;
343 #endif
344 };
345 
346 #endif  // CHROME_RENDERER_NET_NET_ERROR_HELPER_CORE_H_
347