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

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

Powered by Google App Engine
This is Rietveld 408576698