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

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

Issue 10544023: Moving the tabs_module API into a separate directory in api/ (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Correcting the order of includes. Created 8 years, 6 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
OLDNEW
(Empty)
1 // Copyright (c) 2012 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_tabs_module.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <vector>
10
11 #include "base/base64.h"
12 #include "base/bind.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted_memory.h"
15 #include "base/message_loop.h"
16 #include "base/stl_util.h"
17 #include "base/string16.h"
18 #include "base/string_number_conversions.h"
19 #include "base/string_util.h"
20 #include "base/stringprintf.h"
21 #include "base/utf_string_conversions.h"
22 #include "chrome/browser/extensions/extension_function_dispatcher.h"
23 #include "chrome/browser/extensions/extension_function_util.h"
24 #include "chrome/browser/extensions/extension_host.h"
25 #include "chrome/browser/extensions/extension_service.h"
26 #include "chrome/browser/extensions/extension_tab_helper.h"
27 #include "chrome/browser/extensions/extension_tab_util.h"
28 #include "chrome/browser/extensions/extension_tabs_module_constants.h"
29 #include "chrome/browser/extensions/extension_window_controller.h"
30 #include "chrome/browser/extensions/extension_window_list.h"
31 #include "chrome/browser/extensions/script_executor.h"
32 #include "chrome/browser/prefs/incognito_mode_prefs.h"
33 #include "chrome/browser/profiles/profile.h"
34 #include "chrome/browser/sessions/restore_tab_helper.h"
35 #include "chrome/browser/translate/translate_tab_helper.h"
36 #include "chrome/browser/ui/browser.h"
37 #include "chrome/browser/ui/browser_finder.h"
38 #include "chrome/browser/ui/browser_list.h"
39 #include "chrome/browser/ui/browser_navigator.h"
40 #include "chrome/browser/ui/browser_window.h"
41 #include "chrome/browser/ui/extensions/shell_window.h"
42 #include "chrome/browser/ui/panels/panel_manager.h"
43 #include "chrome/browser/ui/snapshot_tab_helper.h"
44 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
45 #include "chrome/browser/ui/tabs/tab_strip_model.h"
46 #include "chrome/browser/ui/window_sizer.h"
47 #include "chrome/browser/web_applications/web_app.h"
48 #include "chrome/common/chrome_notification_types.h"
49 #include "chrome/common/chrome_switches.h"
50 #include "chrome/common/extensions/api/windows.h"
51 #include "chrome/common/extensions/extension.h"
52 #include "chrome/common/extensions/extension_error_utils.h"
53 #include "chrome/common/extensions/extension_manifest_constants.h"
54 #include "chrome/common/extensions/extension_messages.h"
55 #include "chrome/common/extensions/user_script.h"
56 #include "chrome/common/pref_names.h"
57 #include "chrome/common/url_constants.h"
58 #include "content/public/browser/navigation_controller.h"
59 #include "content/public/browser/navigation_entry.h"
60 #include "content/public/browser/notification_details.h"
61 #include "content/public/browser/notification_source.h"
62 #include "content/public/browser/render_view_host.h"
63 #include "content/public/browser/render_widget_host_view.h"
64 #include "content/public/browser/web_contents.h"
65 #include "content/public/browser/web_contents_view.h"
66 #include "content/public/common/url_constants.h"
67 #include "skia/ext/image_operations.h"
68 #include "skia/ext/platform_canvas.h"
69 #include "third_party/skia/include/core/SkBitmap.h"
70 #include "ui/base/ui_base_types.h"
71 #include "ui/gfx/codec/jpeg_codec.h"
72 #include "ui/gfx/codec/png_codec.h"
73
74 #if defined(USE_ASH)
75 #include "ash/ash_switches.h"
76 #include "base/command_line.h"
77 #include "chrome/browser/ui/views/ash/panel_view_aura.h"
78 #endif
79
80 #if defined(OS_WIN)
81 #include "base/win/metro.h"
82 #endif // OS_WIN
83
84 namespace Get = extensions::api::windows::Get;
85 namespace GetAll = extensions::api::windows::GetAll;
86 namespace GetCurrent = extensions::api::windows::GetCurrent;
87 namespace GetLastFocused = extensions::api::windows::GetLastFocused;
88 namespace errors = extension_manifest_errors;
89 namespace keys = extension_tabs_module_constants;
90
91 using content::NavigationController;
92 using content::NavigationEntry;
93 using content::OpenURLParams;
94 using content::Referrer;
95 using content::RenderViewHost;
96 using content::WebContents;
97 using extensions::ScriptExecutor;
98
99 const int CaptureVisibleTabFunction::kDefaultQuality = 90;
100
101 namespace {
102
103 // |error_message| can optionally be passed in a will be set with an appropriate
104 // message if the window cannot be found by id.
105 Browser* GetBrowserInProfileWithId(Profile* profile,
106 const int window_id,
107 bool include_incognito,
108 std::string* error_message) {
109 Profile* incognito_profile =
110 include_incognito && profile->HasOffTheRecordProfile() ?
111 profile->GetOffTheRecordProfile() : NULL;
112 for (BrowserList::const_iterator browser = BrowserList::begin();
113 browser != BrowserList::end(); ++browser) {
114 if (((*browser)->profile() == profile ||
115 (*browser)->profile() == incognito_profile) &&
116 ExtensionTabUtil::GetWindowId(*browser) == window_id &&
117 ((*browser)->window()))
118 return *browser;
119 }
120
121 if (error_message)
122 *error_message = ExtensionErrorUtils::FormatErrorMessage(
123 keys::kWindowNotFoundError, base::IntToString(window_id));
124
125 return NULL;
126 }
127
128 bool GetBrowserFromWindowID(
129 UIThreadExtensionFunction* function, int window_id, Browser** browser) {
130 if (window_id == extension_misc::kCurrentWindowId) {
131 *browser = function->GetCurrentBrowser();
132 if (!(*browser) || !(*browser)->window()) {
133 function->SetError(keys::kNoCurrentWindowError);
134 return false;
135 }
136 } else {
137 std::string error;
138 *browser = GetBrowserInProfileWithId(
139 function->profile(), window_id, function->include_incognito(), &error);
140 if (!*browser) {
141 function->SetError(error);
142 return false;
143 }
144 }
145 return true;
146 }
147
148 bool GetWindowFromWindowID(UIThreadExtensionFunction* function,
149 int window_id,
150 ExtensionWindowController** controller) {
151 if (window_id == extension_misc::kCurrentWindowId) {
152 ExtensionWindowController* extension_window_controller =
153 function->dispatcher()->delegate()->GetExtensionWindowController();
154 // If there is a window controller associated with this extension, use that.
155 if (extension_window_controller) {
156 *controller = extension_window_controller;
157 } else {
158 // Otherwise get the focused or most recently added window.
159 *controller = ExtensionWindowList::GetInstance()->
160 CurrentWindowForFunction(function);
161 }
162 if (!(*controller)) {
163 function->SetError(keys::kNoCurrentWindowError);
164 return false;
165 }
166 } else {
167 *controller = ExtensionWindowList::GetInstance()->FindWindowForFunctionById(
168 function, window_id);
169 if (!(*controller)) {
170 function->SetError(ExtensionErrorUtils::FormatErrorMessage(
171 keys::kWindowNotFoundError, base::IntToString(window_id)));
172 return false;
173 }
174 }
175 return true;
176 }
177 // |error_message| can optionally be passed in and will be set with an
178 // appropriate message if the tab cannot be found by id.
179 bool GetTabById(int tab_id,
180 Profile* profile,
181 bool include_incognito,
182 Browser** browser,
183 TabStripModel** tab_strip,
184 TabContentsWrapper** contents,
185 int* tab_index,
186 std::string* error_message) {
187 if (ExtensionTabUtil::GetTabById(tab_id, profile, include_incognito,
188 browser, tab_strip, contents, tab_index))
189 return true;
190
191 if (error_message)
192 *error_message = ExtensionErrorUtils::FormatErrorMessage(
193 keys::kTabNotFoundError, base::IntToString(tab_id));
194
195 return false;
196 }
197
198 // A three state enum to distinguish between when a boolean query argument is
199 // set or not.
200 enum QueryArg {
201 NOT_SET = -1,
202 MATCH_FALSE,
203 MATCH_TRUE
204 };
205
206 bool MatchesQueryArg(QueryArg arg, bool value) {
207 if (arg == NOT_SET)
208 return true;
209
210 return (arg == MATCH_TRUE && value) || (arg == MATCH_FALSE && !value);
211 }
212
213 QueryArg ParseBoolQueryArg(base::DictionaryValue* query, const char* key) {
214 if (query->HasKey(key)) {
215 bool value = false;
216 CHECK(query->GetBoolean(key, &value));
217 return value ? MATCH_TRUE : MATCH_FALSE;
218 }
219 return NOT_SET;
220 }
221
222 Browser* CreateBrowserWindow(const Browser::CreateParams& params,
223 Profile* profile,
224 const std::string& extension_id) {
225 bool use_existing_browser_window = false;
226
227 #if defined(OS_WIN)
228 // In Windows 8 metro mode we only allow new windows to be created if the
229 // extension id is valid in which case it is created as an application window
230 if (extension_id.empty() && base::win::GetMetroModule())
231 use_existing_browser_window = true;
232 #endif // OS_WIN
233
234 Browser* new_window = NULL;
235 if (use_existing_browser_window)
236 // The false parameter passed below is to ensure that we find a browser
237 // object matching the profile passed in, instead of the original profile
238 new_window = browser::FindTabbedBrowser(profile, false);
239
240 if (!new_window)
241 new_window = Browser::CreateWithParams(params);
242 return new_window;
243 }
244
245 } // namespace
246
247 // Windows ---------------------------------------------------------------------
248
249 bool GetWindowFunction::RunImpl() {
250 scoped_ptr<Get::Params> params(Get::Params::Create(*args_));
251 EXTENSION_FUNCTION_VALIDATE(params.get());
252
253 bool populate_tabs = false;
254 if (params->get_info.get() && params->get_info->populate.get())
255 populate_tabs = *params->get_info->populate;
256
257 ExtensionWindowController* controller;
258 if (!GetWindowFromWindowID(this, params->window_id, &controller))
259 return false;
260
261 if (populate_tabs)
262 result_.reset(controller->CreateWindowValueWithTabs());
263 else
264 result_.reset(controller->CreateWindowValue());
265 return true;
266 }
267
268 bool GetCurrentWindowFunction::RunImpl() {
269 scoped_ptr<GetCurrent::Params> params(GetCurrent::Params::Create(*args_));
270 EXTENSION_FUNCTION_VALIDATE(params.get());
271
272 bool populate_tabs = false;
273 if (params->get_info.get() && params->get_info->populate.get())
274 populate_tabs = *params->get_info->populate;
275
276 ExtensionWindowController* controller;
277 if (!GetWindowFromWindowID(this,
278 extension_misc::kCurrentWindowId,
279 &controller)) {
280 return false;
281 }
282 if (populate_tabs)
283 result_.reset(controller->CreateWindowValueWithTabs());
284 else
285 result_.reset(controller->CreateWindowValue());
286 return true;
287 }
288
289 bool GetLastFocusedWindowFunction::RunImpl() {
290 scoped_ptr<GetLastFocused::Params> params(
291 GetLastFocused::Params::Create(*args_));
292 EXTENSION_FUNCTION_VALIDATE(params.get());
293
294 bool populate_tabs = false;
295 if (params->get_info.get() && params->get_info->populate.get())
296 populate_tabs = *params->get_info->populate;
297
298 // Note: currently this returns the last active browser. If we decide to
299 // include other window types (e.g. panels), we will need to add logic to
300 // ExtensionWindowList that mirrors the active behavior of BrowserList.
301 Browser* browser = browser::FindAnyBrowser(
302 profile(), include_incognito());
303 if (!browser || !browser->window()) {
304 error_ = keys::kNoLastFocusedWindowError;
305 return false;
306 }
307 ExtensionWindowController* controller =
308 browser->extension_window_controller();
309 if (populate_tabs)
310 result_.reset(controller->CreateWindowValueWithTabs());
311 else
312 result_.reset(controller->CreateWindowValue());
313 return true;
314 }
315
316 bool GetAllWindowsFunction::RunImpl() {
317 scoped_ptr<GetAll::Params> params(GetAll::Params::Create(*args_));
318 EXTENSION_FUNCTION_VALIDATE(params.get());
319
320 bool populate_tabs = false;
321 if (params->get_info.get() && params->get_info->populate.get())
322 populate_tabs = *params->get_info->populate;
323
324 ListValue* window_list = new ListValue();
325 const ExtensionWindowList::WindowList& windows =
326 ExtensionWindowList::GetInstance()->windows();
327 for (ExtensionWindowList::WindowList::const_iterator iter =
328 windows.begin();
329 iter != windows.end(); ++iter) {
330 if (!this->CanOperateOnWindow(*iter))
331 continue;
332 if (populate_tabs)
333 window_list->Append((*iter)->CreateWindowValueWithTabs());
334 else
335 window_list->Append((*iter)->CreateWindowValue());
336 }
337 result_.reset(window_list);
338 return true;
339 }
340
341 bool CreateWindowFunction::ShouldOpenIncognitoWindow(
342 const base::DictionaryValue* args,
343 std::vector<GURL>* urls,
344 bool* is_error) {
345 *is_error = false;
346 const IncognitoModePrefs::Availability incognito_availability =
347 IncognitoModePrefs::GetAvailability(profile_->GetPrefs());
348 bool incognito = false;
349 if (args && args->HasKey(keys::kIncognitoKey)) {
350 EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kIncognitoKey,
351 &incognito));
352 if (incognito && incognito_availability == IncognitoModePrefs::DISABLED) {
353 error_ = keys::kIncognitoModeIsDisabled;
354 *is_error = true;
355 return false;
356 }
357 if (!incognito && incognito_availability == IncognitoModePrefs::FORCED) {
358 error_ = keys::kIncognitoModeIsForced;
359 *is_error = true;
360 return false;
361 }
362 } else if (incognito_availability == IncognitoModePrefs::FORCED) {
363 // If incognito argument is not specified explicitly, we default to
364 // incognito when forced so by policy.
365 incognito = true;
366 }
367
368 // Remove all URLs that are not allowed in an incognito session. Note that a
369 // ChromeOS guest session is not considered incognito in this case.
370 if (incognito && !Profile::IsGuestSession()) {
371 std::string first_url_erased;
372 for (size_t i = 0; i < urls->size();) {
373 if (browser::IsURLAllowedInIncognito((*urls)[i])) {
374 i++;
375 } else {
376 if (first_url_erased.empty())
377 first_url_erased = (*urls)[i].spec();
378 urls->erase(urls->begin() + i);
379 }
380 }
381 if (urls->empty() && !first_url_erased.empty()) {
382 error_ = ExtensionErrorUtils::FormatErrorMessage(
383 keys::kURLsNotAllowedInIncognitoError, first_url_erased);
384 *is_error = true;
385 return false;
386 }
387 }
388 return incognito;
389 }
390
391 bool CreateWindowFunction::RunImpl() {
392 DictionaryValue* args = NULL;
393 std::vector<GURL> urls;
394 TabContentsWrapper* contents = NULL;
395
396 if (HasOptionalArgument(0))
397 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
398
399 // Look for optional url.
400 if (args) {
401 if (args->HasKey(keys::kUrlKey)) {
402 Value* url_value;
403 std::vector<std::string> url_strings;
404 args->Get(keys::kUrlKey, &url_value);
405
406 // First, get all the URLs the client wants to open.
407 if (url_value->IsType(Value::TYPE_STRING)) {
408 std::string url_string;
409 url_value->GetAsString(&url_string);
410 url_strings.push_back(url_string);
411 } else if (url_value->IsType(Value::TYPE_LIST)) {
412 const ListValue* url_list = static_cast<const ListValue*>(url_value);
413 for (size_t i = 0; i < url_list->GetSize(); ++i) {
414 std::string url_string;
415 EXTENSION_FUNCTION_VALIDATE(url_list->GetString(i, &url_string));
416 url_strings.push_back(url_string);
417 }
418 }
419
420 // Second, resolve, validate and convert them to GURLs.
421 for (std::vector<std::string>::iterator i = url_strings.begin();
422 i != url_strings.end(); ++i) {
423 GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
424 *i, GetExtension());
425 if (!url.is_valid()) {
426 error_ = ExtensionErrorUtils::FormatErrorMessage(
427 keys::kInvalidUrlError, *i);
428 return false;
429 }
430 // Don't let the extension crash the browser or renderers.
431 if (ExtensionTabUtil::IsCrashURL(url)) {
432 error_ = keys::kNoCrashBrowserError;
433 return false;
434 }
435 urls.push_back(url);
436 }
437 }
438 }
439
440 // Look for optional tab id.
441 if (args) {
442 int tab_id = -1;
443 if (args->HasKey(keys::kTabIdKey)) {
444 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kTabIdKey, &tab_id));
445
446 // Find the tab and detach it from the original window.
447 Browser* source_browser = NULL;
448 TabStripModel* source_tab_strip = NULL;
449 int tab_index = -1;
450 if (!GetTabById(tab_id, profile(), include_incognito(),
451 &source_browser, &source_tab_strip, &contents,
452 &tab_index, &error_))
453 return false;
454 contents = source_tab_strip->DetachTabContentsAt(tab_index);
455 if (!contents) {
456 error_ = ExtensionErrorUtils::FormatErrorMessage(
457 keys::kTabNotFoundError, base::IntToString(tab_id));
458 return false;
459 }
460 }
461 }
462
463 // Try to position the new browser relative its originating browser window.
464 gfx::Rect window_bounds;
465 // The call offsets the bounds by kWindowTilePixels (defined in WindowSizer to
466 // be 10)
467 //
468 // NOTE(rafaelw): It's ok if GetCurrentBrowser() returns NULL here.
469 // GetBrowserWindowBounds will default to saved "default" values for the app.
470 WindowSizer::GetBrowserWindowBounds(std::string(), gfx::Rect(),
471 GetCurrentBrowser(), &window_bounds);
472
473 // Calculate popup and panels bounds separately.
474 gfx::Rect popup_bounds;
475 gfx::Rect panel_bounds; // Use 0x0 for panels. Panel manager sizes them.
476
477 // In ChromiumOS the default popup bounds is 0x0 which indicates default
478 // window sizes in PanelBrowserView. In other OSs use the same default
479 // bounds as windows.
480 #if defined(OS_CHROMEOS)
481 popup_bounds = panel_bounds;
482 #else
483 popup_bounds = window_bounds; // Use window size as default for popups
484 #endif
485
486 Profile* window_profile = profile();
487 Browser::Type window_type = Browser::TYPE_TABBED;
488 bool focused = true;
489 bool saw_focus_key = false;
490 std::string extension_id;
491
492 // Decide whether we are opening a normal window or an incognito window.
493 bool is_error = true;
494 bool open_incognito_window = ShouldOpenIncognitoWindow(args, &urls,
495 &is_error);
496 if (is_error) {
497 // error_ member variable is set inside of ShouldOpenIncognitoWindow.
498 return false;
499 }
500 if (open_incognito_window) {
501 window_profile = window_profile->GetOffTheRecordProfile();
502 }
503
504 if (args) {
505 // Any part of the bounds can optionally be set by the caller.
506 int bounds_val = -1;
507 if (args->HasKey(keys::kLeftKey)) {
508 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kLeftKey,
509 &bounds_val));
510 window_bounds.set_x(bounds_val);
511 popup_bounds.set_x(bounds_val);
512 panel_bounds.set_x(bounds_val);
513 }
514
515 if (args->HasKey(keys::kTopKey)) {
516 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kTopKey,
517 &bounds_val));
518 window_bounds.set_y(bounds_val);
519 popup_bounds.set_y(bounds_val);
520 panel_bounds.set_y(bounds_val);
521 }
522
523 if (args->HasKey(keys::kWidthKey)) {
524 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kWidthKey,
525 &bounds_val));
526 window_bounds.set_width(bounds_val);
527 popup_bounds.set_width(bounds_val);
528 panel_bounds.set_width(bounds_val);
529 }
530
531 if (args->HasKey(keys::kHeightKey)) {
532 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kHeightKey,
533 &bounds_val));
534 window_bounds.set_height(bounds_val);
535 popup_bounds.set_height(bounds_val);
536 panel_bounds.set_height(bounds_val);
537 }
538
539 if (args->HasKey(keys::kFocusedKey)) {
540 EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kFocusedKey,
541 &focused));
542 saw_focus_key = true;
543 }
544
545 std::string type_str;
546 if (args->HasKey(keys::kWindowTypeKey)) {
547 EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kWindowTypeKey,
548 &type_str));
549 if (type_str == keys::kWindowTypeValuePopup) {
550 window_type = Browser::TYPE_POPUP;
551 extension_id = GetExtension()->id();
552 } else if (type_str == keys::kWindowTypeValuePanel) {
553 extension_id = GetExtension()->id();
554 bool use_panels = false;
555 #if !defined(OS_ANDROID)
556 use_panels = PanelManager::ShouldUsePanels(extension_id);
557 #endif
558 #if defined(USE_ASH)
559 if (CommandLine::ForCurrentProcess()->HasSwitch(
560 ash::switches::kAuraPanelManager))
561 use_panels = true;
562 #endif
563 if (use_panels)
564 window_type = Browser::TYPE_PANEL;
565 else
566 window_type = Browser::TYPE_POPUP;
567 } else if (type_str != keys::kWindowTypeValueNormal) {
568 error_ = keys::kInvalidWindowTypeError;
569 return false;
570 }
571 }
572 }
573
574 #if defined(USE_ASH)
575 // Aura Panels create a new PanelViewAura.
576 if (CommandLine::ForCurrentProcess()->HasSwitch(
577 ash::switches::kAuraPanelManager) &&
578 window_type == Browser::TYPE_PANEL) {
579 // Note: Panels ignore all but the first url provided.
580 std::string title =
581 web_app::GenerateApplicationNameFromExtensionId(extension_id);
582 PanelViewAura* panel_view = new PanelViewAura(title);
583 panel_view->Init(window_profile, urls[0], panel_bounds);
584 result_.reset(
585 panel_view->extension_window_controller()->CreateWindowValueWithTabs());
586 return true;
587 }
588 #endif
589
590 // Create a new BrowserWindow.
591 Browser::CreateParams create_params;
592 if (extension_id.empty()) {
593 create_params = Browser::CreateParams(window_type, window_profile);
594 create_params.initial_bounds = window_bounds;
595 } else {
596 create_params = Browser::CreateParams::CreateForApp(
597 window_type,
598 web_app::GenerateApplicationNameFromExtensionId(extension_id),
599 (window_type == Browser::TYPE_PANEL ? panel_bounds : popup_bounds),
600 window_profile);
601 }
602 create_params.initial_show_state = ui::SHOW_STATE_NORMAL;
603
604 Browser* new_window = CreateBrowserWindow(create_params, window_profile,
605 extension_id);
606
607 for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) {
608 TabContentsWrapper* tab = new_window->AddSelectedTabWithURL(
609 *i, content::PAGE_TRANSITION_LINK);
610 if (window_type == Browser::TYPE_PANEL)
611 tab->extension_tab_helper()->SetExtensionAppIconById(extension_id);
612 }
613 if (contents) {
614 TabStripModel* target_tab_strip = new_window->tab_strip_model();
615 target_tab_strip->InsertTabContentsAt(urls.size(), contents,
616 TabStripModel::ADD_NONE);
617 } else if (urls.empty()) {
618 new_window->NewTab();
619 }
620 new_window->SelectNumberedTab(0);
621
622 // Unlike other window types, Panels do not take focus by default.
623 if (!saw_focus_key && window_type == Browser::TYPE_PANEL)
624 focused = false;
625
626 if (focused)
627 new_window->window()->Show();
628 else
629 new_window->window()->ShowInactive();
630
631 if (new_window->profile()->IsOffTheRecord() && !include_incognito()) {
632 // Don't expose incognito windows if the extension isn't allowed.
633 result_.reset(Value::CreateNullValue());
634 } else {
635 result_.reset(
636 new_window->extension_window_controller()->CreateWindowValueWithTabs());
637 }
638
639 return true;
640 }
641
642 bool UpdateWindowFunction::RunImpl() {
643 int window_id = extension_misc::kUnknownWindowId;
644 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
645 DictionaryValue* update_props;
646 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props));
647
648 ExtensionWindowController* controller;
649 if (!GetWindowFromWindowID(this, window_id, &controller))
650 return false;
651
652 ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT; // No change.
653 std::string state_str;
654 if (update_props->HasKey(keys::kShowStateKey)) {
655 EXTENSION_FUNCTION_VALIDATE(update_props->GetString(keys::kShowStateKey,
656 &state_str));
657 if (state_str == keys::kShowStateValueNormal) {
658 show_state = ui::SHOW_STATE_NORMAL;
659 } else if (state_str == keys::kShowStateValueMinimized) {
660 show_state = ui::SHOW_STATE_MINIMIZED;
661 } else if (state_str == keys::kShowStateValueMaximized) {
662 show_state = ui::SHOW_STATE_MAXIMIZED;
663 } else if (state_str == keys::kShowStateValueFullscreen) {
664 show_state = ui::SHOW_STATE_FULLSCREEN;
665 } else {
666 error_ = keys::kInvalidWindowStateError;
667 return false;
668 }
669 }
670
671 if (show_state != ui::SHOW_STATE_FULLSCREEN &&
672 show_state != ui::SHOW_STATE_DEFAULT)
673 controller->SetFullscreenMode(false, GetExtension()->url());
674
675 switch (show_state) {
676 case ui::SHOW_STATE_MINIMIZED:
677 controller->window()->Minimize();
678 break;
679 case ui::SHOW_STATE_MAXIMIZED:
680 controller->window()->Maximize();
681 break;
682 case ui::SHOW_STATE_FULLSCREEN:
683 if (controller->window()->IsMinimized() ||
684 controller->window()->IsMaximized())
685 controller->window()->Restore();
686 controller->SetFullscreenMode(true, GetExtension()->url());
687 break;
688 case ui::SHOW_STATE_NORMAL:
689 controller->window()->Restore();
690 break;
691 default:
692 break;
693 }
694
695 gfx::Rect bounds;
696 if (controller->window()->IsMinimized())
697 bounds = controller->window()->GetRestoredBounds();
698 else
699 bounds = controller->window()->GetBounds();
700 bool set_bounds = false;
701
702 // Any part of the bounds can optionally be set by the caller.
703 int bounds_val;
704 if (update_props->HasKey(keys::kLeftKey)) {
705 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
706 keys::kLeftKey,
707 &bounds_val));
708 bounds.set_x(bounds_val);
709 set_bounds = true;
710 }
711
712 if (update_props->HasKey(keys::kTopKey)) {
713 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
714 keys::kTopKey,
715 &bounds_val));
716 bounds.set_y(bounds_val);
717 set_bounds = true;
718 }
719
720 if (update_props->HasKey(keys::kWidthKey)) {
721 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
722 keys::kWidthKey,
723 &bounds_val));
724 bounds.set_width(bounds_val);
725 set_bounds = true;
726 }
727
728 if (update_props->HasKey(keys::kHeightKey)) {
729 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
730 keys::kHeightKey,
731 &bounds_val));
732 bounds.set_height(bounds_val);
733 set_bounds = true;
734 }
735
736 if (set_bounds) {
737 if (show_state == ui::SHOW_STATE_MINIMIZED ||
738 show_state == ui::SHOW_STATE_MAXIMIZED ||
739 show_state == ui::SHOW_STATE_FULLSCREEN) {
740 error_ = keys::kInvalidWindowStateError;
741 return false;
742 }
743 controller->window()->SetBounds(bounds);
744 }
745
746 bool active_val = false;
747 if (update_props->HasKey(keys::kFocusedKey)) {
748 EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
749 keys::kFocusedKey, &active_val));
750 if (active_val) {
751 if (show_state == ui::SHOW_STATE_MINIMIZED) {
752 error_ = keys::kInvalidWindowStateError;
753 return false;
754 }
755 controller->window()->Activate();
756 } else {
757 if (show_state == ui::SHOW_STATE_MAXIMIZED ||
758 show_state == ui::SHOW_STATE_FULLSCREEN) {
759 error_ = keys::kInvalidWindowStateError;
760 return false;
761 }
762 controller->window()->Deactivate();
763 }
764 }
765
766 bool draw_attention = false;
767 if (update_props->HasKey(keys::kDrawAttentionKey)) {
768 EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
769 keys::kDrawAttentionKey, &draw_attention));
770 controller->window()->FlashFrame(draw_attention);
771 }
772
773 result_.reset(controller->CreateWindowValue());
774
775 return true;
776 }
777
778 bool RemoveWindowFunction::RunImpl() {
779 int window_id = -1;
780 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
781
782 ExtensionWindowController* controller;
783 if (!GetWindowFromWindowID(this, window_id, &controller))
784 return false;
785
786 ExtensionWindowController::Reason reason;
787 if (!controller->CanClose(&reason)) {
788 if (reason == ExtensionWindowController::REASON_NOT_EDITABLE)
789 error_ = keys::kTabStripNotEditableError;
790 return false;
791 }
792 controller->window()->Close();
793 return true;
794 }
795
796 // Tabs ------------------------------------------------------------------------
797
798 bool GetSelectedTabFunction::RunImpl() {
799 // windowId defaults to "current" window.
800 int window_id = extension_misc::kCurrentWindowId;
801
802 if (HasOptionalArgument(0))
803 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
804
805 Browser* browser = NULL;
806 if (!GetBrowserFromWindowID(this, window_id, &browser))
807 return false;
808
809 TabStripModel* tab_strip = browser->tab_strip_model();
810 TabContentsWrapper* contents = tab_strip->GetActiveTabContents();
811 if (!contents) {
812 error_ = keys::kNoSelectedTabError;
813 return false;
814 }
815 result_.reset(ExtensionTabUtil::CreateTabValue(contents->web_contents(),
816 tab_strip,
817 tab_strip->active_index()));
818 return true;
819 }
820
821 bool GetAllTabsInWindowFunction::RunImpl() {
822 // windowId defaults to "current" window.
823 int window_id = extension_misc::kCurrentWindowId;
824 if (HasOptionalArgument(0))
825 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
826
827 Browser* browser = NULL;
828 if (!GetBrowserFromWindowID(this, window_id, &browser))
829 return false;
830
831 result_.reset(ExtensionTabUtil::CreateTabList(browser));
832
833 return true;
834 }
835
836 bool QueryTabsFunction::RunImpl() {
837 DictionaryValue* query = NULL;
838 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &query));
839
840 QueryArg active = ParseBoolQueryArg(query, keys::kActiveKey);
841 QueryArg pinned = ParseBoolQueryArg(query, keys::kPinnedKey);
842 QueryArg selected = ParseBoolQueryArg(query, keys::kHighlightedKey);
843 QueryArg current_window = ParseBoolQueryArg(query, keys::kCurrentWindowKey);
844 QueryArg focused_window =
845 ParseBoolQueryArg(query, keys::kLastFocusedWindowKey);
846
847 QueryArg loading = NOT_SET;
848 if (query->HasKey(keys::kStatusKey)) {
849 std::string status;
850 EXTENSION_FUNCTION_VALIDATE(query->GetString(keys::kStatusKey, &status));
851 loading = (status == keys::kStatusValueLoading) ? MATCH_TRUE : MATCH_FALSE;
852 }
853
854 // It is o.k. to use URLPattern::SCHEME_ALL here because this function does
855 // not grant access to the content of the tabs, only to seeing their URLs and
856 // meta data.
857 URLPattern url_pattern(URLPattern::SCHEME_ALL, "<all_urls>");
858 if (query->HasKey(keys::kUrlKey)) {
859 std::string value;
860 EXTENSION_FUNCTION_VALIDATE(query->GetString(keys::kUrlKey, &value));
861 url_pattern = URLPattern(URLPattern::SCHEME_ALL, value);
862 }
863
864 std::string title;
865 if (query->HasKey(keys::kTitleKey))
866 EXTENSION_FUNCTION_VALIDATE(
867 query->GetString(keys::kTitleKey, &title));
868
869 int window_id = extension_misc::kUnknownWindowId;
870 if (query->HasKey(keys::kWindowIdKey))
871 EXTENSION_FUNCTION_VALIDATE(
872 query->GetInteger(keys::kWindowIdKey, &window_id));
873
874 int index = -1;
875 if (query->HasKey(keys::kIndexKey))
876 EXTENSION_FUNCTION_VALIDATE(
877 query->GetInteger(keys::kIndexKey, &index));
878
879 std::string window_type;
880 if (query->HasKey(keys::kWindowTypeLongKey))
881 EXTENSION_FUNCTION_VALIDATE(
882 query->GetString(keys::kWindowTypeLongKey, &window_type));
883
884 ListValue* result = new ListValue();
885 for (BrowserList::const_iterator browser = BrowserList::begin();
886 browser != BrowserList::end(); ++browser) {
887 if (!profile()->IsSameProfile((*browser)->profile()))
888 continue;
889
890 if (!(*browser)->window())
891 continue;
892
893 if (!include_incognito() && profile() != (*browser)->profile())
894 continue;
895
896 if (window_id >= 0 && window_id != ExtensionTabUtil::GetWindowId(*browser))
897 continue;
898
899 if (window_id == extension_misc::kCurrentWindowId &&
900 *browser != GetCurrentBrowser())
901 continue;
902
903 if (!MatchesQueryArg(current_window, *browser == GetCurrentBrowser()))
904 continue;
905
906 if (!MatchesQueryArg(focused_window,
907 *browser == BrowserList::GetLastActive()))
908 continue;
909
910 if (!window_type.empty() &&
911 window_type !=
912 (*browser)->extension_window_controller()->GetWindowTypeText())
913 continue;
914
915 TabStripModel* tab_strip = (*browser)->tab_strip_model();
916 for (int i = 0; i < tab_strip->count(); ++i) {
917 const WebContents* web_contents =
918 tab_strip->GetTabContentsAt(i)->web_contents();
919
920 if (index > -1 && i != index)
921 continue;
922
923 if (!MatchesQueryArg(selected, tab_strip->IsTabSelected(i)))
924 continue;
925
926 if (!MatchesQueryArg(active, i == tab_strip->active_index()))
927 continue;
928
929 if (!MatchesQueryArg(pinned, tab_strip->IsTabPinned(i)))
930 continue;
931
932 if (!title.empty() && !MatchPattern(web_contents->GetTitle(),
933 UTF8ToUTF16(title)))
934 continue;
935
936 if (!url_pattern.MatchesURL(web_contents->GetURL()))
937 continue;
938
939 if (!MatchesQueryArg(loading, web_contents->IsLoading()))
940 continue;
941
942 result->Append(ExtensionTabUtil::CreateTabValue(
943 web_contents, tab_strip, i));
944 }
945 }
946
947 result_.reset(result);
948 return true;
949 }
950
951 bool CreateTabFunction::RunImpl() {
952 DictionaryValue* args = NULL;
953 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
954
955 // windowId defaults to "current" window.
956 int window_id = extension_misc::kCurrentWindowId;
957 if (args->HasKey(keys::kWindowIdKey))
958 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(
959 keys::kWindowIdKey, &window_id));
960
961 Browser* browser = NULL;
962 if (!GetBrowserFromWindowID(this, window_id, &browser))
963 return false;
964
965 // Ensure the selected browser is tabbed.
966 if (!browser->is_type_tabbed() && browser->IsAttemptingToCloseBrowser())
967 browser = browser::FindTabbedBrowser(profile(), include_incognito());
968
969 if (!browser || !browser->window())
970 return false;
971
972 // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that
973 // represents the active tab.
974 content::NavigationController* opener = NULL;
975 if (args->HasKey(keys::kOpenerTabIdKey)) {
976 int opener_id = -1;
977 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(
978 keys::kOpenerTabIdKey, &opener_id));
979
980 TabContentsWrapper* opener_contents = NULL;
981 if (!ExtensionTabUtil::GetTabById(
982 opener_id, profile(), include_incognito(),
983 NULL, NULL, &opener_contents, NULL))
984 return false;
985
986 opener = &opener_contents->web_contents()->GetController();
987 }
988
989 // TODO(rafaelw): handle setting remaining tab properties:
990 // -title
991 // -favIconUrl
992
993 std::string url_string;
994 GURL url;
995 if (args->HasKey(keys::kUrlKey)) {
996 EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kUrlKey,
997 &url_string));
998 url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string,
999 GetExtension());
1000 if (!url.is_valid()) {
1001 error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kInvalidUrlError,
1002 url_string);
1003 return false;
1004 }
1005 }
1006
1007 // Don't let extensions crash the browser or renderers.
1008 if (ExtensionTabUtil::IsCrashURL(url)) {
1009 error_ = keys::kNoCrashBrowserError;
1010 return false;
1011 }
1012
1013 // Default to foreground for the new tab. The presence of 'selected' property
1014 // will override this default. This property is deprecated ('active' should
1015 // be used instead).
1016 bool active = true;
1017 if (args->HasKey(keys::kSelectedKey))
1018 EXTENSION_FUNCTION_VALIDATE(
1019 args->GetBoolean(keys::kSelectedKey, &active));
1020
1021 // The 'active' property has replaced the 'selected' property.
1022 if (args->HasKey(keys::kActiveKey))
1023 EXTENSION_FUNCTION_VALIDATE(
1024 args->GetBoolean(keys::kActiveKey, &active));
1025
1026 // Default to not pinning the tab. Setting the 'pinned' property to true
1027 // will override this default.
1028 bool pinned = false;
1029 if (args->HasKey(keys::kPinnedKey))
1030 EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kPinnedKey, &pinned));
1031
1032 // We can't load extension URLs into incognito windows unless the extension
1033 // uses split mode. Special case to fall back to a tabbed window.
1034 if (url.SchemeIs(chrome::kExtensionScheme) &&
1035 !GetExtension()->incognito_split_mode() &&
1036 browser->profile()->IsOffTheRecord()) {
1037 Profile* profile = browser->profile()->GetOriginalProfile();
1038 browser = browser::FindTabbedBrowser(profile, false);
1039 if (!browser) {
1040 browser = Browser::Create(profile);
1041 browser->window()->Show();
1042 }
1043 }
1044
1045 // If index is specified, honor the value, but keep it bound to
1046 // -1 <= index <= tab_strip->count() where -1 invokes the default behavior.
1047 int index = -1;
1048 if (args->HasKey(keys::kIndexKey))
1049 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kIndexKey, &index));
1050
1051 TabStripModel* tab_strip = browser->tab_strip_model();
1052
1053 index = std::min(std::max(index, -1), tab_strip->count());
1054
1055 int add_types = active ? TabStripModel::ADD_ACTIVE :
1056 TabStripModel::ADD_NONE;
1057 add_types |= TabStripModel::ADD_FORCE_INDEX;
1058 if (pinned)
1059 add_types |= TabStripModel::ADD_PINNED;
1060 browser::NavigateParams params(browser, url, content::PAGE_TRANSITION_LINK);
1061 params.disposition = active ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
1062 params.tabstrip_index = index;
1063 params.tabstrip_add_types = add_types;
1064 browser::Navigate(&params);
1065
1066 // The tab may have been created in a different window, so make sure we look
1067 // at the right tab strip.
1068 tab_strip = params.browser->tab_strip_model();
1069 int new_index = tab_strip->GetIndexOfTabContents(params.target_contents);
1070 if (opener)
1071 tab_strip->SetOpenerOfTabContentsAt(new_index, opener);
1072
1073 if (active)
1074 params.target_contents->web_contents()->GetView()->SetInitialFocus();
1075
1076 // Return data about the newly created tab.
1077 if (has_callback()) {
1078 result_.reset(ExtensionTabUtil::CreateTabValue(
1079 params.target_contents->web_contents(),
1080 tab_strip, new_index));
1081 }
1082
1083 return true;
1084 }
1085
1086 bool GetTabFunction::RunImpl() {
1087 int tab_id = -1;
1088 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
1089
1090 TabStripModel* tab_strip = NULL;
1091 TabContentsWrapper* contents = NULL;
1092 int tab_index = -1;
1093 if (!GetTabById(tab_id, profile(), include_incognito(),
1094 NULL, &tab_strip, &contents, &tab_index, &error_))
1095 return false;
1096
1097 result_.reset(ExtensionTabUtil::CreateTabValue(contents->web_contents(),
1098 tab_strip,
1099 tab_index));
1100 return true;
1101 }
1102
1103 bool GetCurrentTabFunction::RunImpl() {
1104 DCHECK(dispatcher());
1105
1106 WebContents* contents = dispatcher()->delegate()->GetAssociatedWebContents();
1107 if (contents)
1108 result_.reset(ExtensionTabUtil::CreateTabValue(contents));
1109
1110 return true;
1111 }
1112
1113 bool HighlightTabsFunction::RunImpl() {
1114 DictionaryValue* info = NULL;
1115 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &info));
1116
1117 // Get the window id from the params; default to current window if omitted.
1118 int window_id = extension_misc::kCurrentWindowId;
1119 if (info->HasKey(keys::kWindowIdKey))
1120 EXTENSION_FUNCTION_VALIDATE(
1121 info->GetInteger(keys::kWindowIdKey, &window_id));
1122
1123 Browser* browser = NULL;
1124 if (!GetBrowserFromWindowID(this, window_id, &browser))
1125 return false;
1126
1127 TabStripModel* tabstrip = browser->tab_strip_model();
1128 TabStripSelectionModel selection;
1129 int active_index = -1;
1130
1131 Value* tab_value = NULL;
1132 EXTENSION_FUNCTION_VALIDATE(info->Get(keys::kTabsKey, &tab_value));
1133
1134 std::vector<int> tab_indices;
1135 EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers(
1136 tab_value, &tab_indices));
1137
1138 // Create a new selection model as we read the list of tab indices.
1139 for (size_t i = 0; i < tab_indices.size(); ++i) {
1140 int index = tab_indices[i];
1141
1142 // Make sure the index is in range.
1143 if (!tabstrip->ContainsIndex(index)) {
1144 error_ = ExtensionErrorUtils::FormatErrorMessage(
1145 keys::kTabIndexNotFoundError, base::IntToString(index));
1146 return false;
1147 }
1148
1149 // By default, we make the first tab in the list active.
1150 if (active_index == -1)
1151 active_index = index;
1152
1153 selection.AddIndexToSelection(index);
1154 }
1155
1156 // Make sure they actually specified tabs to select.
1157 if (selection.empty()) {
1158 error_ = keys::kNoHighlightedTabError;
1159 return false;
1160 }
1161
1162 selection.set_active(active_index);
1163 browser->tab_strip_model()->SetSelectionFromModel(selection);
1164 result_.reset(
1165 browser->extension_window_controller()->CreateWindowValueWithTabs());
1166 return true;
1167 }
1168
1169 UpdateTabFunction::UpdateTabFunction() : tab_contents_(NULL) {
1170 }
1171
1172 bool UpdateTabFunction::RunImpl() {
1173 DictionaryValue* update_props = NULL;
1174 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props));
1175
1176 Value* tab_value = NULL;
1177 if (HasOptionalArgument(0)) {
1178 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value));
1179 }
1180
1181 int tab_id = -1;
1182 TabContentsWrapper* contents = NULL;
1183 if (tab_value == NULL || tab_value->IsType(Value::TYPE_NULL)) {
1184 Browser* browser = GetCurrentBrowser();
1185 if (!browser) {
1186 error_ = keys::kNoCurrentWindowError;
1187 return false;
1188 }
1189 contents = browser->tab_strip_model()->GetActiveTabContents();
1190 if (!contents) {
1191 error_ = keys::kNoSelectedTabError;
1192 return false;
1193 }
1194 tab_id = ExtensionTabUtil::GetTabId(contents->web_contents());
1195 } else {
1196 EXTENSION_FUNCTION_VALIDATE(tab_value->GetAsInteger(&tab_id));
1197 }
1198
1199 int tab_index = -1;
1200 TabStripModel* tab_strip = NULL;
1201 if (!GetTabById(tab_id, profile(), include_incognito(),
1202 NULL, &tab_strip, &contents, &tab_index, &error_)) {
1203 return false;
1204 }
1205
1206 tab_contents_ = contents;
1207
1208 // TODO(rafaelw): handle setting remaining tab properties:
1209 // -title
1210 // -favIconUrl
1211
1212 // Navigate the tab to a new location if the url is different.
1213 bool is_async = false;
1214 if (!UpdateURLIfPresent(update_props, &is_async))
1215 return false;
1216
1217 bool active = false;
1218 // TODO(rafaelw): Setting |active| from js doesn't make much sense.
1219 // Move tab selection management up to window.
1220 if (update_props->HasKey(keys::kSelectedKey))
1221 EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
1222 keys::kSelectedKey, &active));
1223
1224 // The 'active' property has replaced 'selected'.
1225 if (update_props->HasKey(keys::kActiveKey))
1226 EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
1227 keys::kActiveKey, &active));
1228
1229 if (active) {
1230 if (tab_strip->active_index() != tab_index) {
1231 tab_strip->ActivateTabAt(tab_index, false);
1232 DCHECK_EQ(contents, tab_strip->GetActiveTabContents());
1233 }
1234 tab_contents_->web_contents()->Focus();
1235 }
1236
1237 if (update_props->HasKey(keys::kHighlightedKey)) {
1238 bool highlighted = false;
1239 EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
1240 keys::kHighlightedKey, &highlighted));
1241 if (highlighted != tab_strip->IsTabSelected(tab_index))
1242 tab_strip->ToggleSelectionAt(tab_index);
1243 }
1244
1245 if (update_props->HasKey(keys::kPinnedKey)) {
1246 bool pinned = false;
1247 EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
1248 keys::kPinnedKey, &pinned));
1249 tab_strip->SetTabPinned(tab_index, pinned);
1250
1251 // Update the tab index because it may move when being pinned.
1252 tab_index = tab_strip->GetIndexOfTabContents(contents);
1253 }
1254
1255 if (update_props->HasKey(keys::kOpenerTabIdKey)) {
1256 int opener_id = -1;
1257 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
1258 keys::kOpenerTabIdKey, &opener_id));
1259
1260 TabContentsWrapper* opener_contents = NULL;
1261 if (!ExtensionTabUtil::GetTabById(
1262 opener_id, profile(), include_incognito(),
1263 NULL, NULL, &opener_contents, NULL))
1264 return false;
1265
1266 tab_strip->SetOpenerOfTabContentsAt(
1267 tab_index, &opener_contents->web_contents()->GetController());
1268 }
1269
1270 if (!is_async) {
1271 PopulateResult();
1272 SendResponse(true);
1273 }
1274 return true;
1275 }
1276
1277 bool UpdateTabFunction::UpdateURLIfPresent(DictionaryValue* update_props,
1278 bool* is_async) {
1279 if (!update_props->HasKey(keys::kUrlKey))
1280 return true;
1281
1282 std::string url_string;
1283 EXTENSION_FUNCTION_VALIDATE(update_props->GetString(
1284 keys::kUrlKey, &url_string));
1285 GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
1286 url_string, GetExtension());
1287
1288 if (!url.is_valid()) {
1289 error_ = ExtensionErrorUtils::FormatErrorMessage(
1290 keys::kInvalidUrlError, url_string);
1291 return false;
1292 }
1293
1294 // Don't let the extension crash the browser or renderers.
1295 if (ExtensionTabUtil::IsCrashURL(url)) {
1296 error_ = keys::kNoCrashBrowserError;
1297 return false;
1298 }
1299
1300 // JavaScript URLs can do the same kinds of things as cross-origin XHR, so
1301 // we need to check host permissions before allowing them.
1302 if (url.SchemeIs(chrome::kJavaScriptScheme)) {
1303 if (!GetExtension()->CanExecuteScriptOnPage(
1304 tab_contents_->web_contents()->GetURL(), NULL, &error_)) {
1305 return false;
1306 }
1307
1308 tab_contents_->extension_tab_helper()->script_executor()->ExecuteScript(
1309 extension_id(),
1310 ScriptExecutor::JAVASCRIPT,
1311 url.path(),
1312 ScriptExecutor::TOP_FRAME,
1313 UserScript::DOCUMENT_IDLE,
1314 ScriptExecutor::MAIN_WORLD,
1315 base::Bind(&UpdateTabFunction::OnExecuteCodeFinished, this));
1316
1317 *is_async = true;
1318 return true;
1319 }
1320
1321 tab_contents_->web_contents()->GetController().LoadURL(
1322 url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string());
1323
1324 // The URL of a tab contents never actually changes to a JavaScript URL, so
1325 // this check only makes sense in other cases.
1326 if (!url.SchemeIs(chrome::kJavaScriptScheme))
1327 DCHECK_EQ(url.spec(), tab_contents_->web_contents()->GetURL().spec());
1328
1329 return true;
1330 }
1331
1332 void UpdateTabFunction::PopulateResult() {
1333 if (!has_callback())
1334 return;
1335
1336 if (GetExtension()->HasAPIPermission(ExtensionAPIPermission::kTab)) {
1337 result_.reset(
1338 ExtensionTabUtil::CreateTabValue(tab_contents_->web_contents()));
1339 } else {
1340 result_.reset(Value::CreateNullValue());
1341 }
1342 }
1343
1344 void UpdateTabFunction::OnExecuteCodeFinished(bool success,
1345 int32 page_id,
1346 const std::string& error) {
1347 if (!error.empty()) {
1348 CHECK(!success);
1349 error_ = error;
1350 }
1351
1352 if (success)
1353 PopulateResult();
1354 SendResponse(success);
1355 }
1356
1357 bool MoveTabsFunction::RunImpl() {
1358 Value* tab_value = NULL;
1359 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value));
1360
1361 std::vector<int> tab_ids;
1362 EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers(
1363 tab_value, &tab_ids));
1364
1365 DictionaryValue* update_props = NULL;
1366 int new_index;
1367 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props));
1368 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(keys::kIndexKey,
1369 &new_index));
1370
1371 ListValue tab_values;
1372 for (size_t i = 0; i < tab_ids.size(); ++i) {
1373 Browser* source_browser = NULL;
1374 TabStripModel* source_tab_strip = NULL;
1375 TabContentsWrapper* contents = NULL;
1376 int tab_index = -1;
1377 if (!GetTabById(tab_ids[i], profile(), include_incognito(),
1378 &source_browser, &source_tab_strip, &contents,
1379 &tab_index, &error_))
1380 return false;
1381
1382 // Don't let the extension move the tab if the user is dragging tabs.
1383 if (!source_browser->IsTabStripEditable()) {
1384 error_ = keys::kTabStripNotEditableError;
1385 return false;
1386 }
1387
1388 // Insert the tabs one after another.
1389 new_index += i;
1390
1391 if (update_props->HasKey(keys::kWindowIdKey)) {
1392 Browser* target_browser = NULL;
1393 int window_id = extension_misc::kUnknownWindowId;
1394 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
1395 keys::kWindowIdKey, &window_id));
1396
1397 if (!GetBrowserFromWindowID(this, window_id, &target_browser))
1398 return false;
1399
1400 if (!target_browser->IsTabStripEditable()) {
1401 error_ = keys::kTabStripNotEditableError;
1402 return false;
1403 }
1404
1405 if (!target_browser->is_type_tabbed()) {
1406 error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError;
1407 return false;
1408 }
1409
1410 if (target_browser->profile() != source_browser->profile()) {
1411 error_ = keys::kCanOnlyMoveTabsWithinSameProfileError;
1412 return false;
1413 }
1414
1415 // If windowId is different from the current window, move between windows.
1416 if (ExtensionTabUtil::GetWindowId(target_browser) !=
1417 ExtensionTabUtil::GetWindowId(source_browser)) {
1418 TabStripModel* target_tab_strip = target_browser->tab_strip_model();
1419 contents = source_tab_strip->DetachTabContentsAt(tab_index);
1420 if (!contents) {
1421 error_ = ExtensionErrorUtils::FormatErrorMessage(
1422 keys::kTabNotFoundError, base::IntToString(tab_ids[i]));
1423 return false;
1424 }
1425
1426 // Clamp move location to the last position.
1427 // This is ">" because it can append to a new index position.
1428 // -1 means set the move location to the last position.
1429 if (new_index > target_tab_strip->count() || new_index < 0)
1430 new_index = target_tab_strip->count();
1431
1432 target_tab_strip->InsertTabContentsAt(
1433 new_index, contents, TabStripModel::ADD_NONE);
1434
1435 if (has_callback())
1436 tab_values.Append(ExtensionTabUtil::CreateTabValue(
1437 contents->web_contents(), target_tab_strip, new_index));
1438
1439 continue;
1440 }
1441 }
1442
1443 // Perform a simple within-window move.
1444 // Clamp move location to the last position.
1445 // This is ">=" because the move must be to an existing location.
1446 // -1 means set the move location to the last position.
1447 if (new_index >= source_tab_strip->count() || new_index < 0)
1448 new_index = source_tab_strip->count() - 1;
1449
1450 if (new_index != tab_index)
1451 source_tab_strip->MoveTabContentsAt(tab_index, new_index, false);
1452
1453 if (has_callback())
1454 tab_values.Append(ExtensionTabUtil::CreateTabValue(
1455 contents->web_contents(), source_tab_strip, new_index));
1456 }
1457
1458 if (!has_callback())
1459 return true;
1460
1461 // Only return the results as an array if there are multiple tabs.
1462 if (tab_ids.size() > 1) {
1463 result_.reset(tab_values.DeepCopy());
1464 } else if (tab_ids.size() == 1) {
1465 Value* value = NULL;
1466 CHECK(tab_values.Get(0, &value));
1467 result_.reset(value->DeepCopy());
1468 }
1469 return true;
1470 }
1471
1472 bool ReloadTabFunction::RunImpl() {
1473 bool bypass_cache = false;
1474 if (HasOptionalArgument(1)) {
1475 DictionaryValue* reload_props = NULL;
1476 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &reload_props));
1477
1478 if (reload_props->HasKey(keys::kBypassCache)) {
1479 EXTENSION_FUNCTION_VALIDATE(reload_props->GetBoolean(
1480 keys::kBypassCache,
1481 &bypass_cache));
1482 }
1483 }
1484
1485 TabContentsWrapper* contents = NULL;
1486
1487 // If |tab_id| is specified, look for it. Otherwise default to selected tab
1488 // in the current window.
1489 Value* tab_value = NULL;
1490 if (HasOptionalArgument(0))
1491 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value));
1492
1493 if (tab_value == NULL || tab_value->IsType(Value::TYPE_NULL)) {
1494 Browser* browser = GetCurrentBrowser();
1495 if (!browser) {
1496 error_ = keys::kNoCurrentWindowError;
1497 return false;
1498 }
1499
1500 if (!ExtensionTabUtil::GetDefaultTab(browser, &contents, NULL))
1501 return false;
1502 } else {
1503 int tab_id = -1;
1504 EXTENSION_FUNCTION_VALIDATE(tab_value->GetAsInteger(&tab_id));
1505
1506 Browser* browser = NULL;
1507 if (!GetTabById(tab_id, profile(), include_incognito(),
1508 &browser, NULL, &contents, NULL, &error_))
1509 return false;
1510 }
1511
1512 WebContents* web_contents = contents->web_contents();
1513 if (web_contents->ShowingInterstitialPage()) {
1514 // This does as same as Browser::ReloadInternal.
1515 NavigationEntry* entry = web_contents->GetController().GetActiveEntry();
1516 OpenURLParams params(entry->GetURL(), Referrer(), CURRENT_TAB,
1517 content::PAGE_TRANSITION_RELOAD, false);
1518 GetCurrentBrowser()->OpenURL(params);
1519 } else if (bypass_cache) {
1520 web_contents->GetController().ReloadIgnoringCache(true);
1521 } else {
1522 web_contents->GetController().Reload(true);
1523 }
1524
1525 return true;
1526 }
1527
1528 bool RemoveTabsFunction::RunImpl() {
1529 Value* tab_value = NULL;
1530 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value));
1531
1532 std::vector<int> tab_ids;
1533 EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers(
1534 tab_value, &tab_ids));
1535
1536 for (size_t i = 0; i < tab_ids.size(); ++i) {
1537 Browser* browser = NULL;
1538 TabContentsWrapper* contents = NULL;
1539 if (!GetTabById(tab_ids[i], profile(), include_incognito(),
1540 &browser, NULL, &contents, NULL, &error_))
1541 return false;
1542
1543 // Don't let the extension remove a tab if the user is dragging tabs around.
1544 if (!browser->IsTabStripEditable()) {
1545 error_ = keys::kTabStripNotEditableError;
1546 return false;
1547 }
1548
1549 // There's a chance that the tab is being dragged, or we're in some other
1550 // nested event loop. This code path ensures that the tab is safely closed
1551 // under such circumstances, whereas |Browser::CloseTabContents()| does not.
1552 contents->web_contents()->Close();
1553 }
1554 return true;
1555 }
1556
1557 bool CaptureVisibleTabFunction::GetTabToCapture(
1558 WebContents** web_contents, TabContentsWrapper** wrapper) {
1559 Browser* browser = NULL;
1560 // windowId defaults to "current" window.
1561 int window_id = extension_misc::kCurrentWindowId;
1562
1563 if (HasOptionalArgument(0))
1564 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
1565
1566 if (!GetBrowserFromWindowID(this, window_id, &browser))
1567 return false;
1568
1569 *web_contents = browser->GetSelectedWebContents();
1570 if (*web_contents == NULL) {
1571 error_ = keys::kInternalVisibleTabCaptureError;
1572 return false;
1573 }
1574
1575 *wrapper = browser->GetSelectedTabContentsWrapper();
1576
1577 return true;
1578 };
1579
1580 bool CaptureVisibleTabFunction::RunImpl() {
1581 WebContents* web_contents = NULL;
1582 TabContentsWrapper* wrapper = NULL;
1583 if (!GetTabToCapture(&web_contents, &wrapper))
1584 return false;
1585
1586 image_format_ = FORMAT_JPEG; // Default format is JPEG.
1587 image_quality_ = kDefaultQuality; // Default quality setting.
1588
1589 if (HasOptionalArgument(1)) {
1590 DictionaryValue* options = NULL;
1591 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options));
1592
1593 if (options->HasKey(keys::kFormatKey)) {
1594 std::string format;
1595 EXTENSION_FUNCTION_VALIDATE(
1596 options->GetString(keys::kFormatKey, &format));
1597
1598 if (format == keys::kFormatValueJpeg) {
1599 image_format_ = FORMAT_JPEG;
1600 } else if (format == keys::kFormatValuePng) {
1601 image_format_ = FORMAT_PNG;
1602 } else {
1603 // Schema validation should make this unreachable.
1604 EXTENSION_FUNCTION_VALIDATE(0);
1605 }
1606 }
1607
1608 if (options->HasKey(keys::kQualityKey)) {
1609 EXTENSION_FUNCTION_VALIDATE(
1610 options->GetInteger(keys::kQualityKey, &image_quality_));
1611 }
1612 }
1613
1614 // captureVisibleTab() can return an image containing sensitive information
1615 // that the browser would otherwise protect. Ensure the extension has
1616 // permission to do this.
1617 if (!GetExtension()->CanCaptureVisiblePage(web_contents->GetURL(), &error_))
1618 return false;
1619
1620 RenderViewHost* render_view_host = web_contents->GetRenderViewHost();
1621 content::RenderWidgetHostView* view = render_view_host->GetView();
1622 if (!view) {
1623 error_ = keys::kInternalVisibleTabCaptureError;
1624 return false;
1625 }
1626 skia::PlatformCanvas* temp_canvas = new skia::PlatformCanvas;
1627 render_view_host->CopyFromBackingStore(
1628 gfx::Rect(),
1629 view->GetViewBounds().size(),
1630 temp_canvas,
1631 base::Bind(&CaptureVisibleTabFunction::CopyFromBackingStoreComplete,
1632 this,
1633 base::Owned(temp_canvas)));
1634 return true;
1635 }
1636
1637 void CaptureVisibleTabFunction::CopyFromBackingStoreComplete(
1638 skia::PlatformCanvas* canvas,
1639 bool succeeded) {
1640 if (succeeded) {
1641 VLOG(1) << "captureVisibleTab() got image from backing store.";
1642 SendResultFromBitmap(skia::GetTopDevice(*canvas)->accessBitmap(false));
1643 return;
1644 }
1645
1646 WebContents* web_contents = NULL;
1647 TabContentsWrapper* wrapper = NULL;
1648 if (!GetTabToCapture(&web_contents, &wrapper)) {
1649 error_ = keys::kInternalVisibleTabCaptureError;
1650 SendResponse(false);
1651 return;
1652 }
1653
1654 // Ask the renderer for a snapshot of the tab.
1655 registrar_.Add(this,
1656 chrome::NOTIFICATION_TAB_SNAPSHOT_TAKEN,
1657 content::Source<WebContents>(web_contents));
1658 AddRef(); // Balanced in CaptureVisibleTabFunction::Observe().
1659 wrapper->snapshot_tab_helper()->CaptureSnapshot();
1660 }
1661
1662 // If a backing store was not available in CaptureVisibleTabFunction::RunImpl,
1663 // than the renderer was asked for a snapshot. Listen for a notification
1664 // that the snapshot is available.
1665 void CaptureVisibleTabFunction::Observe(
1666 int type,
1667 const content::NotificationSource& source,
1668 const content::NotificationDetails& details) {
1669 DCHECK(type == chrome::NOTIFICATION_TAB_SNAPSHOT_TAKEN);
1670
1671 const SkBitmap *screen_capture =
1672 content::Details<const SkBitmap>(details).ptr();
1673 const bool error = screen_capture->empty();
1674
1675 if (error) {
1676 error_ = keys::kInternalVisibleTabCaptureError;
1677 SendResponse(false);
1678 } else {
1679 VLOG(1) << "captureVisibleTab() got image from renderer.";
1680 SendResultFromBitmap(*screen_capture);
1681 }
1682
1683 Release(); // Balanced in CaptureVisibleTabFunction::RunImpl().
1684 }
1685
1686 // Turn a bitmap of the screen into an image, set that image as the result,
1687 // and call SendResponse().
1688 void CaptureVisibleTabFunction::SendResultFromBitmap(
1689 const SkBitmap& screen_capture) {
1690 std::vector<unsigned char> data;
1691 SkAutoLockPixels screen_capture_lock(screen_capture);
1692 bool encoded = false;
1693 std::string mime_type;
1694 switch (image_format_) {
1695 case FORMAT_JPEG:
1696 encoded = gfx::JPEGCodec::Encode(
1697 reinterpret_cast<unsigned char*>(screen_capture.getAddr32(0, 0)),
1698 gfx::JPEGCodec::FORMAT_SkBitmap,
1699 screen_capture.width(),
1700 screen_capture.height(),
1701 static_cast<int>(screen_capture.rowBytes()),
1702 image_quality_,
1703 &data);
1704 mime_type = keys::kMimeTypeJpeg;
1705 break;
1706 case FORMAT_PNG:
1707 encoded = gfx::PNGCodec::EncodeBGRASkBitmap(
1708 screen_capture,
1709 true, // Discard transparency.
1710 &data);
1711 mime_type = keys::kMimeTypePng;
1712 break;
1713 default:
1714 NOTREACHED() << "Invalid image format.";
1715 }
1716
1717 if (!encoded) {
1718 error_ = keys::kInternalVisibleTabCaptureError;
1719 SendResponse(false);
1720 return;
1721 }
1722
1723 std::string base64_result;
1724 base::StringPiece stream_as_string(
1725 reinterpret_cast<const char*>(vector_as_array(&data)), data.size());
1726
1727 base::Base64Encode(stream_as_string, &base64_result);
1728 base64_result.insert(0, base::StringPrintf("data:%s;base64,",
1729 mime_type.c_str()));
1730 result_.reset(new StringValue(base64_result));
1731 SendResponse(true);
1732 }
1733
1734 bool DetectTabLanguageFunction::RunImpl() {
1735 int tab_id = 0;
1736 Browser* browser = NULL;
1737 TabContentsWrapper* contents = NULL;
1738
1739 // If |tab_id| is specified, look for it. Otherwise default to selected tab
1740 // in the current window.
1741 if (HasOptionalArgument(0)) {
1742 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
1743 if (!GetTabById(tab_id, profile(), include_incognito(),
1744 &browser, NULL, &contents, NULL, &error_)) {
1745 return false;
1746 }
1747 if (!browser || !contents)
1748 return false;
1749 } else {
1750 browser = GetCurrentBrowser();
1751 if (!browser)
1752 return false;
1753 contents = browser->tab_strip_model()->GetActiveTabContents();
1754 if (!contents)
1755 return false;
1756 }
1757
1758 if (contents->web_contents()->GetController().NeedsReload()) {
1759 // If the tab hasn't been loaded, don't wait for the tab to load.
1760 error_ = keys::kCannotDetermineLanguageOfUnloadedTab;
1761 return false;
1762 }
1763
1764 AddRef(); // Balanced in GotLanguage()
1765
1766 TranslateTabHelper* helper = contents->translate_tab_helper();
1767 if (!helper->language_state().original_language().empty()) {
1768 // Delay the callback invocation until after the current JS call has
1769 // returned.
1770 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
1771 &DetectTabLanguageFunction::GotLanguage, this,
1772 helper->language_state().original_language()));
1773 return true;
1774 }
1775 // The tab contents does not know its language yet. Let's wait until it
1776 // receives it, or until the tab is closed/navigates to some other page.
1777 registrar_.Add(this, chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED,
1778 content::Source<WebContents>(contents->web_contents()));
1779 registrar_.Add(
1780 this, chrome::NOTIFICATION_TAB_CLOSING,
1781 content::Source<NavigationController>(
1782 &(contents->web_contents()->GetController())));
1783 registrar_.Add(
1784 this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
1785 content::Source<NavigationController>(
1786 &(contents->web_contents()->GetController())));
1787 return true;
1788 }
1789
1790 void DetectTabLanguageFunction::Observe(
1791 int type,
1792 const content::NotificationSource& source,
1793 const content::NotificationDetails& details) {
1794 std::string language;
1795 if (type == chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED)
1796 language = *content::Details<std::string>(details).ptr();
1797
1798 registrar_.RemoveAll();
1799
1800 // Call GotLanguage in all cases as we want to guarantee the callback is
1801 // called for every API call the extension made.
1802 GotLanguage(language);
1803 }
1804
1805 void DetectTabLanguageFunction::GotLanguage(const std::string& language) {
1806 result_.reset(Value::CreateStringValue(language.c_str()));
1807 SendResponse(true);
1808
1809 Release(); // Balanced in Run()
1810 }
OLDNEW
« no previous file with comments | « chrome/browser/extensions/extension_tabs_module.h ('k') | chrome/browser/extensions/extension_tabs_module_constants.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698