OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/renderer/extensions/extension_helper.h" | 5 #include "chrome/renderer/extensions/extension_helper.h" |
6 | 6 |
| 7 #include "base/command_line.h" |
7 #include "base/lazy_instance.h" | 8 #include "base/lazy_instance.h" |
| 9 #include "base/message_loop.h" |
| 10 #include "base/utf_string_conversions.h" |
| 11 #include "chrome/common/chrome_switches.h" |
8 #include "chrome/common/extensions/extension_messages.h" | 12 #include "chrome/common/extensions/extension_messages.h" |
| 13 #include "chrome/common/json_value_serializer.h" |
| 14 #include "chrome/common/url_constants.h" |
9 #include "chrome/renderer/extensions/extension_dispatcher.h" | 15 #include "chrome/renderer/extensions/extension_dispatcher.h" |
10 #include "chrome/renderer/extensions/extension_process_bindings.h" | 16 #include "chrome/renderer/extensions/extension_process_bindings.h" |
11 #include "chrome/renderer/extensions/renderer_extension_bindings.h" | 17 #include "chrome/renderer/extensions/renderer_extension_bindings.h" |
12 #include "chrome/renderer/extensions/user_script_idle_scheduler.h" | 18 #include "chrome/renderer/extensions/user_script_idle_scheduler.h" |
13 #include "chrome/renderer/extensions/user_script_slave.h" | 19 #include "chrome/renderer/extensions/user_script_slave.h" |
14 #include "content/renderer/render_view.h" | 20 #include "content/renderer/render_view.h" |
| 21 #include "webkit/glue/image_resource_fetcher.h" |
| 22 #include "webkit/glue/resource_fetcher.h" |
| 23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebConsoleMessage.h" |
15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | 24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" |
| 25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h" |
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | 26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" |
17 | 27 |
| 28 using WebKit::WebConsoleMessage; |
18 using WebKit::WebDataSource; | 29 using WebKit::WebDataSource; |
19 using WebKit::WebFrame; | 30 using WebKit::WebFrame; |
| 31 using WebKit::WebURLRequest; |
20 using WebKit::WebView; | 32 using WebKit::WebView; |
| 33 using webkit_glue::ImageResourceFetcher; |
| 34 using webkit_glue::ResourceFetcher; |
21 | 35 |
22 namespace { | 36 namespace { |
23 // Keeps a mapping from the frame pointer to a UserScriptIdleScheduler object. | 37 // Keeps a mapping from the frame pointer to a UserScriptIdleScheduler object. |
24 // We store this mapping per process, because a frame can jump from one | 38 // We store this mapping per process, because a frame can jump from one |
25 // document to another with adoptNode, and so having the object be a | 39 // document to another with adoptNode, and so having the object be a |
26 // RenderViewObserver means it might miss some notifications after it moves. | 40 // RenderViewObserver means it might miss some notifications after it moves. |
27 typedef std::map<WebFrame*, UserScriptIdleScheduler*> SchedulerMap; | 41 typedef std::map<WebFrame*, UserScriptIdleScheduler*> SchedulerMap; |
28 static base::LazyInstance<SchedulerMap> g_schedulers(base::LINKER_INITIALIZED); | 42 static base::LazyInstance<SchedulerMap> g_schedulers(base::LINKER_INITIALIZED); |
29 } | 43 } |
30 | 44 |
31 ExtensionHelper::ExtensionHelper(RenderView* render_view, | 45 ExtensionHelper::ExtensionHelper(RenderView* render_view, |
32 ExtensionDispatcher* extension_dispatcher) | 46 ExtensionDispatcher* extension_dispatcher) |
33 : RenderViewObserver(render_view), | 47 : RenderViewObserver(render_view), |
34 extension_dispatcher_(extension_dispatcher) { | 48 RenderViewObserverTracker<ExtensionHelper>(render_view), |
| 49 extension_dispatcher_(extension_dispatcher), |
| 50 pending_app_icon_requests_(0) { |
35 } | 51 } |
36 | 52 |
37 ExtensionHelper::~ExtensionHelper() { | 53 ExtensionHelper::~ExtensionHelper() { |
38 } | 54 } |
39 | 55 |
| 56 bool ExtensionHelper::InstallWebApplicationUsingDefinitionFile( |
| 57 WebFrame* frame, string16* error) { |
| 58 // There is an issue of drive-by installs with the below implementation. A web |
| 59 // site could force a user to install an app by timing the dialog to come up |
| 60 // just before the user clicks. |
| 61 // |
| 62 // We do show a success UI that allows users to uninstall, but it seems that |
| 63 // we might still want to put up an infobar before showing the install dialog. |
| 64 // |
| 65 // TODO(aa): Figure out this issue before removing the kEnableCrxlessWebApps |
| 66 // switch. |
| 67 if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| 68 switches::kEnableCrxlessWebApps)) { |
| 69 *error = ASCIIToUTF16("CRX-less web apps aren't enabled."); |
| 70 return false; |
| 71 } |
| 72 |
| 73 if (frame != frame->top()) { |
| 74 *error = ASCIIToUTF16("Applications can only be installed from the top " |
| 75 "frame."); |
| 76 return false; |
| 77 } |
| 78 |
| 79 if (pending_app_info_.get()) { |
| 80 *error = ASCIIToUTF16("An application install is already in progress."); |
| 81 return false; |
| 82 } |
| 83 |
| 84 pending_app_info_.reset(new WebApplicationInfo()); |
| 85 if (!web_apps::ParseWebAppFromWebDocument(frame, pending_app_info_.get(), |
| 86 error)) { |
| 87 return false; |
| 88 } |
| 89 |
| 90 if (!pending_app_info_->manifest_url.is_valid()) { |
| 91 *error = ASCIIToUTF16("Web application definition not found or invalid."); |
| 92 return false; |
| 93 } |
| 94 |
| 95 app_definition_fetcher_.reset(new ResourceFetcher( |
| 96 pending_app_info_->manifest_url, render_view()->webview()->mainFrame(), |
| 97 WebURLRequest::TargetIsSubresource, |
| 98 NewCallback(this, &ExtensionHelper::DidDownloadApplicationDefinition))); |
| 99 return true; |
| 100 } |
| 101 |
40 bool ExtensionHelper::OnMessageReceived(const IPC::Message& message) { | 102 bool ExtensionHelper::OnMessageReceived(const IPC::Message& message) { |
41 bool handled = true; | 103 bool handled = true; |
42 IPC_BEGIN_MESSAGE_MAP(ExtensionHelper, message) | 104 IPC_BEGIN_MESSAGE_MAP(ExtensionHelper, message) |
43 IPC_MESSAGE_HANDLER(ExtensionMsg_Response, OnExtensionResponse) | 105 IPC_MESSAGE_HANDLER(ExtensionMsg_Response, OnExtensionResponse) |
44 IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke, OnExtensionMessageInvoke) | 106 IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke, OnExtensionMessageInvoke) |
45 IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteCode, OnExecuteCode) | 107 IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteCode, OnExecuteCode) |
| 108 IPC_MESSAGE_HANDLER(ExtensionMsg_GetApplicationInfo, OnGetApplicationInfo) |
46 IPC_MESSAGE_UNHANDLED(handled = false) | 109 IPC_MESSAGE_UNHANDLED(handled = false) |
47 IPC_END_MESSAGE_MAP() | 110 IPC_END_MESSAGE_MAP() |
48 return handled; | 111 return handled; |
49 } | 112 } |
50 | 113 |
51 void ExtensionHelper::DidFinishDocumentLoad(WebFrame* frame) { | 114 void ExtensionHelper::DidFinishDocumentLoad(WebFrame* frame) { |
52 extension_dispatcher_->user_script_slave()->InjectScripts( | 115 extension_dispatcher_->user_script_slave()->InjectScripts( |
53 frame, UserScript::DOCUMENT_END); | 116 frame, UserScript::DOCUMENT_END); |
54 | 117 |
55 SchedulerMap::iterator i = g_schedulers.Get().find(frame); | 118 SchedulerMap::iterator i = g_schedulers.Get().find(frame); |
(...skipping 23 matching lines...) Expand all Loading... |
79 // won't be in the map. | 142 // won't be in the map. |
80 SchedulerMap::iterator i = g_schedulers.Get().find(frame); | 143 SchedulerMap::iterator i = g_schedulers.Get().find(frame); |
81 if (i == g_schedulers.Get().end()) | 144 if (i == g_schedulers.Get().end()) |
82 return; | 145 return; |
83 | 146 |
84 delete i->second; | 147 delete i->second; |
85 g_schedulers.Get().erase(i); | 148 g_schedulers.Get().erase(i); |
86 } | 149 } |
87 | 150 |
88 void ExtensionHelper::DidCreateDataSource(WebFrame* frame, WebDataSource* ds) { | 151 void ExtensionHelper::DidCreateDataSource(WebFrame* frame, WebDataSource* ds) { |
| 152 // If there are any app-related fetches in progress, they can be cancelled now |
| 153 // since we have navigated away from the page that created them. |
| 154 if (!frame->parent()) { |
| 155 app_icon_fetchers_.clear(); |
| 156 app_definition_fetcher_.reset(NULL); |
| 157 } |
| 158 |
89 // Check first if we created a scheduler for the frame, since this function | 159 // Check first if we created a scheduler for the frame, since this function |
90 // gets called for navigations within the document. | 160 // gets called for navigations within the document. |
91 if (g_schedulers.Get().count(frame)) | 161 if (g_schedulers.Get().count(frame)) |
92 return; | 162 return; |
93 | 163 |
94 g_schedulers.Get()[frame] = new UserScriptIdleScheduler( | 164 g_schedulers.Get()[frame] = new UserScriptIdleScheduler( |
95 frame, extension_dispatcher_); | 165 frame, extension_dispatcher_); |
96 } | 166 } |
97 | 167 |
98 void ExtensionHelper::OnExtensionResponse(int request_id, | 168 void ExtensionHelper::OnExtensionResponse(int request_id, |
(...skipping 21 matching lines...) Expand all Loading... |
120 routing_id(), params.request_id, false, "")); | 190 routing_id(), params.request_id, false, "")); |
121 return; | 191 return; |
122 } | 192 } |
123 | 193 |
124 // chrome.tabs.executeScript() only supports execution in either the top frame | 194 // chrome.tabs.executeScript() only supports execution in either the top frame |
125 // or all frames. We handle both cases in the top frame. | 195 // or all frames. We handle both cases in the top frame. |
126 SchedulerMap::iterator i = g_schedulers.Get().find(main_frame); | 196 SchedulerMap::iterator i = g_schedulers.Get().find(main_frame); |
127 if (i != g_schedulers.Get().end()) | 197 if (i != g_schedulers.Get().end()) |
128 i->second->ExecuteCode(params); | 198 i->second->ExecuteCode(params); |
129 } | 199 } |
| 200 |
| 201 void ExtensionHelper::OnGetApplicationInfo(int page_id) { |
| 202 WebApplicationInfo app_info; |
| 203 if (page_id == render_view()->page_id()) { |
| 204 string16 error; |
| 205 web_apps::ParseWebAppFromWebDocument( |
| 206 render_view()->webview()->mainFrame(), &app_info, &error); |
| 207 } |
| 208 |
| 209 // Prune out any data URLs in the set of icons. The browser process expects |
| 210 // any icon with a data URL to have originated from a favicon. We don't want |
| 211 // to decode arbitrary data URLs in the browser process. See |
| 212 // http://b/issue?id=1162972 |
| 213 for (size_t i = 0; i < app_info.icons.size(); ++i) { |
| 214 if (app_info.icons[i].url.SchemeIs(chrome::kDataScheme)) { |
| 215 app_info.icons.erase(app_info.icons.begin() + i); |
| 216 --i; |
| 217 } |
| 218 } |
| 219 |
| 220 Send(new ExtensionHostMsg_DidGetApplicationInfo( |
| 221 routing_id(), page_id, app_info)); |
| 222 } |
| 223 |
| 224 void ExtensionHelper::DidDownloadApplicationDefinition( |
| 225 const WebKit::WebURLResponse& response, |
| 226 const std::string& data) { |
| 227 scoped_ptr<WebApplicationInfo> app_info( |
| 228 pending_app_info_.release()); |
| 229 |
| 230 JSONStringValueSerializer serializer(data); |
| 231 int error_code = 0; |
| 232 std::string error_message; |
| 233 scoped_ptr<Value> result(serializer.Deserialize(&error_code, &error_message)); |
| 234 if (!result.get()) { |
| 235 AddErrorToRootConsole(UTF8ToUTF16(error_message)); |
| 236 return; |
| 237 } |
| 238 |
| 239 string16 error_message_16; |
| 240 if (!web_apps::ParseWebAppFromDefinitionFile(result.get(), app_info.get(), |
| 241 &error_message_16)) { |
| 242 AddErrorToRootConsole(error_message_16); |
| 243 return; |
| 244 } |
| 245 |
| 246 if (!app_info->icons.empty()) { |
| 247 pending_app_info_.reset(app_info.release()); |
| 248 pending_app_icon_requests_ = |
| 249 static_cast<int>(pending_app_info_->icons.size()); |
| 250 for (size_t i = 0; i < pending_app_info_->icons.size(); ++i) { |
| 251 app_icon_fetchers_.push_back(linked_ptr<ImageResourceFetcher>( |
| 252 new ImageResourceFetcher( |
| 253 pending_app_info_->icons[i].url, |
| 254 render_view()->webview()->mainFrame(), |
| 255 static_cast<int>(i), |
| 256 pending_app_info_->icons[i].width, |
| 257 WebURLRequest::TargetIsFavicon, |
| 258 NewCallback( |
| 259 this, &ExtensionHelper::DidDownloadApplicationIcon)))); |
| 260 } |
| 261 } else { |
| 262 Send(new ExtensionHostMsg_InstallApplication(routing_id(), *app_info)); |
| 263 } |
| 264 } |
| 265 |
| 266 void ExtensionHelper::DidDownloadApplicationIcon(ImageResourceFetcher* fetcher, |
| 267 const SkBitmap& image) { |
| 268 pending_app_info_->icons[fetcher->id()].data = image; |
| 269 |
| 270 // Remove the image fetcher from our pending list. We're in the callback from |
| 271 // ImageResourceFetcher, best to delay deletion. |
| 272 RenderView::ImageResourceFetcherList::iterator i; |
| 273 for (i = app_icon_fetchers_.begin(); i != app_icon_fetchers_.end(); ++i) { |
| 274 if (i->get() == fetcher) { |
| 275 i->release(); |
| 276 app_icon_fetchers_.erase(i); |
| 277 break; |
| 278 } |
| 279 } |
| 280 |
| 281 // We're in the callback from the ImageResourceFetcher, best to delay |
| 282 // deletion. |
| 283 MessageLoop::current()->DeleteSoon(FROM_HERE, fetcher); |
| 284 |
| 285 if (--pending_app_icon_requests_ > 0) |
| 286 return; |
| 287 |
| 288 // There is a maximum size of IPC on OS X and Linux that we have run into in |
| 289 // some situations. We're not sure what it is, but our hypothesis is in the |
| 290 // neighborhood of 1 MB. |
| 291 // |
| 292 // To be on the safe side, we give ourselves 128 KB for just the image data. |
| 293 // This should be more than enough for 128, 48, and 16 px 32-bit icons. If we |
| 294 // want to start allowing larger icons (see bug 63406), we'll have to either |
| 295 // experiment mor ewith this and find the real limit, or else come up with |
| 296 // some alternative way to transmit the icon data to the browser process. |
| 297 // |
| 298 // See also: bug 63729. |
| 299 const size_t kMaxIconSize = 1024 * 128; |
| 300 size_t actual_icon_size = 0; |
| 301 for (size_t i = 0; i < pending_app_info_->icons.size(); ++i) { |
| 302 size_t current_size = pending_app_info_->icons[i].data.getSize(); |
| 303 if (current_size > kMaxIconSize - actual_icon_size) { |
| 304 AddErrorToRootConsole(ASCIIToUTF16( |
| 305 "Icons are too large. Maximum total size for app icons is 128 KB.")); |
| 306 return; |
| 307 } |
| 308 actual_icon_size += current_size; |
| 309 } |
| 310 |
| 311 Send(new ExtensionHostMsg_InstallApplication( |
| 312 routing_id(), *pending_app_info_)); |
| 313 pending_app_info_.reset(NULL); |
| 314 } |
| 315 |
| 316 void ExtensionHelper::AddErrorToRootConsole(const string16& message) { |
| 317 if (render_view()->webview() && render_view()->webview()->mainFrame()) { |
| 318 render_view()->webview()->mainFrame()->addMessageToConsole( |
| 319 WebConsoleMessage(WebConsoleMessage::LevelError, message)); |
| 320 } |
| 321 } |
OLD | NEW |