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

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

Powered by Google App Engine
This is Rietveld 408576698