| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/renderer/chrome_render_view_observer.h" |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/command_line.h" |
| #include "base/debug/trace_event.h" |
| #include "base/message_loop.h" |
| #include "base/metrics/histogram.h" |
| #include "base/string_util.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/icon_messages.h" |
| #include "chrome/common/prerender_messages.h" |
| #include "chrome/common/render_messages.h" |
| #include "chrome/common/thumbnail_score.h" |
| #include "chrome/common/thumbnail_support.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/renderer/chrome_render_process_observer.h" |
| #include "chrome/renderer/content_settings_observer.h" |
| #include "chrome/renderer/extensions/extension_dispatcher.h" |
| #include "chrome/renderer/external_host_bindings.h" |
| #include "chrome/renderer/frame_sniffer.h" |
| #include "chrome/renderer/prerender/prerender_helper.h" |
| #include "chrome/renderer/safe_browsing/phishing_classifier_delegate.h" |
| #include "chrome/renderer/translate_helper.h" |
| #include "chrome/renderer/webview_color_overlay.h" |
| #include "content/public/common/bindings_policy.h" |
| #include "content/public/renderer/render_view.h" |
| #include "content/public/renderer/content_renderer_client.h" |
| #include "net/base/data_url.h" |
| #include "skia/ext/image_operations.h" |
| #include "skia/ext/platform_canvas.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityObject.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCString.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLRequest.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" |
| #include "ui/gfx/color_utils.h" |
| #include "ui/gfx/favicon_size.h" |
| #include "ui/gfx/size.h" |
| #include "ui/gfx/skbitmap_operations.h" |
| #include "webkit/glue/image_decoder.h" |
| #include "webkit/glue/image_resource_fetcher.h" |
| #include "webkit/glue/webkit_glue.h" |
| #include "v8/include/v8-testing.h" |
| |
| using WebKit::WebAccessibilityObject; |
| using WebKit::WebCString; |
| using WebKit::WebDataSource; |
| using WebKit::WebDocument; |
| using WebKit::WebFrame; |
| using WebKit::WebIconURL; |
| using WebKit::WebRect; |
| using WebKit::WebSecurityOrigin; |
| using WebKit::WebSize; |
| using WebKit::WebString; |
| using WebKit::WebTouchEvent; |
| using WebKit::WebURL; |
| using WebKit::WebURLRequest; |
| using WebKit::WebView; |
| using WebKit::WebVector; |
| using webkit_glue::ImageResourceFetcher; |
| |
| // Delay in milliseconds that we'll wait before capturing the page contents |
| // and thumbnail. |
| static const int kDelayForCaptureMs = 500; |
| |
| // Typically, we capture the page data once the page is loaded. |
| // Sometimes, the page never finishes to load, preventing the page capture |
| // To workaround this problem, we always perform a capture after the following |
| // delay. |
| static const int kDelayForForcedCaptureMs = 6000; |
| |
| // define to write the time necessary for thumbnail/DOM text retrieval, |
| // respectively, into the system debug log |
| // #define TIME_TEXT_RETRIEVAL |
| |
| // maximum number of characters in the document to index, any text beyond this |
| // point will be clipped |
| static const size_t kMaxIndexChars = 65535; |
| |
| // Size of the thumbnails that we'll generate |
| static const int kThumbnailWidth = 212; |
| static const int kThumbnailHeight = 132; |
| |
| // Constants for UMA statistic collection. |
| static const char kWWWDotGoogleDotCom[] = "www.google.com"; |
| static const char kMailDotGoogleDotCom[] = "mail.google.com"; |
| static const char kPlusDotGoogleDotCom[] = "plus.google.com"; |
| static const char kDocsDotGoogleDotCom[] = "docs.google.com"; |
| static const char kSitesDotGoogleDotCom[] = "sites.google.com"; |
| static const char kPicasawebDotGoogleDotCom[] = "picasaweb.google.com"; |
| static const char kCodeDotGoogleDotCom[] = "code.google.com"; |
| static const char kGroupsDotGoogleDotCom[] = "groups.google.com"; |
| static const char kMapsDotGoogleDotCom[] = "maps.google.com"; |
| static const char kWWWDotYoutubeDotCom[] = "www.youtube.com"; |
| static const char kDotGoogleUserContentDotCom[] = ".googleusercontent.com"; |
| static const char kGoogleReaderPathPrefix[] = "/reader/"; |
| static const char kGoogleSupportPathPrefix[] = "/support/"; |
| static const char kGoogleIntlPathPrefix[] = "/intl/"; |
| static const char kDotJS[] = ".js"; |
| static const char kDotCSS[] = ".css"; |
| static const char kDotSWF[] = ".swf"; |
| static const char kDotHTML[] = ".html"; |
| enum { |
| INSECURE_CONTENT_DISPLAY = 0, |
| INSECURE_CONTENT_DISPLAY_HOST_GOOGLE, |
| INSECURE_CONTENT_DISPLAY_HOST_WWW_GOOGLE, |
| INSECURE_CONTENT_DISPLAY_HTML, |
| INSECURE_CONTENT_RUN, |
| INSECURE_CONTENT_RUN_HOST_GOOGLE, |
| INSECURE_CONTENT_RUN_HOST_WWW_GOOGLE, |
| INSECURE_CONTENT_RUN_TARGET_YOUTUBE, |
| INSECURE_CONTENT_RUN_JS, |
| INSECURE_CONTENT_RUN_CSS, |
| INSECURE_CONTENT_RUN_SWF, |
| INSECURE_CONTENT_DISPLAY_HOST_YOUTUBE, |
| INSECURE_CONTENT_RUN_HOST_YOUTUBE, |
| INSECURE_CONTENT_RUN_HOST_GOOGLEUSERCONTENT, |
| INSECURE_CONTENT_DISPLAY_HOST_MAIL_GOOGLE, |
| INSECURE_CONTENT_RUN_HOST_MAIL_GOOGLE, |
| INSECURE_CONTENT_DISPLAY_HOST_PLUS_GOOGLE, |
| INSECURE_CONTENT_RUN_HOST_PLUS_GOOGLE, |
| INSECURE_CONTENT_DISPLAY_HOST_DOCS_GOOGLE, |
| INSECURE_CONTENT_RUN_HOST_DOCS_GOOGLE, |
| INSECURE_CONTENT_DISPLAY_HOST_SITES_GOOGLE, |
| INSECURE_CONTENT_RUN_HOST_SITES_GOOGLE, |
| INSECURE_CONTENT_DISPLAY_HOST_PICASAWEB_GOOGLE, |
| INSECURE_CONTENT_RUN_HOST_PICASAWEB_GOOGLE, |
| INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_READER, |
| INSECURE_CONTENT_RUN_HOST_GOOGLE_READER, |
| INSECURE_CONTENT_DISPLAY_HOST_CODE_GOOGLE, |
| INSECURE_CONTENT_RUN_HOST_CODE_GOOGLE, |
| INSECURE_CONTENT_DISPLAY_HOST_GROUPS_GOOGLE, |
| INSECURE_CONTENT_RUN_HOST_GROUPS_GOOGLE, |
| INSECURE_CONTENT_DISPLAY_HOST_MAPS_GOOGLE, |
| INSECURE_CONTENT_RUN_HOST_MAPS_GOOGLE, |
| INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_SUPPORT, |
| INSECURE_CONTENT_RUN_HOST_GOOGLE_SUPPORT, |
| INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_INTL, |
| INSECURE_CONTENT_RUN_HOST_GOOGLE_INTL, |
| INSECURE_CONTENT_NUM_EVENTS |
| }; |
| |
| // Constants for mixed-content blocking. |
| static const char kGoogleDotCom[] = "google.com"; |
| |
| static bool PaintViewIntoCanvas(WebView* view, |
| skia::PlatformCanvas& canvas) { |
| view->layout(); |
| const WebSize& size = view->size(); |
| |
| if (!canvas.initialize(size.width, size.height, true)) |
| return false; |
| |
| view->paint(webkit_glue::ToWebCanvas(&canvas), |
| WebRect(0, 0, size.width, size.height)); |
| // TODO: Add a way to snapshot the whole page, not just the currently |
| // visible part. |
| |
| return true; |
| } |
| |
| // Calculates how "boring" a thumbnail is. The boring score is the |
| // 0,1 ranged percentage of pixels that are the most common |
| // luma. Higher boring scores indicate that a higher percentage of a |
| // bitmap are all the same brightness. |
| static double CalculateBoringScore(SkBitmap* bitmap) { |
| int histogram[256] = {0}; |
| color_utils::BuildLumaHistogram(*bitmap, histogram); |
| |
| int color_count = *std::max_element(histogram, histogram + 256); |
| int pixel_count = bitmap->width() * bitmap->height(); |
| return static_cast<double>(color_count) / pixel_count; |
| } |
| |
| static FaviconURL::IconType ToFaviconType(WebIconURL::Type type) { |
| switch (type) { |
| case WebIconURL::TypeFavicon: |
| return FaviconURL::FAVICON; |
| case WebIconURL::TypeTouch: |
| return FaviconURL::TOUCH_ICON; |
| case WebIconURL::TypeTouchPrecomposed: |
| return FaviconURL::TOUCH_PRECOMPOSED_ICON; |
| case WebIconURL::TypeInvalid: |
| return FaviconURL::INVALID_ICON; |
| } |
| return FaviconURL::INVALID_ICON; |
| } |
| |
| static bool isHostInDomain(const std::string& host, const std::string& domain) { |
| return (EndsWith(host, domain, false) && |
| (host.length() == domain.length() || |
| (host.length() > domain.length() && |
| host[host.length() - domain.length() - 1] == '.'))); |
| } |
| |
| namespace { |
| GURL StripRef(const GURL& url) { |
| GURL::Replacements replacements; |
| replacements.ClearRef(); |
| return url.ReplaceComponents(replacements); |
| } |
| } // namespace |
| |
| ChromeRenderViewObserver::ChromeRenderViewObserver( |
| content::RenderView* render_view, |
| ContentSettingsObserver* content_settings, |
| ChromeRenderProcessObserver* chrome_render_process_observer, |
| ExtensionDispatcher* extension_dispatcher, |
| TranslateHelper* translate_helper) |
| : content::RenderViewObserver(render_view), |
| chrome_render_process_observer_(chrome_render_process_observer), |
| extension_dispatcher_(extension_dispatcher), |
| content_settings_(content_settings), |
| translate_helper_(translate_helper), |
| phishing_classifier_(NULL), |
| last_indexed_page_id_(-1), |
| allow_displaying_insecure_content_(false), |
| allow_running_insecure_content_(false), |
| warned_about_insecure_content_(false), |
| capture_timer_(false, false) { |
| const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
| render_view->GetWebView()->setPermissionClient(this); |
| if (!command_line.HasSwitch(switches::kDisableClientSidePhishingDetection)) |
| OnSetClientSidePhishingDetection(true); |
| } |
| |
| ChromeRenderViewObserver::~ChromeRenderViewObserver() { |
| } |
| |
| bool ChromeRenderViewObserver::OnMessageReceived(const IPC::Message& message) { |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(ChromeRenderViewObserver, message) |
| IPC_MESSAGE_HANDLER(ChromeViewMsg_WebUIJavaScript, OnWebUIJavaScript) |
| IPC_MESSAGE_HANDLER(ChromeViewMsg_CaptureSnapshot, OnCaptureSnapshot) |
| IPC_MESSAGE_HANDLER(ChromeViewMsg_HandleMessageFromExternalHost, |
| OnHandleMessageFromExternalHost) |
| IPC_MESSAGE_HANDLER(ChromeViewMsg_JavaScriptStressTestControl, |
| OnJavaScriptStressTestControl) |
| IPC_MESSAGE_HANDLER(IconMsg_DownloadFavicon, OnDownloadFavicon) |
| IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAllowDisplayingInsecureContent, |
| OnSetAllowDisplayingInsecureContent) |
| IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAllowRunningInsecureContent, |
| OnSetAllowRunningInsecureContent) |
| IPC_MESSAGE_HANDLER(ChromeViewMsg_SetClientSidePhishingDetection, |
| OnSetClientSidePhishingDetection) |
| IPC_MESSAGE_HANDLER(ChromeViewMsg_SetVisuallyDeemphasized, |
| OnSetVisuallyDeemphasized) |
| #if defined(OS_CHROMEOS) |
| IPC_MESSAGE_HANDLER(ChromeViewMsg_StartFrameSniffer, OnStartFrameSniffer) |
| #endif |
| IPC_MESSAGE_HANDLER(ChromeViewMsg_GetFPS, OnGetFPS) |
| IPC_MESSAGE_HANDLER(ChromeViewMsg_AddStrictSecurityHost, |
| OnAddStrictSecurityHost) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| |
| // Filter only. |
| IPC_BEGIN_MESSAGE_MAP(ChromeRenderViewObserver, message) |
| IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering); |
| IPC_END_MESSAGE_MAP() |
| |
| return handled; |
| } |
| |
| void ChromeRenderViewObserver::OnWebUIJavaScript( |
| const string16& frame_xpath, |
| const string16& jscript, |
| int id, |
| bool notify_result) { |
| webui_javascript_.reset(new WebUIJavaScript()); |
| webui_javascript_->frame_xpath = frame_xpath; |
| webui_javascript_->jscript = jscript; |
| webui_javascript_->id = id; |
| webui_javascript_->notify_result = notify_result; |
| } |
| |
| void ChromeRenderViewObserver::OnCaptureSnapshot() { |
| SkBitmap snapshot; |
| bool error = false; |
| |
| WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); |
| if (!main_frame) |
| error = true; |
| |
| if (!error && !CaptureSnapshot(render_view()->GetWebView(), &snapshot)) |
| error = true; |
| |
| DCHECK(error == snapshot.empty()) << |
| "Snapshot should be empty on error, non-empty otherwise."; |
| |
| // Send the snapshot to the browser process. |
| Send(new ChromeViewHostMsg_Snapshot(routing_id(), snapshot)); |
| } |
| |
| void ChromeRenderViewObserver::OnHandleMessageFromExternalHost( |
| const std::string& message, |
| const std::string& origin, |
| const std::string& target) { |
| if (message.empty()) |
| return; |
| GetExternalHostBindings()->ForwardMessageFromExternalHost(message, origin, |
| target); |
| } |
| |
| void ChromeRenderViewObserver::OnJavaScriptStressTestControl(int cmd, |
| int param) { |
| if (cmd == kJavaScriptStressTestSetStressRunType) { |
| v8::Testing::SetStressRunType(static_cast<v8::Testing::StressType>(param)); |
| } else if (cmd == kJavaScriptStressTestPrepareStressRun) { |
| v8::Testing::PrepareStressRun(param); |
| } |
| } |
| |
| void ChromeRenderViewObserver::OnDownloadFavicon(int id, |
| const GURL& image_url, |
| int image_size) { |
| bool data_image_failed = false; |
| if (image_url.SchemeIs("data")) { |
| SkBitmap data_image = ImageFromDataUrl(image_url); |
| data_image_failed = data_image.empty(); |
| if (!data_image_failed) { |
| Send(new IconHostMsg_DidDownloadFavicon( |
| routing_id(), id, image_url, false, data_image)); |
| } |
| } |
| |
| if (data_image_failed || |
| !DownloadFavicon(id, image_url, image_size)) { |
| Send(new IconHostMsg_DidDownloadFavicon( |
| routing_id(), id, image_url, true, SkBitmap())); |
| } |
| } |
| |
| void ChromeRenderViewObserver::OnSetAllowDisplayingInsecureContent(bool allow) { |
| allow_displaying_insecure_content_ = allow; |
| WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); |
| if (main_frame) |
| main_frame->reload(); |
| } |
| |
| void ChromeRenderViewObserver::OnSetAllowRunningInsecureContent(bool allow) { |
| allow_running_insecure_content_ = allow; |
| OnSetAllowDisplayingInsecureContent(allow); |
| } |
| |
| void ChromeRenderViewObserver::OnAddStrictSecurityHost( |
| const std::string& host) { |
| strict_security_hosts_.insert(host); |
| } |
| |
| void ChromeRenderViewObserver::Navigate(const GURL& url) { |
| // Execute cache clear operations that were postponed until a navigation |
| // event (including tab reload). |
| if (chrome_render_process_observer_) |
| chrome_render_process_observer_->ExecutePendingClearCache(); |
| } |
| |
| void ChromeRenderViewObserver::OnSetClientSidePhishingDetection( |
| bool enable_phishing_detection) { |
| #if defined(ENABLE_SAFE_BROWSING) && !defined(OS_CHROMEOS) |
| phishing_classifier_ = enable_phishing_detection ? |
| safe_browsing::PhishingClassifierDelegate::Create( |
| render_view(), NULL) : |
| NULL; |
| #endif |
| } |
| |
| void ChromeRenderViewObserver::OnSetVisuallyDeemphasized(bool deemphasized) { |
| bool already_deemphasized = !!dimmed_color_overlay_.get(); |
| if (already_deemphasized == deemphasized) |
| return; |
| |
| if (deemphasized) { |
| // 70% opaque grey. |
| SkColor greyish = SkColorSetARGB(178, 0, 0, 0); |
| dimmed_color_overlay_.reset( |
| new WebViewColorOverlay(render_view(), greyish)); |
| } else { |
| dimmed_color_overlay_.reset(); |
| } |
| } |
| |
| void ChromeRenderViewObserver::OnStartFrameSniffer(const string16& frame_name) { |
| new FrameSniffer(render_view(), frame_name); |
| } |
| |
| void ChromeRenderViewObserver::OnGetFPS() { |
| float fps = (render_view()->GetFilteredTimePerFrame() > 0.0f)? |
| 1.0f / render_view()->GetFilteredTimePerFrame() : 0.0f; |
| Send(new ChromeViewHostMsg_FPS(routing_id(), fps)); |
| } |
| |
| bool ChromeRenderViewObserver::allowDatabase( |
| WebFrame* frame, |
| const WebString& name, |
| const WebString& display_name, |
| unsigned long estimated_size) { |
| return content_settings_->AllowDatabase( |
| frame, name, display_name, estimated_size); |
| } |
| |
| bool ChromeRenderViewObserver::allowFileSystem(WebFrame* frame) { |
| return content_settings_->AllowFileSystem(frame); |
| } |
| |
| bool ChromeRenderViewObserver::allowImage(WebFrame* frame, |
| bool enabled_per_settings, |
| const WebURL& image_url) { |
| return content_settings_->AllowImage(frame, enabled_per_settings, image_url); |
| } |
| |
| bool ChromeRenderViewObserver::allowIndexedDB(WebFrame* frame, |
| const WebString& name, |
| const WebSecurityOrigin& origin) { |
| return content_settings_->AllowIndexedDB(frame, name, origin); |
| } |
| |
| bool ChromeRenderViewObserver::allowPlugins(WebFrame* frame, |
| bool enabled_per_settings) { |
| return content_settings_->AllowPlugins(frame, enabled_per_settings); |
| } |
| |
| bool ChromeRenderViewObserver::allowScript(WebFrame* frame, |
| bool enabled_per_settings) { |
| return content_settings_->AllowScript(frame, enabled_per_settings); |
| } |
| |
| bool ChromeRenderViewObserver::allowScriptFromSource( |
| WebFrame* frame, |
| bool enabled_per_settings, |
| const WebURL& script_url) { |
| return content_settings_->AllowScriptFromSource(frame, |
| enabled_per_settings, |
| script_url); |
| } |
| |
| bool ChromeRenderViewObserver::allowScriptExtension( |
| WebFrame* frame, const WebString& extension_name, int extension_group) { |
| return extension_dispatcher_->AllowScriptExtension( |
| frame, extension_name.utf8(), extension_group); |
| } |
| |
| bool ChromeRenderViewObserver::allowScriptExtension( |
| WebFrame* frame, const WebString& extension_name, int extension_group, |
| int world_id) { |
| return extension_dispatcher_->AllowScriptExtension( |
| frame, extension_name.utf8(), extension_group, world_id); |
| } |
| |
| bool ChromeRenderViewObserver::allowStorage(WebFrame* frame, bool local) { |
| return content_settings_->AllowStorage(frame, local); |
| } |
| |
| bool ChromeRenderViewObserver::allowReadFromClipboard(WebFrame* frame, |
| bool default_value) { |
| bool allowed = false; |
| // TODO(dcheng): Should we consider a toURL() method on WebSecurityOrigin? |
| Send(new ChromeViewHostMsg_CanTriggerClipboardRead( |
| routing_id(), GURL(frame->document().securityOrigin().toString().utf8()), |
| &allowed)); |
| return allowed; |
| } |
| |
| bool ChromeRenderViewObserver::allowWriteToClipboard(WebFrame* frame, |
| bool default_value) { |
| bool allowed = false; |
| Send(new ChromeViewHostMsg_CanTriggerClipboardWrite( |
| routing_id(), GURL(frame->document().securityOrigin().toString().utf8()), |
| &allowed)); |
| return allowed; |
| } |
| |
| bool ChromeRenderViewObserver::IsExperimentalWebFeatureAllowed( |
| const WebDocument& document) { |
| // Experimental Web API is enabled when |
| // - The specific API is allowed from command line flag, or |
| // - If the document is running extensions or apps which |
| // has the "experimental" permission, or |
| // - The document is running Web UI. |
| WebSecurityOrigin origin = document.securityOrigin(); |
| if (EqualsASCII(origin.protocol(), chrome::kChromeUIScheme)) |
| return true; |
| const extensions::Extension* extension = |
| extension_dispatcher_->extensions()->GetExtensionOrAppByURL( |
| ExtensionURLInfo(origin, document.url())); |
| if (!extension) |
| return false; |
| return (extension_dispatcher_->IsExtensionActive(extension->id()) && |
| extension->HasAPIPermission(ExtensionAPIPermission::kExperimental)); |
| } |
| |
| bool ChromeRenderViewObserver::allowWebComponents(const WebDocument& document, |
| bool defaultValue) { |
| if (defaultValue) |
| return true; |
| return IsExperimentalWebFeatureAllowed(document); |
| } |
| |
| static void SendInsecureContentSignal(int signal) { |
| UMA_HISTOGRAM_ENUMERATION("SSL.InsecureContent", signal, |
| INSECURE_CONTENT_NUM_EVENTS); |
| } |
| |
| bool ChromeRenderViewObserver::allowDisplayingInsecureContent( |
| WebKit::WebFrame* frame, |
| bool allowed_per_settings, |
| const WebKit::WebSecurityOrigin& origin, |
| const WebKit::WebURL& resource_url) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY); |
| |
| std::string origin_host(origin.host().utf8()); |
| GURL frame_gurl(frame->document().url()); |
| if (isHostInDomain(origin_host, kGoogleDotCom)) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE); |
| if (StartsWithASCII(frame_gurl.path(), kGoogleSupportPathPrefix, false)) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_SUPPORT); |
| } else if (StartsWithASCII(frame_gurl.path(), |
| kGoogleIntlPathPrefix, |
| false)) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_INTL); |
| } |
| } |
| |
| if (origin_host == kWWWDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_WWW_GOOGLE); |
| if (StartsWithASCII(frame_gurl.path(), kGoogleReaderPathPrefix, false)) |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_READER); |
| } else if (origin_host == kMailDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_MAIL_GOOGLE); |
| } else if (origin_host == kPlusDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_PLUS_GOOGLE); |
| } else if (origin_host == kDocsDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_DOCS_GOOGLE); |
| } else if (origin_host == kSitesDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_SITES_GOOGLE); |
| } else if (origin_host == kPicasawebDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_PICASAWEB_GOOGLE); |
| } else if (origin_host == kCodeDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_CODE_GOOGLE); |
| } else if (origin_host == kGroupsDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GROUPS_GOOGLE); |
| } else if (origin_host == kMapsDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_MAPS_GOOGLE); |
| } else if (origin_host == kWWWDotYoutubeDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_YOUTUBE); |
| } |
| |
| GURL resource_gurl(resource_url); |
| if (EndsWith(resource_gurl.path(), kDotHTML, false)) |
| SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HTML); |
| |
| if (allowed_per_settings || allow_displaying_insecure_content_) |
| return true; |
| |
| if (!IsStrictSecurityHost(origin_host)) |
| Send(new ChromeViewHostMsg_DidBlockDisplayingInsecureContent(routing_id())); |
| |
| return false; |
| } |
| |
| bool ChromeRenderViewObserver::allowRunningInsecureContent( |
| WebKit::WebFrame* frame, |
| bool allowed_per_settings, |
| const WebKit::WebSecurityOrigin& origin, |
| const WebKit::WebURL& resource_url) { |
| std::string origin_host(origin.host().utf8()); |
| GURL frame_gurl(frame->document().url()); |
| DCHECK_EQ(frame_gurl.host(), origin_host); |
| |
| bool is_google = isHostInDomain(origin_host, kGoogleDotCom); |
| if (is_google) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE); |
| if (StartsWithASCII(frame_gurl.path(), kGoogleSupportPathPrefix, false)) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_SUPPORT); |
| } else if (StartsWithASCII(frame_gurl.path(), |
| kGoogleIntlPathPrefix, |
| false)) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_INTL); |
| } |
| } |
| |
| if (origin_host == kWWWDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_WWW_GOOGLE); |
| if (StartsWithASCII(frame_gurl.path(), kGoogleReaderPathPrefix, false)) |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_READER); |
| } else if (origin_host == kMailDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_MAIL_GOOGLE); |
| } else if (origin_host == kPlusDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_PLUS_GOOGLE); |
| } else if (origin_host == kDocsDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_DOCS_GOOGLE); |
| } else if (origin_host == kSitesDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_SITES_GOOGLE); |
| } else if (origin_host == kPicasawebDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_PICASAWEB_GOOGLE); |
| } else if (origin_host == kCodeDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_CODE_GOOGLE); |
| } else if (origin_host == kGroupsDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GROUPS_GOOGLE); |
| } else if (origin_host == kMapsDotGoogleDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_MAPS_GOOGLE); |
| } else if (origin_host == kWWWDotYoutubeDotCom) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_YOUTUBE); |
| } else if (EndsWith(origin_host, kDotGoogleUserContentDotCom, false)) { |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLEUSERCONTENT); |
| } |
| |
| GURL resource_gurl(resource_url); |
| if (resource_gurl.host() == kWWWDotYoutubeDotCom) |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_TARGET_YOUTUBE); |
| |
| if (EndsWith(resource_gurl.path(), kDotJS, false)) |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_JS); |
| else if (EndsWith(resource_gurl.path(), kDotCSS, false)) |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_CSS); |
| else if (EndsWith(resource_gurl.path(), kDotSWF, false)) |
| SendInsecureContentSignal(INSECURE_CONTENT_RUN_SWF); |
| |
| if (!allow_running_insecure_content_ && !allowed_per_settings) { |
| if (!warned_about_insecure_content_ && !IsStrictSecurityHost(origin_host)) { |
| warned_about_insecure_content_ = true; |
| Send(new ChromeViewHostMsg_DidBlockRunningInsecureContent(routing_id())); |
| } |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void ChromeRenderViewObserver::didNotAllowPlugins(WebFrame* frame) { |
| content_settings_->DidNotAllowPlugins(frame); |
| } |
| |
| void ChromeRenderViewObserver::didNotAllowScript(WebFrame* frame) { |
| content_settings_->DidNotAllowScript(frame); |
| } |
| |
| void ChromeRenderViewObserver::OnSetIsPrerendering(bool is_prerendering) { |
| if (is_prerendering) { |
| DCHECK(!prerender::PrerenderHelper::Get(render_view())); |
| // The PrerenderHelper will destroy itself either after recording histograms |
| // or on destruction of the RenderView. |
| new prerender::PrerenderHelper(render_view()); |
| } |
| } |
| |
| void ChromeRenderViewObserver::DidStartLoading() { |
| if ((render_view()->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI) && |
| webui_javascript_.get()) { |
| render_view()->EvaluateScript(webui_javascript_->frame_xpath, |
| webui_javascript_->jscript, |
| webui_javascript_->id, |
| webui_javascript_->notify_result); |
| webui_javascript_.reset(); |
| } |
| } |
| |
| void ChromeRenderViewObserver::DidStopLoading() { |
| CapturePageInfoLater( |
| false, // preliminary_capture |
| base::TimeDelta::FromMilliseconds( |
| render_view()->GetContentStateImmediately() ? |
| 0 : kDelayForCaptureMs)); |
| |
| WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); |
| GURL osd_url = main_frame->document().openSearchDescriptionURL(); |
| if (!osd_url.is_empty()) { |
| Send(new ChromeViewHostMsg_PageHasOSDD( |
| routing_id(), render_view()->GetPageId(), osd_url, |
| search_provider::AUTODETECTED_PROVIDER)); |
| } |
| |
| int icon_types = WebIconURL::TypeFavicon; |
| if (chrome::kEnableTouchIcon) |
| icon_types |= WebIconURL::TypeTouchPrecomposed | WebIconURL::TypeTouch; |
| |
| WebVector<WebIconURL> icon_urls = |
| render_view()->GetWebView()->mainFrame()->iconURLs(icon_types); |
| std::vector<FaviconURL> urls; |
| for (size_t i = 0; i < icon_urls.size(); i++) { |
| WebURL url = icon_urls[i].iconURL(); |
| if (!url.isEmpty()) |
| urls.push_back(FaviconURL(url, ToFaviconType(icon_urls[i].iconType()))); |
| } |
| if (!urls.empty()) { |
| Send(new IconHostMsg_UpdateFaviconURL( |
| routing_id(), render_view()->GetPageId(), urls)); |
| } |
| } |
| |
| void ChromeRenderViewObserver::DidChangeIcon(WebFrame* frame, |
| WebIconURL::Type icon_type) { |
| if (frame->parent()) |
| return; |
| |
| if (!chrome::kEnableTouchIcon && |
| icon_type != WebIconURL::TypeFavicon) |
| return; |
| |
| WebVector<WebIconURL> icon_urls = frame->iconURLs(icon_type); |
| std::vector<FaviconURL> urls; |
| for (size_t i = 0; i < icon_urls.size(); i++) { |
| urls.push_back(FaviconURL(icon_urls[i].iconURL(), |
| ToFaviconType(icon_urls[i].iconType()))); |
| } |
| Send(new IconHostMsg_UpdateFaviconURL( |
| routing_id(), render_view()->GetPageId(), urls)); |
| } |
| |
| void ChromeRenderViewObserver::DidCommitProvisionalLoad( |
| WebFrame* frame, bool is_new_navigation) { |
| if (!is_new_navigation) |
| return; |
| |
| CapturePageInfoLater( |
| true, // preliminary_capture |
| base::TimeDelta::FromMilliseconds(kDelayForForcedCaptureMs)); |
| } |
| |
| void ChromeRenderViewObserver::DidClearWindowObject(WebFrame* frame) { |
| if (render_view()->GetEnabledBindings() & |
| content::BINDINGS_POLICY_EXTERNAL_HOST) { |
| GetExternalHostBindings()->BindToJavascript(frame, "externalHost"); |
| } |
| } |
| |
| void ChromeRenderViewObserver::DidHandleTouchEvent(const WebTouchEvent& event) { |
| // TODO(mazda): Consider using WebKit::WebInputEvent::GestureTap event when |
| // it's implemented. Only sends the message on touch end event |
| // for now. |
| if (event.type != WebKit::WebInputEvent::TouchEnd) |
| return; |
| // Ignore the case of multiple touches |
| if (event.touchesLength != 1) |
| return; |
| if (render_view()->GetWebView()->textInputType() == |
| WebKit::WebTextInputTypeNone) { |
| return; |
| } |
| WebKit::WebNode node = render_view()->GetFocusedNode(); |
| if (node.isNull()) |
| return; |
| WebKit::WebAccessibilityObject accessibility = |
| render_view()->GetWebView()->accessibilityObject(); |
| if (accessibility.isNull()) |
| return; |
| const WebKit::WebTouchPoint point = event.touches[0]; |
| accessibility = accessibility.hitTest(point.position); |
| if (accessibility.isNull()) |
| return; |
| if (accessibility.node() == node) |
| render_view()->Send(new ChromeViewHostMsg_FocusedEditableNodeTouched( |
| render_view()->GetRoutingID())); |
| } |
| |
| void ChromeRenderViewObserver::CapturePageInfoLater(bool preliminary_capture, |
| base::TimeDelta delay) { |
| capture_timer_.Start( |
| FROM_HERE, |
| delay, |
| base::Bind(&ChromeRenderViewObserver::CapturePageInfo, |
| base::Unretained(this), |
| preliminary_capture)); |
| } |
| |
| void ChromeRenderViewObserver::CapturePageInfo(bool preliminary_capture) { |
| int page_id = render_view()->GetPageId(); |
| |
| if (!render_view()->GetWebView()) |
| return; |
| |
| WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); |
| if (!main_frame) |
| return; |
| |
| // Don't index/capture pages that are in view source mode. |
| if (main_frame->isViewSourceModeEnabled()) |
| return; |
| |
| // Don't index/capture pages that failed to load. This only checks the top |
| // level frame so the thumbnail may contain a frame that failed to load. |
| WebDataSource* ds = main_frame->dataSource(); |
| if (ds && ds->hasUnreachableURL()) |
| return; |
| |
| // Don't index/capture pages that are being prerendered. |
| if (prerender::PrerenderHelper::IsPrerendering(render_view())) |
| return; |
| |
| // Retrieve the frame's full text (up to kMaxIndexChars), and pass it to the |
| // translate helper for language detection and possible translation. |
| string16 contents; |
| CaptureText(main_frame, &contents); |
| if (translate_helper_) |
| translate_helper_->PageCaptured(contents); |
| |
| // Skip indexing if this is not a new load. Note that the case where |
| // page_id == last_indexed_page_id_ is more complicated, since we need to |
| // reindex if the toplevel URL has changed (such as from a redirect), even |
| // though this may not cause the page id to be incremented. |
| if (page_id < last_indexed_page_id_) |
| return; |
| |
| bool same_page_id = last_indexed_page_id_ == page_id; |
| if (!preliminary_capture) |
| last_indexed_page_id_ = page_id; |
| |
| // Get the URL for this page. |
| GURL url(main_frame->document().url()); |
| if (url.is_empty()) { |
| if (!preliminary_capture) |
| last_indexed_url_ = GURL(); |
| return; |
| } |
| |
| // If the page id is unchanged, check whether the URL (ignoring fragments) |
| // has changed. If so, we need to reindex. Otherwise, assume this is a |
| // reload, in-page navigation, or some other load type where we don't want to |
| // reindex. Note: subframe navigations after onload increment the page id, |
| // so these will trigger a reindex. |
| GURL stripped_url(StripRef(url)); |
| if (same_page_id && stripped_url == last_indexed_url_) |
| return; |
| |
| if (!preliminary_capture) |
| last_indexed_url_ = stripped_url; |
| |
| TRACE_EVENT0("renderer", "ChromeRenderViewObserver::CapturePageInfo"); |
| |
| if (contents.size()) { |
| // Send the text to the browser for indexing (the browser might decide not |
| // to index, if the URL is HTTPS for instance). |
| Send(new ChromeViewHostMsg_PageContents(routing_id(), url, page_id, |
| contents)); |
| } |
| |
| // Generate the thumbnail here if the in-browser thumbnailing isn't |
| // enabled. TODO(mazda): Remove this and related code once in-browser |
| // thumbnailing is supported on all platforms (http://crbug.com/120003). |
| if (!ShouldEnableInBrowserThumbnailing()) |
| CaptureThumbnail(); |
| |
| #if defined(ENABLE_SAFE_BROWSING) |
| // Will swap out the string. |
| if (phishing_classifier_) |
| phishing_classifier_->PageCaptured(&contents, preliminary_capture); |
| #endif |
| } |
| |
| void ChromeRenderViewObserver::CaptureText(WebFrame* frame, |
| string16* contents) { |
| contents->clear(); |
| if (!frame) |
| return; |
| |
| #ifdef TIME_TEXT_RETRIEVAL |
| double begin = time_util::GetHighResolutionTimeNow(); |
| #endif |
| |
| // get the contents of the frame |
| *contents = frame->contentAsText(kMaxIndexChars); |
| |
| #ifdef TIME_TEXT_RETRIEVAL |
| double end = time_util::GetHighResolutionTimeNow(); |
| char buf[128]; |
| sprintf_s(buf, "%d chars retrieved for indexing in %gms\n", |
| contents.size(), (end - begin)*1000); |
| OutputDebugStringA(buf); |
| #endif |
| |
| // When the contents are clipped to the maximum, we don't want to have a |
| // partial word indexed at the end that might have been clipped. Therefore, |
| // terminate the string at the last space to ensure no words are clipped. |
| if (contents->size() == kMaxIndexChars) { |
| size_t last_space_index = contents->find_last_of(kWhitespaceUTF16); |
| if (last_space_index == std::wstring::npos) |
| return; // don't index if we got a huge block of text with no spaces |
| contents->resize(last_space_index); |
| } |
| } |
| |
| void ChromeRenderViewObserver::CaptureThumbnail() { |
| WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); |
| if (!main_frame) |
| return; |
| |
| // get the URL for this page |
| GURL url(main_frame->document().url()); |
| if (url.is_empty()) |
| return; |
| |
| if (render_view()->GetSize().IsEmpty()) |
| return; // Don't create an empty thumbnail! |
| |
| TRACE_EVENT0("renderer", "ChromeRenderViewObserver::CaptureThumbnail"); |
| |
| ThumbnailScore score; |
| SkBitmap thumbnail; |
| if (!CaptureFrameThumbnail(render_view()->GetWebView(), kThumbnailWidth, |
| kThumbnailHeight, &thumbnail, &score)) |
| return; |
| |
| // send the thumbnail message to the browser process |
| Send(new ChromeViewHostMsg_Thumbnail(routing_id(), url, score, thumbnail)); |
| } |
| |
| bool ChromeRenderViewObserver::CaptureFrameThumbnail(WebView* view, |
| int w, |
| int h, |
| SkBitmap* thumbnail, |
| ThumbnailScore* score) { |
| base::TimeTicks beginning_time = base::TimeTicks::Now(); |
| |
| skia::PlatformCanvas canvas; |
| |
| { |
| TRACE_EVENT0("renderer", |
| "ChromeRenderViewObserver::CaptureFrameThumbnail::PaintViewIntoCanvas"); |
| // Paint |view| into |canvas|. |
| if (!PaintViewIntoCanvas(view, canvas)) |
| return false; |
| } |
| |
| SkDevice* device = skia::GetTopDevice(canvas); |
| |
| const SkBitmap& src_bmp = device->accessBitmap(false); |
| // Cut off the vertical scrollbars (if any). |
| int src_bmp_width = view->mainFrame()->contentsSize().width; |
| |
| SkRect dest_rect = { 0, 0, SkIntToScalar(w), SkIntToScalar(h) }; |
| float dest_aspect = dest_rect.width() / dest_rect.height(); |
| |
| // Get the src rect so that we can preserve the aspect ratio while filling |
| // the destination. |
| SkIRect src_rect; |
| if (src_bmp_width < dest_rect.width() || |
| src_bmp.height() < dest_rect.height()) { |
| // Source image is smaller: we clip the part of source image within the |
| // dest rect, and then stretch it to fill the dest rect. We don't respect |
| // the aspect ratio in this case. |
| src_rect.set(0, 0, static_cast<S16CPU>(dest_rect.width()), |
| static_cast<S16CPU>(dest_rect.height())); |
| score->good_clipping = false; |
| } else { |
| float src_aspect = static_cast<float>(src_bmp_width) / src_bmp.height(); |
| if (src_aspect > dest_aspect) { |
| // Wider than tall, clip horizontally: we center the smaller thumbnail in |
| // the wider screen. |
| S16CPU new_width = static_cast<S16CPU>(src_bmp.height() * dest_aspect); |
| S16CPU x_offset = (src_bmp_width - new_width) / 2; |
| src_rect.set(x_offset, 0, new_width + x_offset, src_bmp.height()); |
| score->good_clipping = |
| (src_aspect >= ThumbnailScore::kTooWideAspectRatio) ? false : true; |
| } else { |
| src_rect.set(0, 0, src_bmp_width, |
| static_cast<S16CPU>(src_bmp_width / dest_aspect)); |
| score->good_clipping = true; |
| } |
| } |
| |
| score->at_top = (view->mainFrame()->scrollOffset().height == 0); |
| |
| SkBitmap subset; |
| device->accessBitmap(false).extractSubset(&subset, src_rect); |
| |
| TRACE_EVENT_BEGIN0("renderer", |
| "ChromeRenderViewObserver::CaptureFrameThumbnail::DownsampleByTwo"); |
| // First do a fast downsample by powers of two to get close to the final size. |
| SkBitmap downsampled_subset = |
| SkBitmapOperations::DownsampleByTwoUntilSize(subset, w, h); |
| TRACE_EVENT_END0("renderer", |
| "ChromeRenderViewObserver::CaptureFrameThumbnail::DownsampleByTwo"); |
| |
| { |
| TRACE_EVENT0("renderer", |
| "ChromeRenderViewObserver::CaptureFrameThumbnail::DownsampleLanczos3"); |
| // Do a high-quality resize from the downscaled size to the final size. |
| *thumbnail = skia::ImageOperations::Resize( |
| downsampled_subset, skia::ImageOperations::RESIZE_LANCZOS3, w, h); |
| } |
| |
| score->boring_score = CalculateBoringScore(thumbnail); |
| |
| HISTOGRAM_TIMES("Renderer4.Thumbnail", |
| base::TimeTicks::Now() - beginning_time); |
| |
| return true; |
| } |
| |
| bool ChromeRenderViewObserver::CaptureSnapshot(WebView* view, |
| SkBitmap* snapshot) { |
| base::TimeTicks beginning_time = base::TimeTicks::Now(); |
| |
| skia::PlatformCanvas canvas; |
| if (!PaintViewIntoCanvas(view, canvas)) |
| return false; |
| |
| SkDevice* device = skia::GetTopDevice(canvas); |
| |
| const SkBitmap& bitmap = device->accessBitmap(false); |
| if (!bitmap.copyTo(snapshot, SkBitmap::kARGB_8888_Config)) |
| return false; |
| |
| HISTOGRAM_TIMES("Renderer4.Snapshot", |
| base::TimeTicks::Now() - beginning_time); |
| return true; |
| } |
| |
| ExternalHostBindings* ChromeRenderViewObserver::GetExternalHostBindings() { |
| if (!external_host_bindings_.get()) { |
| external_host_bindings_.reset(new ExternalHostBindings( |
| render_view(), routing_id())); |
| } |
| return external_host_bindings_.get(); |
| } |
| |
| bool ChromeRenderViewObserver::DownloadFavicon(int id, |
| const GURL& image_url, |
| int image_size) { |
| // Make sure webview was not shut down. |
| if (!render_view()->GetWebView()) |
| return false; |
| // Create an image resource fetcher and assign it with a call back object. |
| image_fetchers_.push_back(linked_ptr<ImageResourceFetcher>( |
| new ImageResourceFetcher( |
| image_url, render_view()->GetWebView()->mainFrame(), id, image_size, |
| WebURLRequest::TargetIsFavicon, |
| base::Bind(&ChromeRenderViewObserver::DidDownloadFavicon, |
| base::Unretained(this))))); |
| return true; |
| } |
| |
| void ChromeRenderViewObserver::DidDownloadFavicon( |
| ImageResourceFetcher* fetcher, const SkBitmap& image) { |
| // Notify requester of image download status. |
| Send(new IconHostMsg_DidDownloadFavicon(routing_id(), |
| fetcher->id(), |
| fetcher->image_url(), |
| image.isNull(), |
| image)); |
| |
| // Remove the image fetcher from our pending list. We're in the callback from |
| // ImageResourceFetcher, best to delay deletion. |
| ImageResourceFetcherList::iterator iter; |
| for (iter = image_fetchers_.begin(); iter != image_fetchers_.end(); ++iter) { |
| if (iter->get() == fetcher) { |
| iter->release(); |
| image_fetchers_.erase(iter); |
| break; |
| } |
| } |
| MessageLoop::current()->DeleteSoon(FROM_HERE, fetcher); |
| } |
| |
| SkBitmap ChromeRenderViewObserver::ImageFromDataUrl(const GURL& url) const { |
| std::string mime_type, char_set, data; |
| if (net::DataURL::Parse(url, &mime_type, &char_set, &data) && !data.empty()) { |
| // Decode the favicon using WebKit's image decoder. |
| webkit_glue::ImageDecoder decoder( |
| gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize)); |
| const unsigned char* src_data = |
| reinterpret_cast<const unsigned char*>(&data[0]); |
| |
| return decoder.Decode(src_data, data.size()); |
| } |
| return SkBitmap(); |
| } |
| |
| bool ChromeRenderViewObserver::IsStrictSecurityHost(const std::string& host) { |
| return (strict_security_hosts_.find(host) != strict_security_hosts_.end()); |
| } |