Fix vectorized icon for PPAPI broker website setting icon
[chromium-blink-merge.git] / chrome / renderer / chrome_render_view_observer.cc
blobccc1818da214e468849342b5836dfe0b92631630
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 void PageInfo::CapturePageInfo(RenderFrame* render_frame,
115 CaptureType capture_type) {
116 if (!render_frame)
117 return;
119 WebLocalFrame* frame = render_frame->GetWebFrame();
120 if (!frame)
121 return;
123 // Don't index/capture pages that are in view source mode.
124 if (frame->isViewSourceModeEnabled())
125 return;
127 // Don't index/capture pages that failed to load. This only checks the top
128 // level frame so the thumbnail may contain a frame that failed to load.
129 // TODO(dglazkov): Verify that this is actually necessary.
130 WebDataSource* ds = frame->dataSource();
131 if (ds && ds->hasUnreachableURL())
132 return;
134 // Don't index/capture pages that are being prerendered.
135 if (prerender::PrerenderHelper::IsPrerendering(render_frame)) {
136 return;
139 // Retrieve the frame's full text (up to kMaxIndexChars), and pass it to the
140 // translate helper for language detection and possible translation.
141 base::string16 contents;
142 base::TimeTicks capture_begin_time = base::TimeTicks::Now();
143 CaptureText(frame, &contents);
144 UMA_HISTOGRAM_TIMES(kTranslateCaptureText,
145 base::TimeTicks::Now() - capture_begin_time);
146 context_->PageCaptured(&contents, capture_type);
149 void PageInfo::CaptureText(WebLocalFrame* frame, base::string16* content) {
150 content->clear();
151 if (!frame)
152 return;
154 // get the contents of the frame
155 *content = frame->contentAsText(kMaxIndexChars);
157 // When the contents are clipped to the maximum, we don't want to have a
158 // partial word indexed at the end that might have been clipped. Therefore,
159 // terminate the string at the last space to ensure no words are clipped.
160 if (content->size() == kMaxIndexChars) {
161 size_t last_space_index = content->find_last_of(base::kWhitespaceUTF16);
162 if (last_space_index != base::string16::npos)
163 content->resize(last_space_index);
167 ChromeRenderViewObserver::ChromeRenderViewObserver(
168 content::RenderView* render_view,
169 web_cache::WebCacheRenderProcessObserver* web_cache_render_process_observer)
170 : content::RenderViewObserver(render_view),
171 web_cache_render_process_observer_(web_cache_render_process_observer),
172 translate_helper_(
173 new translate::TranslateHelper(render_view,
174 chrome::ISOLATED_WORLD_ID_TRANSLATE,
176 extensions::kExtensionScheme)),
177 phishing_classifier_(NULL),
178 webview_visually_deemphasized_(false),
179 page_info_(this) {
180 const base::CommandLine& command_line =
181 *base::CommandLine::ForCurrentProcess();
182 if (!command_line.HasSwitch(switches::kDisableClientSidePhishingDetection))
183 OnSetClientSidePhishingDetection(true);
186 ChromeRenderViewObserver::~ChromeRenderViewObserver() {
189 bool ChromeRenderViewObserver::OnMessageReceived(const IPC::Message& message) {
190 bool handled = true;
191 IPC_BEGIN_MESSAGE_MAP(ChromeRenderViewObserver, message)
192 #if !defined(OS_ANDROID) && !defined(OS_IOS)
193 IPC_MESSAGE_HANDLER(ChromeViewMsg_WebUIJavaScript, OnWebUIJavaScript)
194 #endif
195 #if defined(ENABLE_EXTENSIONS)
196 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetVisuallyDeemphasized,
197 OnSetVisuallyDeemphasized)
198 #endif
199 #if defined(OS_ANDROID)
200 IPC_MESSAGE_HANDLER(ChromeViewMsg_UpdateTopControlsState,
201 OnUpdateTopControlsState)
202 #endif
203 IPC_MESSAGE_HANDLER(ChromeViewMsg_GetWebApplicationInfo,
204 OnGetWebApplicationInfo)
205 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetClientSidePhishingDetection,
206 OnSetClientSidePhishingDetection)
207 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetWindowFeatures, OnSetWindowFeatures)
208 IPC_MESSAGE_UNHANDLED(handled = false)
209 IPC_END_MESSAGE_MAP()
211 return handled;
214 #if !defined(OS_ANDROID) && !defined(OS_IOS)
215 void ChromeRenderViewObserver::OnWebUIJavaScript(
216 const base::string16& javascript) {
217 webui_javascript_.push_back(javascript);
219 #endif
221 #if defined(OS_ANDROID)
222 void ChromeRenderViewObserver::OnUpdateTopControlsState(
223 content::TopControlsState constraints,
224 content::TopControlsState current,
225 bool animate) {
226 render_view()->UpdateTopControlsState(constraints, current, animate);
228 #endif
230 void ChromeRenderViewObserver::OnGetWebApplicationInfo() {
231 WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
232 DCHECK(main_frame);
234 WebApplicationInfo web_app_info;
235 web_apps::ParseWebAppFromWebDocument(main_frame, &web_app_info);
237 // The warning below is specific to mobile but it doesn't hurt to show it even
238 // if the Chromium build is running on a desktop. It will get more exposition.
239 if (web_app_info.mobile_capable ==
240 WebApplicationInfo::MOBILE_CAPABLE_APPLE) {
241 blink::WebConsoleMessage message(
242 blink::WebConsoleMessage::LevelWarning,
243 "<meta name=\"apple-mobile-web-app-capable\" content=\"yes\"> is "
244 "deprecated. Please include <meta name=\"mobile-web-app-capable\" "
245 "content=\"yes\"> - "
246 "http://developers.google.com/chrome/mobile/docs/installtohomescreen");
247 main_frame->addMessageToConsole(message);
250 // Prune out any data URLs in the set of icons. The browser process expects
251 // any icon with a data URL to have originated from a favicon. We don't want
252 // to decode arbitrary data URLs in the browser process. See
253 // http://b/issue?id=1162972
254 for (std::vector<WebApplicationInfo::IconInfo>::iterator it =
255 web_app_info.icons.begin(); it != web_app_info.icons.end();) {
256 if (it->url.SchemeIs(url::kDataScheme))
257 it = web_app_info.icons.erase(it);
258 else
259 ++it;
262 // Truncate the strings we send to the browser process.
263 web_app_info.title =
264 web_app_info.title.substr(0, chrome::kMaxMetaTagAttributeLength);
265 web_app_info.description =
266 web_app_info.description.substr(0, chrome::kMaxMetaTagAttributeLength);
268 Send(new ChromeViewHostMsg_DidGetWebApplicationInfo(
269 routing_id(), web_app_info));
272 void ChromeRenderViewObserver::OnSetWindowFeatures(
273 const WebWindowFeatures& window_features) {
274 render_view()->GetWebView()->setWindowFeatures(window_features);
277 void ChromeRenderViewObserver::Navigate(const GURL& url) {
278 // Execute cache clear operations that were postponed until a navigation
279 // event (including tab reload).
280 if (web_cache_render_process_observer_)
281 web_cache_render_process_observer_->ExecutePendingClearCache();
282 // Let translate_helper do any preparatory work for loading a URL.
283 if (translate_helper_)
284 translate_helper_->PrepareForUrl(url);
287 void ChromeRenderViewObserver::OnSetClientSidePhishingDetection(
288 bool enable_phishing_detection) {
289 #if defined(FULL_SAFE_BROWSING) && !defined(OS_CHROMEOS)
290 phishing_classifier_ = enable_phishing_detection ?
291 safe_browsing::PhishingClassifierDelegate::Create(render_view(), NULL) :
292 NULL;
293 #endif
296 #if defined(ENABLE_EXTENSIONS)
297 void ChromeRenderViewObserver::OnSetVisuallyDeemphasized(bool deemphasized) {
298 if (webview_visually_deemphasized_ == deemphasized)
299 return;
301 webview_visually_deemphasized_ = deemphasized;
303 if (deemphasized) {
304 // 70% opaque grey.
305 SkColor greyish = SkColorSetARGB(178, 0, 0, 0);
306 render_view()->GetWebView()->setPageOverlayColor(greyish);
307 } else {
308 render_view()->GetWebView()->setPageOverlayColor(SK_ColorTRANSPARENT);
311 #endif
313 void ChromeRenderViewObserver::DidStartLoading() {
314 if ((render_view()->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI) &&
315 !webui_javascript_.empty()) {
316 for (size_t i = 0; i < webui_javascript_.size(); ++i) {
317 render_view()->GetMainRenderFrame()->ExecuteJavaScript(
318 webui_javascript_[i]);
320 webui_javascript_.clear();
324 void ChromeRenderViewObserver::DidStopLoading() {
325 WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
327 // Remote frames don't host a document, so return early if that's the case.
328 if (main_frame->isWebRemoteFrame())
329 return;
331 GURL osdd_url = main_frame->document().openSearchDescriptionURL();
332 if (!osdd_url.is_empty()) {
333 Send(new ChromeViewHostMsg_PageHasOSDD(
334 routing_id(), main_frame->document().url(), osdd_url,
335 search_provider::AUTODETECTED_PROVIDER));
338 // Don't capture pages including refresh meta tag.
339 if (HasRefreshMetaTag(main_frame))
340 return;
342 page_info_.CapturePageInfoLater(
343 FINAL_CAPTURE, render_view()->GetMainRenderFrame(),
344 base::TimeDelta::FromMilliseconds(
345 render_view()->GetContentStateImmediately() ? 0
346 : kDelayForCaptureMs));
349 void ChromeRenderViewObserver::DidCommitProvisionalLoad(
350 WebLocalFrame* frame, bool is_new_navigation) {
351 // Don't capture pages being not new, or including refresh meta tag.
352 if (!is_new_navigation || HasRefreshMetaTag(frame))
353 return;
355 base::debug::SetCrashKeyValue(
356 crash_keys::kViewCount,
357 base::SizeTToString(content::RenderView::GetRenderViewCount()));
359 page_info_.CapturePageInfoLater(
360 PRELIMINARY_CAPTURE, render_view()->GetMainRenderFrame(),
361 base::TimeDelta::FromMilliseconds(kDelayForForcedCaptureMs));
364 bool ChromeRenderViewObserver::HasRefreshMetaTag(WebFrame* frame) {
365 if (!frame)
366 return false;
367 WebElement head = frame->document().head();
368 if (head.isNull() || !head.hasChildNodes())
369 return false;
371 const WebString tag_name(base::ASCIIToUTF16("meta"));
372 const WebString attribute_name(base::ASCIIToUTF16("http-equiv"));
374 WebNodeList children = head.childNodes();
375 for (size_t i = 0; i < children.length(); ++i) {
376 WebNode node = children.item(i);
377 if (!node.isElementNode())
378 continue;
379 WebElement element = node.to<WebElement>();
380 if (!element.hasHTMLTagName(tag_name))
381 continue;
382 WebString value = element.getAttribute(attribute_name);
383 if (value.isNull() ||
384 !base::LowerCaseEqualsASCII(base::StringPiece16(value), "refresh"))
385 continue;
386 return true;
388 return false;
391 void ChromeRenderViewObserver::PageCaptured(base::string16* content,
392 CaptureType capture_type) {
393 if (translate_helper_)
394 translate_helper_->PageCaptured(*content);
396 TRACE_EVENT0("renderer", "ChromeRenderViewObserver::CapturePageInfo");
398 #if defined(FULL_SAFE_BROWSING)
399 // Will swap out the string.
400 if (phishing_classifier_)
401 phishing_classifier_->PageCaptured(content,
402 capture_type == PRELIMINARY_CAPTURE);
403 #endif