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/renderer/chrome_render_view_observer.h" 6 7 #include "base/bind.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/utf_string_conversions.h" 14 #include "chrome/common/chrome_constants.h" 15 #include "chrome/common/chrome_switches.h" 16 #include "chrome/common/prerender_messages.h" 17 #include "chrome/common/render_messages.h" 18 #include "chrome/common/url_constants.h" 19 #include "chrome/renderer/chrome_render_process_observer.h" 20 #include "chrome/renderer/content_settings_observer.h" 21 #include "chrome/renderer/extensions/dispatcher.h" 22 #include "chrome/renderer/external_host_bindings.h" 23 #include "chrome/renderer/frame_sniffer.h" 24 #include "chrome/renderer/prerender/prerender_helper.h" 25 #include "chrome/renderer/safe_browsing/phishing_classifier_delegate.h" 26 #include "chrome/renderer/translate/translate_helper.h" 27 #include "chrome/renderer/webview_color_overlay.h" 28 #include "content/public/common/bindings_policy.h" 29 #include "content/public/renderer/content_renderer_client.h" 30 #include "content/public/renderer/render_view.h" 31 #include "extensions/common/constants.h" 32 #include "net/base/data_url.h" 33 #include "skia/ext/image_operations.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/WebAccessibilityObject.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/WebFrame.h" 46 #include "third_party/WebKit/public/web/WebInputEvent.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/size.h" 53 #include "ui/gfx/size_f.h" 54 #include "ui/gfx/skbitmap_operations.h" 55 #include "v8/include/v8-testing.h" 56 #include "webkit/glue/webkit_glue.h" 57 58 using WebKit::WebAccessibilityObject; 59 using WebKit::WebCString; 60 using WebKit::WebDataSource; 61 using WebKit::WebDocument; 62 using WebKit::WebElement; 63 using WebKit::WebFrame; 64 using WebKit::WebGestureEvent; 65 using WebKit::WebIconURL; 66 using WebKit::WebNode; 67 using WebKit::WebNodeList; 68 using WebKit::WebRect; 69 using WebKit::WebSecurityOrigin; 70 using WebKit::WebSize; 71 using WebKit::WebString; 72 using WebKit::WebTouchEvent; 73 using WebKit::WebURL; 74 using WebKit::WebURLRequest; 75 using WebKit::WebView; 76 using WebKit::WebVector; 77 using WebKit::WebWindowFeatures; 78 using extensions::APIPermission; 79 80 // Delay in milliseconds that we'll wait before capturing the page contents 81 // and thumbnail. 82 static const int kDelayForCaptureMs = 500; 83 84 // Typically, we capture the page data once the page is loaded. 85 // Sometimes, the page never finishes to load, preventing the page capture 86 // To workaround this problem, we always perform a capture after the following 87 // delay. 88 static const int kDelayForForcedCaptureMs = 6000; 89 90 // define to write the time necessary for thumbnail/DOM text retrieval, 91 // respectively, into the system debug log 92 // #define TIME_TEXT_RETRIEVAL 93 94 // maximum number of characters in the document to index, any text beyond this 95 // point will be clipped 96 static const size_t kMaxIndexChars = 65535; 97 98 // Constants for UMA statistic collection. 99 static const char kWWWDotGoogleDotCom[] = "www.google.com"; 100 static const char kMailDotGoogleDotCom[] = "mail.google.com"; 101 static const char kPlusDotGoogleDotCom[] = "plus.google.com"; 102 static const char kDocsDotGoogleDotCom[] = "docs.google.com"; 103 static const char kSitesDotGoogleDotCom[] = "sites.google.com"; 104 static const char kPicasawebDotGoogleDotCom[] = "picasaweb.google.com"; 105 static const char kCodeDotGoogleDotCom[] = "code.google.com"; 106 static const char kGroupsDotGoogleDotCom[] = "groups.google.com"; 107 static const char kMapsDotGoogleDotCom[] = "maps.google.com"; 108 static const char kWWWDotYoutubeDotCom[] = "www.youtube.com"; 109 static const char kDotGoogleUserContentDotCom[] = ".googleusercontent.com"; 110 static const char kGoogleReaderPathPrefix[] = "/reader/"; 111 static const char kGoogleSupportPathPrefix[] = "/support/"; 112 static const char kGoogleIntlPathPrefix[] = "/intl/"; 113 static const char kDotJS[] = ".js"; 114 static const char kDotCSS[] = ".css"; 115 static const char kDotSWF[] = ".swf"; 116 static const char kDotHTML[] = ".html"; 117 static const char kTranslateCaptureText[] = "Translate.CaptureText"; 118 enum { 119 INSECURE_CONTENT_DISPLAY = 0, 120 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE, 121 INSECURE_CONTENT_DISPLAY_HOST_WWW_GOOGLE, 122 INSECURE_CONTENT_DISPLAY_HTML, 123 INSECURE_CONTENT_RUN, 124 INSECURE_CONTENT_RUN_HOST_GOOGLE, 125 INSECURE_CONTENT_RUN_HOST_WWW_GOOGLE, 126 INSECURE_CONTENT_RUN_TARGET_YOUTUBE, 127 INSECURE_CONTENT_RUN_JS, 128 INSECURE_CONTENT_RUN_CSS, 129 INSECURE_CONTENT_RUN_SWF, 130 INSECURE_CONTENT_DISPLAY_HOST_YOUTUBE, 131 INSECURE_CONTENT_RUN_HOST_YOUTUBE, 132 INSECURE_CONTENT_RUN_HOST_GOOGLEUSERCONTENT, 133 INSECURE_CONTENT_DISPLAY_HOST_MAIL_GOOGLE, 134 INSECURE_CONTENT_RUN_HOST_MAIL_GOOGLE, 135 INSECURE_CONTENT_DISPLAY_HOST_PLUS_GOOGLE, 136 INSECURE_CONTENT_RUN_HOST_PLUS_GOOGLE, 137 INSECURE_CONTENT_DISPLAY_HOST_DOCS_GOOGLE, 138 INSECURE_CONTENT_RUN_HOST_DOCS_GOOGLE, 139 INSECURE_CONTENT_DISPLAY_HOST_SITES_GOOGLE, 140 INSECURE_CONTENT_RUN_HOST_SITES_GOOGLE, 141 INSECURE_CONTENT_DISPLAY_HOST_PICASAWEB_GOOGLE, 142 INSECURE_CONTENT_RUN_HOST_PICASAWEB_GOOGLE, 143 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_READER, 144 INSECURE_CONTENT_RUN_HOST_GOOGLE_READER, 145 INSECURE_CONTENT_DISPLAY_HOST_CODE_GOOGLE, 146 INSECURE_CONTENT_RUN_HOST_CODE_GOOGLE, 147 INSECURE_CONTENT_DISPLAY_HOST_GROUPS_GOOGLE, 148 INSECURE_CONTENT_RUN_HOST_GROUPS_GOOGLE, 149 INSECURE_CONTENT_DISPLAY_HOST_MAPS_GOOGLE, 150 INSECURE_CONTENT_RUN_HOST_MAPS_GOOGLE, 151 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_SUPPORT, 152 INSECURE_CONTENT_RUN_HOST_GOOGLE_SUPPORT, 153 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_INTL, 154 INSECURE_CONTENT_RUN_HOST_GOOGLE_INTL, 155 INSECURE_CONTENT_NUM_EVENTS 156 }; 157 158 // Constants for mixed-content blocking. 159 static const char kGoogleDotCom[] = "google.com"; 160 161 static bool isHostInDomain(const std::string& host, const std::string& domain) { 162 return (EndsWith(host, domain, false) && 163 (host.length() == domain.length() || 164 (host.length() > domain.length() && 165 host[host.length() - domain.length() - 1] == '.'))); 166 } 167 168 namespace { 169 GURL StripRef(const GURL& url) { 170 GURL::Replacements replacements; 171 replacements.ClearRef(); 172 return url.ReplaceComponents(replacements); 173 } 174 175 // If the source image is null or occupies less area than 176 // |thumbnail_min_area_pixels|, we return the image unmodified. Otherwise, we 177 // scale down the image so that the width and height do not exceed 178 // |thumbnail_max_size_pixels|, preserving the original aspect ratio. 179 static SkBitmap Downscale(WebKit::WebImage image, 180 int thumbnail_min_area_pixels, 181 gfx::Size thumbnail_max_size_pixels) { 182 if (image.isNull()) 183 return SkBitmap(); 184 185 gfx::Size image_size = image.size(); 186 187 if (image_size.GetArea() < thumbnail_min_area_pixels) 188 return image.getSkBitmap(); 189 190 if (image_size.width() <= thumbnail_max_size_pixels.width() && 191 image_size.height() <= thumbnail_max_size_pixels.height()) 192 return image.getSkBitmap(); 193 194 gfx::SizeF scaled_size = image_size; 195 196 if (scaled_size.width() > thumbnail_max_size_pixels.width()) { 197 scaled_size.Scale(thumbnail_max_size_pixels.width() / scaled_size.width()); 198 } 199 200 if (scaled_size.height() > thumbnail_max_size_pixels.height()) { 201 scaled_size.Scale( 202 thumbnail_max_size_pixels.height() / scaled_size.height()); 203 } 204 205 return skia::ImageOperations::Resize(image.getSkBitmap(), 206 skia::ImageOperations::RESIZE_GOOD, 207 static_cast<int>(scaled_size.width()), 208 static_cast<int>(scaled_size.height())); 209 } 210 } // namespace 211 212 ChromeRenderViewObserver::ChromeRenderViewObserver( 213 content::RenderView* render_view, 214 ContentSettingsObserver* content_settings, 215 ChromeRenderProcessObserver* chrome_render_process_observer, 216 extensions::Dispatcher* extension_dispatcher) 217 : content::RenderViewObserver(render_view), 218 chrome_render_process_observer_(chrome_render_process_observer), 219 extension_dispatcher_(extension_dispatcher), 220 content_settings_(content_settings), 221 translate_helper_(new TranslateHelper(render_view)), 222 phishing_classifier_(NULL), 223 last_indexed_page_id_(-1), 224 allow_displaying_insecure_content_(false), 225 allow_running_insecure_content_(false), 226 capture_timer_(false, false) { 227 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 228 render_view->GetWebView()->setPermissionClient(this); 229 if (!command_line.HasSwitch(switches::kDisableClientSidePhishingDetection)) 230 OnSetClientSidePhishingDetection(true); 231 } 232 233 ChromeRenderViewObserver::~ChromeRenderViewObserver() { 234 } 235 236 bool ChromeRenderViewObserver::OnMessageReceived(const IPC::Message& message) { 237 bool handled = true; 238 IPC_BEGIN_MESSAGE_MAP(ChromeRenderViewObserver, message) 239 IPC_MESSAGE_HANDLER(ChromeViewMsg_WebUIJavaScript, OnWebUIJavaScript) 240 IPC_MESSAGE_HANDLER(ChromeViewMsg_HandleMessageFromExternalHost, 241 OnHandleMessageFromExternalHost) 242 IPC_MESSAGE_HANDLER(ChromeViewMsg_JavaScriptStressTestControl, 243 OnJavaScriptStressTestControl) 244 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAllowDisplayingInsecureContent, 245 OnSetAllowDisplayingInsecureContent) 246 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAllowRunningInsecureContent, 247 OnSetAllowRunningInsecureContent) 248 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetClientSidePhishingDetection, 249 OnSetClientSidePhishingDetection) 250 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetVisuallyDeemphasized, 251 OnSetVisuallyDeemphasized) 252 IPC_MESSAGE_HANDLER(ChromeViewMsg_RequestThumbnailForContextNode, 253 OnRequestThumbnailForContextNode) 254 #if defined(OS_CHROMEOS) 255 IPC_MESSAGE_HANDLER(ChromeViewMsg_StartFrameSniffer, OnStartFrameSniffer) 256 #endif 257 IPC_MESSAGE_HANDLER(ChromeViewMsg_GetFPS, OnGetFPS) 258 IPC_MESSAGE_HANDLER(ChromeViewMsg_AddStrictSecurityHost, 259 OnAddStrictSecurityHost) 260 IPC_MESSAGE_HANDLER(ChromeViewMsg_NPAPINotSupported, OnNPAPINotSupported) 261 #if defined(OS_ANDROID) 262 IPC_MESSAGE_HANDLER(ChromeViewMsg_UpdateTopControlsState, 263 OnUpdateTopControlsState) 264 #endif 265 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetWindowFeatures, OnSetWindowFeatures) 266 IPC_MESSAGE_UNHANDLED(handled = false) 267 IPC_END_MESSAGE_MAP() 268 269 // Filter only. 270 IPC_BEGIN_MESSAGE_MAP(ChromeRenderViewObserver, message) 271 IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering); 272 IPC_END_MESSAGE_MAP() 273 274 return handled; 275 } 276 277 void ChromeRenderViewObserver::OnWebUIJavaScript( 278 const string16& frame_xpath, 279 const string16& jscript, 280 int id, 281 bool notify_result) { 282 webui_javascript_.reset(new WebUIJavaScript()); 283 webui_javascript_->frame_xpath = frame_xpath; 284 webui_javascript_->jscript = jscript; 285 webui_javascript_->id = id; 286 webui_javascript_->notify_result = notify_result; 287 } 288 289 void ChromeRenderViewObserver::OnHandleMessageFromExternalHost( 290 const std::string& message, 291 const std::string& origin, 292 const std::string& target) { 293 if (message.empty()) 294 return; 295 GetExternalHostBindings()->ForwardMessageFromExternalHost(message, origin, 296 target); 297 } 298 299 void ChromeRenderViewObserver::OnJavaScriptStressTestControl(int cmd, 300 int param) { 301 if (cmd == kJavaScriptStressTestSetStressRunType) { 302 v8::Testing::SetStressRunType(static_cast<v8::Testing::StressType>(param)); 303 } else if (cmd == kJavaScriptStressTestPrepareStressRun) { 304 v8::Testing::PrepareStressRun(param); 305 } 306 } 307 308 void ChromeRenderViewObserver::OnSetAllowDisplayingInsecureContent(bool allow) { 309 allow_displaying_insecure_content_ = allow; 310 WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); 311 if (main_frame) 312 main_frame->reload(); 313 } 314 315 void ChromeRenderViewObserver::OnSetAllowRunningInsecureContent(bool allow) { 316 allow_running_insecure_content_ = allow; 317 OnSetAllowDisplayingInsecureContent(allow); 318 } 319 320 void ChromeRenderViewObserver::OnAddStrictSecurityHost( 321 const std::string& host) { 322 strict_security_hosts_.insert(host); 323 } 324 325 void ChromeRenderViewObserver::OnNPAPINotSupported() { 326 #if defined(USE_AURA) && defined(OS_WIN) 327 content_settings_->BlockNPAPIPlugins(); 328 #else 329 NOTREACHED(); 330 #endif 331 } 332 333 #if defined(OS_ANDROID) 334 void ChromeRenderViewObserver::OnUpdateTopControlsState( 335 content::TopControlsState constraints, 336 content::TopControlsState current, 337 bool animate) { 338 render_view()->UpdateTopControlsState(constraints, current, animate); 339 } 340 #endif 341 342 void ChromeRenderViewObserver::OnSetWindowFeatures( 343 const WebWindowFeatures& window_features) { 344 render_view()->GetWebView()->setWindowFeatures(window_features); 345 } 346 347 void ChromeRenderViewObserver::Navigate(const GURL& url) { 348 // Execute cache clear operations that were postponed until a navigation 349 // event (including tab reload). 350 if (chrome_render_process_observer_) 351 chrome_render_process_observer_->ExecutePendingClearCache(); 352 } 353 354 void ChromeRenderViewObserver::OnSetClientSidePhishingDetection( 355 bool enable_phishing_detection) { 356 #if defined(FULL_SAFE_BROWSING) && !defined(OS_CHROMEOS) 357 phishing_classifier_ = enable_phishing_detection ? 358 safe_browsing::PhishingClassifierDelegate::Create( 359 render_view(), NULL) : 360 NULL; 361 #endif 362 } 363 364 void ChromeRenderViewObserver::OnSetVisuallyDeemphasized(bool deemphasized) { 365 bool already_deemphasized = !!dimmed_color_overlay_.get(); 366 if (already_deemphasized == deemphasized) 367 return; 368 369 if (deemphasized) { 370 // 70% opaque grey. 371 SkColor greyish = SkColorSetARGB(178, 0, 0, 0); 372 dimmed_color_overlay_.reset( 373 new WebViewColorOverlay(render_view(), greyish)); 374 } else { 375 dimmed_color_overlay_.reset(); 376 } 377 } 378 379 void ChromeRenderViewObserver::OnRequestThumbnailForContextNode( 380 int thumbnail_min_area_pixels, gfx::Size thumbnail_max_size_pixels) { 381 WebNode context_node = render_view()->GetContextMenuNode(); 382 SkBitmap thumbnail; 383 if (context_node.isElementNode()) { 384 WebKit::WebImage image = context_node.to<WebElement>().imageContents(); 385 thumbnail = Downscale(image, 386 thumbnail_min_area_pixels, 387 thumbnail_max_size_pixels); 388 } 389 Send(new ChromeViewHostMsg_RequestThumbnailForContextNode_ACK(routing_id(), 390 thumbnail)); 391 } 392 393 void ChromeRenderViewObserver::OnStartFrameSniffer(const string16& frame_name) { 394 new FrameSniffer(render_view(), frame_name); 395 } 396 397 void ChromeRenderViewObserver::OnGetFPS() { 398 float fps = (render_view()->GetFilteredTimePerFrame() > 0.0f)? 399 1.0f / render_view()->GetFilteredTimePerFrame() : 0.0f; 400 Send(new ChromeViewHostMsg_FPS(routing_id(), fps)); 401 } 402 403 bool ChromeRenderViewObserver::allowDatabase( 404 WebFrame* frame, 405 const WebString& name, 406 const WebString& display_name, 407 unsigned long estimated_size) { 408 return content_settings_->AllowDatabase( 409 frame, name, display_name, estimated_size); 410 } 411 412 bool ChromeRenderViewObserver::allowFileSystem(WebFrame* frame) { 413 return content_settings_->AllowFileSystem(frame); 414 } 415 416 bool ChromeRenderViewObserver::allowImage(WebFrame* frame, 417 bool enabled_per_settings, 418 const WebURL& image_url) { 419 return content_settings_->AllowImage(frame, enabled_per_settings, image_url); 420 } 421 422 bool ChromeRenderViewObserver::allowIndexedDB(WebFrame* frame, 423 const WebString& name, 424 const WebSecurityOrigin& origin) { 425 return content_settings_->AllowIndexedDB(frame, name, origin); 426 } 427 428 bool ChromeRenderViewObserver::allowPlugins(WebFrame* frame, 429 bool enabled_per_settings) { 430 return content_settings_->AllowPlugins(frame, enabled_per_settings); 431 } 432 433 bool ChromeRenderViewObserver::allowScript(WebFrame* frame, 434 bool enabled_per_settings) { 435 return content_settings_->AllowScript(frame, enabled_per_settings); 436 } 437 438 bool ChromeRenderViewObserver::allowScriptFromSource( 439 WebFrame* frame, 440 bool enabled_per_settings, 441 const WebURL& script_url) { 442 return content_settings_->AllowScriptFromSource(frame, 443 enabled_per_settings, 444 script_url); 445 } 446 447 bool ChromeRenderViewObserver::allowStorage(WebFrame* frame, bool local) { 448 return content_settings_->AllowStorage(frame, local); 449 } 450 451 bool ChromeRenderViewObserver::allowReadFromClipboard(WebFrame* frame, 452 bool default_value) { 453 bool allowed = false; 454 // TODO(dcheng): Should we consider a toURL() method on WebSecurityOrigin? 455 Send(new ChromeViewHostMsg_CanTriggerClipboardRead( 456 routing_id(), GURL(frame->document().securityOrigin().toString().utf8()), 457 &allowed)); 458 return allowed; 459 } 460 461 bool ChromeRenderViewObserver::allowWriteToClipboard(WebFrame* frame, 462 bool default_value) { 463 bool allowed = false; 464 Send(new ChromeViewHostMsg_CanTriggerClipboardWrite( 465 routing_id(), GURL(frame->document().securityOrigin().toString().utf8()), 466 &allowed)); 467 return allowed; 468 } 469 470 bool ChromeRenderViewObserver::allowWebComponents(const WebDocument& document, 471 bool defaultValue) { 472 if (defaultValue) 473 return true; 474 475 WebSecurityOrigin origin = document.securityOrigin(); 476 if (EqualsASCII(origin.protocol(), chrome::kChromeUIScheme)) 477 return true; 478 479 if (const extensions::Extension* extension = GetExtension(origin)) { 480 if (extension->HasAPIPermission(APIPermission::kExperimental)) 481 return true; 482 } 483 484 return false; 485 } 486 487 bool ChromeRenderViewObserver::allowHTMLNotifications( 488 const WebDocument& document) { 489 CommandLine* command_line = CommandLine::ForCurrentProcess(); 490 if (command_line->HasSwitch(switches::kDisableHTMLNotifications)) 491 return false; 492 493 WebSecurityOrigin origin = document.securityOrigin(); 494 const extensions::Extension* extension = GetExtension(origin); 495 return extension && extension->HasAPIPermission(APIPermission::kNotification); 496 } 497 498 bool ChromeRenderViewObserver::allowMutationEvents(const WebDocument& document, 499 bool default_value) { 500 WebSecurityOrigin origin = document.securityOrigin(); 501 const extensions::Extension* extension = GetExtension(origin); 502 if (extension && extension->is_platform_app()) 503 return false; 504 return default_value; 505 } 506 507 bool ChromeRenderViewObserver::allowPushState(const WebDocument& document) { 508 WebSecurityOrigin origin = document.securityOrigin(); 509 const extensions::Extension* extension = GetExtension(origin); 510 return !extension || !extension->is_platform_app(); 511 } 512 513 static void SendInsecureContentSignal(int signal) { 514 UMA_HISTOGRAM_ENUMERATION("SSL.InsecureContent", signal, 515 INSECURE_CONTENT_NUM_EVENTS); 516 } 517 518 bool ChromeRenderViewObserver::allowDisplayingInsecureContent( 519 WebKit::WebFrame* frame, 520 bool allowed_per_settings, 521 const WebKit::WebSecurityOrigin& origin, 522 const WebKit::WebURL& resource_url) { 523 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY); 524 525 std::string origin_host(origin.host().utf8()); 526 GURL frame_gurl(frame->document().url()); 527 if (isHostInDomain(origin_host, kGoogleDotCom)) { 528 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE); 529 if (StartsWithASCII(frame_gurl.path(), kGoogleSupportPathPrefix, false)) { 530 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_SUPPORT); 531 } else if (StartsWithASCII(frame_gurl.path(), 532 kGoogleIntlPathPrefix, 533 false)) { 534 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_INTL); 535 } 536 } 537 538 if (origin_host == kWWWDotGoogleDotCom) { 539 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_WWW_GOOGLE); 540 if (StartsWithASCII(frame_gurl.path(), kGoogleReaderPathPrefix, false)) 541 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_READER); 542 } else if (origin_host == kMailDotGoogleDotCom) { 543 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_MAIL_GOOGLE); 544 } else if (origin_host == kPlusDotGoogleDotCom) { 545 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_PLUS_GOOGLE); 546 } else if (origin_host == kDocsDotGoogleDotCom) { 547 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_DOCS_GOOGLE); 548 } else if (origin_host == kSitesDotGoogleDotCom) { 549 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_SITES_GOOGLE); 550 } else if (origin_host == kPicasawebDotGoogleDotCom) { 551 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_PICASAWEB_GOOGLE); 552 } else if (origin_host == kCodeDotGoogleDotCom) { 553 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_CODE_GOOGLE); 554 } else if (origin_host == kGroupsDotGoogleDotCom) { 555 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GROUPS_GOOGLE); 556 } else if (origin_host == kMapsDotGoogleDotCom) { 557 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_MAPS_GOOGLE); 558 } else if (origin_host == kWWWDotYoutubeDotCom) { 559 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_YOUTUBE); 560 } 561 562 GURL resource_gurl(resource_url); 563 if (EndsWith(resource_gurl.path(), kDotHTML, false)) 564 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HTML); 565 566 if (allowed_per_settings || allow_displaying_insecure_content_) 567 return true; 568 569 Send(new ChromeViewHostMsg_DidBlockDisplayingInsecureContent(routing_id())); 570 571 return false; 572 } 573 574 bool ChromeRenderViewObserver::allowRunningInsecureContent( 575 WebKit::WebFrame* frame, 576 bool allowed_per_settings, 577 const WebKit::WebSecurityOrigin& origin, 578 const WebKit::WebURL& resource_url) { 579 std::string origin_host(origin.host().utf8()); 580 GURL frame_gurl(frame->document().url()); 581 DCHECK_EQ(frame_gurl.host(), origin_host); 582 583 bool is_google = isHostInDomain(origin_host, kGoogleDotCom); 584 if (is_google) { 585 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE); 586 if (StartsWithASCII(frame_gurl.path(), kGoogleSupportPathPrefix, false)) { 587 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_SUPPORT); 588 } else if (StartsWithASCII(frame_gurl.path(), 589 kGoogleIntlPathPrefix, 590 false)) { 591 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_INTL); 592 } 593 } 594 595 if (origin_host == kWWWDotGoogleDotCom) { 596 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_WWW_GOOGLE); 597 if (StartsWithASCII(frame_gurl.path(), kGoogleReaderPathPrefix, false)) 598 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_READER); 599 } else if (origin_host == kMailDotGoogleDotCom) { 600 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_MAIL_GOOGLE); 601 } else if (origin_host == kPlusDotGoogleDotCom) { 602 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_PLUS_GOOGLE); 603 } else if (origin_host == kDocsDotGoogleDotCom) { 604 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_DOCS_GOOGLE); 605 } else if (origin_host == kSitesDotGoogleDotCom) { 606 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_SITES_GOOGLE); 607 } else if (origin_host == kPicasawebDotGoogleDotCom) { 608 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_PICASAWEB_GOOGLE); 609 } else if (origin_host == kCodeDotGoogleDotCom) { 610 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_CODE_GOOGLE); 611 } else if (origin_host == kGroupsDotGoogleDotCom) { 612 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GROUPS_GOOGLE); 613 } else if (origin_host == kMapsDotGoogleDotCom) { 614 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_MAPS_GOOGLE); 615 } else if (origin_host == kWWWDotYoutubeDotCom) { 616 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_YOUTUBE); 617 } else if (EndsWith(origin_host, kDotGoogleUserContentDotCom, false)) { 618 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLEUSERCONTENT); 619 } 620 621 GURL resource_gurl(resource_url); 622 if (resource_gurl.host() == kWWWDotYoutubeDotCom) 623 SendInsecureContentSignal(INSECURE_CONTENT_RUN_TARGET_YOUTUBE); 624 625 if (EndsWith(resource_gurl.path(), kDotJS, false)) 626 SendInsecureContentSignal(INSECURE_CONTENT_RUN_JS); 627 else if (EndsWith(resource_gurl.path(), kDotCSS, false)) 628 SendInsecureContentSignal(INSECURE_CONTENT_RUN_CSS); 629 else if (EndsWith(resource_gurl.path(), kDotSWF, false)) 630 SendInsecureContentSignal(INSECURE_CONTENT_RUN_SWF); 631 632 if (!allow_running_insecure_content_ && !allowed_per_settings) { 633 content_settings_->DidNotAllowMixedScript(); 634 return false; 635 } 636 637 return true; 638 } 639 640 void ChromeRenderViewObserver::didNotAllowPlugins(WebFrame* frame) { 641 content_settings_->DidNotAllowPlugins(); 642 } 643 644 void ChromeRenderViewObserver::didNotAllowScript(WebFrame* frame) { 645 content_settings_->DidNotAllowScript(); 646 } 647 648 void ChromeRenderViewObserver::OnSetIsPrerendering(bool is_prerendering) { 649 if (is_prerendering) { 650 DCHECK(!prerender::PrerenderHelper::Get(render_view())); 651 // The PrerenderHelper will destroy itself either after recording histograms 652 // or on destruction of the RenderView. 653 new prerender::PrerenderHelper(render_view()); 654 } 655 } 656 657 void ChromeRenderViewObserver::DidStartLoading() { 658 if ((render_view()->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI) && 659 webui_javascript_.get()) { 660 render_view()->EvaluateScript(webui_javascript_->frame_xpath, 661 webui_javascript_->jscript, 662 webui_javascript_->id, 663 webui_javascript_->notify_result); 664 webui_javascript_.reset(); 665 } 666 } 667 668 void ChromeRenderViewObserver::DidStopLoading() { 669 WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); 670 GURL osd_url = main_frame->document().openSearchDescriptionURL(); 671 if (!osd_url.is_empty()) { 672 Send(new ChromeViewHostMsg_PageHasOSDD( 673 routing_id(), render_view()->GetPageId(), osd_url, 674 search_provider::AUTODETECTED_PROVIDER)); 675 } 676 677 // Don't capture pages including refresh meta tag. 678 if (HasRefreshMetaTag(main_frame)) 679 return; 680 681 CapturePageInfoLater( 682 render_view()->GetPageId(), 683 false, // preliminary_capture 684 base::TimeDelta::FromMilliseconds( 685 render_view()->GetContentStateImmediately() ? 686 0 : kDelayForCaptureMs)); 687 } 688 689 void ChromeRenderViewObserver::DidCommitProvisionalLoad( 690 WebFrame* frame, bool is_new_navigation) { 691 // Don't capture pages being not new, or including refresh meta tag. 692 if (!is_new_navigation || HasRefreshMetaTag(frame)) 693 return; 694 695 CapturePageInfoLater( 696 render_view()->GetPageId(), 697 true, // preliminary_capture 698 base::TimeDelta::FromMilliseconds(kDelayForForcedCaptureMs)); 699 } 700 701 void ChromeRenderViewObserver::DidClearWindowObject(WebFrame* frame) { 702 if (render_view()->GetEnabledBindings() & 703 content::BINDINGS_POLICY_EXTERNAL_HOST) { 704 GetExternalHostBindings()->BindToJavascript(frame, "externalHost"); 705 } 706 } 707 708 void ChromeRenderViewObserver::DidHandleGestureEvent( 709 const WebGestureEvent& event) { 710 if (event.type != WebKit::WebGestureEvent::GestureTap) 711 return; 712 713 WebKit::WebTextInputType text_input_type = 714 render_view()->GetWebView()->textInputInfo().type; 715 716 render_view()->Send(new ChromeViewHostMsg_FocusedNodeTouched( 717 routing_id(), 718 text_input_type != WebKit::WebTextInputTypeNone)); 719 } 720 721 void ChromeRenderViewObserver::CapturePageInfoLater(int page_id, 722 bool preliminary_capture, 723 base::TimeDelta delay) { 724 capture_timer_.Start( 725 FROM_HERE, 726 delay, 727 base::Bind(&ChromeRenderViewObserver::CapturePageInfo, 728 base::Unretained(this), 729 page_id, 730 preliminary_capture)); 731 } 732 733 void ChromeRenderViewObserver::CapturePageInfo(int page_id, 734 bool preliminary_capture) { 735 // If |page_id| is obsolete, we should stop indexing and capturing a page. 736 if (render_view()->GetPageId() != page_id) 737 return; 738 739 if (!render_view()->GetWebView()) 740 return; 741 742 WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); 743 if (!main_frame) 744 return; 745 746 // Don't index/capture pages that are in view source mode. 747 if (main_frame->isViewSourceModeEnabled()) 748 return; 749 750 // Don't index/capture pages that failed to load. This only checks the top 751 // level frame so the thumbnail may contain a frame that failed to load. 752 WebDataSource* ds = main_frame->dataSource(); 753 if (ds && ds->hasUnreachableURL()) 754 return; 755 756 // Don't index/capture pages that are being prerendered. 757 if (prerender::PrerenderHelper::IsPrerendering(render_view())) 758 return; 759 760 // Retrieve the frame's full text (up to kMaxIndexChars), and pass it to the 761 // translate helper for language detection and possible translation. 762 string16 contents; 763 base::TimeTicks capture_begin_time = base::TimeTicks::Now(); 764 CaptureText(main_frame, &contents); 765 UMA_HISTOGRAM_TIMES(kTranslateCaptureText, 766 base::TimeTicks::Now() - capture_begin_time); 767 if (translate_helper_) 768 translate_helper_->PageCaptured(page_id, contents); 769 770 // Skip indexing if this is not a new load. Note that the case where 771 // page_id == last_indexed_page_id_ is more complicated, since we need to 772 // reindex if the toplevel URL has changed (such as from a redirect), even 773 // though this may not cause the page id to be incremented. 774 if (page_id < last_indexed_page_id_) 775 return; 776 777 bool same_page_id = last_indexed_page_id_ == page_id; 778 if (!preliminary_capture) 779 last_indexed_page_id_ = page_id; 780 781 // Get the URL for this page. 782 GURL url(main_frame->document().url()); 783 if (url.is_empty()) { 784 if (!preliminary_capture) 785 last_indexed_url_ = GURL(); 786 return; 787 } 788 789 // If the page id is unchanged, check whether the URL (ignoring fragments) 790 // has changed. If so, we need to reindex. Otherwise, assume this is a 791 // reload, in-page navigation, or some other load type where we don't want to 792 // reindex. Note: subframe navigations after onload increment the page id, 793 // so these will trigger a reindex. 794 GURL stripped_url(StripRef(url)); 795 if (same_page_id && stripped_url == last_indexed_url_) 796 return; 797 798 if (!preliminary_capture) 799 last_indexed_url_ = stripped_url; 800 801 TRACE_EVENT0("renderer", "ChromeRenderViewObserver::CapturePageInfo"); 802 803 if (contents.size()) { 804 // Send the text to the browser for indexing (the browser might decide not 805 // to index, if the URL is HTTPS for instance). 806 Send(new ChromeViewHostMsg_PageContents(routing_id(), url, contents)); 807 } 808 809 #if defined(FULL_SAFE_BROWSING) 810 // Will swap out the string. 811 if (phishing_classifier_) 812 phishing_classifier_->PageCaptured(&contents, preliminary_capture); 813 #endif 814 } 815 816 void ChromeRenderViewObserver::CaptureText(WebFrame* frame, 817 string16* contents) { 818 contents->clear(); 819 if (!frame) 820 return; 821 822 #ifdef TIME_TEXT_RETRIEVAL 823 double begin = time_util::GetHighResolutionTimeNow(); 824 #endif 825 826 // get the contents of the frame 827 *contents = frame->contentAsText(kMaxIndexChars); 828 829 #ifdef TIME_TEXT_RETRIEVAL 830 double end = time_util::GetHighResolutionTimeNow(); 831 char buf[128]; 832 sprintf_s(buf, "%d chars retrieved for indexing in %gms\n", 833 contents.size(), (end - begin)*1000); 834 OutputDebugStringA(buf); 835 #endif 836 837 // When the contents are clipped to the maximum, we don't want to have a 838 // partial word indexed at the end that might have been clipped. Therefore, 839 // terminate the string at the last space to ensure no words are clipped. 840 if (contents->size() == kMaxIndexChars) { 841 size_t last_space_index = contents->find_last_of(kWhitespaceUTF16); 842 if (last_space_index == std::wstring::npos) 843 return; // don't index if we got a huge block of text with no spaces 844 contents->resize(last_space_index); 845 } 846 } 847 848 ExternalHostBindings* ChromeRenderViewObserver::GetExternalHostBindings() { 849 if (!external_host_bindings_.get()) { 850 external_host_bindings_.reset(new ExternalHostBindings( 851 render_view(), routing_id())); 852 } 853 return external_host_bindings_.get(); 854 } 855 856 bool ChromeRenderViewObserver::IsStrictSecurityHost(const std::string& host) { 857 return (strict_security_hosts_.find(host) != strict_security_hosts_.end()); 858 } 859 860 const extensions::Extension* ChromeRenderViewObserver::GetExtension( 861 const WebSecurityOrigin& origin) const { 862 if (!EqualsASCII(origin.protocol(), extensions::kExtensionScheme)) 863 return NULL; 864 865 const std::string extension_id = origin.host().utf8().data(); 866 if (!extension_dispatcher_->IsExtensionActive(extension_id)) 867 return NULL; 868 869 return extension_dispatcher_->extensions()->GetByID(extension_id); 870 } 871 872 bool ChromeRenderViewObserver::HasRefreshMetaTag(WebFrame* frame) { 873 if (!frame) 874 return false; 875 WebElement head = frame->document().head(); 876 if (head.isNull() || !head.hasChildNodes()) 877 return false; 878 879 const WebString tag_name(ASCIIToUTF16("meta")); 880 const WebString attribute_name(ASCIIToUTF16("http-equiv")); 881 882 WebNodeList children = head.childNodes(); 883 for (size_t i = 0; i < children.length(); ++i) { 884 WebNode node = children.item(i); 885 if (!node.isElementNode()) 886 continue; 887 WebElement element = node.to<WebElement>(); 888 if (!element.hasTagName(tag_name)) 889 continue; 890 WebString value = element.getAttribute(attribute_name); 891 if (value.isNull() || !LowerCaseEqualsASCII(value, "refresh")) 892 continue; 893 return true; 894 } 895 return false; 896 } 897