• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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 #include "chrome/browser/prerender/prerender_tab_helper.h"
6 
7 #include "base/bind.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/time/time.h"
11 #include "chrome/browser/password_manager/password_manager.h"
12 #include "chrome/browser/predictors/logged_in_predictor_table.h"
13 #include "chrome/browser/prerender/prerender_histograms.h"
14 #include "chrome/browser/prerender/prerender_local_predictor.h"
15 #include "chrome/browser/prerender/prerender_manager.h"
16 #include "chrome/browser/prerender/prerender_manager_factory.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/navigation_details.h"
20 #include "content/public/browser/navigation_entry.h"
21 #include "content/public/browser/render_view_host.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/browser/web_contents_view.h"
24 #include "content/public/common/frame_navigate_params.h"
25 #include "skia/ext/platform_canvas.h"
26 #include "third_party/skia/include/core/SkBitmap.h"
27 #include "ui/gfx/rect.h"
28 
29 using content::WebContents;
30 
31 DEFINE_WEB_CONTENTS_USER_DATA_KEY(prerender::PrerenderTabHelper);
32 
33 namespace prerender {
34 
35 namespace {
36 
ReportTabHelperURLSeenToLocalPredictor(PrerenderManager * prerender_manager,const GURL & url,WebContents * web_contents)37 void ReportTabHelperURLSeenToLocalPredictor(
38     PrerenderManager* prerender_manager,
39     const GURL& url,
40     WebContents* web_contents) {
41   if (!prerender_manager)
42     return;
43   PrerenderLocalPredictor* local_predictor =
44       prerender_manager->local_predictor();
45   if (!local_predictor)
46     return;
47   local_predictor->OnTabHelperURLSeen(url, web_contents);
48 }
49 
50 }  // namespace
51 
52 // Helper class to compute pixel-based stats on the paint progress
53 // between when a prerendered page is swapped in and when the onload event
54 // fires.
55 class PrerenderTabHelper::PixelStats {
56  public:
PixelStats(PrerenderTabHelper * tab_helper)57   explicit PixelStats(PrerenderTabHelper* tab_helper) :
58       bitmap_web_contents_(NULL),
59       weak_factory_(this),
60       tab_helper_(tab_helper) {
61   }
62 
63   // Reasons why we need to fetch bitmaps: either a prerender was swapped in,
64   // or a prerendered page has finished loading.
65   enum BitmapType {
66       BITMAP_SWAP_IN,
67       BITMAP_ON_LOAD
68   };
69 
GetBitmap(BitmapType bitmap_type,WebContents * web_contents)70   void GetBitmap(BitmapType bitmap_type, WebContents* web_contents) {
71     if (bitmap_type == BITMAP_SWAP_IN) {
72       bitmap_.reset();
73       bitmap_web_contents_ = web_contents;
74     }
75 
76     if (bitmap_type == BITMAP_ON_LOAD && bitmap_web_contents_ != web_contents)
77       return;
78 
79     if (!web_contents || !web_contents->GetView() ||
80         !web_contents->GetRenderViewHost()) {
81       return;
82     }
83 
84     web_contents->GetRenderViewHost()->CopyFromBackingStore(
85         gfx::Rect(),
86         gfx::Size(),
87         base::Bind(&PrerenderTabHelper::PixelStats::HandleBitmapResult,
88                    weak_factory_.GetWeakPtr(),
89                    bitmap_type,
90                    web_contents));
91   }
92 
93  private:
HandleBitmapResult(BitmapType bitmap_type,WebContents * web_contents,bool succeeded,const SkBitmap & canvas_bitmap)94   void HandleBitmapResult(BitmapType bitmap_type,
95                           WebContents* web_contents,
96                           bool succeeded,
97                           const SkBitmap& canvas_bitmap) {
98     scoped_ptr<SkBitmap> bitmap;
99     if (succeeded) {
100       // TODO(nick): This copy may now be unnecessary.
101       bitmap.reset(new SkBitmap());
102       canvas_bitmap.copyTo(bitmap.get(), SkBitmap::kARGB_8888_Config);
103     }
104 
105     if (bitmap_web_contents_ != web_contents)
106       return;
107 
108     if (bitmap_type == BITMAP_SWAP_IN)
109       bitmap_.swap(bitmap);
110 
111     if (bitmap_type == BITMAP_ON_LOAD) {
112       PrerenderManager* prerender_manager =
113           tab_helper_->MaybeGetPrerenderManager();
114       if (prerender_manager) {
115         prerender_manager->RecordFractionPixelsFinalAtSwapin(
116             web_contents, CompareBitmaps(bitmap_.get(), bitmap.get()));
117       }
118       bitmap_.reset();
119       bitmap_web_contents_ = NULL;
120     }
121   }
122 
123   // Helper comparing two bitmaps of identical size.
124   // Returns a value < 0.0 if there is an error, and otherwise, a double in
125   // [0, 1] indicating the fraction of pixels that are the same.
CompareBitmaps(SkBitmap * bitmap1,SkBitmap * bitmap2)126   double CompareBitmaps(SkBitmap* bitmap1, SkBitmap* bitmap2) {
127     if (!bitmap1 || !bitmap2) {
128       return -2.0;
129     }
130     if (bitmap1->width() != bitmap2->width() ||
131         bitmap1->height() != bitmap2->height()) {
132       return -1.0;
133     }
134     int pixels = bitmap1->width() * bitmap1->height();
135     int same_pixels = 0;
136     for (int y = 0; y < bitmap1->height(); ++y) {
137       for (int x = 0; x < bitmap1->width(); ++x) {
138         if (bitmap1->getColor(x, y) == bitmap2->getColor(x, y))
139           same_pixels++;
140       }
141     }
142     return static_cast<double>(same_pixels) / static_cast<double>(pixels);
143   }
144 
145   // Bitmap of what the last swapped in prerendered tab looked like at swapin,
146   // and the WebContents that it was swapped into.
147   scoped_ptr<SkBitmap> bitmap_;
148   WebContents* bitmap_web_contents_;
149 
150   base::WeakPtrFactory<PixelStats> weak_factory_;
151 
152   PrerenderTabHelper* tab_helper_;
153 };
154 
155 // static
CreateForWebContentsWithPasswordManager(content::WebContents * web_contents,PasswordManager * password_manager)156 void PrerenderTabHelper::CreateForWebContentsWithPasswordManager(
157     content::WebContents* web_contents,
158     PasswordManager* password_manager) {
159   if (!FromWebContents(web_contents)) {
160     web_contents->SetUserData(UserDataKey(),
161                               new PrerenderTabHelper(web_contents,
162                                                      password_manager));
163   }
164 }
165 
PrerenderTabHelper(content::WebContents * web_contents,PasswordManager * password_manager)166 PrerenderTabHelper::PrerenderTabHelper(content::WebContents* web_contents,
167                                        PasswordManager* password_manager)
168     : content::WebContentsObserver(web_contents),
169       weak_factory_(this) {
170   password_manager->AddSubmissionCallback(
171       base::Bind(&PrerenderTabHelper::PasswordSubmitted,
172                  weak_factory_.GetWeakPtr()));
173 }
174 
~PrerenderTabHelper()175 PrerenderTabHelper::~PrerenderTabHelper() {
176 }
177 
ProvisionalChangeToMainFrameUrl(const GURL & url,content::RenderViewHost * render_view_host)178 void PrerenderTabHelper::ProvisionalChangeToMainFrameUrl(
179     const GURL& url,
180     content::RenderViewHost* render_view_host) {
181   url_ = url;
182   RecordEvent(EVENT_MAINFRAME_CHANGE);
183   RecordEventIfLoggedInURL(EVENT_MAINFRAME_CHANGE_DOMAIN_LOGGED_IN, url);
184   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
185   if (!prerender_manager)
186     return;
187   if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL))
188     return;
189   prerender_manager->MarkWebContentsAsNotPrerendered(web_contents());
190   ReportTabHelperURLSeenToLocalPredictor(prerender_manager, url,
191                                          web_contents());
192 }
193 
DidCommitProvisionalLoadForFrame(int64 frame_id,const base::string16 & frame_unique_name,bool is_main_frame,const GURL & validated_url,content::PageTransition transition_type,content::RenderViewHost * render_view_host)194 void PrerenderTabHelper::DidCommitProvisionalLoadForFrame(
195     int64 frame_id,
196     const base::string16& frame_unique_name,
197     bool is_main_frame,
198     const GURL& validated_url,
199     content::PageTransition transition_type,
200     content::RenderViewHost* render_view_host) {
201   if (!is_main_frame)
202     return;
203   RecordEvent(EVENT_MAINFRAME_COMMIT);
204   RecordEventIfLoggedInURL(EVENT_MAINFRAME_COMMIT_DOMAIN_LOGGED_IN,
205                            validated_url);
206   url_ = validated_url;
207   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
208   if (!prerender_manager)
209     return;
210   if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL))
211     return;
212   prerender_manager->RecordNavigation(validated_url);
213   ReportTabHelperURLSeenToLocalPredictor(prerender_manager, validated_url,
214                                          web_contents());
215 }
216 
DidStopLoading(content::RenderViewHost * render_view_host)217 void PrerenderTabHelper::DidStopLoading(
218     content::RenderViewHost* render_view_host) {
219   // Compute the PPLT metric and report it in a histogram, if needed.
220   // We include pages that are still prerendering and have just finished
221   // loading -- PrerenderManager will sort this out and handle it correctly
222   // (putting those times into a separate histogram).
223   if (!pplt_load_start_.is_null()) {
224     double fraction_elapsed_at_swapin = -1.0;
225     base::TimeTicks now = base::TimeTicks::Now();
226     if (!actual_load_start_.is_null()) {
227       double plt = (now - actual_load_start_).InMillisecondsF();
228       if (plt > 0.0) {
229         fraction_elapsed_at_swapin = 1.0 -
230             (now - pplt_load_start_).InMillisecondsF() / plt;
231       } else {
232         fraction_elapsed_at_swapin = 1.0;
233       }
234       DCHECK_GE(fraction_elapsed_at_swapin, 0.0);
235       DCHECK_LE(fraction_elapsed_at_swapin, 1.0);
236     }
237     PrerenderManager::RecordPerceivedPageLoadTime(
238         now - pplt_load_start_, fraction_elapsed_at_swapin, web_contents(),
239         url_);
240     if (IsPrerendered() && pixel_stats_.get())
241       pixel_stats_->GetBitmap(PixelStats::BITMAP_ON_LOAD, web_contents());
242   }
243 
244   // Reset the PPLT metric.
245   pplt_load_start_ = base::TimeTicks();
246   actual_load_start_ = base::TimeTicks();
247 }
248 
DidStartProvisionalLoadForFrame(int64 frame_id,int64 parent_frame_id,bool is_main_frame,const GURL & validated_url,bool is_error_page,bool is_iframe_srcdoc,content::RenderViewHost * render_view_host)249 void PrerenderTabHelper::DidStartProvisionalLoadForFrame(
250       int64 frame_id,
251       int64 parent_frame_id,
252       bool is_main_frame,
253       const GURL& validated_url,
254       bool is_error_page,
255       bool is_iframe_srcdoc,
256       content::RenderViewHost* render_view_host) {
257   if (is_main_frame) {
258     // Record the beginning of a new PPLT navigation.
259     pplt_load_start_ = base::TimeTicks::Now();
260     actual_load_start_ = base::TimeTicks();
261   }
262 }
263 
PasswordSubmitted(const autofill::PasswordForm & form)264 void PrerenderTabHelper::PasswordSubmitted(const autofill::PasswordForm& form) {
265   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
266   if (prerender_manager) {
267     prerender_manager->RecordLikelyLoginOnURL(form.origin);
268     RecordEvent(EVENT_LOGIN_ACTION_ADDED);
269     if (form.password_value.empty())
270       RecordEvent(EVENT_LOGIN_ACTION_ADDED_PW_EMPTY);
271   }
272 }
273 
MaybeGetPrerenderManager() const274 PrerenderManager* PrerenderTabHelper::MaybeGetPrerenderManager() const {
275   return PrerenderManagerFactory::GetForProfile(
276       Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
277 }
278 
IsPrerendering()279 bool PrerenderTabHelper::IsPrerendering() {
280   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
281   if (!prerender_manager)
282     return false;
283   return prerender_manager->IsWebContentsPrerendering(web_contents(), NULL);
284 }
285 
IsPrerendered()286 bool PrerenderTabHelper::IsPrerendered() {
287   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
288   if (!prerender_manager)
289     return false;
290   return prerender_manager->IsWebContentsPrerendered(web_contents(), NULL);
291 }
292 
PrerenderSwappedIn()293 void PrerenderTabHelper::PrerenderSwappedIn() {
294   // Ensure we are not prerendering any more.
295   DCHECK(!IsPrerendering());
296   if (pplt_load_start_.is_null()) {
297     // If we have already finished loading, report a 0 PPLT.
298     PrerenderManager::RecordPerceivedPageLoadTime(base::TimeDelta(), 1.0,
299                                                   web_contents(), url_);
300     PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
301     if (prerender_manager)
302       prerender_manager->RecordFractionPixelsFinalAtSwapin(web_contents(), 1.0);
303   } else {
304     // If we have not finished loading yet, record the actual load start, and
305     // rebase the start time to now.
306     actual_load_start_ = pplt_load_start_;
307     pplt_load_start_ = base::TimeTicks::Now();
308     if (pixel_stats_.get())
309       pixel_stats_->GetBitmap(PixelStats::BITMAP_SWAP_IN, web_contents());
310   }
311 }
312 
RecordEvent(PrerenderTabHelper::Event event) const313 void PrerenderTabHelper::RecordEvent(PrerenderTabHelper::Event event) const {
314   UMA_HISTOGRAM_ENUMERATION("Prerender.TabHelperEvent",
315                             event, PrerenderTabHelper::EVENT_MAX_VALUE);
316 }
317 
RecordEventIfLoggedInURL(PrerenderTabHelper::Event event,const GURL & url)318 void PrerenderTabHelper::RecordEventIfLoggedInURL(
319     PrerenderTabHelper::Event event, const GURL& url) {
320   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
321   if (!prerender_manager)
322     return;
323   scoped_ptr<bool> is_present(new bool);
324   scoped_ptr<bool> lookup_succeeded(new bool);
325   bool* is_present_ptr = is_present.get();
326   bool* lookup_succeeded_ptr = lookup_succeeded.get();
327   prerender_manager->CheckIfLikelyLoggedInOnURL(
328       url,
329       is_present_ptr,
330       lookup_succeeded_ptr,
331       base::Bind(&PrerenderTabHelper::RecordEventIfLoggedInURLResult,
332                  weak_factory_.GetWeakPtr(),
333                  event,
334                  base::Passed(&is_present),
335                  base::Passed(&lookup_succeeded)));
336 }
337 
RecordEventIfLoggedInURLResult(PrerenderTabHelper::Event event,scoped_ptr<bool> is_present,scoped_ptr<bool> lookup_succeeded)338 void PrerenderTabHelper::RecordEventIfLoggedInURLResult(
339     PrerenderTabHelper::Event event,
340     scoped_ptr<bool> is_present,
341     scoped_ptr<bool> lookup_succeeded) {
342   if (*lookup_succeeded && *is_present)
343     RecordEvent(event);
344 }
345 
346 }  // namespace prerender
347