|
OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/extensions/extension_offscreen_tabs_module.h" | |
6 | |
7 #include <algorithm> | |
8 #include <hash_map> | |
9 #include <vector> | |
10 | |
11 #include "base/base64.h" | |
12 #include "base/json/json_writer.h" | |
13 #include "base/memory/ref_counted_memory.h" | |
14 #include "base/stl_util.h" | |
15 #include "base/string_number_conversions.h" | |
16 #include "base/string_util.h" | |
17 #include "base/values.h" | |
18 #include "chrome/browser/extensions/extension_event_router.h" | |
19 #include "chrome/browser/extensions/extension_function_dispatcher.h" | |
20 #include "chrome/browser/extensions/extension_host.h" | |
21 #include "chrome/browser/extensions/extension_message_service.h" | |
22 #include "chrome/browser/extensions/extension_offscreen_tabs_module_constants.h" | |
23 #include "chrome/browser/extensions/extension_service.h" | |
24 #include "chrome/browser/extensions/extension_tabs_module.h" | |
25 #include "chrome/browser/net/url_fixer_upper.h" | |
26 #include "chrome/browser/profiles/profile.h" | |
27 #include "chrome/browser/ui/browser.h" | |
28 #include "chrome/browser/ui/browser_navigator.h" | |
29 #include "chrome/browser/ui/browser_window.h" | |
30 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | |
31 #include "chrome/browser/ui/window_sizer.h" | |
32 #include "chrome/common/chrome_notification_types.h" | |
33 #include "chrome/common/extensions/extension.h" | |
34 #include "chrome/common/extensions/extension_error_utils.h" | |
35 #include "chrome/common/extensions/extension_messages.h" | |
36 #include "chrome/common/pref_names.h" | |
37 #include "chrome/common/url_constants.h" | |
38 #include "content/browser/renderer_host/backing_store.h" | |
39 #include "content/browser/renderer_host/render_view_host.h" | |
40 #include "content/browser/renderer_host/render_view_host_delegate.h" | |
41 #include "content/browser/tab_contents/navigation_entry.h" | |
42 #include "content/browser/tab_contents/tab_contents.h" | |
43 #include "content/browser/tab_contents/tab_contents_view.h" | |
44 #include "content/common/content_client.h" | |
45 #include "content/common/notification_service.h" | |
46 #include "skia/ext/image_operations.h" | |
47 #include "skia/ext/platform_canvas.h" | |
48 #include "third_party/skia/include/core/SkBitmap.h" | |
49 #include "ui/gfx/codec/jpeg_codec.h" | |
50 #include "ui/gfx/codec/png_codec.h" | |
51 | |
52 using WebKit::WebInputEvent; | |
53 | |
54 namespace keys = extension_offscreen_tabs_module_constants; | |
55 | |
56 const int ToDataUrlOffscreenTabFunction::kDefaultQuality = 90; | |
sky
2011/09/08 18:12:26
Can you assign the value in the definition? If not
alexbost
2011/09/10 04:47:53
Done.
| |
57 | |
58 namespace { | |
59 | |
60 // Maps | |
61 // tab id -> tab contents | |
62 typedef base::hash_map<int, TabContents*> TabIdToTabMap; | |
63 TabIdToTabMap* tabs_map = NULL; | |
jstritar
2011/09/08 16:58:20
nit: add 1 line of whitespace to make it easier to
jstritar
2011/09/08 16:58:20
actually, I don't think you need TabIdToTabMap at
alexbost
2011/09/10 04:47:53
Done.
alexbost
2011/09/10 04:47:53
I have combined all maps into a Map class.
| |
64 // tab id -> vector of offscreen tab contents | |
65 typedef base::hash_map<int, std::vector<TabContents*> > TabIdToOffscreenTabsMap; | |
66 TabIdToOffscreenTabsMap* offscreen_tabs_map = NULL; | |
jstritar
2011/09/08 16:58:20
how about calling this map OffscreenTabsMap and up
alexbost
2011/09/10 04:47:53
Done.
| |
67 // tab id -> extension id | |
68 typedef base::hash_map<int, const std::string*> TabIdToExtensionIdMap; | |
69 TabIdToExtensionIdMap* extension_id_map = NULL; | |
sky
2011/09/08 18:12:26
Do you really need so many mappings? Could you ins
alexbost
2011/09/10 04:47:53
Done.
| |
70 | |
71 // Event managers | |
72 TabsEventManager* tabs_event_manager = NULL; | |
73 OffscreenTabsEventManager* offscreen_tabs_event_manager = NULL; | |
jstritar
2011/09/08 16:58:20
move these to ExtensionService?
alexbost
2011/09/10 04:47:53
I would leave this for a future refactoring patch.
| |
74 | |
75 // Background page tab contents. Used by the test API. | |
76 TabContents* background_page_tab_contents = NULL; | |
77 | |
78 // Util ------------------------------------------------------------------------ | |
79 | |
80 TabIdToTabMap* GetTabIdToTabMap() { | |
81 if (tabs_map == NULL) | |
82 tabs_map = new TabIdToTabMap(); | |
83 | |
84 return tabs_map; | |
85 } | |
86 | |
87 TabIdToOffscreenTabsMap* GetTabIdToOffscreenTabsMap() { | |
88 if (offscreen_tabs_map == NULL) | |
89 offscreen_tabs_map = new TabIdToOffscreenTabsMap(); | |
90 | |
91 return offscreen_tabs_map; | |
92 } | |
93 | |
94 TabIdToExtensionIdMap* GetTabIdToExtensionIdMap() { | |
95 if (extension_id_map == NULL) | |
96 extension_id_map = new TabIdToExtensionIdMap(); | |
97 | |
98 return extension_id_map; | |
99 } | |
100 | |
101 TabsEventManager* GetTabsEventManager() { | |
102 if (tabs_event_manager == NULL) | |
103 tabs_event_manager = new TabsEventManager(); | |
104 | |
105 return tabs_event_manager; | |
106 } | |
107 | |
108 OffscreenTabsEventManager* GetOffscreenTabsEventManager() { | |
109 if (offscreen_tabs_event_manager == NULL) | |
110 offscreen_tabs_event_manager = new OffscreenTabsEventManager(); | |
111 | |
112 return offscreen_tabs_event_manager; | |
113 } | |
114 | |
115 TabContents* GetBackgroundPageTabContents(Profile* profile = NULL) { | |
jstritar
2011/09/08 16:58:20
We usually try to stay way from default arguments.
alexbost
2011/09/10 04:47:53
Done.
| |
116 if (profile && background_page_tab_contents == NULL) | |
sky
2011/09/08 18:12:26
What if the profile differs from the profile you c
alexbost
2011/09/10 04:47:53
We are assuming that offscreen tabs will not be cr
| |
117 background_page_tab_contents = | |
118 new TabContents(profile, NULL, MSG_ROUTING_NONE, NULL, NULL); | |
119 | |
120 return background_page_tab_contents; | |
121 } | |
122 | |
123 int GetTabId(const TabContents* tab_contents) { | |
jstritar
2011/09/08 16:58:20
can you get rid of this method and call ExtensionT
alexbost
2011/09/10 04:47:53
Done.
| |
124 return ExtensionTabUtil::GetTabId(tab_contents); | |
125 } | |
126 | |
127 bool GetTabById(const int tab_id, | |
128 TabContents** tab_contents, | |
129 std::string* error_message) { | |
130 *tab_contents = (*GetTabIdToTabMap())[tab_id]; | |
sky
2011/09/08 18:12:26
Don't you want to use find so that you don't creat
alexbost
2011/09/10 04:47:53
Done.
| |
131 | |
132 if (tab_contents) | |
sky
2011/09/08 18:12:26
Shouldn't this be *tab_contents?
alexbost
2011/09/10 04:47:53
Done.
| |
133 return true; | |
134 | |
135 *error_message = ExtensionErrorUtils::FormatErrorMessage( | |
136 keys::kTabNotFoundError, base::IntToString(tab_id)); | |
137 return false; | |
138 } | |
139 | |
140 bool GetOffscreenTabById(const int tab_id, | |
141 TabContentsWrapper** offscreen_tab, | |
142 const TabContents* tab_contents, | |
143 std::string* error_message) { | |
jstritar
2011/09/08 16:58:20
Could you group the input and output arguments tog
alexbost
2011/09/10 04:47:53
Done.
| |
144 std::vector<TabContents*> offscreen_tab_contents_vector = | |
jstritar
2011/09/08 16:58:20
nit: renaming this offscreen_tabs might make same
alexbost
2011/09/10 04:47:53
Done.
| |
145 (*GetTabIdToOffscreenTabsMap())[GetTabId(tab_contents)]; | |
jstritar
2011/09/08 16:58:20
I think it'd be a little more readable to do:
GetT
alexbost
2011/09/10 04:47:53
Done.
| |
146 | |
147 for (std::vector<TabContents*>::iterator | |
148 it_offscreen_tab_contents = offscreen_tab_contents_vector.begin(); | |
jstritar
2011/09/08 16:58:20
you could probably rename this to just "it" or "of
alexbost
2011/09/10 04:47:53
Done.
| |
149 it_offscreen_tab_contents != offscreen_tab_contents_vector.end(); | |
150 ++it_offscreen_tab_contents) | |
151 if (GetTabId(*it_offscreen_tab_contents) == tab_id) { | |
152 *offscreen_tab = TabContentsWrapper::GetCurrentWrapperForContents( | |
153 *it_offscreen_tab_contents); | |
154 | |
155 return true; | |
156 } | |
157 | |
158 *error_message = ExtensionErrorUtils::FormatErrorMessage( | |
159 keys::kOffscreenTabNotFoundError, base::IntToString(tab_id)); | |
160 return false; | |
161 } | |
162 | |
163 bool GetTabOfOffscreenTab(TabContents* offscreen_tab_contents, | |
164 TabContents** tab_contents, | |
165 std::string* error_message) { | |
166 int offscreen_tab_id = GetTabId(offscreen_tab_contents); | |
167 | |
168 for (TabIdToOffscreenTabsMap::iterator | |
169 it = GetTabIdToOffscreenTabsMap()->begin(); | |
170 it != GetTabIdToOffscreenTabsMap()->end(); ++it) | |
171 for (std::vector<TabContents*>::iterator it_offscreen_tab_contents = | |
172 it->second.begin(); | |
173 it_offscreen_tab_contents != it->second.end(); | |
174 ++it_offscreen_tab_contents) | |
175 if (GetTabId(*it_offscreen_tab_contents) == offscreen_tab_id) { | |
176 if (!GetTabById(it->first, tab_contents, error_message)) | |
177 return false; | |
178 | |
179 return true; | |
180 } | |
181 | |
182 *error_message = ExtensionErrorUtils::FormatErrorMessage( | |
183 keys::kTabOfOffscreenTabNotFoundError, | |
184 base::IntToString(offscreen_tab_id)); | |
185 return false; | |
186 } | |
187 | |
188 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module. | |
189 // Takes |url_string| and returns a GURL which is either valid and absolute | |
190 // or invalid. If |url_string| is not directly interpretable as a valid (it is | |
191 // likely a relative URL) an attempt is made to resolve it. |extension| is | |
192 // provided so it can be resolved relative to its extension base | |
193 // (chrome-extension://<id>/). Using the source frame url would be more correct, | |
194 // but because the api shipped with urls resolved relative to their extension | |
195 // base, we decided it wasn't worth breaking existing extensions to fix. | |
196 GURL ResolvePossiblyRelativeURL(const std::string& url_string, | |
197 const Extension* extension) { | |
198 GURL url = GURL(url_string); | |
199 if (!url.is_valid()) | |
200 url = extension->GetResourceURL(url_string); | |
201 | |
202 return url; | |
203 } | |
204 | |
205 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module. | |
206 bool IsCrashURL(const GURL& url) { | |
207 // Check a fixed-up URL, to normalize the scheme and parse hosts correctly. | |
208 GURL fixed_url = | |
209 URLFixerUpper::FixupURL(url.possibly_invalid_spec(), std::string()); | |
210 return (fixed_url.SchemeIs(chrome::kChromeUIScheme) && | |
211 (fixed_url.host() == chrome::kChromeUIBrowserCrashHost || | |
212 fixed_url.host() == chrome::kChromeUICrashHost)); | |
213 } | |
214 | |
215 std::string GetTabUrl(const TabContents* tab_contents) { | |
jstritar
2011/09/08 16:58:20
Not sure you really need this method...
alexbost
2011/09/10 04:47:53
Done.
| |
216 return tab_contents->GetURL().spec(); | |
217 } | |
218 | |
219 int GetTabWidth(const TabContents* tab_contents) { | |
220 const TabContentsWrapper* tab = | |
221 TabContentsWrapper::GetCurrentWrapperForContents(tab_contents); | |
222 | |
223 if (tab && tab->view()) | |
224 return tab->view()->GetContainerSize().width(); | |
225 | |
226 return -1; | |
jstritar
2011/09/08 16:58:20
under what circumstances would tab this return -1?
alexbost
2011/09/10 04:47:53
I did this as a precaution so we don't segfault on
| |
227 } | |
228 | |
229 int GetTabHeight(const TabContents* tab_contents) { | |
230 const TabContentsWrapper* tab = | |
231 TabContentsWrapper::GetCurrentWrapperForContents(tab_contents); | |
232 | |
233 if (tab && tab->view()) | |
234 return tab->view()->GetContainerSize().height(); | |
235 | |
236 return -1; | |
237 } | |
238 | |
239 bool GetCurrentTab(TabContents** tab_contents, | |
240 ExtensionFunctionDispatcher* dispatcher, | |
241 Profile* profile, | |
242 std::string error_message) { | |
243 *tab_contents = dispatcher->delegate()->GetAssociatedTabContents(); | |
244 | |
245 // Background page (no associated tab contents) | |
246 if (!*tab_contents) | |
247 *tab_contents = GetBackgroundPageTabContents(profile); | |
248 | |
249 if (tab_contents) | |
250 return true; | |
251 | |
252 error_message = keys::kCurrentTabNotFound; | |
253 return false; | |
254 } | |
255 | |
256 void CloseOffscreenTab(TabContents* tab_contents) { | |
257 GetOffscreenTabsEventManager()->UnregisterForTabNotifications(tab_contents); | |
258 | |
259 // TODO(alexbost): Is this the best way to close an offscreen tab? | |
260 delete TabContentsWrapper::GetCurrentWrapperForContents(tab_contents); | |
261 } | |
262 | |
263 DictionaryValue* CreateOffscreenTabValue(const TabContentsWrapper* tab) { | |
264 DictionaryValue* result = new DictionaryValue(); | |
265 result->SetInteger(keys::kIdKey, GetTabId(tab->tab_contents())); | |
jstritar
2011/09/08 16:58:20
How about you put the TabContents in a local varia
alexbost
2011/09/10 04:47:53
Done.
| |
266 result->SetString(keys::kUrlKey, GetTabUrl(tab->tab_contents())); | |
267 result->SetInteger(keys::kWidthKey, GetTabWidth(tab->tab_contents())); | |
268 result->SetInteger(keys::kHeightKey, GetTabHeight(tab->tab_contents())); | |
269 | |
270 return result; | |
271 } | |
272 | |
273 DictionaryValue* CreateOffscreenTabValue(const TabContents* tab_contents) { | |
274 return CreateOffscreenTabValue( | |
275 TabContentsWrapper::GetCurrentWrapperForContents(tab_contents)); | |
276 } | |
277 | |
278 } // namespace | |
279 | |
280 // Tabs Event Manager ---------------------------------------------------------- | |
281 | |
282 TabsEventManager::TabsEventManager() {} | |
283 TabsEventManager::~TabsEventManager() {} | |
284 | |
285 void TabsEventManager::RegisterForTabNotifications( | |
286 const TabContents* tab_contents) { | |
287 registrar_.Add(this, | |
288 content::NOTIFICATION_NAV_ENTRY_COMMITTED, | |
289 Source<NavigationController>(&tab_contents->controller())); | |
290 | |
291 registrar_.Add(this, | |
292 content::NOTIFICATION_TAB_CONTENTS_DESTROYED, | |
293 Source<TabContents>(tab_contents)); | |
294 } | |
295 | |
296 void TabsEventManager::UnregisterForTabNotifications( | |
297 const TabContents* tab_contents) { | |
298 registrar_.Remove(this, | |
299 content::NOTIFICATION_NAV_ENTRY_COMMITTED, | |
300 Source<NavigationController>(&tab_contents->controller())); | |
301 | |
302 registrar_.Remove(this, | |
303 content::NOTIFICATION_TAB_CONTENTS_DESTROYED, | |
304 Source<TabContents>(tab_contents)); | |
305 } | |
306 | |
307 void TabsEventManager::Observe(const int type, | |
308 const NotificationSource& source, | |
309 const NotificationDetails& details) { | |
310 TabContents* tab_contents; | |
311 | |
312 if (type == content::NOTIFICATION_TAB_CONTENTS_DESTROYED) | |
313 tab_contents = Source<TabContents>(source).ptr(); | |
314 else if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED) | |
315 tab_contents = Source<NavigationController>(source).ptr()->tab_contents(); | |
316 else | |
317 return; | |
jstritar
2011/09/08 16:58:20
You can add a NOTREACHED() here since you should o
alexbost
2011/09/10 04:47:53
Done.
| |
318 | |
319 DCHECK(tab_contents); | |
320 | |
321 int tab_contents_id = GetTabId(tab_contents); | |
322 | |
323 // Close all offscreen tabs spawned by this tab | |
324 std::vector<TabContents*> offscreen_tab_contents_vector = | |
jstritar
2011/09/08 16:58:20
can you shorten these variable names a bit too? of
alexbost
2011/09/10 04:47:53
Done.
| |
325 (*GetTabIdToOffscreenTabsMap())[tab_contents_id]; | |
326 for (std::vector<TabContents*>::iterator | |
327 it_offscreen_tab_contents = offscreen_tab_contents_vector.begin(); | |
328 it_offscreen_tab_contents != offscreen_tab_contents_vector.end(); | |
329 ++it_offscreen_tab_contents) | |
330 CloseOffscreenTab(*it_offscreen_tab_contents); | |
331 | |
332 GetTabsEventManager()->UnregisterForTabNotifications(tab_contents); | |
jstritar
2011/09/08 16:58:20
this can just be UnregisterForTabNotifications(tab
alexbost
2011/09/10 04:47:53
Done.
| |
333 | |
334 GetTabIdToOffscreenTabsMap()->erase(tab_contents_id); | |
335 GetTabIdToTabMap()->erase(tab_contents_id); | |
336 GetTabIdToExtensionIdMap()->erase(tab_contents_id); | |
337 } | |
338 | |
339 // Offscreen Tabs Event Manager ------------------------------------------------ | |
340 | |
341 OffscreenTabsEventManager::OffscreenTabsEventManager() {} | |
342 OffscreenTabsEventManager::~OffscreenTabsEventManager() {} | |
343 | |
344 void OffscreenTabsEventManager::RegisterForTabNotifications( | |
345 const TabContents* tab_contents) { | |
346 registrar_.Add(this, | |
347 content::NOTIFICATION_NAV_ENTRY_COMMITTED, | |
348 Source<NavigationController>(&tab_contents->controller())); | |
349 } | |
350 | |
351 void OffscreenTabsEventManager::UnregisterForTabNotifications( | |
352 const TabContents* tab_contents) { | |
353 registrar_.Remove(this, | |
354 content::NOTIFICATION_NAV_ENTRY_COMMITTED, | |
355 Source<NavigationController>(&tab_contents->controller())); | |
356 } | |
357 | |
358 void OffscreenTabsEventManager::Observe(const int type, | |
359 const NotificationSource& source, | |
360 const NotificationDetails& details) { | |
361 DCHECK(type == content::NOTIFICATION_NAV_ENTRY_COMMITTED); | |
362 | |
363 TabContents* offscreen_tab_contents = | |
364 Source<NavigationController>(source).ptr()->tab_contents(); | |
365 DCHECK(offscreen_tab_contents); | |
366 | |
367 DictionaryValue* changed_properties = new DictionaryValue(); | |
368 changed_properties->SetString(keys::kUrlKey, | |
369 GetTabUrl(offscreen_tab_contents)); | |
370 | |
371 int offscreen_tab_id = GetTabId(offscreen_tab_contents); | |
372 | |
373 ListValue args; | |
374 args.Append(Value::CreateIntegerValue(offscreen_tab_id)); | |
375 args.Append(changed_properties); | |
376 args.Append(CreateOffscreenTabValue(offscreen_tab_contents)); | |
377 std::string json_args; | |
378 base::JSONWriter::Write(&args, false, &json_args); | |
379 | |
380 TabContents* tab_contents; | |
381 std::string error_; | |
382 if (!GetTabOfOffscreenTab(offscreen_tab_contents, &tab_contents, &error_)) { | |
383 LOG(ERROR) << error_; | |
384 return; | |
385 } | |
386 | |
387 ListValue event_args; | |
388 event_args.Set(0, Value::CreateStringValue(keys::kEventOnUpdated)); | |
389 event_args.Set(1, Value::CreateStringValue(json_args)); | |
390 | |
391 // Dispatch "experimental.offscreenTabs.onUpdated" event. | |
392 | |
393 // The primary use case for broadcasting the event is | |
394 // when the offscreen tab is generated by a test API background page. | |
395 if (tab_contents == GetBackgroundPageTabContents()) { | |
396 Profile* profile = Profile::FromBrowserContext( | |
397 offscreen_tab_contents->browser_context()); | |
398 profile->GetExtensionEventRouter()->DispatchEventToRenderers( | |
399 keys::kEventOnUpdated, json_args, profile, GURL()); | |
400 // Send a routed event directly the tab that spawned the offscreen tab. | |
jstritar
2011/09/08 16:58:20
nit: "directly to the tab"
alexbost
2011/09/10 04:47:53
Done.
| |
401 } else { | |
402 tab_contents->render_view_host()->process()->Send( | |
403 new ExtensionMsg_MessageInvoke( | |
404 tab_contents->render_view_host()->routing_id(), | |
405 *(*GetTabIdToExtensionIdMap())[GetTabId(tab_contents)], | |
406 keys::kDispatchEvent, | |
407 event_args, | |
408 GURL())); | |
409 } | |
410 } | |
411 | |
412 // create ---------------------------------------------------------------------- | |
413 | |
414 CreateOffscreenTabFunction::CreateOffscreenTabFunction() {} | |
415 CreateOffscreenTabFunction::~CreateOffscreenTabFunction() {} | |
416 | |
417 bool CreateOffscreenTabFunction::RunImpl() { | |
418 Browser* browser = GetCurrentBrowser(); | |
419 if (!browser) { | |
sky
2011/09/08 18:12:26
Why do many of your run methods require a browser?
alexbost
2011/09/10 04:47:53
Done.
| |
420 error_ = keys::kNoCurrentWindowError; | |
421 return false; | |
422 } | |
423 | |
424 DictionaryValue* create_props; | |
425 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &create_props)); | |
426 | |
427 // Url | |
jstritar
2011/09/08 16:58:20
Implementation comments should usually be complete
alexbost
2011/09/10 04:47:53
Done.
| |
428 std::string url_string; | |
429 GURL url; | |
430 EXTENSION_FUNCTION_VALIDATE( | |
431 create_props->GetString(keys::kUrlKey, &url_string)); | |
432 url = ResolvePossiblyRelativeURL(url_string, GetExtension()); | |
433 if (!url.is_valid()) { | |
434 error_ = ExtensionErrorUtils::FormatErrorMessage( | |
435 keys::kInvalidUrlError, url_string); | |
436 return false; | |
437 } | |
438 if (IsCrashURL(url)) { | |
439 error_ = keys::kNoCrashBrowserError; | |
440 return false; | |
441 } | |
442 | |
443 // Width and height | |
444 gfx::Rect window_bounds; | |
445 bool maximized; | |
446 if (!create_props->HasKey(keys::kWidthKey) || | |
447 !create_props->HasKey(keys::kHeightKey)) | |
448 WindowSizer::GetBrowserWindowBounds(std::string(), gfx::Rect(), | |
449 browser, &window_bounds, | |
450 &maximized); | |
451 | |
452 int width; | |
453 if (create_props->HasKey(keys::kWidthKey)) | |
454 EXTENSION_FUNCTION_VALIDATE( | |
455 create_props->GetInteger(keys::kWidthKey, &width)); | |
456 else | |
457 width = window_bounds.width(); | |
jstritar
2011/09/08 16:58:20
Can you initialize width with window_bounds.width(
alexbost
2011/09/10 04:47:53
Done.
| |
458 | |
459 int height; | |
460 if (create_props->HasKey(keys::kHeightKey)) | |
461 EXTENSION_FUNCTION_VALIDATE( | |
462 create_props->GetInteger(keys::kHeightKey, &height)); | |
463 else | |
464 height = window_bounds.height(); | |
jstritar
2011/09/08 16:58:20
ditto
alexbost
2011/09/10 04:47:53
Done.
| |
465 | |
466 // New offscreen tab | |
jstritar
2011/09/08 16:58:20
fix these comments too
alexbost
2011/09/10 04:47:53
Done.
| |
467 TabContents* offscreen_tab_contents = | |
468 new TabContents(profile_, NULL, MSG_ROUTING_NONE, NULL, NULL); | |
469 TabContentsWrapper* offscreen_tab = | |
470 new TabContentsWrapper(offscreen_tab_contents); | |
471 | |
472 // Setting the size starts the renderer | |
473 offscreen_tab_contents->view()->SizeContents(gfx::Size(width, height)); | |
474 | |
475 // Navigate | |
476 offscreen_tab_contents->controller().LoadURL( | |
477 url, GURL(), PageTransition::LINK); | |
478 | |
479 // Map the offscreen tab to the tab that spawned it | |
480 TabContents* tab_contents; | |
481 if (!GetCurrentTab(&tab_contents, dispatcher(), profile_, error_)) | |
482 return false; | |
sky
2011/09/08 18:12:26
If you return here you leak the tabs you just crea
alexbost
2011/09/10 04:47:53
Good catch.
| |
483 | |
484 int tab_contents_id = GetTabId(tab_contents); | |
485 | |
486 // Register for offscreen tab notifications | |
487 (*GetTabIdToOffscreenTabsMap())[tab_contents_id]. | |
488 push_back(offscreen_tab_contents); | |
489 GetOffscreenTabsEventManager()-> | |
490 RegisterForTabNotifications(offscreen_tab_contents); | |
491 | |
492 // If this is the first offscreen tab spawned by the tab, | |
493 // register for tab notifications and update maps | |
494 if ((*GetTabIdToOffscreenTabsMap())[tab_contents_id]. | |
sky
2011/09/08 18:12:26
Rather than using a bunch of static maps and what
alexbost
2011/09/10 04:47:53
Done.
| |
495 size() == 1) { | |
496 (*GetTabIdToTabMap())[tab_contents_id] = tab_contents; | |
497 (*GetTabIdToExtensionIdMap())[tab_contents_id] = &extension_id(); | |
498 | |
499 GetTabsEventManager()->RegisterForTabNotifications(tab_contents); | |
500 } | |
501 | |
502 // Callback | |
503 if (has_callback()) { | |
504 result_.reset(CreateOffscreenTabValue(offscreen_tab)); | |
505 SendResponse(true); | |
506 } | |
507 | |
508 return true; | |
509 } | |
510 | |
511 // get ------------------------------------------------------------------------- | |
512 | |
513 GetOffscreenTabFunction::GetOffscreenTabFunction() {} | |
514 GetOffscreenTabFunction::~GetOffscreenTabFunction() {} | |
515 | |
516 bool GetOffscreenTabFunction::RunImpl() { | |
517 int tab_id; | |
518 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); | |
519 | |
520 Browser* browser = GetCurrentBrowser(); | |
521 if (!browser) { | |
522 error_ = keys::kNoCurrentWindowError; | |
523 return false; | |
524 } | |
525 TabContents* tab_contents; | |
526 if (!GetCurrentTab(&tab_contents, dispatcher(), profile_, error_)) | |
527 return false; | |
528 | |
529 TabContentsWrapper* offscreen_tab = NULL; | |
530 if (!GetOffscreenTabById(tab_id, &offscreen_tab, tab_contents, &error_)) | |
531 return false; | |
532 | |
533 result_.reset(CreateOffscreenTabValue(offscreen_tab)); | |
534 SendResponse(true); | |
535 | |
536 return true; | |
537 } | |
538 | |
539 // getAll ---------------------------------------------------------------------- | |
540 | |
541 GetAllOffscreenTabFunction::GetAllOffscreenTabFunction() {} | |
542 GetAllOffscreenTabFunction::~GetAllOffscreenTabFunction() {} | |
543 | |
544 bool GetAllOffscreenTabFunction::RunImpl() { | |
545 Browser* browser = GetCurrentBrowser(); | |
546 if (!browser) { | |
547 error_ = keys::kNoCurrentWindowError; | |
548 return false; | |
549 } | |
550 TabContents* current_tab_contents; | |
551 if (!GetCurrentTab(¤t_tab_contents, dispatcher(), profile_, error_)) | |
552 return false; | |
553 | |
554 std::vector<TabContents*> offscreen_tab_contents_vector = | |
555 (*GetTabIdToOffscreenTabsMap())[GetTabId(current_tab_contents)]; | |
556 | |
557 ListValue* tab_list = new ListValue(); | |
558 for (std::vector<TabContents*>::iterator | |
559 it_offscreen_tab_contents = offscreen_tab_contents_vector.begin(); | |
560 it_offscreen_tab_contents != offscreen_tab_contents_vector.end(); | |
561 ++it_offscreen_tab_contents) | |
562 tab_list->Append(CreateOffscreenTabValue(TabContentsWrapper:: | |
563 GetCurrentWrapperForContents(*it_offscreen_tab_contents))); | |
564 | |
565 result_.reset(tab_list); | |
566 SendResponse(true); | |
567 | |
568 return true; | |
569 } | |
570 | |
571 // remove ---------------------------------------------------------------------- | |
572 | |
573 RemoveOffscreenTabFunction::RemoveOffscreenTabFunction() {} | |
574 RemoveOffscreenTabFunction::~RemoveOffscreenTabFunction() {} | |
575 | |
576 bool RemoveOffscreenTabFunction::RunImpl() { | |
577 int tab_id; | |
578 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); | |
579 | |
580 for (TabIdToOffscreenTabsMap::iterator | |
581 it = GetTabIdToOffscreenTabsMap()->begin(); | |
582 it != GetTabIdToOffscreenTabsMap()->end(); ++it) | |
583 for (std::vector<TabContents*>::iterator it_offscreen_tab_contents = | |
584 it->second.begin(); | |
585 it_offscreen_tab_contents != it->second.end(); | |
586 ++it_offscreen_tab_contents) | |
587 if (GetTabId(*it_offscreen_tab_contents) == tab_id) { | |
588 TabContents* tab_contents; | |
589 if (!GetTabById( | |
590 it->first, &tab_contents, &error_)) | |
591 return false; | |
592 | |
593 CloseOffscreenTab(*it_offscreen_tab_contents); | |
594 | |
595 it->second.erase(it_offscreen_tab_contents); | |
596 | |
597 // If this was the last offscreen tab for the tab, | |
598 // unregister the tab for notifications and remove it from the maps | |
599 if (it->second.empty()) { | |
600 GetTabsEventManager()->UnregisterForTabNotifications(tab_contents); | |
601 | |
602 GetTabIdToOffscreenTabsMap()->erase(it); | |
603 GetTabIdToTabMap()->erase(it->first); | |
604 GetTabIdToExtensionIdMap()->erase(it->first); | |
605 } | |
606 | |
607 return true; | |
608 } | |
609 | |
610 error_ = ExtensionErrorUtils::FormatErrorMessage( | |
611 keys::kOffscreenTabNotFoundError, base::IntToString(tab_id)); | |
612 return false; | |
613 } | |
614 | |
615 // sendKeyboardEvent ----------------------------------------------------------- | |
616 | |
617 SendKeyboardEventOffscreenTabFunction:: | |
618 SendKeyboardEventOffscreenTabFunction() {} | |
619 SendKeyboardEventOffscreenTabFunction:: | |
620 ~SendKeyboardEventOffscreenTabFunction() {} | |
621 | |
622 bool SendKeyboardEventOffscreenTabFunction::RunImpl() { | |
623 int tab_id; | |
624 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); | |
625 | |
626 Browser* browser = GetCurrentBrowser(); | |
627 if (!browser) { | |
628 error_ = keys::kNoCurrentWindowError; | |
629 return false; | |
630 } | |
631 TabContents* current_tab_contents; | |
jstritar
2011/09/08 16:58:20
initialize to NULL
alexbost
2011/09/10 04:47:53
Done.
| |
632 if (!GetCurrentTab(¤t_tab_contents, dispatcher(), profile_, error_)) | |
633 return false; | |
634 TabContentsWrapper* tab = NULL; | |
635 if (!GetOffscreenTabById(tab_id, &tab, current_tab_contents, &error_)) | |
636 return false; | |
637 | |
638 // JavaScript KeyboardEvent | |
639 DictionaryValue* dict; | |
jstritar
2011/09/08 16:58:20
ditto
alexbost
2011/09/10 04:47:53
Done.
| |
640 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &dict)); | |
641 | |
642 NativeWebKeyboardEvent keyboard_event; | |
643 | |
644 // Type | |
645 std::string type; | |
646 if (dict->HasKey(keys::kKeyboardEventTypeKey)) { | |
647 EXTENSION_FUNCTION_VALIDATE( | |
648 dict->GetString(keys::kKeyboardEventTypeKey, &type)); | |
649 } else { | |
650 error_ = keys::kInvalidKeyboardEventObjectError; | |
651 return false; | |
652 } | |
653 | |
654 if (type.compare(keys::kKeyboardEventTypeValueKeypress) == 0) { | |
655 keyboard_event.type = WebInputEvent::Char; | |
656 } else if (type.compare(keys::kKeyboardEventTypeValueKeydown) == 0) { | |
657 keyboard_event.type = WebInputEvent::KeyDown; | |
658 } else if (type.compare(keys::kKeyboardEventTypeValueKeyup) == 0) { | |
659 keyboard_event.type = WebInputEvent::KeyUp; | |
660 } else { | |
661 error_ = keys::kInvalidKeyboardEventObjectError; | |
662 return false; | |
663 } | |
664 | |
665 // Key code | |
666 int key_code; | |
667 if (dict->HasKey(keys::kKeyboardEventKeyCodeKey)) { | |
668 EXTENSION_FUNCTION_VALIDATE( | |
669 dict->GetInteger(keys::kKeyboardEventKeyCodeKey, &key_code)); | |
670 } else { | |
671 error_ = keys::kInvalidKeyboardEventObjectError; | |
672 return false; | |
673 } | |
674 | |
675 keyboard_event.nativeKeyCode = key_code; | |
676 keyboard_event.windowsKeyCode = key_code; | |
677 | |
678 // Key identifier | |
679 keyboard_event.setKeyIdentifierFromWindowsKeyCode(); | |
680 | |
681 // Keypress = type character | |
682 if (type.compare(keys::kKeyboardEventTypeValueKeypress) == 0) { | |
683 int char_code; | |
684 if (dict->HasKey(keys::kKeyboardEventCharCodeKey)) { | |
685 EXTENSION_FUNCTION_VALIDATE( | |
686 dict->GetInteger(keys::kKeyboardEventCharCodeKey, &char_code)); | |
687 keyboard_event.text[0] = char_code; | |
688 keyboard_event.unmodifiedText[0] = char_code; | |
689 } else { | |
690 error_ = keys::kInvalidKeyboardEventObjectError; | |
691 return false; | |
692 } | |
693 } | |
694 | |
695 // Modifiers | |
696 bool alt_key; | |
697 if (dict->HasKey(keys::kKeyboardEventAltKeyKey)) | |
698 EXTENSION_FUNCTION_VALIDATE( | |
699 dict->GetBoolean(keys::kKeyboardEventAltKeyKey, &alt_key)); | |
700 if (alt_key) | |
701 keyboard_event.modifiers |= WebInputEvent::AltKey; | |
702 | |
703 bool ctrl_key; | |
704 if (dict->HasKey(keys::kKeyboardEventCtrlKeyKey)) | |
705 EXTENSION_FUNCTION_VALIDATE( | |
706 dict->GetBoolean(keys::kKeyboardEventCtrlKeyKey, &ctrl_key)); | |
707 if (ctrl_key) | |
708 keyboard_event.modifiers |= WebInputEvent::ControlKey; | |
709 | |
710 bool meta_key = false; | |
711 if (dict->HasKey(keys::kMouseEventMetaKeyKey)) | |
712 EXTENSION_FUNCTION_VALIDATE( | |
713 dict->GetBoolean(keys::kMouseEventMetaKeyKey, &meta_key)); | |
714 if (meta_key) | |
715 keyboard_event.modifiers |= WebInputEvent::MetaKey; | |
716 | |
717 bool shift_key; | |
718 if (dict->HasKey(keys::kKeyboardEventShiftKeyKey)) | |
719 EXTENSION_FUNCTION_VALIDATE( | |
720 dict->GetBoolean(keys::kKeyboardEventShiftKeyKey, &shift_key)); | |
721 if (shift_key) | |
722 keyboard_event.modifiers |= WebInputEvent::ShiftKey; | |
723 | |
724 // Forward event | |
725 tab->tab_contents()->render_view_host()-> | |
726 ForwardKeyboardEvent(keyboard_event); | |
727 | |
728 // Callback | |
729 if (has_callback()) { | |
730 result_.reset(CreateOffscreenTabValue(tab)); | |
731 SendResponse(true); | |
732 } | |
733 | |
734 return true; | |
735 } | |
736 | |
737 // sendMouseEvent -------------------------------------------------------------- | |
738 | |
739 SendMouseEventOffscreenTabFunction::SendMouseEventOffscreenTabFunction() {} | |
740 SendMouseEventOffscreenTabFunction::~SendMouseEventOffscreenTabFunction() {} | |
741 | |
742 bool SendMouseEventOffscreenTabFunction::RunImpl() { | |
743 int tab_id; | |
744 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); | |
745 | |
746 Browser* browser = GetCurrentBrowser(); | |
747 if (!browser) { | |
748 error_ = keys::kNoCurrentWindowError; | |
749 return false; | |
750 } | |
751 TabContents* current_tab_contents; | |
752 if (!GetCurrentTab(¤t_tab_contents, dispatcher(), profile_, error_)) | |
753 return false; | |
754 TabContentsWrapper* tab = NULL; | |
755 if (!GetOffscreenTabById(tab_id, &tab, current_tab_contents, &error_)) | |
756 return false; | |
757 | |
758 // JavaScript MouseEvent | |
759 DictionaryValue* dict; | |
760 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &dict)); | |
761 | |
762 std::string type; | |
763 if (dict->HasKey(keys::kMouseEventTypeKey)) { | |
764 EXTENSION_FUNCTION_VALIDATE( | |
765 dict->GetString(keys::kMouseEventTypeKey, &type)); | |
766 } else { | |
767 error_ = keys::kInvalidMouseEventObjectError; | |
768 return false; | |
769 } | |
770 | |
771 if (type.compare(keys::kMouseEventTypeValueMousewheel) == 0) { | |
772 WebKit::WebMouseWheelEvent wheel_event; | |
773 | |
774 // Type | |
775 wheel_event.type = WebInputEvent::MouseWheel; | |
776 | |
777 // deltaX, deltaY | |
778 if (dict->HasKey(keys::kMouseEventWheelDeltaXKey) && | |
779 dict->HasKey(keys::kMouseEventWheelDeltaYKey)) { | |
780 int delta_x, delta_y; | |
781 EXTENSION_FUNCTION_VALIDATE( | |
782 dict->GetInteger(keys::kMouseEventWheelDeltaXKey, &delta_x)); | |
783 EXTENSION_FUNCTION_VALIDATE( | |
784 dict->GetInteger(keys::kMouseEventWheelDeltaYKey, &delta_y)); | |
785 wheel_event.deltaX = delta_x; | |
786 wheel_event.deltaY = delta_y; | |
787 } else { | |
788 error_ = keys::kInvalidMouseEventObjectError; | |
789 return false; | |
790 } | |
791 | |
792 // Forward the event | |
793 tab->tab_contents()->render_view_host()->ForwardWheelEvent(wheel_event); | |
794 } else { | |
795 WebKit::WebMouseEvent mouse_event; | |
796 | |
797 // Type | |
798 if (type.compare(keys::kMouseEventTypeValueMousedown) == 0 || | |
799 type.compare(keys::kMouseEventTypeValueClick) == 0) { | |
800 mouse_event.type = WebKit::WebInputEvent::MouseDown; | |
801 } else if (type.compare(keys::kMouseEventTypeValueMouseup) == 0) { | |
802 mouse_event.type = WebKit::WebInputEvent::MouseUp; | |
803 } else if (type.compare(keys::kMouseEventTypeValueMousemove) == 0) { | |
804 mouse_event.type = WebKit::WebInputEvent::MouseMove; | |
805 } else { | |
806 error_ = keys::kInvalidMouseEventObjectError; | |
807 return false; | |
808 } | |
809 | |
810 // Button | |
811 int button; | |
812 if (dict->HasKey(keys::kMouseEventButtonKey)) { | |
813 EXTENSION_FUNCTION_VALIDATE( | |
814 dict->GetInteger(keys::kMouseEventButtonKey, &button)); | |
815 } else { | |
816 error_ = keys::kInvalidMouseEventObjectError; | |
817 return false; | |
818 } | |
819 | |
820 if (button == keys::kMouseEventButtonValueLeft) { | |
821 mouse_event.button = WebKit::WebMouseEvent::ButtonLeft; | |
822 } else if (button == keys::kMouseEventButtonValueMiddle) { | |
823 mouse_event.button = WebKit::WebMouseEvent::ButtonMiddle; | |
824 } else if (button == keys::kMouseEventButtonValueRight) { | |
825 mouse_event.button = WebKit::WebMouseEvent::ButtonRight; | |
826 } else { | |
827 error_ = keys::kInvalidMouseEventObjectError; | |
828 return false; | |
829 } | |
830 | |
831 // x, y | |
832 if (HasOptionalArgument(2) && HasOptionalArgument(3)) { | |
833 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(2, &mouse_event.x)); | |
834 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(3, &mouse_event.y)); | |
835 } else { | |
836 error_ = keys::kNoMouseCoordinatesError; | |
837 return false; | |
838 } | |
839 | |
840 // Modifiers | |
841 bool alt_key = false; | |
842 if (dict->HasKey(keys::kMouseEventAltKeyKey)) | |
843 EXTENSION_FUNCTION_VALIDATE( | |
844 dict->GetBoolean(keys::kMouseEventAltKeyKey, &alt_key)); | |
845 if (alt_key) | |
846 mouse_event.modifiers |= WebInputEvent::AltKey; | |
847 | |
848 bool ctrl_key = false; | |
849 if (dict->HasKey(keys::kMouseEventCtrlKeyKey)) | |
850 EXTENSION_FUNCTION_VALIDATE( | |
851 dict->GetBoolean(keys::kMouseEventCtrlKeyKey, &ctrl_key)); | |
852 if (ctrl_key) | |
853 mouse_event.modifiers |= WebInputEvent::ControlKey; | |
854 | |
855 bool meta_key = false; | |
856 if (dict->HasKey(keys::kMouseEventMetaKeyKey)) | |
857 EXTENSION_FUNCTION_VALIDATE( | |
858 dict->GetBoolean(keys::kMouseEventMetaKeyKey, &meta_key)); | |
859 if (meta_key) | |
860 mouse_event.modifiers |= WebInputEvent::MetaKey; | |
861 | |
862 bool shift_key = false; | |
863 if (dict->HasKey(keys::kMouseEventShiftKeyKey)) | |
864 EXTENSION_FUNCTION_VALIDATE( | |
865 dict->GetBoolean(keys::kMouseEventShiftKeyKey, &shift_key)); | |
866 if (shift_key) | |
867 mouse_event.modifiers |= WebInputEvent::ShiftKey; | |
868 | |
869 // Click count | |
870 mouse_event.clickCount = 1; | |
871 | |
872 // Forward the event | |
873 tab->tab_contents()->render_view_host()->ForwardMouseEvent(mouse_event); | |
874 | |
875 // If the event is a click, | |
876 // fire a mouseup event in addition to the mousedown | |
877 if (type.compare(keys::kMouseEventTypeValueClick) == 0) { | |
878 mouse_event.type = WebKit::WebInputEvent::MouseUp; | |
879 tab->tab_contents()->render_view_host()->ForwardMouseEvent(mouse_event); | |
880 } | |
881 } | |
882 | |
883 // Callback | |
884 if (has_callback()) { | |
885 result_.reset(CreateOffscreenTabValue(tab)); | |
886 SendResponse(true); | |
887 } | |
888 | |
889 return true; | |
890 } | |
891 | |
892 // toDataUrl ------------------------------------------------------------------- | |
893 | |
894 ToDataUrlOffscreenTabFunction::ToDataUrlOffscreenTabFunction() {} | |
895 ToDataUrlOffscreenTabFunction::~ToDataUrlOffscreenTabFunction() {} | |
896 | |
897 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module. | |
898 // TODO(alexbost): We want to optimize this function in order to get more image | |
899 // updates on the browser side. One improvement would be to implement another | |
900 // hash map in order to get offscreen tabs in O(1). | |
901 bool ToDataUrlOffscreenTabFunction::RunImpl() { | |
jstritar
2011/09/08 16:58:20
How do you know how often to call this method? It
alexbost
2011/09/10 04:47:53
We let the application explicitly call this method
| |
902 int tab_id; | |
903 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); | |
904 | |
905 Browser* browser = GetCurrentBrowser(); | |
906 if (!browser) { | |
907 error_ = keys::kNoCurrentWindowError; | |
908 return false; | |
909 } | |
910 TabContents* current_tab_contents; | |
911 if (!GetCurrentTab(¤t_tab_contents, dispatcher(), profile_, error_)) | |
912 return false; | |
913 TabContentsWrapper* tab = NULL; | |
914 if (!GetOffscreenTabById(tab_id, &tab, current_tab_contents, &error_)) | |
915 return false; | |
916 | |
917 image_format_ = FORMAT_JPEG; // Default format is JPEG. | |
918 image_quality_ = kDefaultQuality; // Default quality setting. | |
919 | |
920 if (HasOptionalArgument(1)) { | |
921 DictionaryValue* options; | |
922 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options)); | |
923 | |
924 if (options->HasKey(keys::kFormatKey)) { | |
925 std::string format; | |
926 EXTENSION_FUNCTION_VALIDATE( | |
927 options->GetString(keys::kFormatKey, &format)); | |
928 | |
929 if (format == keys::kFormatValueJpeg) { | |
930 image_format_ = FORMAT_JPEG; | |
931 } else if (format == keys::kFormatValuePng) { | |
932 image_format_ = FORMAT_PNG; | |
933 } else { | |
934 // Schema validation should make this unreachable. | |
935 EXTENSION_FUNCTION_VALIDATE(0); | |
936 } | |
937 } | |
938 | |
939 if (options->HasKey(keys::kQualityKey)) { | |
940 EXTENSION_FUNCTION_VALIDATE( | |
941 options->GetInteger(keys::kQualityKey, &image_quality_)); | |
942 } | |
943 } | |
944 | |
945 // captureVisibleTab() can return an image containing sensitive information | |
946 // that the browser would otherwise protect. Ensure the extension has | |
947 // permission to do this. | |
948 if (!GetExtension()-> | |
949 CanCaptureVisiblePage(tab->tab_contents()->GetURL(), &error_)) | |
950 return false; | |
951 | |
952 // The backing store approach works on Linux but not on Mac. | |
953 // TODO(alexbost): Test on Windows | |
954 #if !defined(OS_MACOSX) | |
955 RenderViewHost* render_view_host = tab->tab_contents()->render_view_host(); | |
956 | |
957 // If a backing store is cached for the tab we want to capture, | |
958 // and it can be copied into a bitmap, then use it to generate the image. | |
959 BackingStore* backing_store = render_view_host->GetBackingStore(false); | |
960 if (backing_store && CaptureSnapshotFromBackingStore(backing_store)) | |
961 return true; | |
962 #endif | |
963 | |
964 // Ask the renderer for a snapshot of the tab. | |
965 tab->CaptureSnapshot(); | |
966 registrar_.Add(this, | |
967 chrome::NOTIFICATION_TAB_SNAPSHOT_TAKEN, | |
968 Source<TabContentsWrapper>(tab)); | |
969 | |
970 AddRef(); // Balanced in ToDataUrlOffscreenTabFunction::Observe(). | |
971 | |
972 return true; | |
973 } | |
974 | |
975 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module. | |
976 // Build the image of a tab's contents out of a backing store. | |
977 // This may fail if we can not copy a backing store into a bitmap. | |
978 // For example, some uncommon X11 visual modes are not supported by | |
979 // CopyFromBackingStore(). | |
980 bool ToDataUrlOffscreenTabFunction::CaptureSnapshotFromBackingStore( | |
981 BackingStore* backing_store) { | |
982 | |
983 skia::PlatformCanvas temp_canvas; | |
984 if (!backing_store->CopyFromBackingStore(gfx::Rect(backing_store->size()), | |
985 &temp_canvas)) { | |
986 return false; | |
987 } | |
988 VLOG(1) << "captureVisibleTab() got image from backing store."; | |
989 | |
990 SendResultFromBitmap( | |
991 skia::GetTopDevice(temp_canvas)->accessBitmap(false)); | |
992 return true; | |
993 } | |
994 | |
995 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module. | |
996 void ToDataUrlOffscreenTabFunction::Observe(const int type, | |
997 const NotificationSource& source, | |
998 const NotificationDetails& details) { | |
999 DCHECK(type == chrome::NOTIFICATION_TAB_SNAPSHOT_TAKEN); | |
1000 | |
1001 const SkBitmap *screen_capture = Details<const SkBitmap>(details).ptr(); | |
1002 const bool error = screen_capture->empty(); | |
1003 | |
1004 if (error) { | |
1005 error_ = keys::kInternalVisibleTabCaptureError; | |
1006 SendResponse(false); | |
1007 } else { | |
1008 VLOG(1) << "Got image from renderer."; | |
1009 SendResultFromBitmap(*screen_capture); | |
1010 } | |
1011 | |
1012 Release(); // Balanced in ToDataUrlOffscreenTabFunction::RunImpl(). | |
1013 } | |
1014 | |
1015 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module. | |
1016 // Turn a bitmap of the screen into an image, set that image as the result, | |
1017 // and call SendResponse(). | |
1018 void ToDataUrlOffscreenTabFunction::SendResultFromBitmap( | |
1019 const SkBitmap& screen_capture) { | |
1020 std::vector<unsigned char> data; | |
1021 SkAutoLockPixels screen_capture_lock(screen_capture); | |
1022 bool encoded = false; | |
1023 std::string mime_type; | |
1024 switch (image_format_) { | |
1025 case FORMAT_JPEG: | |
1026 encoded = gfx::JPEGCodec::Encode( | |
1027 reinterpret_cast<unsigned char*>(screen_capture.getAddr32(0, 0)), | |
1028 gfx::JPEGCodec::FORMAT_SkBitmap, | |
1029 screen_capture.width(), | |
1030 screen_capture.height(), | |
1031 static_cast<int>(screen_capture.rowBytes()), | |
1032 image_quality_, | |
1033 &data); | |
1034 mime_type = keys::kMimeTypeJpeg; | |
1035 break; | |
1036 case FORMAT_PNG: | |
1037 encoded = gfx::PNGCodec::EncodeBGRASkBitmap( | |
1038 screen_capture, | |
1039 true, // Discard transparency. | |
1040 &data); | |
1041 mime_type = keys::kMimeTypePng; | |
1042 break; | |
1043 default: | |
1044 NOTREACHED() << "Invalid image format."; | |
1045 } | |
1046 | |
1047 if (!encoded) { | |
1048 error_ = keys::kInternalVisibleTabCaptureError; | |
1049 SendResponse(false); | |
1050 return; | |
1051 } | |
1052 | |
1053 std::string base64_result; | |
1054 base::StringPiece stream_as_string( | |
1055 reinterpret_cast<const char*>(vector_as_array(&data)), data.size()); | |
1056 | |
1057 base::Base64Encode(stream_as_string, &base64_result); | |
1058 base64_result.insert(0, base::StringPrintf("data:%s;base64,", | |
1059 mime_type.c_str())); | |
1060 result_.reset(new StringValue(base64_result)); | |
1061 SendResponse(true); | |
1062 } | |
1063 | |
1064 // update ---------------------------------------------------------------------- | |
1065 | |
1066 UpdateOffscreenTabFunction::UpdateOffscreenTabFunction() {} | |
1067 UpdateOffscreenTabFunction::~UpdateOffscreenTabFunction() {} | |
1068 | |
1069 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module. | |
1070 bool UpdateOffscreenTabFunction::RunImpl() { | |
1071 int tab_id; | |
1072 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); | |
1073 | |
1074 Browser* browser = GetCurrentBrowser(); | |
1075 if (!browser) { | |
1076 error_ = keys::kNoCurrentWindowError; | |
1077 return false; | |
1078 } | |
1079 TabContents* current_tab_contents; | |
1080 if (!GetCurrentTab(¤t_tab_contents, dispatcher(), profile_, error_)) | |
1081 return false; | |
1082 TabContentsWrapper* tab = NULL; | |
1083 if (!GetOffscreenTabById(tab_id, &tab, current_tab_contents, &error_)) | |
1084 return false; | |
1085 | |
1086 DictionaryValue* update_props; | |
1087 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props)); | |
1088 | |
1089 // Url | |
1090 if (update_props->HasKey(keys::kUrlKey)) { | |
1091 std::string url_string; | |
1092 GURL url; | |
1093 EXTENSION_FUNCTION_VALIDATE( | |
1094 update_props->GetString(keys::kUrlKey, &url_string)); | |
1095 url = ResolvePossiblyRelativeURL(url_string, GetExtension()); | |
1096 if (!url.is_valid()) { | |
1097 error_ = ExtensionErrorUtils::FormatErrorMessage( | |
1098 keys::kInvalidUrlError, url_string); | |
1099 return false; | |
1100 } | |
1101 if (IsCrashURL(url)) { | |
1102 error_ = keys::kNoCrashBrowserError; | |
1103 return false; | |
1104 } | |
1105 | |
1106 // JavaScript URLs can do the same kinds of things as cross-origin XHR, so | |
1107 // we need to check host permissions before allowing them. | |
1108 if (url.SchemeIs(chrome::kJavaScriptScheme)) { | |
1109 if (!GetExtension()->CanExecuteScriptOnPage( | |
1110 tab->tab_contents()->GetURL(), NULL, &error_)) { | |
1111 return false; | |
1112 } | |
1113 | |
1114 ExtensionMsg_ExecuteCode_Params params; | |
1115 params.request_id = request_id(); | |
1116 params.extension_id = extension_id(); | |
1117 params.is_javascript = true; | |
1118 params.code = url.path(); | |
1119 params.all_frames = false; | |
1120 params.in_main_world = true; | |
1121 | |
1122 RenderViewHost* render_view_host = | |
1123 tab->tab_contents()->render_view_host(); | |
1124 render_view_host->Send( | |
1125 new ExtensionMsg_ExecuteCode(render_view_host->routing_id(), | |
1126 params)); | |
1127 | |
1128 Observe(tab->tab_contents()); | |
1129 AddRef(); // balanced in Observe() | |
1130 | |
1131 return true; | |
1132 } | |
1133 | |
1134 tab->tab_contents()->controller(). | |
1135 LoadURL(url, GURL(), PageTransition::LINK); | |
1136 | |
1137 // The URL of a tab contents never actually changes to a JavaScript URL, so | |
1138 // this check only makes sense in other cases. | |
1139 if (!url.SchemeIs(chrome::kJavaScriptScheme)) | |
1140 DCHECK_EQ(url.spec(), tab->tab_contents()->GetURL().spec()); | |
1141 } | |
1142 | |
1143 // Width and height | |
1144 if (update_props->HasKey(keys::kWidthKey) || | |
1145 update_props->HasKey(keys::kHeightKey)) { | |
1146 int width; | |
1147 if (update_props->HasKey(keys::kWidthKey)) | |
1148 EXTENSION_FUNCTION_VALIDATE( | |
1149 update_props->GetInteger(keys::kWidthKey, &width)); | |
1150 else | |
1151 GetTabWidth(tab->tab_contents()); | |
1152 | |
1153 int height; | |
1154 if (update_props->HasKey(keys::kHeightKey)) | |
1155 EXTENSION_FUNCTION_VALIDATE( | |
1156 update_props->GetInteger(keys::kHeightKey, &height)); | |
1157 else | |
1158 GetTabHeight(tab->tab_contents()); | |
1159 | |
1160 tab->tab_contents()->view()->SizeContents(gfx::Size(width, height)); | |
1161 } | |
1162 | |
1163 // Callback | |
1164 if (has_callback()) { | |
1165 result_.reset(CreateOffscreenTabValue(tab)); | |
1166 SendResponse(true); | |
1167 } | |
1168 | |
1169 return true; | |
1170 } | |
1171 | |
1172 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module. | |
1173 bool UpdateOffscreenTabFunction::OnMessageReceived( | |
1174 const IPC::Message& message) { | |
1175 if (message.type() != ExtensionHostMsg_ExecuteCodeFinished::ID) | |
1176 return false; | |
1177 | |
1178 int message_request_id; | |
1179 void* iter = NULL; | |
1180 if (!message.ReadInt(&iter, &message_request_id)) { | |
1181 NOTREACHED() << "malformed extension message"; | |
1182 return true; | |
1183 } | |
1184 | |
1185 if (message_request_id != request_id()) | |
1186 return false; | |
1187 | |
1188 IPC_BEGIN_MESSAGE_MAP(UpdateOffscreenTabFunction, message) | |
1189 IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExecuteCodeFinished, | |
1190 OnExecuteCodeFinished) | |
1191 IPC_END_MESSAGE_MAP() | |
1192 return true; | |
1193 } | |
1194 | |
1195 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module. | |
1196 void UpdateOffscreenTabFunction::OnExecuteCodeFinished(int request_id, | |
1197 bool success, | |
1198 const std::string& error) { | |
1199 if (!error.empty()) { | |
1200 CHECK(!success); | |
1201 error_ = error; | |
1202 } | |
1203 | |
1204 SendResponse(success); | |
1205 | |
1206 Observe(NULL); | |
1207 Release(); // balanced in Execute() | |
1208 } | |
1209 | |
OLD | NEW |