Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(185)

Side by Side Diff: chrome/browser/extensions/extension_offscreen_tabs_module.cc

Issue 7720002: Chrome Extensions chrome.experimental.offscreenTabs.* API implementation, docs, and test. (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(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(&current_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(&current_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(&current_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(&current_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(&current_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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698