Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / renderer / chrome_render_view_observer.cc
blob9313e95543812cd9ec6082139279923df8d877f1
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.
5 #include "chrome/renderer/chrome_render_view_observer.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/debug/crash_logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/trace_event/trace_event.h"
17 #include "chrome/common/chrome_constants.h"
18 #include "chrome/common/chrome_isolated_world_ids.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/crash_keys.h"
21 #include "chrome/common/render_messages.h"
22 #include "chrome/common/url_constants.h"
23 #include "chrome/renderer/prerender/prerender_helper.h"
24 #include "chrome/renderer/safe_browsing/phishing_classifier_delegate.h"
25 #include "chrome/renderer/web_apps.h"
26 #include "components/translate/content/renderer/translate_helper.h"
27 #include "components/web_cache/renderer/web_cache_render_process_observer.h"
28 #include "content/public/common/bindings_policy.h"
29 #include "content/public/renderer/content_renderer_client.h"
30 #include "content/public/renderer/render_frame.h"
31 #include "content/public/renderer/render_view.h"
32 #include "extensions/common/constants.h"
33 #include "net/base/data_url.h"
34 #include "skia/ext/platform_canvas.h"
35 #include "third_party/WebKit/public/platform/WebCString.h"
36 #include "third_party/WebKit/public/platform/WebRect.h"
37 #include "third_party/WebKit/public/platform/WebSize.h"
38 #include "third_party/WebKit/public/platform/WebString.h"
39 #include "third_party/WebKit/public/platform/WebURLRequest.h"
40 #include "third_party/WebKit/public/platform/WebVector.h"
41 #include "third_party/WebKit/public/web/WebAXObject.h"
42 #include "third_party/WebKit/public/web/WebDataSource.h"
43 #include "third_party/WebKit/public/web/WebDocument.h"
44 #include "third_party/WebKit/public/web/WebElement.h"
45 #include "third_party/WebKit/public/web/WebInputEvent.h"
46 #include "third_party/WebKit/public/web/WebLocalFrame.h"
47 #include "third_party/WebKit/public/web/WebNode.h"
48 #include "third_party/WebKit/public/web/WebNodeList.h"
49 #include "third_party/WebKit/public/web/WebView.h"
50 #include "ui/base/ui_base_switches_util.h"
51 #include "ui/gfx/favicon_size.h"
52 #include "ui/gfx/geometry/size.h"
53 #include "ui/gfx/geometry/size_f.h"
54 #include "ui/gfx/skbitmap_operations.h"
55 #include "v8/include/v8-testing.h"
57 #if defined(ENABLE_EXTENSIONS)
58 #include "chrome/common/extensions/chrome_extension_messages.h"
59 #endif
61 using blink::WebAXObject;
62 using blink::WebCString;
63 using blink::WebDataSource;
64 using blink::WebDocument;
65 using blink::WebElement;
66 using blink::WebFrame;
67 using blink::WebGestureEvent;
68 using blink::WebIconURL;
69 using blink::WebLocalFrame;
70 using blink::WebNode;
71 using blink::WebNodeList;
72 using blink::WebRect;
73 using blink::WebSecurityOrigin;
74 using blink::WebSize;
75 using blink::WebString;
76 using blink::WebTouchEvent;
77 using blink::WebURL;
78 using blink::WebURLRequest;
79 using blink::WebView;
80 using blink::WebVector;
81 using blink::WebWindowFeatures;
82 using content::RenderFrame;
84 // Delay in milliseconds that we'll wait before capturing the page contents.
85 static const int kDelayForCaptureMs = 500;
87 // Typically, we capture the page data once the page is loaded.
88 // Sometimes, the page never finishes to load, preventing the page capture
89 // To workaround this problem, we always perform a capture after the following
90 // delay.
91 static const int kDelayForForcedCaptureMs = 6000;
93 // maximum number of characters in the document to index, any text beyond this
94 // point will be clipped
95 static const size_t kMaxIndexChars = 65535;
97 // Constants for UMA statistic collection.
98 static const char kTranslateCaptureText[] = "Translate.CaptureText";
100 PageInfo::PageInfo(PageInfoReceiver* context)
101 : context_(context), capture_timer_(false, false) {
102 DCHECK(context_);
105 void PageInfo::CapturePageInfoLater(CaptureType capture_type,
106 RenderFrame* render_frame,
107 base::TimeDelta delay) {
108 capture_timer_.Start(
109 FROM_HERE, delay,
110 base::Bind(&PageInfo::CapturePageInfo, base::Unretained(this),
111 render_frame, capture_type));
114 bool PageInfo::IsErrorPage(WebLocalFrame* frame) {
115 WebDataSource* ds = frame->dataSource();
116 return ds && ds->hasUnreachableURL();
119 void PageInfo::CapturePageInfo(RenderFrame* render_frame,
120 CaptureType capture_type) {
121 if (!render_frame)
122 return;
124 WebLocalFrame* frame = render_frame->GetWebFrame();
125 if (!frame)
126 return;
128 // Don't index/capture pages that are in view source mode.
129 if (frame->isViewSourceModeEnabled())
130 return;
132 if (IsErrorPage(frame))
133 return;
135 // Don't index/capture pages that are being prerendered.
136 if (prerender::PrerenderHelper::IsPrerendering(render_frame)) {
137 return;
140 // Retrieve the frame's full text (up to kMaxIndexChars), and pass it to the
141 // translate helper for language detection and possible translation.
142 base::string16 contents;
143 base::TimeTicks capture_begin_time = base::TimeTicks::Now();
144 CaptureText(frame, &contents);
145 UMA_HISTOGRAM_TIMES(kTranslateCaptureText,
146 base::TimeTicks::Now() - capture_begin_time);
147 context_->PageCaptured(&contents, capture_type);
150 void PageInfo::CaptureText(WebLocalFrame* frame, base::string16* content) {
151 content->clear();
153 // get the contents of the frame
154 *content = frame->contentAsText(kMaxIndexChars);
156 // When the contents are clipped to the maximum, we don't want to have a
157 // partial word indexed at the end that might have been clipped. Therefore,
158 // terminate the string at the last space to ensure no words are clipped.
159 if (content->size() == kMaxIndexChars) {
160 size_t last_space_index = content->find_last_of(base::kWhitespaceUTF16);
161 if (last_space_index != base::string16::npos)
162 content->resize(last_space_index);
166 ChromeRenderViewObserver::ChromeRenderViewObserver(
167 content::RenderView* render_view,
168 web_cache::WebCacheRenderProcessObserver* web_cache_render_process_observer)
169 : content::RenderViewObserver(render_view),
170 web_cache_render_process_observer_(web_cache_render_process_observer),
171 translate_helper_(
172 new translate::TranslateHelper(render_view,
173 chrome::ISOLATED_WORLD_ID_TRANSLATE,
175 extensions::kExtensionScheme)),
176 phishing_classifier_(NULL),
177 webview_visually_deemphasized_(false),
178 page_info_(this) {
179 const base::CommandLine& command_line =
180 *base::CommandLine::ForCurrentProcess();
181 if (!command_line.HasSwitch(switches::kDisableClientSidePhishingDetection))
182 OnSetClientSidePhishingDetection(true);
185 ChromeRenderViewObserver::~ChromeRenderViewObserver() {
188 bool ChromeRenderViewObserver::OnMessageReceived(const IPC::Message& message) {
189 bool handled = true;
190 IPC_BEGIN_MESSAGE_MAP(ChromeRenderViewObserver, message)
191 #if !defined(OS_ANDROID) && !defined(OS_IOS)
192 IPC_MESSAGE_HANDLER(ChromeViewMsg_WebUIJavaScript, OnWebUIJavaScript)
193 #endif
194 #if defined(ENABLE_EXTENSIONS)
195 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetVisuallyDeemphasized,
196 OnSetVisuallyDeemphasized)
197 #endif
198 #if defined(OS_ANDROID)
199 IPC_MESSAGE_HANDLER(ChromeViewMsg_UpdateTopControlsState,
200 OnUpdateTopControlsState)
201 #endif
202 IPC_MESSAGE_HANDLER(ChromeViewMsg_GetWebApplicationInfo,
203 OnGetWebApplicationInfo)
204 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetClientSidePhishingDetection,
205 OnSetClientSidePhishingDetection)
206 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetWindowFeatures, OnSetWindowFeatures)
207 IPC_MESSAGE_UNHANDLED(handled = false)
208 IPC_END_MESSAGE_MAP()
210 return handled;
213 #if !defined(OS_ANDROID) && !defined(OS_IOS)
214 void ChromeRenderViewObserver::OnWebUIJavaScript(
215 const base::string16& javascript) {
216 webui_javascript_.push_back(javascript);
218 #endif
220 #if defined(OS_ANDROID)
221 void ChromeRenderViewObserver::OnUpdateTopControlsState(
222 content::TopControlsState constraints,
223 content::TopControlsState current,
224 bool animate) {
225 render_view()->UpdateTopControlsState(constraints, current, animate);
227 #endif
229 void ChromeRenderViewObserver::OnGetWebApplicationInfo() {
230 WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
231 DCHECK(main_frame);
233 WebApplicationInfo web_app_info;
234 web_apps::ParseWebAppFromWebDocument(main_frame, &web_app_info);
236 // The warning below is specific to mobile but it doesn't hurt to show it even
237 // if the Chromium build is running on a desktop. It will get more exposition.
238 if (web_app_info.mobile_capable ==
239 WebApplicationInfo::MOBILE_CAPABLE_APPLE) {
240 blink::WebConsoleMessage message(
241 blink::WebConsoleMessage::LevelWarning,
242 "<meta name=\"apple-mobile-web-app-capable\" content=\"yes\"> is "
243 "deprecated. Please include <meta name=\"mobile-web-app-capable\" "
244 "content=\"yes\"> - "
245 "http://developers.google.com/chrome/mobile/docs/installtohomescreen");
246 main_frame->addMessageToConsole(message);
249 // Prune out any data URLs in the set of icons. The browser process expects
250 // any icon with a data URL to have originated from a favicon. We don't want
251 // to decode arbitrary data URLs in the browser process. See
252 // http://b/issue?id=1162972
253 for (std::vector<WebApplicationInfo::IconInfo>::iterator it =
254 web_app_info.icons.begin(); it != web_app_info.icons.end();) {
255 if (it->url.SchemeIs(url::kDataScheme))
256 it = web_app_info.icons.erase(it);
257 else
258 ++it;
261 // Truncate the strings we send to the browser process.
262 web_app_info.title =
263 web_app_info.title.substr(0, chrome::kMaxMetaTagAttributeLength);
264 web_app_info.description =
265 web_app_info.description.substr(0, chrome::kMaxMetaTagAttributeLength);
267 Send(new ChromeViewHostMsg_DidGetWebApplicationInfo(
268 routing_id(), web_app_info));
271 void ChromeRenderViewObserver::OnSetWindowFeatures(
272 const WebWindowFeatures& window_features) {
273 render_view()->GetWebView()->setWindowFeatures(window_features);
276 void ChromeRenderViewObserver::Navigate(const GURL& url) {
277 // Execute cache clear operations that were postponed until a navigation
278 // event (including tab reload).
279 if (web_cache_render_process_observer_)
280 web_cache_render_process_observer_->ExecutePendingClearCache();
283 void ChromeRenderViewObserver::OnSetClientSidePhishingDetection(
284 bool enable_phishing_detection) {
285 #if defined(FULL_SAFE_BROWSING) && !defined(OS_CHROMEOS)
286 phishing_classifier_ = enable_phishing_detection ?
287 safe_browsing::PhishingClassifierDelegate::Create(render_view(), NULL) :
288 NULL;
289 #endif
292 #if defined(ENABLE_EXTENSIONS)
293 void ChromeRenderViewObserver::OnSetVisuallyDeemphasized(bool deemphasized) {
294 if (webview_visually_deemphasized_ == deemphasized)
295 return;
297 webview_visually_deemphasized_ = deemphasized;
299 if (deemphasized) {
300 // 70% opaque grey.
301 SkColor greyish = SkColorSetARGB(178, 0, 0, 0);
302 render_view()->GetWebView()->setPageOverlayColor(greyish);
303 } else {
304 render_view()->GetWebView()->setPageOverlayColor(SK_ColorTRANSPARENT);
307 #endif
309 void ChromeRenderViewObserver::DidStartLoading() {
310 if ((render_view()->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI) &&
311 !webui_javascript_.empty()) {
312 for (size_t i = 0; i < webui_javascript_.size(); ++i) {
313 render_view()->GetMainRenderFrame()->ExecuteJavaScript(
314 webui_javascript_[i]);
316 webui_javascript_.clear();
320 void ChromeRenderViewObserver::DidFinishLoad(blink::WebLocalFrame* frame) {
321 // Don't do anything for subframes.
322 if (frame->parent())
323 return;
325 GURL osdd_url = frame->document().openSearchDescriptionURL();
326 if (!osdd_url.is_empty()) {
327 Send(new ChromeViewHostMsg_PageHasOSDD(
328 routing_id(), frame->document().url(), osdd_url,
329 search_provider::AUTODETECTED_PROVIDER));
332 // Don't capture pages including refresh meta tag.
333 if (HasRefreshMetaTag(frame))
334 return;
336 page_info_.CapturePageInfoLater(
337 FINAL_CAPTURE, render_view()->GetMainRenderFrame(),
338 base::TimeDelta::FromMilliseconds(
339 render_view()->GetContentStateImmediately() ? 0
340 : kDelayForCaptureMs));
343 void ChromeRenderViewObserver::DidStartProvisionalLoad(
344 blink::WebLocalFrame* frame) {
345 // Let translate_helper do any preparatory work for loading a URL.
346 if (translate_helper_)
347 translate_helper_->PrepareForUrl(frame->document().url());
350 void ChromeRenderViewObserver::DidCommitProvisionalLoad(
351 WebLocalFrame* frame, bool is_new_navigation) {
352 // Don't capture pages being not new, or including refresh meta tag.
353 if (!is_new_navigation || HasRefreshMetaTag(frame))
354 return;
356 base::debug::SetCrashKeyValue(
357 crash_keys::kViewCount,
358 base::SizeTToString(content::RenderView::GetRenderViewCount()));
360 page_info_.CapturePageInfoLater(
361 PRELIMINARY_CAPTURE, render_view()->GetMainRenderFrame(),
362 base::TimeDelta::FromMilliseconds(kDelayForForcedCaptureMs));
365 bool ChromeRenderViewObserver::HasRefreshMetaTag(WebFrame* frame) {
366 if (!frame)
367 return false;
368 WebElement head = frame->document().head();
369 if (head.isNull() || !head.hasChildNodes())
370 return false;
372 const WebString tag_name(base::ASCIIToUTF16("meta"));
373 const WebString attribute_name(base::ASCIIToUTF16("http-equiv"));
375 WebNodeList children = head.childNodes();
376 for (size_t i = 0; i < children.length(); ++i) {
377 WebNode node = children.item(i);
378 if (!node.isElementNode())
379 continue;
380 WebElement element = node.to<WebElement>();
381 if (!element.hasHTMLTagName(tag_name))
382 continue;
383 WebString value = element.getAttribute(attribute_name);
384 if (value.isNull() ||
385 !base::LowerCaseEqualsASCII(base::StringPiece16(value), "refresh"))
386 continue;
387 return true;
389 return false;
392 void ChromeRenderViewObserver::PageCaptured(base::string16* content,
393 CaptureType capture_type) {
394 if (translate_helper_)
395 translate_helper_->PageCaptured(*content);
397 TRACE_EVENT0("renderer", "ChromeRenderViewObserver::CapturePageInfo");
399 #if defined(FULL_SAFE_BROWSING)
400 // Will swap out the string.
401 if (phishing_classifier_)
402 phishing_classifier_->PageCaptured(content,
403 capture_type == PRELIMINARY_CAPTURE);
404 #endif