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"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/debug/trace_event.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/common/chrome_constants.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/prerender_messages.h"
19 #include "chrome/common/render_messages.h"
20 #include "chrome/common/url_constants.h"
21 #include "chrome/renderer/chrome_render_process_observer.h"
22 #include "chrome/renderer/content_settings_observer.h"
23 #include "chrome/renderer/extensions/dispatcher.h"
24 #include "chrome/renderer/external_host_bindings.h"
25 #include "chrome/renderer/prerender/prerender_helper.h"
26 #include "chrome/renderer/safe_browsing/phishing_classifier_delegate.h"
27 #include "chrome/renderer/translate/translate_helper.h"
28 #include "chrome/renderer/webview_color_overlay.h"
29 #include "content/public/common/bindings_policy.h"
30 #include "content/public/renderer/content_renderer_client.h"
31 #include "content/public/renderer/render_view.h"
32 #include "extensions/common/constants.h"
33 #include "extensions/common/stack_frame.h"
34 #include "net/base/data_url.h"
35 #include "skia/ext/image_operations.h"
36 #include "skia/ext/platform_canvas.h"
37 #include "third_party/WebKit/public/platform/WebCString.h"
38 #include "third_party/WebKit/public/platform/WebRect.h"
39 #include "third_party/WebKit/public/platform/WebSize.h"
40 #include "third_party/WebKit/public/platform/WebString.h"
41 #include "third_party/WebKit/public/platform/WebURLRequest.h"
42 #include "third_party/WebKit/public/platform/WebVector.h"
43 #include "third_party/WebKit/public/web/WebAXObject.h"
44 #include "third_party/WebKit/public/web/WebDataSource.h"
45 #include "third_party/WebKit/public/web/WebDocument.h"
46 #include "third_party/WebKit/public/web/WebElement.h"
47 #include "third_party/WebKit/public/web/WebFrame.h"
48 #include "third_party/WebKit/public/web/WebInputEvent.h"
49 #include "third_party/WebKit/public/web/WebNode.h"
50 #include "third_party/WebKit/public/web/WebNodeList.h"
51 #include "third_party/WebKit/public/web/WebView.h"
52 #include "ui/base/ui_base_switches_util.h"
53 #include "ui/gfx/favicon_size.h"
54 #include "ui/gfx/size.h"
55 #include "ui/gfx/size_f.h"
56 #include "ui/gfx/skbitmap_operations.h"
57 #include "v8/include/v8-testing.h"
58 #include "webkit/glue/webkit_glue.h"
61 using extensions::APIPermission;
62 using WebKit::WebAXObject;
63 using WebKit::WebCString;
64 using WebKit::WebDataSource;
65 using WebKit::WebDocument;
66 using WebKit::WebElement;
67 using WebKit::WebFrame;
68 using WebKit::WebGestureEvent;
69 using WebKit::WebIconURL;
70 using WebKit::WebNode;
71 using WebKit::WebNodeList;
72 using WebKit::WebRect;
73 using WebKit::WebSecurityOrigin;
74 using WebKit::WebSize;
75 using WebKit::WebString;
76 using WebKit::WebTouchEvent;
78 using WebKit::WebURLRequest;
79 using WebKit::WebView;
80 using WebKit::WebVector;
81 using WebKit::WebWindowFeatures;
83 // 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
91 static const int kDelayForForcedCaptureMs = 6000;
93 // define to write the time necessary for thumbnail/DOM text retrieval,
94 // respectively, into the system debug log
95 // #define TIME_TEXT_RETRIEVAL
97 // maximum number of characters in the document to index, any text beyond this
98 // point will be clipped
99 static const size_t kMaxIndexChars = 65535;
101 // Constants for UMA statistic collection.
102 static const char kWWWDotGoogleDotCom[] = "www.google.com";
103 static const char kMailDotGoogleDotCom[] = "mail.google.com";
104 static const char kPlusDotGoogleDotCom[] = "plus.google.com";
105 static const char kDocsDotGoogleDotCom[] = "docs.google.com";
106 static const char kSitesDotGoogleDotCom[] = "sites.google.com";
107 static const char kPicasawebDotGoogleDotCom[] = "picasaweb.google.com";
108 static const char kCodeDotGoogleDotCom[] = "code.google.com";
109 static const char kGroupsDotGoogleDotCom[] = "groups.google.com";
110 static const char kMapsDotGoogleDotCom[] = "maps.google.com";
111 static const char kWWWDotYoutubeDotCom[] = "www.youtube.com";
112 static const char kDotGoogleUserContentDotCom[] = ".googleusercontent.com";
113 static const char kGoogleReaderPathPrefix[] = "/reader/";
114 static const char kGoogleSupportPathPrefix[] = "/support/";
115 static const char kGoogleIntlPathPrefix[] = "/intl/";
116 static const char kDotJS[] = ".js";
117 static const char kDotCSS[] = ".css";
118 static const char kDotSWF[] = ".swf";
119 static const char kDotHTML[] = ".html";
120 static const char kTranslateCaptureText[] = "Translate.CaptureText";
122 INSECURE_CONTENT_DISPLAY = 0,
123 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE,
124 INSECURE_CONTENT_DISPLAY_HOST_WWW_GOOGLE,
125 INSECURE_CONTENT_DISPLAY_HTML,
126 INSECURE_CONTENT_RUN,
127 INSECURE_CONTENT_RUN_HOST_GOOGLE,
128 INSECURE_CONTENT_RUN_HOST_WWW_GOOGLE,
129 INSECURE_CONTENT_RUN_TARGET_YOUTUBE,
130 INSECURE_CONTENT_RUN_JS,
131 INSECURE_CONTENT_RUN_CSS,
132 INSECURE_CONTENT_RUN_SWF,
133 INSECURE_CONTENT_DISPLAY_HOST_YOUTUBE,
134 INSECURE_CONTENT_RUN_HOST_YOUTUBE,
135 INSECURE_CONTENT_RUN_HOST_GOOGLEUSERCONTENT,
136 INSECURE_CONTENT_DISPLAY_HOST_MAIL_GOOGLE,
137 INSECURE_CONTENT_RUN_HOST_MAIL_GOOGLE,
138 INSECURE_CONTENT_DISPLAY_HOST_PLUS_GOOGLE,
139 INSECURE_CONTENT_RUN_HOST_PLUS_GOOGLE,
140 INSECURE_CONTENT_DISPLAY_HOST_DOCS_GOOGLE,
141 INSECURE_CONTENT_RUN_HOST_DOCS_GOOGLE,
142 INSECURE_CONTENT_DISPLAY_HOST_SITES_GOOGLE,
143 INSECURE_CONTENT_RUN_HOST_SITES_GOOGLE,
144 INSECURE_CONTENT_DISPLAY_HOST_PICASAWEB_GOOGLE,
145 INSECURE_CONTENT_RUN_HOST_PICASAWEB_GOOGLE,
146 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_READER,
147 INSECURE_CONTENT_RUN_HOST_GOOGLE_READER,
148 INSECURE_CONTENT_DISPLAY_HOST_CODE_GOOGLE,
149 INSECURE_CONTENT_RUN_HOST_CODE_GOOGLE,
150 INSECURE_CONTENT_DISPLAY_HOST_GROUPS_GOOGLE,
151 INSECURE_CONTENT_RUN_HOST_GROUPS_GOOGLE,
152 INSECURE_CONTENT_DISPLAY_HOST_MAPS_GOOGLE,
153 INSECURE_CONTENT_RUN_HOST_MAPS_GOOGLE,
154 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_SUPPORT,
155 INSECURE_CONTENT_RUN_HOST_GOOGLE_SUPPORT,
156 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_INTL,
157 INSECURE_CONTENT_RUN_HOST_GOOGLE_INTL,
158 INSECURE_CONTENT_NUM_EVENTS
161 // Constants for mixed-content blocking.
162 static const char kGoogleDotCom[] = "google.com";
164 static bool isHostInDomain(const std::string& host, const std::string& domain) {
165 return (EndsWith(host, domain, false) &&
166 (host.length() == domain.length() ||
167 (host.length() > domain.length() &&
168 host[host.length() - domain.length() - 1] == '.')));
173 GURL StripRef(const GURL& url) {
174 GURL::Replacements replacements;
175 replacements.ClearRef();
176 return url.ReplaceComponents(replacements);
179 // If the source image is null or occupies less area than
180 // |thumbnail_min_area_pixels|, we return the image unmodified. Otherwise, we
181 // scale down the image so that the width and height do not exceed
182 // |thumbnail_max_size_pixels|, preserving the original aspect ratio.
183 SkBitmap Downscale(WebKit::WebImage image,
184 int thumbnail_min_area_pixels,
185 gfx::Size thumbnail_max_size_pixels) {
189 gfx::Size image_size = image.size();
191 if (image_size.GetArea() < thumbnail_min_area_pixels)
192 return image.getSkBitmap();
194 if (image_size.width() <= thumbnail_max_size_pixels.width() &&
195 image_size.height() <= thumbnail_max_size_pixels.height())
196 return image.getSkBitmap();
198 gfx::SizeF scaled_size = image_size;
200 if (scaled_size.width() > thumbnail_max_size_pixels.width()) {
201 scaled_size.Scale(thumbnail_max_size_pixels.width() / scaled_size.width());
204 if (scaled_size.height() > thumbnail_max_size_pixels.height()) {
206 thumbnail_max_size_pixels.height() / scaled_size.height());
209 return skia::ImageOperations::Resize(image.getSkBitmap(),
210 skia::ImageOperations::RESIZE_GOOD,
211 static_cast<int>(scaled_size.width()),
212 static_cast<int>(scaled_size.height()));
215 // The delimiter for a stack trace provided by WebKit.
216 const char kStackFrameDelimiter[] = "\n at ";
218 // Get a stack trace from a WebKit console message.
219 // There are three possible scenarios:
220 // 1. WebKit gives us a stack trace in |stack_trace|.
221 // 2. The stack trace is embedded in the error |message| by an internal
222 // script. This will be more useful than |stack_trace|, since |stack_trace|
223 // will include the internal bindings trace, instead of a developer's code.
224 // 3. No stack trace is included. In this case, we should mock one up from
225 // the given line number and source.
226 // |message| will be populated with the error message only (i.e., will not
227 // include any stack trace).
228 extensions::StackTrace GetStackTraceFromMessage(string16* message,
229 const string16& source,
230 const string16& stack_trace,
232 extensions::StackTrace result;
233 std::vector<base::string16> pieces;
236 if (message->find(base::UTF8ToUTF16(kStackFrameDelimiter)) !=
238 base::SplitStringUsingSubstr(*message,
239 base::UTF8ToUTF16(kStackFrameDelimiter),
241 *message = pieces[0];
243 } else if (!stack_trace.empty()) {
244 base::SplitStringUsingSubstr(stack_trace,
245 base::UTF8ToUTF16(kStackFrameDelimiter),
249 // If we got a stack trace, parse each frame from the text.
250 if (index < pieces.size()) {
251 for (; index < pieces.size(); ++index) {
252 scoped_ptr<extensions::StackFrame> frame =
253 extensions::StackFrame::CreateFromText(pieces[index]);
255 result.push_back(*frame);
259 if (result.empty()) { // If we don't have a stack trace, mock one up.
261 extensions::StackFrame(line_number,
264 EmptyString16() /* no function name */ ));
272 ChromeRenderViewObserver::ChromeRenderViewObserver(
273 content::RenderView* render_view,
274 ContentSettingsObserver* content_settings,
275 ChromeRenderProcessObserver* chrome_render_process_observer,
276 extensions::Dispatcher* extension_dispatcher)
277 : content::RenderViewObserver(render_view),
278 chrome_render_process_observer_(chrome_render_process_observer),
279 extension_dispatcher_(extension_dispatcher),
280 content_settings_(content_settings),
281 translate_helper_(new TranslateHelper(render_view)),
282 phishing_classifier_(NULL),
283 last_indexed_page_id_(-1),
284 allow_displaying_insecure_content_(false),
285 allow_running_insecure_content_(false),
286 capture_timer_(false, false) {
287 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
288 render_view->GetWebView()->setPermissionClient(this);
289 if (!command_line.HasSwitch(switches::kDisableClientSidePhishingDetection))
290 OnSetClientSidePhishingDetection(true);
293 ChromeRenderViewObserver::~ChromeRenderViewObserver() {
296 bool ChromeRenderViewObserver::OnMessageReceived(const IPC::Message& message) {
298 IPC_BEGIN_MESSAGE_MAP(ChromeRenderViewObserver, message)
299 IPC_MESSAGE_HANDLER(ChromeViewMsg_WebUIJavaScript, OnWebUIJavaScript)
300 IPC_MESSAGE_HANDLER(ChromeViewMsg_HandleMessageFromExternalHost,
301 OnHandleMessageFromExternalHost)
302 IPC_MESSAGE_HANDLER(ChromeViewMsg_JavaScriptStressTestControl,
303 OnJavaScriptStressTestControl)
304 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAllowDisplayingInsecureContent,
305 OnSetAllowDisplayingInsecureContent)
306 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAllowRunningInsecureContent,
307 OnSetAllowRunningInsecureContent)
308 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetClientSidePhishingDetection,
309 OnSetClientSidePhishingDetection)
310 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetVisuallyDeemphasized,
311 OnSetVisuallyDeemphasized)
312 IPC_MESSAGE_HANDLER(ChromeViewMsg_RequestThumbnailForContextNode,
313 OnRequestThumbnailForContextNode)
314 IPC_MESSAGE_HANDLER(ChromeViewMsg_GetFPS, OnGetFPS)
315 IPC_MESSAGE_HANDLER(ChromeViewMsg_AddStrictSecurityHost,
316 OnAddStrictSecurityHost)
317 IPC_MESSAGE_HANDLER(ChromeViewMsg_NPAPINotSupported, OnNPAPINotSupported)
318 #if defined(OS_ANDROID)
319 IPC_MESSAGE_HANDLER(ChromeViewMsg_UpdateTopControlsState,
320 OnUpdateTopControlsState)
321 IPC_MESSAGE_HANDLER(ChromeViewMsg_RetrieveWebappInformation,
322 OnRetrieveWebappInformation)
324 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetWindowFeatures, OnSetWindowFeatures)
325 IPC_MESSAGE_UNHANDLED(handled = false)
326 IPC_END_MESSAGE_MAP()
329 IPC_BEGIN_MESSAGE_MAP(ChromeRenderViewObserver, message)
330 IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering);
331 IPC_END_MESSAGE_MAP()
336 void ChromeRenderViewObserver::OnWebUIJavaScript(
337 const string16& frame_xpath,
338 const string16& jscript,
340 bool notify_result) {
341 webui_javascript_.reset(new WebUIJavaScript());
342 webui_javascript_->frame_xpath = frame_xpath;
343 webui_javascript_->jscript = jscript;
344 webui_javascript_->id = id;
345 webui_javascript_->notify_result = notify_result;
348 void ChromeRenderViewObserver::OnHandleMessageFromExternalHost(
349 const std::string& message,
350 const std::string& origin,
351 const std::string& target) {
354 GetExternalHostBindings()->ForwardMessageFromExternalHost(message, origin,
358 void ChromeRenderViewObserver::OnJavaScriptStressTestControl(int cmd,
360 if (cmd == kJavaScriptStressTestSetStressRunType) {
361 v8::Testing::SetStressRunType(static_cast<v8::Testing::StressType>(param));
362 } else if (cmd == kJavaScriptStressTestPrepareStressRun) {
363 v8::Testing::PrepareStressRun(param);
367 void ChromeRenderViewObserver::OnSetAllowDisplayingInsecureContent(bool allow) {
368 allow_displaying_insecure_content_ = allow;
369 WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
371 main_frame->reload();
374 void ChromeRenderViewObserver::OnSetAllowRunningInsecureContent(bool allow) {
375 allow_running_insecure_content_ = allow;
376 OnSetAllowDisplayingInsecureContent(allow);
379 void ChromeRenderViewObserver::OnAddStrictSecurityHost(
380 const std::string& host) {
381 strict_security_hosts_.insert(host);
384 void ChromeRenderViewObserver::OnNPAPINotSupported() {
385 #if defined(USE_AURA) && defined(OS_WIN)
386 content_settings_->BlockNPAPIPlugins();
392 #if defined(OS_ANDROID)
393 void ChromeRenderViewObserver::OnUpdateTopControlsState(
394 content::TopControlsState constraints,
395 content::TopControlsState current,
397 render_view()->UpdateTopControlsState(constraints, current, animate);
400 void ChromeRenderViewObserver::OnRetrieveWebappInformation(
401 const GURL& expected_url) {
402 WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
403 WebDocument document =
404 main_frame ? main_frame->document() : WebDocument();
406 WebElement head = document.isNull() ? WebElement() : document.head();
407 GURL document_url = document.isNull() ? GURL() : GURL(document.url());
409 // Make sure we're checking the right page.
410 bool success = document_url == expected_url;
412 bool is_mobile_webapp_capable = false;
413 bool is_apple_mobile_webapp_capable = false;
415 // Search the DOM for the webapp <meta> tags.
416 if (!head.isNull()) {
417 WebNodeList children = head.childNodes();
418 for (unsigned i = 0; i < children.length(); ++i) {
419 WebNode child = children.item(i);
420 if (!child.isElementNode())
422 WebElement elem = child.to<WebElement>();
424 if (elem.hasTagName("meta") && elem.hasAttribute("name")) {
425 std::string name = elem.getAttribute("name").utf8();
426 WebString content = elem.getAttribute("content");
427 if (LowerCaseEqualsASCII(content, "yes")) {
428 if (name == "mobile-web-app-capable") {
429 is_mobile_webapp_capable = true;
430 } else if (name == "apple-mobile-web-app-capable") {
431 is_apple_mobile_webapp_capable = true;
440 bool is_only_apple_mobile_webapp_capable =
441 is_apple_mobile_webapp_capable && !is_mobile_webapp_capable;
442 if (main_frame && is_only_apple_mobile_webapp_capable) {
443 WebKit::WebConsoleMessage message(
444 WebKit::WebConsoleMessage::LevelWarning,
445 "<meta name=\"apple-mobile-web-app-capable\" content=\"yes\"> is "
446 "deprecated. Please include <meta name=\"mobile-web-app-capable\" "
447 "content=\"yes\"> - "
448 "http://developers.google.com/chrome/mobile/docs/installtohomescreen");
449 main_frame->addMessageToConsole(message);
452 Send(new ChromeViewHostMsg_DidRetrieveWebappInformation(
455 is_mobile_webapp_capable,
456 is_apple_mobile_webapp_capable,
461 void ChromeRenderViewObserver::OnSetWindowFeatures(
462 const WebWindowFeatures& window_features) {
463 render_view()->GetWebView()->setWindowFeatures(window_features);
466 void ChromeRenderViewObserver::Navigate(const GURL& url) {
467 // Execute cache clear operations that were postponed until a navigation
468 // event (including tab reload).
469 if (chrome_render_process_observer_)
470 chrome_render_process_observer_->ExecutePendingClearCache();
473 void ChromeRenderViewObserver::OnSetClientSidePhishingDetection(
474 bool enable_phishing_detection) {
475 #if defined(FULL_SAFE_BROWSING) && !defined(OS_CHROMEOS)
476 phishing_classifier_ = enable_phishing_detection ?
477 safe_browsing::PhishingClassifierDelegate::Create(
478 render_view(), NULL) :
483 void ChromeRenderViewObserver::OnSetVisuallyDeemphasized(bool deemphasized) {
484 bool already_deemphasized = !!dimmed_color_overlay_.get();
485 if (already_deemphasized == deemphasized)
490 SkColor greyish = SkColorSetARGB(178, 0, 0, 0);
491 dimmed_color_overlay_.reset(
492 new WebViewColorOverlay(render_view(), greyish));
494 dimmed_color_overlay_.reset();
498 void ChromeRenderViewObserver::OnRequestThumbnailForContextNode(
499 int thumbnail_min_area_pixels, gfx::Size thumbnail_max_size_pixels) {
500 WebNode context_node = render_view()->GetContextMenuNode();
502 gfx::Size original_size;
503 if (!context_node.isNull() && context_node.isElementNode()) {
504 WebKit::WebImage image = context_node.to<WebElement>().imageContents();
505 original_size = image.size();
506 thumbnail = Downscale(image,
507 thumbnail_min_area_pixels,
508 thumbnail_max_size_pixels);
510 Send(new ChromeViewHostMsg_RequestThumbnailForContextNode_ACK(
511 routing_id(), thumbnail, original_size));
514 void ChromeRenderViewObserver::OnGetFPS() {
515 float fps = (render_view()->GetFilteredTimePerFrame() > 0.0f)?
516 1.0f / render_view()->GetFilteredTimePerFrame() : 0.0f;
517 Send(new ChromeViewHostMsg_FPS(routing_id(), fps));
520 bool ChromeRenderViewObserver::allowDatabase(
522 const WebString& name,
523 const WebString& display_name,
524 unsigned long estimated_size) {
525 return content_settings_->AllowDatabase(
526 frame, name, display_name, estimated_size);
529 bool ChromeRenderViewObserver::allowFileSystem(WebFrame* frame) {
530 return content_settings_->AllowFileSystem(frame);
533 bool ChromeRenderViewObserver::allowImage(WebFrame* frame,
534 bool enabled_per_settings,
535 const WebURL& image_url) {
536 return content_settings_->AllowImage(frame, enabled_per_settings, image_url);
539 bool ChromeRenderViewObserver::allowIndexedDB(WebFrame* frame,
540 const WebString& name,
541 const WebSecurityOrigin& origin) {
542 return content_settings_->AllowIndexedDB(frame, name, origin);
545 bool ChromeRenderViewObserver::allowPlugins(WebFrame* frame,
546 bool enabled_per_settings) {
547 return content_settings_->AllowPlugins(frame, enabled_per_settings);
550 bool ChromeRenderViewObserver::allowScript(WebFrame* frame,
551 bool enabled_per_settings) {
552 return content_settings_->AllowScript(frame, enabled_per_settings);
555 bool ChromeRenderViewObserver::allowScriptFromSource(
557 bool enabled_per_settings,
558 const WebURL& script_url) {
559 return content_settings_->AllowScriptFromSource(frame,
560 enabled_per_settings,
564 bool ChromeRenderViewObserver::allowStorage(WebFrame* frame, bool local) {
565 return content_settings_->AllowStorage(frame, local);
568 bool ChromeRenderViewObserver::allowReadFromClipboard(WebFrame* frame,
569 bool default_value) {
570 bool allowed = false;
571 // TODO(dcheng): Should we consider a toURL() method on WebSecurityOrigin?
572 Send(new ChromeViewHostMsg_CanTriggerClipboardRead(
573 routing_id(), GURL(frame->document().securityOrigin().toString().utf8()),
578 bool ChromeRenderViewObserver::allowWriteToClipboard(WebFrame* frame,
579 bool default_value) {
580 bool allowed = false;
581 Send(new ChromeViewHostMsg_CanTriggerClipboardWrite(
582 routing_id(), GURL(frame->document().securityOrigin().toString().utf8()),
587 bool ChromeRenderViewObserver::allowWebComponents(const WebDocument& document,
592 WebSecurityOrigin origin = document.securityOrigin();
593 if (EqualsASCII(origin.protocol(), chrome::kChromeUIScheme))
596 if (const extensions::Extension* extension = GetExtension(origin)) {
597 if (extension->HasAPIPermission(APIPermission::kExperimental))
604 bool ChromeRenderViewObserver::allowHTMLNotifications(
605 const WebDocument& document) {
606 CommandLine* command_line = CommandLine::ForCurrentProcess();
607 if (command_line->HasSwitch(switches::kDisableHTMLNotifications))
610 WebSecurityOrigin origin = document.securityOrigin();
611 const extensions::Extension* extension = GetExtension(origin);
612 return extension && extension->HasAPIPermission(APIPermission::kNotification);
615 bool ChromeRenderViewObserver::allowMutationEvents(const WebDocument& document,
616 bool default_value) {
617 WebSecurityOrigin origin = document.securityOrigin();
618 const extensions::Extension* extension = GetExtension(origin);
619 if (extension && extension->is_platform_app())
621 return default_value;
624 bool ChromeRenderViewObserver::allowPushState(const WebDocument& document) {
625 WebSecurityOrigin origin = document.securityOrigin();
626 const extensions::Extension* extension = GetExtension(origin);
627 return !extension || !extension->is_platform_app();
630 static void SendInsecureContentSignal(int signal) {
631 UMA_HISTOGRAM_ENUMERATION("SSL.InsecureContent", signal,
632 INSECURE_CONTENT_NUM_EVENTS);
635 bool ChromeRenderViewObserver::allowDisplayingInsecureContent(
636 WebKit::WebFrame* frame,
637 bool allowed_per_settings,
638 const WebKit::WebSecurityOrigin& origin,
639 const WebKit::WebURL& resource_url) {
640 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY);
642 std::string origin_host(origin.host().utf8());
643 GURL frame_gurl(frame->document().url());
644 if (isHostInDomain(origin_host, kGoogleDotCom)) {
645 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE);
646 if (StartsWithASCII(frame_gurl.path(), kGoogleSupportPathPrefix, false)) {
647 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_SUPPORT);
648 } else if (StartsWithASCII(frame_gurl.path(),
649 kGoogleIntlPathPrefix,
651 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_INTL);
655 if (origin_host == kWWWDotGoogleDotCom) {
656 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_WWW_GOOGLE);
657 if (StartsWithASCII(frame_gurl.path(), kGoogleReaderPathPrefix, false))
658 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_READER);
659 } else if (origin_host == kMailDotGoogleDotCom) {
660 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_MAIL_GOOGLE);
661 } else if (origin_host == kPlusDotGoogleDotCom) {
662 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_PLUS_GOOGLE);
663 } else if (origin_host == kDocsDotGoogleDotCom) {
664 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_DOCS_GOOGLE);
665 } else if (origin_host == kSitesDotGoogleDotCom) {
666 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_SITES_GOOGLE);
667 } else if (origin_host == kPicasawebDotGoogleDotCom) {
668 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_PICASAWEB_GOOGLE);
669 } else if (origin_host == kCodeDotGoogleDotCom) {
670 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_CODE_GOOGLE);
671 } else if (origin_host == kGroupsDotGoogleDotCom) {
672 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GROUPS_GOOGLE);
673 } else if (origin_host == kMapsDotGoogleDotCom) {
674 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_MAPS_GOOGLE);
675 } else if (origin_host == kWWWDotYoutubeDotCom) {
676 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_YOUTUBE);
679 GURL resource_gurl(resource_url);
680 if (EndsWith(resource_gurl.path(), kDotHTML, false))
681 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HTML);
683 if (allowed_per_settings || allow_displaying_insecure_content_)
686 Send(new ChromeViewHostMsg_DidBlockDisplayingInsecureContent(routing_id()));
691 bool ChromeRenderViewObserver::allowRunningInsecureContent(
692 WebKit::WebFrame* frame,
693 bool allowed_per_settings,
694 const WebKit::WebSecurityOrigin& origin,
695 const WebKit::WebURL& resource_url) {
696 std::string origin_host(origin.host().utf8());
697 GURL frame_gurl(frame->document().url());
698 DCHECK_EQ(frame_gurl.host(), origin_host);
700 bool is_google = isHostInDomain(origin_host, kGoogleDotCom);
702 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE);
703 if (StartsWithASCII(frame_gurl.path(), kGoogleSupportPathPrefix, false)) {
704 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_SUPPORT);
705 } else if (StartsWithASCII(frame_gurl.path(),
706 kGoogleIntlPathPrefix,
708 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_INTL);
712 if (origin_host == kWWWDotGoogleDotCom) {
713 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_WWW_GOOGLE);
714 if (StartsWithASCII(frame_gurl.path(), kGoogleReaderPathPrefix, false))
715 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_READER);
716 } else if (origin_host == kMailDotGoogleDotCom) {
717 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_MAIL_GOOGLE);
718 } else if (origin_host == kPlusDotGoogleDotCom) {
719 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_PLUS_GOOGLE);
720 } else if (origin_host == kDocsDotGoogleDotCom) {
721 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_DOCS_GOOGLE);
722 } else if (origin_host == kSitesDotGoogleDotCom) {
723 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_SITES_GOOGLE);
724 } else if (origin_host == kPicasawebDotGoogleDotCom) {
725 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_PICASAWEB_GOOGLE);
726 } else if (origin_host == kCodeDotGoogleDotCom) {
727 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_CODE_GOOGLE);
728 } else if (origin_host == kGroupsDotGoogleDotCom) {
729 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GROUPS_GOOGLE);
730 } else if (origin_host == kMapsDotGoogleDotCom) {
731 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_MAPS_GOOGLE);
732 } else if (origin_host == kWWWDotYoutubeDotCom) {
733 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_YOUTUBE);
734 } else if (EndsWith(origin_host, kDotGoogleUserContentDotCom, false)) {
735 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLEUSERCONTENT);
738 GURL resource_gurl(resource_url);
739 if (resource_gurl.host() == kWWWDotYoutubeDotCom)
740 SendInsecureContentSignal(INSECURE_CONTENT_RUN_TARGET_YOUTUBE);
742 if (EndsWith(resource_gurl.path(), kDotJS, false))
743 SendInsecureContentSignal(INSECURE_CONTENT_RUN_JS);
744 else if (EndsWith(resource_gurl.path(), kDotCSS, false))
745 SendInsecureContentSignal(INSECURE_CONTENT_RUN_CSS);
746 else if (EndsWith(resource_gurl.path(), kDotSWF, false))
747 SendInsecureContentSignal(INSECURE_CONTENT_RUN_SWF);
749 if (!allow_running_insecure_content_ && !allowed_per_settings) {
750 content_settings_->DidNotAllowMixedScript();
757 bool ChromeRenderViewObserver::allowWebGLDebugRendererInfo(WebFrame* frame) {
758 bool allowed = false;
759 Send(new ChromeViewHostMsg_IsWebGLDebugRendererInfoAllowed(
761 GURL(frame->top()->document().securityOrigin().toString().utf8()),
766 void ChromeRenderViewObserver::didNotAllowPlugins(WebFrame* frame) {
767 content_settings_->DidNotAllowPlugins();
770 void ChromeRenderViewObserver::didNotAllowScript(WebFrame* frame) {
771 content_settings_->DidNotAllowScript();
774 void ChromeRenderViewObserver::OnSetIsPrerendering(bool is_prerendering) {
775 if (is_prerendering) {
776 DCHECK(!prerender::PrerenderHelper::Get(render_view()));
777 // The PrerenderHelper will destroy itself either after recording histograms
778 // or on destruction of the RenderView.
779 new prerender::PrerenderHelper(render_view());
783 void ChromeRenderViewObserver::DidStartLoading() {
784 if ((render_view()->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI) &&
785 webui_javascript_.get()) {
786 render_view()->EvaluateScript(webui_javascript_->frame_xpath,
787 webui_javascript_->jscript,
788 webui_javascript_->id,
789 webui_javascript_->notify_result);
790 webui_javascript_.reset();
794 void ChromeRenderViewObserver::DidStopLoading() {
795 WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
796 GURL osd_url = main_frame->document().openSearchDescriptionURL();
797 if (!osd_url.is_empty()) {
798 Send(new ChromeViewHostMsg_PageHasOSDD(
799 routing_id(), render_view()->GetPageId(), osd_url,
800 search_provider::AUTODETECTED_PROVIDER));
803 // Don't capture pages including refresh meta tag.
804 if (HasRefreshMetaTag(main_frame))
807 CapturePageInfoLater(
808 render_view()->GetPageId(),
809 false, // preliminary_capture
810 base::TimeDelta::FromMilliseconds(
811 render_view()->GetContentStateImmediately() ?
812 0 : kDelayForCaptureMs));
815 void ChromeRenderViewObserver::DidCommitProvisionalLoad(
816 WebFrame* frame, bool is_new_navigation) {
817 // Don't capture pages being not new, or including refresh meta tag.
818 if (!is_new_navigation || HasRefreshMetaTag(frame))
821 CapturePageInfoLater(
822 render_view()->GetPageId(),
823 true, // preliminary_capture
824 base::TimeDelta::FromMilliseconds(kDelayForForcedCaptureMs));
827 void ChromeRenderViewObserver::DidClearWindowObject(WebFrame* frame) {
828 if (render_view()->GetEnabledBindings() &
829 content::BINDINGS_POLICY_EXTERNAL_HOST) {
830 GetExternalHostBindings()->BindToJavascript(frame, "externalHost");
834 void ChromeRenderViewObserver::DidHandleGestureEvent(
835 const WebGestureEvent& event) {
836 if (event.type != WebKit::WebGestureEvent::GestureTap)
839 WebKit::WebTextInputType text_input_type =
840 render_view()->GetWebView()->textInputInfo().type;
842 render_view()->Send(new ChromeViewHostMsg_FocusedNodeTouched(
844 text_input_type != WebKit::WebTextInputTypeNone));
847 void ChromeRenderViewObserver::DetailedConsoleMessageAdded(
848 const base::string16& message,
849 const base::string16& source,
850 const base::string16& stack_trace_string,
852 int32 severity_level) {
853 string16 trimmed_message = message;
854 extensions::StackTrace stack_trace = GetStackTraceFromMessage(
859 Send(new ChromeViewHostMsg_DetailedConsoleMessageAdded(routing_id(),
866 void ChromeRenderViewObserver::CapturePageInfoLater(int page_id,
867 bool preliminary_capture,
868 base::TimeDelta delay) {
869 capture_timer_.Start(
872 base::Bind(&ChromeRenderViewObserver::CapturePageInfo,
873 base::Unretained(this),
875 preliminary_capture));
878 void ChromeRenderViewObserver::CapturePageInfo(int page_id,
879 bool preliminary_capture) {
880 // If |page_id| is obsolete, we should stop indexing and capturing a page.
881 if (render_view()->GetPageId() != page_id)
884 if (!render_view()->GetWebView())
887 WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
891 // Don't index/capture pages that are in view source mode.
892 if (main_frame->isViewSourceModeEnabled())
895 // Don't index/capture pages that failed to load. This only checks the top
896 // level frame so the thumbnail may contain a frame that failed to load.
897 WebDataSource* ds = main_frame->dataSource();
898 if (ds && ds->hasUnreachableURL())
901 // Don't index/capture pages that are being prerendered.
902 if (prerender::PrerenderHelper::IsPrerendering(render_view()))
905 // Retrieve the frame's full text (up to kMaxIndexChars), and pass it to the
906 // translate helper for language detection and possible translation.
908 base::TimeTicks capture_begin_time = base::TimeTicks::Now();
909 CaptureText(main_frame, &contents);
910 UMA_HISTOGRAM_TIMES(kTranslateCaptureText,
911 base::TimeTicks::Now() - capture_begin_time);
912 if (translate_helper_)
913 translate_helper_->PageCaptured(page_id, contents);
915 // Skip indexing if this is not a new load. Note that the case where
916 // page_id == last_indexed_page_id_ is more complicated, since we need to
917 // reindex if the toplevel URL has changed (such as from a redirect), even
918 // though this may not cause the page id to be incremented.
919 if (page_id < last_indexed_page_id_)
922 bool same_page_id = last_indexed_page_id_ == page_id;
923 if (!preliminary_capture)
924 last_indexed_page_id_ = page_id;
926 // Get the URL for this page.
927 GURL url(main_frame->document().url());
928 if (url.is_empty()) {
929 if (!preliminary_capture)
930 last_indexed_url_ = GURL();
934 // If the page id is unchanged, check whether the URL (ignoring fragments)
935 // has changed. If so, we need to reindex. Otherwise, assume this is a
936 // reload, in-page navigation, or some other load type where we don't want to
937 // reindex. Note: subframe navigations after onload increment the page id,
938 // so these will trigger a reindex.
939 GURL stripped_url(StripRef(url));
940 if (same_page_id && stripped_url == last_indexed_url_)
943 if (!preliminary_capture)
944 last_indexed_url_ = stripped_url;
946 TRACE_EVENT0("renderer", "ChromeRenderViewObserver::CapturePageInfo");
948 if (contents.size()) {
949 // Send the text to the browser for indexing (the browser might decide not
950 // to index, if the URL is HTTPS for instance).
951 Send(new ChromeViewHostMsg_PageContents(routing_id(), url, contents));
954 #if defined(FULL_SAFE_BROWSING)
955 // Will swap out the string.
956 if (phishing_classifier_)
957 phishing_classifier_->PageCaptured(&contents, preliminary_capture);
961 void ChromeRenderViewObserver::CaptureText(WebFrame* frame,
962 string16* contents) {
967 #ifdef TIME_TEXT_RETRIEVAL
968 double begin = time_util::GetHighResolutionTimeNow();
971 // get the contents of the frame
972 *contents = frame->contentAsText(kMaxIndexChars);
974 #ifdef TIME_TEXT_RETRIEVAL
975 double end = time_util::GetHighResolutionTimeNow();
977 sprintf_s(buf, "%d chars retrieved for indexing in %gms\n",
978 contents.size(), (end - begin)*1000);
979 OutputDebugStringA(buf);
982 // When the contents are clipped to the maximum, we don't want to have a
983 // partial word indexed at the end that might have been clipped. Therefore,
984 // terminate the string at the last space to ensure no words are clipped.
985 if (contents->size() == kMaxIndexChars) {
986 size_t last_space_index = contents->find_last_of(kWhitespaceUTF16);
987 if (last_space_index == std::wstring::npos)
988 return; // don't index if we got a huge block of text with no spaces
989 contents->resize(last_space_index);
993 ExternalHostBindings* ChromeRenderViewObserver::GetExternalHostBindings() {
994 if (!external_host_bindings_.get()) {
995 external_host_bindings_.reset(new ExternalHostBindings(
996 render_view(), routing_id()));
998 return external_host_bindings_.get();
1001 bool ChromeRenderViewObserver::IsStrictSecurityHost(const std::string& host) {
1002 return (strict_security_hosts_.find(host) != strict_security_hosts_.end());
1005 const extensions::Extension* ChromeRenderViewObserver::GetExtension(
1006 const WebSecurityOrigin& origin) const {
1007 if (!EqualsASCII(origin.protocol(), extensions::kExtensionScheme))
1010 const std::string extension_id = origin.host().utf8().data();
1011 if (!extension_dispatcher_->IsExtensionActive(extension_id))
1014 return extension_dispatcher_->extensions()->GetByID(extension_id);
1017 bool ChromeRenderViewObserver::HasRefreshMetaTag(WebFrame* frame) {
1020 WebElement head = frame->document().head();
1021 if (head.isNull() || !head.hasChildNodes())
1024 const WebString tag_name(ASCIIToUTF16("meta"));
1025 const WebString attribute_name(ASCIIToUTF16("http-equiv"));
1027 WebNodeList children = head.childNodes();
1028 for (size_t i = 0; i < children.length(); ++i) {
1029 WebNode node = children.item(i);
1030 if (!node.isElementNode())
1032 WebElement element = node.to<WebElement>();
1033 if (!element.hasTagName(tag_name))
1035 WebString value = element.getAttribute(attribute_name);
1036 if (value.isNull() || !LowerCaseEqualsASCII(value, "refresh"))