Home | History | Annotate | Download | only in renderer
      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