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

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, 4 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/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/stringprintf.h"
17 #include "base/utf_string_conversions.h"
18 #include "chrome/browser/extensions/extension_function_dispatcher.h"
19 #include "chrome/browser/extensions/extension_host.h"
20 #include "chrome/browser/extensions/extension_offscreen_tabs_module_constants.h"
21 #include "chrome/browser/extensions/extension_service.h"
22 #include "chrome/browser/extensions/extension_tabs_module.h"
23 #include "chrome/browser/net/url_fixer_upper.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/browser/sessions/restore_tab_helper.h"
26 #include "chrome/browser/tabs/tab_strip_model.h"
27 #include "chrome/browser/translate/translate_tab_helper.h"
28 #include "chrome/browser/ui/browser.h"
29 #include "chrome/browser/ui/browser_list.h"
30 #include "chrome/browser/ui/browser_navigator.h"
31 #include "chrome/browser/ui/browser_window.h"
32 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
33 #include "chrome/browser/ui/window_sizer.h"
34 #include "chrome/common/chrome_notification_types.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/extensions/extension.h"
37 #include "chrome/common/extensions/extension_error_utils.h"
38 #include "chrome/common/extensions/extension_messages.h"
39 #include "chrome/common/pref_names.h"
40 #include "chrome/common/url_constants.h"
41 #include "content/browser/renderer_host/backing_store.h"
42 #include "content/browser/renderer_host/render_view_host.h"
43 #include "content/browser/renderer_host/render_view_host_delegate.h"
44 #include "content/browser/tab_contents/navigation_entry.h"
45 #include "content/browser/tab_contents/tab_contents.h"
46 #include "content/browser/tab_contents/tab_contents_view.h"
47 #include "content/common/notification_service.h"
48 #include "skia/ext/image_operations.h"
49 #include "skia/ext/platform_canvas.h"
50 #include "third_party/skia/include/core/SkBitmap.h"
51 #include "ui/gfx/codec/jpeg_codec.h"
52 #include "ui/gfx/codec/png_codec.h"
53
54 using WebKit::WebInputEvent;
55
56 namespace keys = extension_offscreen_tabs_module_constants;
57
58 const int ToDataUrlOffscreenTabFunction::kDefaultQuality = 90;
59
60 typedef base::hash_map<int, std::vector<TabContents*> > OffscreenTabsMap;
61 OffscreenTabsMap* offscreen_tabs_map = new OffscreenTabsMap();
Ken Russell (switch to Gerrit) 2011/08/23 22:46:53 It's not OK to have a nontrivial initializer for t
alexbost 2011/08/24 20:06:21 Done.
62
63 OffscreenTabsEventManager* event_manager;
Ken Russell (switch to Gerrit) 2011/08/23 22:46:53 Must go in the anonymous namespace as well. Also m
alexbost 2011/08/24 20:06:21 Done.
64
65 namespace {
66
67 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module.
68 // Takes |url_string| and returns a GURL which is either valid and absolute
69 // or invalid. If |url_string| is not directly interpretable as a valid (it is
70 // likely a relative URL) an attempt is made to resolve it. |extension| is
71 // provided so it can be resolved relative to its extension base
72 // (chrome-extension://<id>/). Using the source frame url would be more correct,
73 // but because the api shipped with urls resolved relative to their extension
74 // base, we decided it wasn't worth breaking existing extensions to fix.
75 GURL ResolvePossiblyRelativeURL(const std::string& url_string,
76 const Extension* extension) {
77 GURL url = GURL(url_string);
78 if (!url.is_valid())
79 url = extension->GetResourceURL(url_string);
80
81 return url;
82 }
83
84 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module.
85 bool IsCrashURL(const GURL& url) {
86 // Check a fixed-up URL, to normalize the scheme and parse hosts correctly.
87 GURL fixed_url =
88 URLFixerUpper::FixupURL(url.possibly_invalid_spec(), std::string());
89 return (fixed_url.SchemeIs(chrome::kChromeUIScheme) &&
90 (fixed_url.host() == chrome::kChromeUIBrowserCrashHost ||
91 fixed_url.host() == chrome::kChromeUICrashHost));
92 }
93
94 bool GetOffscreenTabById(const int tab_id,
95 TabContentsWrapper** offscreen_tab,
96 const TabContents* current_tab_contents,
97 std::string* error_message) {
98 std::vector<TabContents*> offscreen_tab_contents_vector =
99 (*offscreen_tabs_map)[ExtensionTabUtil::GetTabId(current_tab_contents)];
100
101 for (std::vector<TabContents*>::iterator
102 it_offscreen_tab_contents = offscreen_tab_contents_vector.begin();
103 it_offscreen_tab_contents != offscreen_tab_contents_vector.end();
104 ++it_offscreen_tab_contents)
105 if (ExtensionTabUtil::GetTabId(*it_offscreen_tab_contents) == tab_id) {
106 *offscreen_tab = TabContentsWrapper::GetCurrentWrapperForContents(
107 *it_offscreen_tab_contents);
108
109 return true;
110 }
111
112 *error_message = ExtensionErrorUtils::FormatErrorMessage(
113 keys::kOffscreenTabNotFoundError, base::IntToString(tab_id));
114
115 return false;
116 }
117
118 bool GetTabById(const int tab_id,
119 Browser* browser,
120 TabContents** tab_contents,
121 std::string* error_message) {
122 TabStripModel* tab_strip = browser->tabstrip_model();
123 for (int i = 0; i <tab_strip->count(); ++i) {
124 *tab_contents = tab_strip->GetTabContentsAt(i)->tab_contents();
125
126 if (ExtensionTabUtil::GetTabId(*tab_contents) == tab_id)
127 return true;
128 }
129
130 *error_message = ExtensionErrorUtils::FormatErrorMessage(
131 keys::kTabNotFoundError, base::IntToString(tab_id));
132
133 return false;
134 }
135
136 std::string GetTabUrl(const TabContents* tab_contents) {
137 return tab_contents->GetURL().spec();
138 }
139
140 int GetTabWidth(const TabContents* tab_contents) {
141 const TabContentsWrapper* tab =
142 TabContentsWrapper::GetCurrentWrapperForContents(tab_contents);
143
144 return tab ? tab->view()->GetContainerSize().width() : -1;
145 }
146
147 int GetTabHeight(const TabContents* tab_contents) {
148 const TabContentsWrapper* tab =
149 TabContentsWrapper::GetCurrentWrapperForContents(tab_contents);
150
151 return tab ? tab->view()->GetContainerSize().height() : -1;
152 }
153
154 void CloseOffscreenTab(TabContents* tab_contents) {
155 // TODO(alexbost): Is this the best way to close an offscreen tab?
156 delete TabContentsWrapper::GetCurrentWrapperForContents(tab_contents);
157 }
158
159 DictionaryValue* CreateOffscreenTabValue(const TabContentsWrapper* tab) {
160 DictionaryValue* result = new DictionaryValue();
161 result->SetInteger(keys::kIdKey,
162 ExtensionTabUtil::GetTabId(tab->tab_contents()));
163 result->SetString(keys::kUrlKey, GetTabUrl(tab->tab_contents()));
164 result->SetInteger(keys::kWidthKey, GetTabWidth(tab->tab_contents()));
165 result->SetInteger(keys::kHeightKey, GetTabHeight(tab->tab_contents()));
166
167 return result;
168 }
169
170 } // namespace
171
172 // Event Manager ---------------------------------------------------------------
173
174 void OffscreenTabsEventManager::RegisterForTabNotifications(
175 const TabContents* tab_contents) {
176 registrar_.Add(this,
177 content::NOTIFICATION_NAV_ENTRY_COMMITTED,
178 Source<NavigationController>(&tab_contents->controller()));
179
180 registrar_.Add(this,
181 content::NOTIFICATION_TAB_CONTENTS_DESTROYED,
182 Source<TabContents>(tab_contents));
183 }
184
185 void OffscreenTabsEventManager::UnregisterForTabNotifications(
186 const TabContents* tab_contents) {
187 registrar_.Remove(this,
188 content::NOTIFICATION_NAV_ENTRY_COMMITTED,
189 Source<NavigationController>(&tab_contents->controller()));
190
191 registrar_.Remove(this,
192 content::NOTIFICATION_TAB_CONTENTS_DESTROYED,
193 Source<TabContents>(tab_contents));
194 }
195
196 void OffscreenTabsEventManager::Observe(const int type,
197 const NotificationSource& source,
198 const NotificationDetails& details) {
199 TabContents* tab_contents;
200
201 if (type == content::NOTIFICATION_TAB_CONTENTS_DESTROYED)
202 tab_contents = Source<TabContents>(source).ptr();
203 else if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED)
204 tab_contents = Source<NavigationController>(source).ptr()->tab_contents();
205 else
206 return;
207
Ken Russell (switch to Gerrit) 2011/08/23 22:46:53 DCHECK(tab_contents)?
alexbost 2011/08/24 20:06:21 Done.
208 // Close all offscreen tabs spawned by this tab
209 std::vector<TabContents*> offscreen_tab_contents_vector =
210 (*offscreen_tabs_map)[ExtensionTabUtil::GetTabId(tab_contents)];
211 for (std::vector<TabContents*>::iterator
212 it_offscreen_tab_contents = offscreen_tab_contents_vector.begin();
213 it_offscreen_tab_contents != offscreen_tab_contents_vector.end();
214 ++it_offscreen_tab_contents)
215 CloseOffscreenTab(*it_offscreen_tab_contents);
216
217 offscreen_tabs_map->erase(ExtensionTabUtil::GetTabId(tab_contents));
218
219 event_manager->UnregisterForTabNotifications(tab_contents);
220 }
221
222 // create ----------------------------------------------------------------------
223
224 bool CreateOffscreenTabFunction::RunImpl() {
225 Browser* browser = GetCurrentBrowser();
226 if (!browser) {
227 error_ = keys::kNoCurrentWindowError;
228 return false;
Ken Russell (switch to Gerrit) 2011/08/23 22:46:53 Do you need to SendResponse(false) here, and elsew
alexbost 2011/08/24 20:06:21 Looking at extension_tabs_module.cc, they do not c
229 }
230
231 DictionaryValue* create_props;
232 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &create_props));
233
234 // Url
235 std::string url_string;
236 GURL url;
237 EXTENSION_FUNCTION_VALIDATE(
238 create_props->GetString(keys::kUrlKey, &url_string));
239 url = ResolvePossiblyRelativeURL(url_string, GetExtension());
240 if (!url.is_valid()) {
241 error_ = ExtensionErrorUtils::FormatErrorMessage(
242 keys::kInvalidUrlError, url_string);
243 return false;
244 }
245 if (IsCrashURL(url)) {
246 error_ = keys::kNoCrashBrowserError;
247 return false;
248 }
249
250 // Width and height
251 gfx::Rect window_bounds;
252 bool maximized;
253 if (!create_props->HasKey(keys::kWidthKey) ||
254 !create_props->HasKey(keys::kHeightKey))
255 WindowSizer::GetBrowserWindowBounds(std::string(), gfx::Rect(),
256 browser, &window_bounds,
257 &maximized);
258
259 int width;
260 if (create_props->HasKey(keys::kWidthKey))
261 EXTENSION_FUNCTION_VALIDATE(
262 create_props->GetInteger(keys::kWidthKey, &width));
263 else
264 width = window_bounds.width();
265
266 int height;
267 if (create_props->HasKey(keys::kHeightKey))
268 EXTENSION_FUNCTION_VALIDATE(
269 create_props->GetInteger(keys::kHeightKey, &height));
270 else
271 height = window_bounds.height();
272
273 // New offscreen tab
274 TabContents* offscreen_tab_contents =
275 new TabContents(profile_, NULL, MSG_ROUTING_NONE, NULL, NULL);
276 TabContentsWrapper* offscreen_tab =
277 new TabContentsWrapper(offscreen_tab_contents);
278
279 // Setting the size starts the renderer
280 offscreen_tab_contents->view()->SizeContents(*new gfx::Size(width, height));
Ken Russell (switch to Gerrit) 2011/08/23 22:46:53 This is a memory leak. Just use: ->SizeContents(gf
alexbost 2011/08/24 20:06:21 Done.
281
282 // Navigate
283 offscreen_tab_contents->controller().LoadURL(
284 url, GURL(), PageTransition::LINK);
285
286 // Map the offscreen tab to the tab that spawned it
287 TabContents* tab_contents =
288 dispatcher()->delegate()->GetAssociatedTabContents();
289
290 (*offscreen_tabs_map)[ExtensionTabUtil::GetTabId(tab_contents)].
291 push_back(offscreen_tab_contents);
292
293 // Register for tab notifications
294 // if this is the first offscreen tab spawned by the tab
295 if ((*offscreen_tabs_map)[ExtensionTabUtil::GetTabId(tab_contents)].
296 size() == 1) {
297 if (!event_manager)
298 event_manager = new OffscreenTabsEventManager();
299
300 event_manager->RegisterForTabNotifications(tab_contents);
301 }
302
303 // Callback
304 if (has_callback()) {
305 result_.reset(CreateOffscreenTabValue(offscreen_tab));
306 SendResponse(true);
307 }
308
309 return true;
310 }
311
312 // get -------------------------------------------------------------------------
313
314 bool GetOffscreenTabFunction::RunImpl() {
315 int tab_id;
316 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
317 TabContentsWrapper* tab = NULL;
318 TabContents* current_tab_contents =
319 dispatcher()->delegate()->GetAssociatedTabContents();
320 if (!GetOffscreenTabById(tab_id, &tab, current_tab_contents, &error_))
321 return false;
Ken Russell (switch to Gerrit) 2011/08/23 22:46:53 Is a SendReponse(false) needed here? If so, also e
alexbost 2011/08/24 20:06:21 See above.
322
323 result_.reset(CreateOffscreenTabValue(tab));
324 SendResponse(true);
325
326 return true;
327 }
328
329 // getAll ----------------------------------------------------------------------
330
331 bool GetAllOffscreenTabFunction::RunImpl() {
332 TabContents* current_tab_contents =
333 dispatcher()->delegate()->GetAssociatedTabContents();
334
335 std::vector<TabContents*> offscreen_tab_contents_vector =
336 (*offscreen_tabs_map)[ExtensionTabUtil::GetTabId(current_tab_contents)];
337
338 ListValue* tab_list = new ListValue();
339
340 for (std::vector<TabContents*>::iterator
341 it_offscreen_tab_contents = offscreen_tab_contents_vector.begin();
342 it_offscreen_tab_contents != offscreen_tab_contents_vector.end();
343 ++it_offscreen_tab_contents)
344 tab_list->Append(CreateOffscreenTabValue(TabContentsWrapper::
345 GetCurrentWrapperForContents(*it_offscreen_tab_contents)));
346
347 result_.reset(tab_list);
348 SendResponse(true);
349
350 return true;
351 }
352
353 // remove ----------------------------------------------------------------------
354
355 bool RemoveOffscreenTabFunction::RunImpl() {
356 int tab_id;
357 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
358
359 for (OffscreenTabsMap::iterator it = offscreen_tabs_map->begin();
360 it != offscreen_tabs_map->end(); ++it)
361 for (std::vector<TabContents*>::iterator it_offscreen_tab_contents =
362 it->second.begin();
363 it_offscreen_tab_contents != it->second.end();
364 ++it_offscreen_tab_contents)
365 if (ExtensionTabUtil::GetTabId(*it_offscreen_tab_contents) == tab_id) {
366 TabContents* tab_contents;
367 if (!GetTabById(
368 it->first, GetCurrentBrowser(), &tab_contents, &error_))
369 return false;
370
371 CloseOffscreenTab(*it_offscreen_tab_contents);
372
373 it->second.erase(it_offscreen_tab_contents);
374
375 // If this was the last offscreen tab for the tab,
376 // unregister the tab for notifications and remove it from the map
377 if (it->second.empty()) {
378 event_manager->UnregisterForTabNotifications(tab_contents);
379 offscreen_tabs_map->erase(it);
380 }
381
382 return true;
383 }
384
385 error_ = ExtensionErrorUtils::FormatErrorMessage(
386 keys::kOffscreenTabNotFoundError, base::IntToString(tab_id));
387 return false;
388 }
389
390 // sendKeyboardEvent -----------------------------------------------------------
391
392 bool SendKeyboardEventOffscreenTabFunction::RunImpl() {
393 int tab_id;
394 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
395 TabContentsWrapper* tab = NULL;
396 TabContents* current_tab_contents =
397 dispatcher()->delegate()->GetAssociatedTabContents();
398 if (!GetOffscreenTabById(tab_id, &tab, current_tab_contents, &error_))
399 return false;
Ken Russell (switch to Gerrit) 2011/08/23 22:46:53 Here and below, is any SendResponse(false) needed?
alexbost 2011/08/24 20:06:21 See above.
400
401 // JavaScript KeyboardEvent
402 DictionaryValue* dict;
403 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &dict));
404
405 NativeWebKeyboardEvent keyboard_event;
406
407 // Type
408 std::string type;
409 if (dict->HasKey(keys::kKeyboardEventTypeKey)) {
410 EXTENSION_FUNCTION_VALIDATE(
411 dict->GetString(keys::kKeyboardEventTypeKey, &type));
412 } else {
413 error_ = keys::kInvalidKeyboardEventObjectError;
414 return false;
415 }
416
417 if (type.compare(keys::kKeyboardEventTypeValueKeypress) == 0) {
418 keyboard_event.type = WebInputEvent::Char;
419 } else if (type.compare(keys::kKeyboardEventTypeValueKeydown) == 0) {
420 keyboard_event.type = WebInputEvent::KeyDown;
421 } else if (type.compare(keys::kKeyboardEventTypeValueKeyup) == 0) {
422 keyboard_event.type = WebInputEvent::KeyUp;
423 } else {
424 error_ = keys::kInvalidKeyboardEventObjectError;
425 return false;
426 }
427
428 // Key code
429 int key_code;
430 if (dict->HasKey(keys::kKeyboardEventKeyCodeKey)) {
431 EXTENSION_FUNCTION_VALIDATE(
432 dict->GetInteger(keys::kKeyboardEventKeyCodeKey, &key_code));
433 } else {
434 error_ = keys::kInvalidKeyboardEventObjectError;
435 return false;
436 }
437
438 keyboard_event.nativeKeyCode = key_code;
439 keyboard_event.windowsKeyCode = key_code;
440
441 // Key identifier
442 keyboard_event.setKeyIdentifierFromWindowsKeyCode();
443
444 // Keypress = type character
445 if (type.compare(keys::kKeyboardEventTypeValueKeypress) == 0) {
446 int char_code;
447 if (dict->HasKey(keys::kKeyboardEventCharCodeKey)) {
448 EXTENSION_FUNCTION_VALIDATE(
449 dict->GetInteger(keys::kKeyboardEventCharCodeKey, &char_code));
450 keyboard_event.text[0] = char_code;
451 keyboard_event.unmodifiedText[0] = char_code;
452 } else {
453 error_ = keys::kInvalidKeyboardEventObjectError;
454 return false;
455 }
456 }
457
458 // Modifiers
459 bool alt_key;
460 if (dict->HasKey(keys::kKeyboardEventAltKeyKey))
461 EXTENSION_FUNCTION_VALIDATE(
462 dict->GetBoolean(keys::kKeyboardEventAltKeyKey, &alt_key));
463 if (alt_key)
464 keyboard_event.modifiers |= WebInputEvent::AltKey;
465
466 bool ctrl_key;
467 if (dict->HasKey(keys::kKeyboardEventCtrlKeyKey))
468 EXTENSION_FUNCTION_VALIDATE(
469 dict->GetBoolean(keys::kKeyboardEventCtrlKeyKey, &ctrl_key));
470 if (ctrl_key)
471 keyboard_event.modifiers |= WebInputEvent::ControlKey;
472
473 bool meta_key = false;
474 if (dict->HasKey(keys::kMouseEventMetaKeyKey))
475 EXTENSION_FUNCTION_VALIDATE(
476 dict->GetBoolean(keys::kMouseEventMetaKeyKey, &meta_key));
477 if (meta_key)
478 keyboard_event.modifiers |= WebInputEvent::MetaKey;
479
480 bool shift_key;
481 if (dict->HasKey(keys::kKeyboardEventShiftKeyKey))
482 EXTENSION_FUNCTION_VALIDATE(
483 dict->GetBoolean(keys::kKeyboardEventShiftKeyKey, &shift_key));
484 if (shift_key)
485 keyboard_event.modifiers |= WebInputEvent::ShiftKey;
486
487 // Forward event
488 tab->tab_contents()->render_view_host()->
489 ForwardKeyboardEvent(keyboard_event);
490
491 // Callback
492 if (has_callback()) {
493 result_.reset(CreateOffscreenTabValue(tab));
494 SendResponse(true);
495 }
496
497 return true;
498 }
499
500 // sendMouseEvent --------------------------------------------------------------
501
502 bool SendMouseEventOffscreenTabFunction::RunImpl() {
503 int tab_id;
504 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
505 TabContentsWrapper* tab = NULL;
506 TabContents* current_tab_contents =
507 dispatcher()->delegate()->GetAssociatedTabContents();
508 if (!GetOffscreenTabById(tab_id, &tab, current_tab_contents, &error_))
509 return false;
510
511 // JavaScript MouseEvent
512 DictionaryValue* dict;
513 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &dict));
514
515 std::string type;
516 if (dict->HasKey(keys::kMouseEventTypeKey)) {
517 EXTENSION_FUNCTION_VALIDATE(
518 dict->GetString(keys::kMouseEventTypeKey, &type));
519 } else {
520 error_ = keys::kInvalidMouseEventObjectError;
521 return false;
522 }
523
524 if (type.compare(keys::kMouseEventTypeValueMousewheel) == 0) {
525 WebKit::WebMouseWheelEvent wheel_event;
526
527 // Type
528 wheel_event.type = WebInputEvent::MouseWheel;
529
530 // deltaX, deltaY
531 if (dict->HasKey(keys::kMouseEventWheelDeltaXKey) &&
532 dict->HasKey(keys::kMouseEventWheelDeltaYKey)) {
533 int delta_x, delta_y;
534 EXTENSION_FUNCTION_VALIDATE(
535 dict->GetInteger(keys::kMouseEventWheelDeltaXKey, &delta_x));
536 EXTENSION_FUNCTION_VALIDATE(
537 dict->GetInteger(keys::kMouseEventWheelDeltaYKey, &delta_y));
538 wheel_event.deltaX = delta_x;
539 wheel_event.deltaY = delta_y;
540 } else {
541 error_ = keys::kInvalidMouseEventObjectError;
542 return false;
543 }
544
545 // Forward the event
546 tab->tab_contents()->render_view_host()->ForwardWheelEvent(wheel_event);
547 } else {
548 WebKit::WebMouseEvent mouse_event;
549
550 // Type
551 if (type.compare(keys::kMouseEventTypeValueMousedown) == 0 ||
552 type.compare(keys::kMouseEventTypeValueClick) == 0) {
553 mouse_event.type = WebKit::WebInputEvent::MouseDown;
554 } else if (type.compare(keys::kMouseEventTypeValueMouseup) == 0) {
555 mouse_event.type = WebKit::WebInputEvent::MouseUp;
556 } else if (type.compare(keys::kMouseEventTypeValueMousemove) == 0) {
557 mouse_event.type = WebKit::WebInputEvent::MouseMove;
558 } else {
559 error_ = keys::kInvalidMouseEventObjectError;
560 return false;
561 }
562
563 // Button
564 int button;
565 if (dict->HasKey(keys::kMouseEventButtonKey)) {
566 EXTENSION_FUNCTION_VALIDATE(
567 dict->GetInteger(keys::kMouseEventButtonKey, &button));
568 } else {
569 error_ = keys::kInvalidMouseEventObjectError;
570 return false;
571 }
572
573 if (button == keys::kMouseEventButtonValueLeft) {
574 mouse_event.button = WebKit::WebMouseEvent::ButtonLeft;
575 } else if (button == keys::kMouseEventButtonValueMiddle) {
576 mouse_event.button = WebKit::WebMouseEvent::ButtonMiddle;
577 } else if (button == keys::kMouseEventButtonValueRight) {
578 mouse_event.button = WebKit::WebMouseEvent::ButtonRight;
579 } else {
580 error_ = keys::kInvalidMouseEventObjectError;
581 return false;
582 }
583
584 // x, y
585 if (HasOptionalArgument(2) && HasOptionalArgument(3)) {
586 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(2, &mouse_event.x));
587 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(3, &mouse_event.y));
588 } else {
589 error_ = keys::kNoMouseCoordinatesError;
590 return false;
591 }
592
593 // Modifiers
594 bool alt_key = false;
595 if (dict->HasKey(keys::kMouseEventAltKeyKey))
596 EXTENSION_FUNCTION_VALIDATE(
597 dict->GetBoolean(keys::kMouseEventAltKeyKey, &alt_key));
598 if (alt_key)
599 mouse_event.modifiers |= WebInputEvent::AltKey;
600
601 bool ctrl_key = false;
602 if (dict->HasKey(keys::kMouseEventCtrlKeyKey))
603 EXTENSION_FUNCTION_VALIDATE(
604 dict->GetBoolean(keys::kMouseEventCtrlKeyKey, &ctrl_key));
605 if (ctrl_key)
606 mouse_event.modifiers |= WebInputEvent::ControlKey;
607
608 bool meta_key = false;
609 if (dict->HasKey(keys::kMouseEventMetaKeyKey))
610 EXTENSION_FUNCTION_VALIDATE(
611 dict->GetBoolean(keys::kMouseEventMetaKeyKey, &meta_key));
612 if (meta_key)
613 mouse_event.modifiers |= WebInputEvent::MetaKey;
614
615 bool shift_key = false;
616 if (dict->HasKey(keys::kMouseEventShiftKeyKey))
617 EXTENSION_FUNCTION_VALIDATE(
618 dict->GetBoolean(keys::kMouseEventShiftKeyKey, &shift_key));
619 if (shift_key)
620 mouse_event.modifiers |= WebInputEvent::ShiftKey;
621
622 // Click count
623 mouse_event.clickCount = 1;
624
625 // Forward the event
626 tab->tab_contents()->render_view_host()->ForwardMouseEvent(mouse_event);
627
628 // If the event is a click,
629 // fire a mouseup event in addition to the mousedown
630 if (type.compare(keys::kMouseEventTypeValueClick) == 0) {
631 mouse_event.type = WebKit::WebInputEvent::MouseUp;
632 tab->tab_contents()->render_view_host()->ForwardMouseEvent(mouse_event);
633 }
634 }
635
636 // Callback
637 if (has_callback()) {
638 result_.reset(CreateOffscreenTabValue(tab));
639 SendResponse(true);
640 }
641
642 return true;
643 }
644
645 // toDataUrl -------------------------------------------------------------------
646
647 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module.
648 // TODO(alexbost): We want to optimize this function in order to get more image
649 // updates on the browser side. One improvement would be to implement another
650 // hash map to get offscreen tabs in O(1).
651 bool ToDataUrlOffscreenTabFunction::RunImpl() {
652 int tab_id;
653 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
654 TabContentsWrapper* tab = NULL;
655 TabContents* current_tab_contents =
656 dispatcher()->delegate()->GetAssociatedTabContents();
657 if (!GetOffscreenTabById(tab_id, &tab, current_tab_contents, &error_))
658 return false;
659
660 image_format_ = FORMAT_JPEG; // Default format is JPEG.
661 image_quality_ = kDefaultQuality; // Default quality setting.
662
663 if (HasOptionalArgument(1)) {
664 DictionaryValue* options;
665 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options));
666
667 if (options->HasKey(keys::kFormatKey)) {
668 std::string format;
669 EXTENSION_FUNCTION_VALIDATE(
670 options->GetString(keys::kFormatKey, &format));
671
672 if (format == keys::kFormatValueJpeg) {
673 image_format_ = FORMAT_JPEG;
674 } else if (format == keys::kFormatValuePng) {
675 image_format_ = FORMAT_PNG;
676 } else {
677 // Schema validation should make this unreachable.
678 EXTENSION_FUNCTION_VALIDATE(0);
679 }
680 }
681
682 if (options->HasKey(keys::kQualityKey)) {
683 EXTENSION_FUNCTION_VALIDATE(
684 options->GetInteger(keys::kQualityKey, &image_quality_));
685 }
686 }
687
688 // captureVisibleTab() can return an image containing sensitive information
689 // that the browser would otherwise protect. Ensure the extension has
690 // permission to do this.
691 if (!GetExtension()->
692 CanCaptureVisiblePage(tab->tab_contents()->GetURL(), &error_))
693 return false;
694
695 // The backing store approach does not work on Mac
696 // TODO(alexbost): Test on Windows
697 #if !defined(OS_MACOSX)
698 RenderViewHost* render_view_host = tab->tab_contents()->render_view_host();
699
700 // If a backing store is cached for the tab we want to capture,
701 // and it can be copied into a bitmap, then use it to generate the image.
702 BackingStore* backing_store = render_view_host->GetBackingStore(false);
703 if (backing_store && CaptureSnapshotFromBackingStore(backing_store))
704 return true;
705 #endif
706
707 // Ask the renderer for a snapshot of the tab.
708 tab->CaptureSnapshot();
709 registrar_.Add(this,
710 chrome::NOTIFICATION_TAB_SNAPSHOT_TAKEN,
711 Source<TabContentsWrapper>(tab));
712
713 AddRef(); // Balanced in ToDataUrlOffscreenTabFunction::Observe().
714
715 return true;
716 }
717
718 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module.
719 // Build the image of a tab's contents out of a backing store.
720 // This may fail if we can not copy a backing store into a bitmap.
721 // For example, some uncommon X11 visual modes are not supported by
722 // CopyFromBackingStore().
723 bool ToDataUrlOffscreenTabFunction::CaptureSnapshotFromBackingStore(
724 BackingStore* backing_store) {
725
726 skia::PlatformCanvas temp_canvas;
727 if (!backing_store->CopyFromBackingStore(gfx::Rect(backing_store->size()),
728 &temp_canvas)) {
729 return false;
730 }
731 VLOG(1) << "captureVisibleTab() got image from backing store.";
732
733 SendResultFromBitmap(
734 skia::GetTopDevice(temp_canvas)->accessBitmap(false));
735 return true;
736 }
737
738 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module.
739 void ToDataUrlOffscreenTabFunction::Observe(const int type,
740 const NotificationSource& source,
741 const NotificationDetails& details) {
742 DCHECK(type == chrome::NOTIFICATION_TAB_SNAPSHOT_TAKEN);
743
744 const SkBitmap *screen_capture = Details<const SkBitmap>(details).ptr();
745 const bool error = screen_capture->empty();
746
747 if (error) {
748 error_ = keys::kInternalVisibleTabCaptureError;
749 SendResponse(false);
750 } else {
751 VLOG(1) << "Got image from renderer.";
752 SendResultFromBitmap(*screen_capture);
753 }
754
755 Release(); // Balanced in ToDataUrlOffscreenTabFunction::RunImpl().
756 }
757
758 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module.
759 // Turn a bitmap of the screen into an image, set that image as the result,
760 // and call SendResponse().
761 void ToDataUrlOffscreenTabFunction::SendResultFromBitmap(
762 const SkBitmap& screen_capture) {
763 std::vector<unsigned char> data;
764 SkAutoLockPixels screen_capture_lock(screen_capture);
765 bool encoded = false;
766 std::string mime_type;
767 switch (image_format_) {
768 case FORMAT_JPEG:
769 encoded = gfx::JPEGCodec::Encode(
770 reinterpret_cast<unsigned char*>(screen_capture.getAddr32(0, 0)),
771 gfx::JPEGCodec::FORMAT_SkBitmap,
772 screen_capture.width(),
773 screen_capture.height(),
774 static_cast<int>(screen_capture.rowBytes()),
775 image_quality_,
776 &data);
777 mime_type = keys::kMimeTypeJpeg;
778 break;
779 case FORMAT_PNG:
780 encoded = gfx::PNGCodec::EncodeBGRASkBitmap(
781 screen_capture,
782 true, // Discard transparency.
783 &data);
784 mime_type = keys::kMimeTypePng;
785 break;
786 default:
787 NOTREACHED() << "Invalid image format.";
788 }
789
790 if (!encoded) {
791 error_ = keys::kInternalVisibleTabCaptureError;
792 SendResponse(false);
793 return;
794 }
795
796 std::string base64_result;
797 base::StringPiece stream_as_string(
798 reinterpret_cast<const char*>(vector_as_array(&data)), data.size());
799
800 base::Base64Encode(stream_as_string, &base64_result);
801 base64_result.insert(0, base::StringPrintf("data:%s;base64,",
802 mime_type.c_str()));
803 result_.reset(new StringValue(base64_result));
804 SendResponse(true);
805 }
806
807 // update ----------------------------------------------------------------------
808
809 UpdateOffscreenTabFunction::UpdateOffscreenTabFunction() {
810 }
811
812 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module.
813 bool UpdateOffscreenTabFunction::RunImpl() {
814 int tab_id;
815 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
816 TabContentsWrapper* tab = NULL;
817 TabContents* current_tab_contents =
818 dispatcher()->delegate()->GetAssociatedTabContents();
819 if (!GetOffscreenTabById(tab_id, &tab, current_tab_contents, &error_))
820 return false;
821
822 DictionaryValue* update_props;
823 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props));
824
825 // Url
826 if (update_props->HasKey(keys::kUrlKey)) {
827 std::string url_string;
828 GURL url;
829 EXTENSION_FUNCTION_VALIDATE(
830 update_props->GetString(keys::kUrlKey, &url_string));
831 url = ResolvePossiblyRelativeURL(url_string, GetExtension());
832 if (!url.is_valid()) {
833 error_ = ExtensionErrorUtils::FormatErrorMessage(
834 keys::kInvalidUrlError, url_string);
835 return false;
836 }
837 if (IsCrashURL(url)) {
838 error_ = keys::kNoCrashBrowserError;
839 return false;
840 }
841
842 // JavaScript URLs can do the same kinds of things as cross-origin XHR, so
843 // we need to check host permissions before allowing them.
844 if (url.SchemeIs(chrome::kJavaScriptScheme)) {
845 if (!GetExtension()->CanExecuteScriptOnPage(
846 tab->tab_contents()->GetURL(), NULL, &error_)) {
847 return false;
848 }
849
850 ExtensionMsg_ExecuteCode_Params params;
851 params.request_id = request_id();
852 params.extension_id = extension_id();
853 params.is_javascript = true;
854 params.code = url.path();
855 params.all_frames = false;
856 params.in_main_world = true;
857
858 RenderViewHost* render_view_host =
859 tab->tab_contents()->render_view_host();
860 render_view_host->Send(
861 new ExtensionMsg_ExecuteCode(render_view_host->routing_id(),
862 params));
863
864 Observe(tab->tab_contents());
865 AddRef(); // balanced in Observe()
866
867 return true;
868 }
869
870 tab->tab_contents()->controller().
871 LoadURL(url, GURL(), PageTransition::LINK);
872
873 // The URL of a tab contents never actually changes to a JavaScript URL, so
874 // this check only makes sense in other cases.
875 if (!url.SchemeIs(chrome::kJavaScriptScheme))
876 DCHECK_EQ(url.spec(), tab->tab_contents()->GetURL().spec());
877 }
878
879 // Width and height
880 if (update_props->HasKey(keys::kWidthKey) ||
881 update_props->HasKey(keys::kHeightKey)) {
882 int width;
883 if (update_props->HasKey(keys::kWidthKey))
884 EXTENSION_FUNCTION_VALIDATE(
885 update_props->GetInteger(keys::kWidthKey, &width));
886 else
887 GetTabWidth(tab->tab_contents());
888
889 int height;
890 if (update_props->HasKey(keys::kHeightKey))
891 EXTENSION_FUNCTION_VALIDATE(
892 update_props->GetInteger(keys::kHeightKey, &height));
893 else
894 GetTabHeight(tab->tab_contents());
895
896 tab->tab_contents()->view()->SizeContents(*new gfx::Size(width, height));
897 }
898
899 // Callback
900 if (has_callback()) {
901 result_.reset(CreateOffscreenTabValue(tab));
902 SendResponse(true);
903 }
904
905 return true;
906 }
907
908 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module.
909 bool UpdateOffscreenTabFunction::OnMessageReceived(
910 const IPC::Message& message) {
911 if (message.type() != ExtensionHostMsg_ExecuteCodeFinished::ID)
912 return false;
913
914 int message_request_id;
915 void* iter = NULL;
916 if (!message.ReadInt(&iter, &message_request_id)) {
917 NOTREACHED() << "malformed extension message";
918 return true;
919 }
920
921 if (message_request_id != request_id())
922 return false;
923
924 IPC_BEGIN_MESSAGE_MAP(UpdateOffscreenTabFunction, message)
925 IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExecuteCodeFinished,
926 OnExecuteCodeFinished)
927 IPC_END_MESSAGE_MAP()
928 return true;
929 }
930
931 // TODO(alexbost): Needs refactoring. Similar method in extension_tabs_module.
932 void UpdateOffscreenTabFunction::OnExecuteCodeFinished(int request_id,
933 bool success,
934 const std::string& error) {
935 if (!error.empty()) {
936 CHECK(!success);
937 error_ = error;
938 }
939
940 SendResponse(success);
941
942 Observe(NULL);
943 Release(); // balanced in Execute()
944 }
945
946
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698