tabs_api.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/api/tabs/tabs_api.h"
6
7#include <algorithm>
8#include <limits>
9#include <vector>
10
11#include "apps/app_window.h"
12#include "base/bind.h"
13#include "base/command_line.h"
14#include "base/logging.h"
15#include "base/memory/ref_counted_memory.h"
16#include "base/message_loop/message_loop.h"
17#include "base/prefs/pref_service.h"
18#include "base/stl_util.h"
19#include "base/strings/string16.h"
20#include "base/strings/string_number_conversions.h"
21#include "base/strings/string_util.h"
22#include "base/strings/utf_string_conversions.h"
23#include "chrome/browser/chrome_notification_types.h"
24#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
25#include "chrome/browser/extensions/api/tabs/windows_util.h"
26#include "chrome/browser/extensions/extension_function_dispatcher.h"
27#include "chrome/browser/extensions/extension_function_util.h"
28#include "chrome/browser/extensions/extension_host.h"
29#include "chrome/browser/extensions/extension_service.h"
30#include "chrome/browser/extensions/extension_tab_util.h"
31#include "chrome/browser/extensions/script_executor.h"
32#include "chrome/browser/extensions/tab_helper.h"
33#include "chrome/browser/extensions/window_controller.h"
34#include "chrome/browser/extensions/window_controller_list.h"
35#include "chrome/browser/prefs/incognito_mode_prefs.h"
36#include "chrome/browser/profiles/profile.h"
37#include "chrome/browser/translate/translate_tab_helper.h"
38#include "chrome/browser/ui/apps/chrome_shell_window_delegate.h"
39#include "chrome/browser/ui/browser.h"
40#include "chrome/browser/ui/browser_commands.h"
41#include "chrome/browser/ui/browser_finder.h"
42#include "chrome/browser/ui/browser_iterator.h"
43#include "chrome/browser/ui/browser_navigator.h"
44#include "chrome/browser/ui/browser_tabstrip.h"
45#include "chrome/browser/ui/browser_window.h"
46#include "chrome/browser/ui/host_desktop.h"
47#include "chrome/browser/ui/panels/panel_manager.h"
48#include "chrome/browser/ui/tabs/tab_strip_model.h"
49#include "chrome/browser/ui/window_sizer/window_sizer.h"
50#include "chrome/browser/web_applications/web_app.h"
51#include "chrome/common/chrome_switches.h"
52#include "chrome/common/extensions/api/i18n/default_locale_handler.h"
53#include "chrome/common/extensions/api/tabs.h"
54#include "chrome/common/extensions/api/windows.h"
55#include "chrome/common/extensions/extension_constants.h"
56#include "chrome/common/extensions/extension_file_util.h"
57#include "chrome/common/extensions/extension_l10n_util.h"
58#include "chrome/common/extensions/extension_messages.h"
59#include "chrome/common/extensions/message_bundle.h"
60#include "chrome/common/pref_names.h"
61#include "chrome/common/url_constants.h"
62#include "components/translate/core/common/language_detection_details.h"
63#include "components/user_prefs/pref_registry_syncable.h"
64#include "content/public/browser/navigation_controller.h"
65#include "content/public/browser/navigation_entry.h"
66#include "content/public/browser/notification_details.h"
67#include "content/public/browser/notification_source.h"
68#include "content/public/browser/render_process_host.h"
69#include "content/public/browser/render_view_host.h"
70#include "content/public/browser/render_widget_host_view.h"
71#include "content/public/browser/web_contents.h"
72#include "content/public/browser/web_contents_view.h"
73#include "content/public/common/url_constants.h"
74#include "extensions/browser/file_reader.h"
75#include "extensions/common/constants.h"
76#include "extensions/common/error_utils.h"
77#include "extensions/common/extension.h"
78#include "extensions/common/manifest_constants.h"
79#include "extensions/common/manifest_handlers/incognito_info.h"
80#include "extensions/common/permissions/permissions_data.h"
81#include "extensions/common/user_script.h"
82#include "skia/ext/image_operations.h"
83#include "skia/ext/platform_canvas.h"
84#include "third_party/skia/include/core/SkBitmap.h"
85#include "ui/base/models/list_selection_model.h"
86#include "ui/base/ui_base_types.h"
87
88#if defined(OS_WIN)
89#include "win8/util/win8_util.h"
90#endif  // OS_WIN
91
92#if defined(USE_ASH)
93#include "apps/app_window_registry.h"
94#include "ash/ash_switches.h"
95#include "chrome/browser/extensions/api/tabs/ash_panel_contents.h"
96#endif
97
98using apps::AppWindow;
99using content::BrowserThread;
100using content::NavigationController;
101using content::NavigationEntry;
102using content::OpenURLParams;
103using content::Referrer;
104using content::WebContents;
105
106namespace extensions {
107
108namespace windows = api::windows;
109namespace keys = tabs_constants;
110namespace tabs = api::tabs;
111
112using api::tabs::InjectDetails;
113
114namespace {
115
116// |error_message| can optionally be passed in a will be set with an appropriate
117// message if the window cannot be found by id.
118Browser* GetBrowserInProfileWithId(Profile* profile,
119                                   const int window_id,
120                                   bool include_incognito,
121                                   std::string* error_message) {
122  Profile* incognito_profile =
123      include_incognito && profile->HasOffTheRecordProfile() ?
124          profile->GetOffTheRecordProfile() : NULL;
125  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
126    Browser* browser = *it;
127    if ((browser->profile() == profile ||
128         browser->profile() == incognito_profile) &&
129        ExtensionTabUtil::GetWindowId(browser) == window_id &&
130        browser->window()) {
131      return browser;
132    }
133  }
134
135  if (error_message)
136    *error_message = ErrorUtils::FormatErrorMessage(
137        keys::kWindowNotFoundError, base::IntToString(window_id));
138
139  return NULL;
140}
141
142bool GetBrowserFromWindowID(ChromeAsyncExtensionFunction* function,
143                            int window_id,
144                            Browser** browser) {
145  if (window_id == extension_misc::kCurrentWindowId) {
146    *browser = function->GetCurrentBrowser();
147    if (!(*browser) || !(*browser)->window()) {
148      function->SetError(keys::kNoCurrentWindowError);
149      return false;
150    }
151  } else {
152    std::string error;
153    *browser = GetBrowserInProfileWithId(function->GetProfile(),
154                                         window_id,
155                                         function->include_incognito(),
156                                         &error);
157    if (!*browser) {
158      function->SetError(error);
159      return false;
160    }
161  }
162  return true;
163}
164
165// |error_message| can optionally be passed in and will be set with an
166// appropriate message if the tab cannot be found by id.
167bool GetTabById(int tab_id,
168                Profile* profile,
169                bool include_incognito,
170                Browser** browser,
171                TabStripModel** tab_strip,
172                content::WebContents** contents,
173                int* tab_index,
174                std::string* error_message) {
175  if (ExtensionTabUtil::GetTabById(tab_id, profile, include_incognito,
176                                   browser, tab_strip, contents, tab_index))
177    return true;
178
179  if (error_message)
180    *error_message = ErrorUtils::FormatErrorMessage(
181        keys::kTabNotFoundError, base::IntToString(tab_id));
182
183  return false;
184}
185
186// Returns true if either |boolean| is a null pointer, or if |*boolean| and
187// |value| are equal. This function is used to check if a tab's parameters match
188// those of the browser.
189bool MatchesBool(bool* boolean, bool value) {
190  return !boolean || *boolean == value;
191}
192
193Browser* CreateBrowserWindow(const Browser::CreateParams& params,
194                             Profile* profile,
195                             const std::string& extension_id) {
196  bool use_existing_browser_window = false;
197
198#if defined(OS_WIN)
199  // In windows 8 metro mode we don't allow windows to be created.
200  if (win8::IsSingleWindowMetroMode())
201    use_existing_browser_window = true;
202#endif  // OS_WIN
203
204  Browser* new_window = NULL;
205  if (use_existing_browser_window)
206    // The false parameter passed below is to ensure that we find a browser
207    // object matching the profile passed in, instead of the original profile
208    new_window = chrome::FindTabbedBrowser(profile, false,
209                                           params.host_desktop_type);
210
211  if (!new_window)
212    new_window = new Browser(params);
213  return new_window;
214}
215
216}  // namespace
217
218// Windows ---------------------------------------------------------------------
219
220bool WindowsGetFunction::RunImpl() {
221  scoped_ptr<windows::Get::Params> params(windows::Get::Params::Create(*args_));
222  EXTENSION_FUNCTION_VALIDATE(params.get());
223
224  bool populate_tabs = false;
225  if (params->get_info.get() && params->get_info->populate.get())
226    populate_tabs = *params->get_info->populate;
227
228  WindowController* controller;
229  if (!windows_util::GetWindowFromWindowID(this,
230                                           params->window_id,
231                                           &controller)) {
232    return false;
233  }
234
235  if (populate_tabs)
236    SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
237  else
238    SetResult(controller->CreateWindowValue());
239  return true;
240}
241
242bool WindowsGetCurrentFunction::RunImpl() {
243  scoped_ptr<windows::GetCurrent::Params> params(
244      windows::GetCurrent::Params::Create(*args_));
245  EXTENSION_FUNCTION_VALIDATE(params.get());
246
247  bool populate_tabs = false;
248  if (params->get_info.get() && params->get_info->populate.get())
249    populate_tabs = *params->get_info->populate;
250
251  WindowController* controller;
252  if (!windows_util::GetWindowFromWindowID(this,
253                                           extension_misc::kCurrentWindowId,
254                                           &controller)) {
255    return false;
256  }
257  if (populate_tabs)
258    SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
259  else
260    SetResult(controller->CreateWindowValue());
261  return true;
262}
263
264bool WindowsGetLastFocusedFunction::RunImpl() {
265  scoped_ptr<windows::GetLastFocused::Params> params(
266      windows::GetLastFocused::Params::Create(*args_));
267  EXTENSION_FUNCTION_VALIDATE(params.get());
268
269  bool populate_tabs = false;
270  if (params->get_info.get() && params->get_info->populate.get())
271    populate_tabs = *params->get_info->populate;
272
273  // Note: currently this returns the last active browser. If we decide to
274  // include other window types (e.g. panels), we will need to add logic to
275  // WindowControllerList that mirrors the active behavior of BrowserList.
276  Browser* browser = chrome::FindAnyBrowser(
277      GetProfile(), include_incognito(), chrome::GetActiveDesktop());
278  if (!browser || !browser->window()) {
279    error_ = keys::kNoLastFocusedWindowError;
280    return false;
281  }
282  WindowController* controller =
283      browser->extension_window_controller();
284  if (populate_tabs)
285    SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
286  else
287    SetResult(controller->CreateWindowValue());
288  return true;
289}
290
291bool WindowsGetAllFunction::RunImpl() {
292  scoped_ptr<windows::GetAll::Params> params(
293      windows::GetAll::Params::Create(*args_));
294  EXTENSION_FUNCTION_VALIDATE(params.get());
295
296  bool populate_tabs = false;
297  if (params->get_info.get() && params->get_info->populate.get())
298    populate_tabs = *params->get_info->populate;
299
300  base::ListValue* window_list = new base::ListValue();
301  const WindowControllerList::ControllerList& windows =
302      WindowControllerList::GetInstance()->windows();
303  for (WindowControllerList::ControllerList::const_iterator iter =
304           windows.begin();
305       iter != windows.end(); ++iter) {
306    if (!this->CanOperateOnWindow(*iter))
307      continue;
308    if (populate_tabs)
309      window_list->Append((*iter)->CreateWindowValueWithTabs(GetExtension()));
310    else
311      window_list->Append((*iter)->CreateWindowValue());
312  }
313  SetResult(window_list);
314  return true;
315}
316
317bool WindowsCreateFunction::ShouldOpenIncognitoWindow(
318    const windows::Create::Params::CreateData* create_data,
319    std::vector<GURL>* urls, bool* is_error) {
320  *is_error = false;
321  const IncognitoModePrefs::Availability incognito_availability =
322      IncognitoModePrefs::GetAvailability(GetProfile()->GetPrefs());
323  bool incognito = false;
324  if (create_data && create_data->incognito) {
325    incognito = *create_data->incognito;
326    if (incognito && incognito_availability == IncognitoModePrefs::DISABLED) {
327      error_ = keys::kIncognitoModeIsDisabled;
328      *is_error = true;
329      return false;
330    }
331    if (!incognito && incognito_availability == IncognitoModePrefs::FORCED) {
332      error_ = keys::kIncognitoModeIsForced;
333      *is_error = true;
334      return false;
335    }
336  } else if (incognito_availability == IncognitoModePrefs::FORCED) {
337    // If incognito argument is not specified explicitly, we default to
338    // incognito when forced so by policy.
339    incognito = true;
340  }
341
342  // Remove all URLs that are not allowed in an incognito session. Note that a
343  // ChromeOS guest session is not considered incognito in this case.
344  if (incognito && !GetProfile()->IsGuestSession()) {
345    std::string first_url_erased;
346    for (size_t i = 0; i < urls->size();) {
347      if (chrome::IsURLAllowedInIncognito((*urls)[i], GetProfile())) {
348        i++;
349      } else {
350        if (first_url_erased.empty())
351          first_url_erased = (*urls)[i].spec();
352        urls->erase(urls->begin() + i);
353      }
354    }
355    if (urls->empty() && !first_url_erased.empty()) {
356      error_ = ErrorUtils::FormatErrorMessage(
357          keys::kURLsNotAllowedInIncognitoError, first_url_erased);
358      *is_error = true;
359      return false;
360    }
361  }
362  return incognito;
363}
364
365bool WindowsCreateFunction::RunImpl() {
366  scoped_ptr<windows::Create::Params> params(
367      windows::Create::Params::Create(*args_));
368  EXTENSION_FUNCTION_VALIDATE(params);
369  std::vector<GURL> urls;
370  TabStripModel* source_tab_strip = NULL;
371  int tab_index = -1;
372
373  windows::Create::Params::CreateData* create_data = params->create_data.get();
374
375  // Look for optional url.
376  if (create_data && create_data->url) {
377    std::vector<std::string> url_strings;
378    // First, get all the URLs the client wants to open.
379    if (create_data->url->as_string)
380      url_strings.push_back(*create_data->url->as_string);
381    else if (create_data->url->as_strings)
382      url_strings.swap(*create_data->url->as_strings);
383
384    // Second, resolve, validate and convert them to GURLs.
385    for (std::vector<std::string>::iterator i = url_strings.begin();
386         i != url_strings.end(); ++i) {
387      GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
388          *i, GetExtension());
389      if (!url.is_valid()) {
390        error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, *i);
391        return false;
392      }
393      // Don't let the extension crash the browser or renderers.
394      if (ExtensionTabUtil::IsCrashURL(url)) {
395        error_ = keys::kNoCrashBrowserError;
396        return false;
397      }
398      urls.push_back(url);
399    }
400  }
401
402  // Look for optional tab id.
403  if (create_data && create_data->tab_id) {
404    // Find the tab. |source_tab_strip| and |tab_index| will later be used to
405    // move the tab into the created window.
406    if (!GetTabById(*create_data->tab_id,
407                    GetProfile(),
408                    include_incognito(),
409                    NULL,
410                    &source_tab_strip,
411                    NULL,
412                    &tab_index,
413                    &error_))
414      return false;
415  }
416
417  Profile* window_profile = GetProfile();
418  Browser::Type window_type = Browser::TYPE_TABBED;
419  bool create_panel = false;
420
421  // panel_create_mode only applies if create_panel = true
422  PanelManager::CreateMode panel_create_mode = PanelManager::CREATE_AS_DOCKED;
423
424  gfx::Rect window_bounds;
425  bool focused = true;
426  bool saw_focus_key = false;
427  std::string extension_id;
428
429  // Decide whether we are opening a normal window or an incognito window.
430  bool is_error = true;
431  bool open_incognito_window = ShouldOpenIncognitoWindow(create_data, &urls,
432                                                         &is_error);
433  if (is_error) {
434    // error_ member variable is set inside of ShouldOpenIncognitoWindow.
435    return false;
436  }
437  if (open_incognito_window) {
438    window_profile = window_profile->GetOffTheRecordProfile();
439  }
440
441  if (create_data) {
442    // Figure out window type before figuring out bounds so that default
443    // bounds can be set according to the window type.
444    switch (create_data->type) {
445      case windows::Create::Params::CreateData::TYPE_POPUP:
446        window_type = Browser::TYPE_POPUP;
447        extension_id = GetExtension()->id();
448        break;
449      case windows::Create::Params::CreateData::TYPE_PANEL:
450      case windows::Create::Params::CreateData::TYPE_DETACHED_PANEL: {
451        extension_id = GetExtension()->id();
452        bool use_panels = false;
453#if !defined(OS_ANDROID)
454        use_panels = PanelManager::ShouldUsePanels(extension_id);
455#endif
456        if (use_panels) {
457          create_panel = true;
458          // Non-ash supports both docked and detached panel types.
459          if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH &&
460              create_data->type ==
461              windows::Create::Params::CreateData::TYPE_DETACHED_PANEL) {
462            panel_create_mode = PanelManager::CREATE_AS_DETACHED;
463          }
464        } else {
465          window_type = Browser::TYPE_POPUP;
466        }
467        break;
468      }
469      case windows::Create::Params::CreateData::TYPE_NONE:
470      case windows::Create::Params::CreateData::TYPE_NORMAL:
471        break;
472      default:
473        error_ = keys::kInvalidWindowTypeError;
474        return false;
475    }
476
477    // Initialize default window bounds according to window type.
478    if (window_type == Browser::TYPE_TABBED ||
479        window_type == Browser::TYPE_POPUP ||
480        create_panel) {
481      // Try to position the new browser relative to its originating
482      // browser window. The call offsets the bounds by kWindowTilePixels
483      // (defined in WindowSizer to be 10).
484      //
485      // NOTE(rafaelw): It's ok if GetCurrentBrowser() returns NULL here.
486      // GetBrowserWindowBounds will default to saved "default" values for
487      // the app.
488      ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
489      WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(),
490                                                      gfx::Rect(),
491                                                      GetCurrentBrowser(),
492                                                      &window_bounds,
493                                                      &show_state);
494    }
495
496    if (create_panel && PanelManager::CREATE_AS_DETACHED == panel_create_mode) {
497      window_bounds.set_origin(
498          PanelManager::GetInstance()->GetDefaultDetachedPanelOrigin());
499    }
500
501    // Any part of the bounds can optionally be set by the caller.
502    if (create_data->left)
503      window_bounds.set_x(*create_data->left);
504
505    if (create_data->top)
506      window_bounds.set_y(*create_data->top);
507
508    if (create_data->width)
509      window_bounds.set_width(*create_data->width);
510
511    if (create_data->height)
512      window_bounds.set_height(*create_data->height);
513
514    if (create_data->focused) {
515      focused = *create_data->focused;
516      saw_focus_key = true;
517    }
518  }
519
520  if (create_panel) {
521    if (urls.empty())
522      urls.push_back(GURL(chrome::kChromeUINewTabURL));
523
524#if defined(USE_ASH)
525    if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH) {
526      AppWindow::CreateParams create_params;
527      create_params.window_type = AppWindow::WINDOW_TYPE_V1_PANEL;
528      create_params.bounds = window_bounds;
529      create_params.focused = saw_focus_key && focused;
530      AppWindow* app_window = new AppWindow(
531          window_profile, new ChromeShellWindowDelegate(), GetExtension());
532      AshPanelContents* ash_panel_contents = new AshPanelContents(app_window);
533      app_window->Init(urls[0], ash_panel_contents, create_params);
534      SetResult(ash_panel_contents->GetExtensionWindowController()->
535                CreateWindowValueWithTabs(GetExtension()));
536      return true;
537    }
538#endif
539    std::string title =
540        web_app::GenerateApplicationNameFromExtensionId(extension_id);
541    // Note: Panels ignore all but the first url provided.
542    Panel* panel = PanelManager::GetInstance()->CreatePanel(
543        title, window_profile, urls[0], window_bounds, panel_create_mode);
544
545    // Unlike other window types, Panels do not take focus by default.
546    if (!saw_focus_key || !focused)
547      panel->ShowInactive();
548    else
549      panel->Show();
550
551    SetResult(
552        panel->extension_window_controller()->CreateWindowValueWithTabs(
553            GetExtension()));
554    return true;
555  }
556
557  // Create a new BrowserWindow.
558  chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
559  if (create_panel)
560    window_type = Browser::TYPE_POPUP;
561  Browser::CreateParams create_params(window_type, window_profile,
562                                      host_desktop_type);
563  if (extension_id.empty()) {
564    create_params.initial_bounds = window_bounds;
565  } else {
566    create_params = Browser::CreateParams::CreateForApp(
567        window_type,
568        web_app::GenerateApplicationNameFromExtensionId(extension_id),
569        window_bounds,
570        window_profile,
571        host_desktop_type);
572  }
573  create_params.initial_show_state = ui::SHOW_STATE_NORMAL;
574  create_params.host_desktop_type = chrome::GetActiveDesktop();
575
576  Browser* new_window = CreateBrowserWindow(create_params, window_profile,
577                                            extension_id);
578
579  for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) {
580    WebContents* tab = chrome::AddSelectedTabWithURL(
581        new_window, *i, content::PAGE_TRANSITION_LINK);
582    if (create_panel) {
583      TabHelper::FromWebContents(tab)->SetExtensionAppIconById(extension_id);
584    }
585  }
586
587  WebContents* contents = NULL;
588  // Move the tab into the created window only if it's an empty popup or it's
589  // a tabbed window.
590  if ((window_type == Browser::TYPE_POPUP && urls.empty()) ||
591      window_type == Browser::TYPE_TABBED) {
592    if (source_tab_strip)
593      contents = source_tab_strip->DetachWebContentsAt(tab_index);
594    if (contents) {
595      TabStripModel* target_tab_strip = new_window->tab_strip_model();
596      target_tab_strip->InsertWebContentsAt(urls.size(), contents,
597                                            TabStripModel::ADD_NONE);
598    }
599  }
600  // Create a new tab if the created window is still empty. Don't create a new
601  // tab when it is intended to create an empty popup.
602  if (!contents && urls.empty() && window_type != Browser::TYPE_POPUP) {
603    chrome::NewTab(new_window);
604  }
605  chrome::SelectNumberedTab(new_window, 0);
606
607  // Unlike other window types, Panels do not take focus by default.
608  if (!saw_focus_key && create_panel)
609    focused = false;
610
611  if (focused)
612    new_window->window()->Show();
613  else
614    new_window->window()->ShowInactive();
615
616  if (new_window->profile()->IsOffTheRecord() &&
617      !GetProfile()->IsOffTheRecord() && !include_incognito()) {
618    // Don't expose incognito windows if extension itself works in non-incognito
619    // profile and CanCrossIncognito isn't allowed.
620    SetResult(base::Value::CreateNullValue());
621  } else {
622    SetResult(
623        new_window->extension_window_controller()->CreateWindowValueWithTabs(
624            GetExtension()));
625  }
626
627  return true;
628}
629
630bool WindowsUpdateFunction::RunImpl() {
631  scoped_ptr<windows::Update::Params> params(
632      windows::Update::Params::Create(*args_));
633  EXTENSION_FUNCTION_VALIDATE(params);
634
635  WindowController* controller;
636  if (!windows_util::GetWindowFromWindowID(this, params->window_id,
637                                            &controller))
638    return false;
639
640#if defined(OS_WIN)
641  // Silently ignore changes on the window for metro mode.
642  if (win8::IsSingleWindowMetroMode()) {
643    SetResult(controller->CreateWindowValue());
644    return true;
645  }
646#endif
647
648  ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;  // No change.
649  switch (params->update_info.state) {
650    case windows::Update::Params::UpdateInfo::STATE_NORMAL:
651      show_state = ui::SHOW_STATE_NORMAL;
652      break;
653    case windows::Update::Params::UpdateInfo::STATE_MINIMIZED:
654      show_state = ui::SHOW_STATE_MINIMIZED;
655      break;
656    case windows::Update::Params::UpdateInfo::STATE_MAXIMIZED:
657      show_state = ui::SHOW_STATE_MAXIMIZED;
658      break;
659    case windows::Update::Params::UpdateInfo::STATE_FULLSCREEN:
660      show_state = ui::SHOW_STATE_FULLSCREEN;
661      break;
662    case windows::Update::Params::UpdateInfo::STATE_NONE:
663      break;
664    default:
665      error_ = keys::kInvalidWindowStateError;
666      return false;
667  }
668
669  if (show_state != ui::SHOW_STATE_FULLSCREEN &&
670      show_state != ui::SHOW_STATE_DEFAULT)
671    controller->SetFullscreenMode(false, GetExtension()->url());
672
673  switch (show_state) {
674    case ui::SHOW_STATE_MINIMIZED:
675      controller->window()->Minimize();
676      break;
677    case ui::SHOW_STATE_MAXIMIZED:
678      controller->window()->Maximize();
679      break;
680    case ui::SHOW_STATE_FULLSCREEN:
681      if (controller->window()->IsMinimized() ||
682          controller->window()->IsMaximized())
683        controller->window()->Restore();
684      controller->SetFullscreenMode(true, GetExtension()->url());
685      break;
686    case ui::SHOW_STATE_NORMAL:
687      controller->window()->Restore();
688      break;
689    default:
690      break;
691  }
692
693  gfx::Rect bounds;
694  if (controller->window()->IsMinimized())
695    bounds = controller->window()->GetRestoredBounds();
696  else
697    bounds = controller->window()->GetBounds();
698  bool set_bounds = false;
699
700  // Any part of the bounds can optionally be set by the caller.
701  if (params->update_info.left) {
702    bounds.set_x(*params->update_info.left);
703    set_bounds = true;
704  }
705
706  if (params->update_info.top) {
707    bounds.set_y(*params->update_info.top);
708    set_bounds = true;
709  }
710
711  if (params->update_info.width) {
712    bounds.set_width(*params->update_info.width);
713    set_bounds = true;
714  }
715
716  if (params->update_info.height) {
717    bounds.set_height(*params->update_info.height);
718    set_bounds = true;
719  }
720
721  if (set_bounds) {
722    if (show_state == ui::SHOW_STATE_MINIMIZED ||
723        show_state == ui::SHOW_STATE_MAXIMIZED ||
724        show_state == ui::SHOW_STATE_FULLSCREEN) {
725      error_ = keys::kInvalidWindowStateError;
726      return false;
727    }
728    // TODO(varkha): Updating bounds during a drag can cause problems and a more
729    // general solution is needed. See http://crbug.com/251813 .
730    controller->window()->SetBounds(bounds);
731  }
732
733  if (params->update_info.focused) {
734    if (*params->update_info.focused) {
735      if (show_state == ui::SHOW_STATE_MINIMIZED) {
736        error_ = keys::kInvalidWindowStateError;
737        return false;
738      }
739      controller->window()->Activate();
740    } else {
741      if (show_state == ui::SHOW_STATE_MAXIMIZED ||
742          show_state == ui::SHOW_STATE_FULLSCREEN) {
743        error_ = keys::kInvalidWindowStateError;
744        return false;
745      }
746      controller->window()->Deactivate();
747    }
748  }
749
750  if (params->update_info.draw_attention)
751    controller->window()->FlashFrame(*params->update_info.draw_attention);
752
753  SetResult(controller->CreateWindowValue());
754
755  return true;
756}
757
758bool WindowsRemoveFunction::RunImpl() {
759  scoped_ptr<windows::Remove::Params> params(
760      windows::Remove::Params::Create(*args_));
761  EXTENSION_FUNCTION_VALIDATE(params);
762
763  WindowController* controller;
764  if (!windows_util::GetWindowFromWindowID(this, params->window_id,
765                                           &controller))
766    return false;
767
768#if defined(OS_WIN)
769  // In Windows 8 metro mode, an existing Browser instance is reused for
770  // hosting the extension tab. We should not be closing it as we don't own it.
771  if (win8::IsSingleWindowMetroMode())
772    return false;
773#endif
774
775  WindowController::Reason reason;
776  if (!controller->CanClose(&reason)) {
777    if (reason == WindowController::REASON_NOT_EDITABLE)
778      error_ = keys::kTabStripNotEditableError;
779    return false;
780  }
781  controller->window()->Close();
782  return true;
783}
784
785// Tabs ------------------------------------------------------------------------
786
787bool TabsGetSelectedFunction::RunImpl() {
788  // windowId defaults to "current" window.
789  int window_id = extension_misc::kCurrentWindowId;
790
791  scoped_ptr<tabs::GetSelected::Params> params(
792      tabs::GetSelected::Params::Create(*args_));
793  EXTENSION_FUNCTION_VALIDATE(params.get());
794  if (params->window_id.get())
795    window_id = *params->window_id;
796
797  Browser* browser = NULL;
798  if (!GetBrowserFromWindowID(this, window_id, &browser))
799    return false;
800
801  TabStripModel* tab_strip = browser->tab_strip_model();
802  WebContents* contents = tab_strip->GetActiveWebContents();
803  if (!contents) {
804    error_ = keys::kNoSelectedTabError;
805    return false;
806  }
807  SetResult(ExtensionTabUtil::CreateTabValue(contents,
808                                             tab_strip,
809                                             tab_strip->active_index(),
810                                             GetExtension()));
811  return true;
812}
813
814bool TabsGetAllInWindowFunction::RunImpl() {
815  scoped_ptr<tabs::GetAllInWindow::Params> params(
816      tabs::GetAllInWindow::Params::Create(*args_));
817  EXTENSION_FUNCTION_VALIDATE(params.get());
818  // windowId defaults to "current" window.
819  int window_id = extension_misc::kCurrentWindowId;
820  if (params->window_id.get())
821    window_id = *params->window_id;
822
823  Browser* browser = NULL;
824  if (!GetBrowserFromWindowID(this, window_id, &browser))
825    return false;
826
827  SetResult(ExtensionTabUtil::CreateTabList(browser, GetExtension()));
828
829  return true;
830}
831
832bool TabsQueryFunction::RunImpl() {
833  scoped_ptr<tabs::Query::Params> params(tabs::Query::Params::Create(*args_));
834  EXTENSION_FUNCTION_VALIDATE(params.get());
835
836  bool loading_status_set = params->query_info.status !=
837      tabs::Query::Params::QueryInfo::STATUS_NONE;
838  bool loading = params->query_info.status ==
839      tabs::Query::Params::QueryInfo::STATUS_LOADING;
840
841  // It is o.k. to use URLPattern::SCHEME_ALL here because this function does
842  // not grant access to the content of the tabs, only to seeing their URLs and
843  // meta data.
844  URLPattern url_pattern(URLPattern::SCHEME_ALL, "<all_urls>");
845  if (params->query_info.url.get())
846    url_pattern = URLPattern(URLPattern::SCHEME_ALL, *params->query_info.url);
847
848  std::string title;
849  if (params->query_info.title.get())
850    title = *params->query_info.title;
851
852  int window_id = extension_misc::kUnknownWindowId;
853  if (params->query_info.window_id.get())
854    window_id = *params->query_info.window_id;
855
856  int index = -1;
857  if (params->query_info.index.get())
858    index = *params->query_info.index;
859
860  std::string window_type;
861  if (params->query_info.window_type !=
862      tabs::Query::Params::QueryInfo::WINDOW_TYPE_NONE) {
863    window_type = tabs::Query::Params::QueryInfo::ToString(
864        params->query_info.window_type);
865  }
866
867  base::ListValue* result = new base::ListValue();
868  Browser* last_active_browser = chrome::FindAnyBrowser(
869      GetProfile(), include_incognito(), chrome::GetActiveDesktop());
870  Browser* current_browser = GetCurrentBrowser();
871  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
872    Browser* browser = *it;
873    if (!GetProfile()->IsSameProfile(browser->profile()))
874      continue;
875
876    if (!browser->window())
877      continue;
878
879    if (!include_incognito() && GetProfile() != browser->profile())
880      continue;
881
882    if (window_id >= 0 && window_id != ExtensionTabUtil::GetWindowId(browser))
883      continue;
884
885    if (window_id == extension_misc::kCurrentWindowId &&
886        browser != current_browser) {
887      continue;
888    }
889
890    if (!MatchesBool(params->query_info.current_window.get(),
891                     browser == current_browser)) {
892      continue;
893    }
894
895    if (!MatchesBool(params->query_info.last_focused_window.get(),
896                     browser == last_active_browser)) {
897      continue;
898    }
899
900    if (!window_type.empty() &&
901        window_type !=
902            browser->extension_window_controller()->GetWindowTypeText()) {
903      continue;
904    }
905
906    TabStripModel* tab_strip = browser->tab_strip_model();
907    for (int i = 0; i < tab_strip->count(); ++i) {
908      const WebContents* web_contents = tab_strip->GetWebContentsAt(i);
909
910      if (index > -1 && i != index)
911        continue;
912
913      if (!MatchesBool(params->query_info.highlighted.get(),
914                       tab_strip->IsTabSelected(i))) {
915        continue;
916      }
917
918      if (!MatchesBool(params->query_info.active.get(),
919                       i == tab_strip->active_index())) {
920        continue;
921      }
922
923      if (!MatchesBool(params->query_info.pinned.get(),
924                       tab_strip->IsTabPinned(i))) {
925        continue;
926      }
927
928      if (!title.empty() && !MatchPattern(web_contents->GetTitle(),
929                                          base::UTF8ToUTF16(title)))
930        continue;
931
932      if (!url_pattern.MatchesURL(web_contents->GetURL()))
933        continue;
934
935      if (loading_status_set && loading != web_contents->IsLoading())
936        continue;
937
938      result->Append(ExtensionTabUtil::CreateTabValue(
939          web_contents, tab_strip, i, GetExtension()));
940    }
941  }
942
943  SetResult(result);
944  return true;
945}
946
947bool TabsCreateFunction::RunImpl() {
948  scoped_ptr<tabs::Create::Params> params(tabs::Create::Params::Create(*args_));
949  EXTENSION_FUNCTION_VALIDATE(params.get());
950
951  // windowId defaults to "current" window.
952  int window_id = extension_misc::kCurrentWindowId;
953  if (params->create_properties.window_id.get())
954    window_id = *params->create_properties.window_id;
955
956  Browser* browser = NULL;
957  if (!GetBrowserFromWindowID(this, window_id, &browser))
958    return false;
959
960  // Ensure the selected browser is tabbed.
961  if (!browser->is_type_tabbed() && browser->IsAttemptingToCloseBrowser())
962    browser = chrome::FindTabbedBrowser(
963        GetProfile(), include_incognito(), browser->host_desktop_type());
964
965  if (!browser || !browser->window())
966    return false;
967
968  // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that
969  // represents the active tab.
970  WebContents* opener = NULL;
971  if (params->create_properties.opener_tab_id.get()) {
972    int opener_id = *params->create_properties.opener_tab_id;
973
974    if (!ExtensionTabUtil::GetTabById(opener_id,
975                                      GetProfile(),
976                                      include_incognito(),
977                                      NULL,
978                                      NULL,
979                                      &opener,
980                                      NULL)) {
981      return false;
982    }
983  }
984
985  // TODO(rafaelw): handle setting remaining tab properties:
986  // -title
987  // -favIconUrl
988
989  std::string url_string;
990  GURL url;
991  if (params->create_properties.url.get()) {
992    url_string = *params->create_properties.url;
993    url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string,
994                                                       GetExtension());
995    if (!url.is_valid()) {
996      error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError,
997                                                       url_string);
998      return false;
999    }
1000  }
1001
1002  // Don't let extensions crash the browser or renderers.
1003  if (ExtensionTabUtil::IsCrashURL(url)) {
1004    error_ = keys::kNoCrashBrowserError;
1005    return false;
1006  }
1007
1008  // Default to foreground for the new tab. The presence of 'selected' property
1009  // will override this default. This property is deprecated ('active' should
1010  // be used instead).
1011  bool active = true;
1012  if (params->create_properties.selected.get())
1013    active = *params->create_properties.selected;
1014
1015  // The 'active' property has replaced the 'selected' property.
1016  if (params->create_properties.active.get())
1017    active = *params->create_properties.active;
1018
1019  // Default to not pinning the tab. Setting the 'pinned' property to true
1020  // will override this default.
1021  bool pinned = false;
1022  if (params->create_properties.pinned.get())
1023    pinned = *params->create_properties.pinned;
1024
1025  // We can't load extension URLs into incognito windows unless the extension
1026  // uses split mode. Special case to fall back to a tabbed window.
1027  if (url.SchemeIs(kExtensionScheme) &&
1028      !IncognitoInfo::IsSplitMode(GetExtension()) &&
1029      browser->profile()->IsOffTheRecord()) {
1030    Profile* profile = browser->profile()->GetOriginalProfile();
1031    chrome::HostDesktopType desktop_type = browser->host_desktop_type();
1032
1033    browser = chrome::FindTabbedBrowser(profile, false, desktop_type);
1034    if (!browser) {
1035      browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED,
1036                                                  profile, desktop_type));
1037      browser->window()->Show();
1038    }
1039  }
1040
1041  // If index is specified, honor the value, but keep it bound to
1042  // -1 <= index <= tab_strip->count() where -1 invokes the default behavior.
1043  int index = -1;
1044  if (params->create_properties.index.get())
1045    index = *params->create_properties.index;
1046
1047  TabStripModel* tab_strip = browser->tab_strip_model();
1048
1049  index = std::min(std::max(index, -1), tab_strip->count());
1050
1051  int add_types = active ? TabStripModel::ADD_ACTIVE :
1052                             TabStripModel::ADD_NONE;
1053  add_types |= TabStripModel::ADD_FORCE_INDEX;
1054  if (pinned)
1055    add_types |= TabStripModel::ADD_PINNED;
1056  chrome::NavigateParams navigate_params(
1057      browser, url, content::PAGE_TRANSITION_LINK);
1058  navigate_params.disposition =
1059      active ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
1060  navigate_params.tabstrip_index = index;
1061  navigate_params.tabstrip_add_types = add_types;
1062  chrome::Navigate(&navigate_params);
1063
1064  // The tab may have been created in a different window, so make sure we look
1065  // at the right tab strip.
1066  tab_strip = navigate_params.browser->tab_strip_model();
1067  int new_index = tab_strip->GetIndexOfWebContents(
1068      navigate_params.target_contents);
1069  if (opener)
1070    tab_strip->SetOpenerOfWebContentsAt(new_index, opener);
1071
1072  if (active)
1073    navigate_params.target_contents->GetView()->SetInitialFocus();
1074
1075  // Return data about the newly created tab.
1076  if (has_callback()) {
1077    SetResult(ExtensionTabUtil::CreateTabValue(
1078        navigate_params.target_contents,
1079        tab_strip, new_index, GetExtension()));
1080  }
1081
1082  return true;
1083}
1084
1085bool TabsDuplicateFunction::RunImpl() {
1086  scoped_ptr<tabs::Duplicate::Params> params(
1087      tabs::Duplicate::Params::Create(*args_));
1088  EXTENSION_FUNCTION_VALIDATE(params.get());
1089  int tab_id = params->tab_id;
1090
1091  Browser* browser = NULL;
1092  TabStripModel* tab_strip = NULL;
1093  int tab_index = -1;
1094  if (!GetTabById(tab_id,
1095                  GetProfile(),
1096                  include_incognito(),
1097                  &browser,
1098                  &tab_strip,
1099                  NULL,
1100                  &tab_index,
1101                  &error_)) {
1102    return false;
1103  }
1104
1105  WebContents* new_contents = chrome::DuplicateTabAt(browser, tab_index);
1106  if (!has_callback())
1107    return true;
1108
1109  // Duplicated tab may not be in the same window as the original, so find
1110  // the window and the tab.
1111  TabStripModel* new_tab_strip = NULL;
1112  int new_tab_index = -1;
1113  ExtensionTabUtil::GetTabStripModel(new_contents,
1114                                     &new_tab_strip,
1115                                     &new_tab_index);
1116  if (!new_tab_strip || new_tab_index == -1) {
1117    return false;
1118  }
1119
1120  // Return data about the newly created tab.
1121  SetResult(ExtensionTabUtil::CreateTabValue(
1122      new_contents,
1123      new_tab_strip, new_tab_index, GetExtension()));
1124
1125  return true;
1126}
1127
1128bool TabsGetFunction::RunImpl() {
1129  scoped_ptr<tabs::Get::Params> params(tabs::Get::Params::Create(*args_));
1130  EXTENSION_FUNCTION_VALIDATE(params.get());
1131  int tab_id = params->tab_id;
1132
1133  TabStripModel* tab_strip = NULL;
1134  WebContents* contents = NULL;
1135  int tab_index = -1;
1136  if (!GetTabById(tab_id,
1137                  GetProfile(),
1138                  include_incognito(),
1139                  NULL,
1140                  &tab_strip,
1141                  &contents,
1142                  &tab_index,
1143                  &error_))
1144    return false;
1145
1146  SetResult(ExtensionTabUtil::CreateTabValue(contents,
1147                                             tab_strip,
1148                                             tab_index,
1149                                             GetExtension()));
1150  return true;
1151}
1152
1153bool TabsGetCurrentFunction::RunImpl() {
1154  DCHECK(dispatcher());
1155
1156  WebContents* contents = dispatcher()->delegate()->GetAssociatedWebContents();
1157  if (contents)
1158    SetResult(ExtensionTabUtil::CreateTabValue(contents, GetExtension()));
1159
1160  return true;
1161}
1162
1163bool TabsHighlightFunction::RunImpl() {
1164  scoped_ptr<tabs::Highlight::Params> params(
1165      tabs::Highlight::Params::Create(*args_));
1166  EXTENSION_FUNCTION_VALIDATE(params.get());
1167
1168  // Get the window id from the params; default to current window if omitted.
1169  int window_id = extension_misc::kCurrentWindowId;
1170  if (params->highlight_info.window_id.get())
1171    window_id = *params->highlight_info.window_id;
1172
1173  Browser* browser = NULL;
1174  if (!GetBrowserFromWindowID(this, window_id, &browser))
1175    return false;
1176
1177  TabStripModel* tabstrip = browser->tab_strip_model();
1178  ui::ListSelectionModel selection;
1179  int active_index = -1;
1180
1181  if (params->highlight_info.tabs.as_integers) {
1182    std::vector<int>& tab_indices = *params->highlight_info.tabs.as_integers;
1183    // Create a new selection model as we read the list of tab indices.
1184    for (size_t i = 0; i < tab_indices.size(); ++i) {
1185      if (!HighlightTab(tabstrip, &selection, &active_index, tab_indices[i]))
1186        return false;
1187    }
1188  } else {
1189    EXTENSION_FUNCTION_VALIDATE(params->highlight_info.tabs.as_integer);
1190    if (!HighlightTab(tabstrip,
1191                      &selection,
1192                      &active_index,
1193                      *params->highlight_info.tabs.as_integer)) {
1194      return false;
1195    }
1196  }
1197
1198  // Make sure they actually specified tabs to select.
1199  if (selection.empty()) {
1200    error_ = keys::kNoHighlightedTabError;
1201    return false;
1202  }
1203
1204  selection.set_active(active_index);
1205  browser->tab_strip_model()->SetSelectionFromModel(selection);
1206  SetResult(
1207      browser->extension_window_controller()->CreateWindowValueWithTabs(
1208          GetExtension()));
1209  return true;
1210}
1211
1212bool TabsHighlightFunction::HighlightTab(TabStripModel* tabstrip,
1213                                         ui::ListSelectionModel* selection,
1214                                         int *active_index,
1215                                         int index) {
1216  // Make sure the index is in range.
1217  if (!tabstrip->ContainsIndex(index)) {
1218    error_ = ErrorUtils::FormatErrorMessage(
1219        keys::kTabIndexNotFoundError, base::IntToString(index));
1220    return false;
1221  }
1222
1223  // By default, we make the first tab in the list active.
1224  if (*active_index == -1)
1225    *active_index = index;
1226
1227  selection->AddIndexToSelection(index);
1228  return true;
1229}
1230
1231TabsUpdateFunction::TabsUpdateFunction() : web_contents_(NULL) {
1232}
1233
1234bool TabsUpdateFunction::RunImpl() {
1235  scoped_ptr<tabs::Update::Params> params(tabs::Update::Params::Create(*args_));
1236  EXTENSION_FUNCTION_VALIDATE(params.get());
1237
1238  int tab_id = -1;
1239  WebContents* contents = NULL;
1240  if (!params->tab_id.get()) {
1241    Browser* browser = GetCurrentBrowser();
1242    if (!browser) {
1243      error_ = keys::kNoCurrentWindowError;
1244      return false;
1245    }
1246    contents = browser->tab_strip_model()->GetActiveWebContents();
1247    if (!contents) {
1248      error_ = keys::kNoSelectedTabError;
1249      return false;
1250    }
1251    tab_id = SessionID::IdForTab(contents);
1252  } else {
1253    tab_id = *params->tab_id;
1254  }
1255
1256  int tab_index = -1;
1257  TabStripModel* tab_strip = NULL;
1258  if (!GetTabById(tab_id,
1259                  GetProfile(),
1260                  include_incognito(),
1261                  NULL,
1262                  &tab_strip,
1263                  &contents,
1264                  &tab_index,
1265                  &error_)) {
1266    return false;
1267  }
1268
1269  web_contents_ = contents;
1270
1271  // TODO(rafaelw): handle setting remaining tab properties:
1272  // -title
1273  // -favIconUrl
1274
1275  // Navigate the tab to a new location if the url is different.
1276  bool is_async = false;
1277  if (params->update_properties.url.get() &&
1278      !UpdateURL(*params->update_properties.url, tab_id, &is_async)) {
1279    return false;
1280  }
1281
1282  bool active = false;
1283  // TODO(rafaelw): Setting |active| from js doesn't make much sense.
1284  // Move tab selection management up to window.
1285  if (params->update_properties.selected.get())
1286    active = *params->update_properties.selected;
1287
1288  // The 'active' property has replaced 'selected'.
1289  if (params->update_properties.active.get())
1290    active = *params->update_properties.active;
1291
1292  if (active) {
1293    if (tab_strip->active_index() != tab_index) {
1294      tab_strip->ActivateTabAt(tab_index, false);
1295      DCHECK_EQ(contents, tab_strip->GetActiveWebContents());
1296    }
1297  }
1298
1299  if (params->update_properties.highlighted.get()) {
1300    bool highlighted = *params->update_properties.highlighted;
1301    if (highlighted != tab_strip->IsTabSelected(tab_index))
1302      tab_strip->ToggleSelectionAt(tab_index);
1303  }
1304
1305  if (params->update_properties.pinned.get()) {
1306    bool pinned = *params->update_properties.pinned;
1307    tab_strip->SetTabPinned(tab_index, pinned);
1308
1309    // Update the tab index because it may move when being pinned.
1310    tab_index = tab_strip->GetIndexOfWebContents(contents);
1311  }
1312
1313  if (params->update_properties.opener_tab_id.get()) {
1314    int opener_id = *params->update_properties.opener_tab_id;
1315
1316    WebContents* opener_contents = NULL;
1317    if (!ExtensionTabUtil::GetTabById(opener_id,
1318                                      GetProfile(),
1319                                      include_incognito(),
1320                                      NULL,
1321                                      NULL,
1322                                      &opener_contents,
1323                                      NULL))
1324      return false;
1325
1326    tab_strip->SetOpenerOfWebContentsAt(tab_index, opener_contents);
1327  }
1328
1329  if (!is_async) {
1330    PopulateResult();
1331    SendResponse(true);
1332  }
1333  return true;
1334}
1335
1336bool TabsUpdateFunction::UpdateURL(const std::string &url_string,
1337                                   int tab_id,
1338                                   bool* is_async) {
1339  GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
1340      url_string, GetExtension());
1341
1342  if (!url.is_valid()) {
1343    error_ = ErrorUtils::FormatErrorMessage(
1344        keys::kInvalidUrlError, url_string);
1345    return false;
1346  }
1347
1348  // Don't let the extension crash the browser or renderers.
1349  if (ExtensionTabUtil::IsCrashURL(url)) {
1350    error_ = keys::kNoCrashBrowserError;
1351    return false;
1352  }
1353
1354  // JavaScript URLs can do the same kinds of things as cross-origin XHR, so
1355  // we need to check host permissions before allowing them.
1356  if (url.SchemeIs(content::kJavaScriptScheme)) {
1357    content::RenderProcessHost* process = web_contents_->GetRenderProcessHost();
1358    if (!PermissionsData::CanExecuteScriptOnPage(
1359            GetExtension(),
1360            web_contents_->GetURL(),
1361            web_contents_->GetURL(),
1362            tab_id,
1363            NULL,
1364            process ? process->GetID() : -1,
1365            &error_)) {
1366      return false;
1367    }
1368
1369    TabHelper::FromWebContents(web_contents_)->
1370        script_executor()->ExecuteScript(
1371            extension_id(),
1372            ScriptExecutor::JAVASCRIPT,
1373            url.GetContent(),
1374            ScriptExecutor::TOP_FRAME,
1375            UserScript::DOCUMENT_IDLE,
1376            ScriptExecutor::MAIN_WORLD,
1377            ScriptExecutor::DEFAULT_PROCESS,
1378            GURL(),
1379            ScriptExecutor::NO_RESULT,
1380            base::Bind(&TabsUpdateFunction::OnExecuteCodeFinished, this));
1381
1382    *is_async = true;
1383    return true;
1384  }
1385
1386  web_contents_->GetController().LoadURL(
1387      url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string());
1388
1389  // The URL of a tab contents never actually changes to a JavaScript URL, so
1390  // this check only makes sense in other cases.
1391  if (!url.SchemeIs(content::kJavaScriptScheme))
1392    DCHECK_EQ(url.spec(), web_contents_->GetURL().spec());
1393
1394  return true;
1395}
1396
1397void TabsUpdateFunction::PopulateResult() {
1398  if (!has_callback())
1399    return;
1400
1401  SetResult(ExtensionTabUtil::CreateTabValue(web_contents_, GetExtension()));
1402}
1403
1404void TabsUpdateFunction::OnExecuteCodeFinished(
1405    const std::string& error,
1406    int32 on_page_id,
1407    const GURL& url,
1408    const base::ListValue& script_result) {
1409  if (error.empty())
1410    PopulateResult();
1411  else
1412    error_ = error;
1413  SendResponse(error.empty());
1414}
1415
1416bool TabsMoveFunction::RunImpl() {
1417  scoped_ptr<tabs::Move::Params> params(tabs::Move::Params::Create(*args_));
1418  EXTENSION_FUNCTION_VALIDATE(params.get());
1419
1420  int new_index = params->move_properties.index;
1421  int* window_id = params->move_properties.window_id.get();
1422  base::ListValue tab_values;
1423
1424  size_t num_tabs = 0;
1425  if (params->tab_ids.as_integers) {
1426    std::vector<int>& tab_ids = *params->tab_ids.as_integers;
1427    num_tabs = tab_ids.size();
1428    for (size_t i = 0; i < tab_ids.size(); ++i) {
1429      if (!MoveTab(tab_ids[i], &new_index, i, &tab_values, window_id))
1430        return false;
1431    }
1432  } else {
1433    EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1434    num_tabs = 1;
1435    if (!MoveTab(*params->tab_ids.as_integer,
1436                 &new_index,
1437                 0,
1438                 &tab_values,
1439                 window_id)) {
1440      return false;
1441    }
1442  }
1443
1444  if (!has_callback())
1445    return true;
1446
1447  // Only return the results as an array if there are multiple tabs.
1448  if (num_tabs > 1) {
1449    SetResult(tab_values.DeepCopy());
1450  } else {
1451    base::Value* value = NULL;
1452    CHECK(tab_values.Get(0, &value));
1453    SetResult(value->DeepCopy());
1454  }
1455  return true;
1456}
1457
1458bool TabsMoveFunction::MoveTab(int tab_id,
1459                               int *new_index,
1460                               int iteration,
1461                               base::ListValue* tab_values,
1462                               int* window_id) {
1463  Browser* source_browser = NULL;
1464  TabStripModel* source_tab_strip = NULL;
1465  WebContents* contents = NULL;
1466  int tab_index = -1;
1467  if (!GetTabById(tab_id,
1468                  GetProfile(),
1469                  include_incognito(),
1470                  &source_browser,
1471                  &source_tab_strip,
1472                  &contents,
1473                  &tab_index,
1474                  &error_)) {
1475    return false;
1476  }
1477
1478  // Don't let the extension move the tab if the user is dragging tabs.
1479  if (!source_browser->window()->IsTabStripEditable()) {
1480    error_ = keys::kTabStripNotEditableError;
1481    return false;
1482  }
1483
1484  // Insert the tabs one after another.
1485  *new_index += iteration;
1486
1487  if (window_id) {
1488    Browser* target_browser = NULL;
1489
1490    if (!GetBrowserFromWindowID(this, *window_id, &target_browser))
1491      return false;
1492
1493    if (!target_browser->window()->IsTabStripEditable()) {
1494      error_ = keys::kTabStripNotEditableError;
1495      return false;
1496    }
1497
1498    if (!target_browser->is_type_tabbed()) {
1499      error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError;
1500      return false;
1501    }
1502
1503    if (target_browser->profile() != source_browser->profile()) {
1504      error_ = keys::kCanOnlyMoveTabsWithinSameProfileError;
1505      return false;
1506    }
1507
1508    // If windowId is different from the current window, move between windows.
1509    if (ExtensionTabUtil::GetWindowId(target_browser) !=
1510        ExtensionTabUtil::GetWindowId(source_browser)) {
1511      TabStripModel* target_tab_strip = target_browser->tab_strip_model();
1512      WebContents* web_contents =
1513          source_tab_strip->DetachWebContentsAt(tab_index);
1514      if (!web_contents) {
1515        error_ = ErrorUtils::FormatErrorMessage(
1516            keys::kTabNotFoundError, base::IntToString(tab_id));
1517        return false;
1518      }
1519
1520      // Clamp move location to the last position.
1521      // This is ">" because it can append to a new index position.
1522      // -1 means set the move location to the last position.
1523      if (*new_index > target_tab_strip->count() || *new_index < 0)
1524        *new_index = target_tab_strip->count();
1525
1526      target_tab_strip->InsertWebContentsAt(
1527          *new_index, web_contents, TabStripModel::ADD_NONE);
1528
1529      if (has_callback()) {
1530        tab_values->Append(ExtensionTabUtil::CreateTabValue(
1531            web_contents,
1532            target_tab_strip,
1533            *new_index,
1534            GetExtension()));
1535      }
1536
1537      return true;
1538    }
1539  }
1540
1541  // Perform a simple within-window move.
1542  // Clamp move location to the last position.
1543  // This is ">=" because the move must be to an existing location.
1544  // -1 means set the move location to the last position.
1545  if (*new_index >= source_tab_strip->count() || *new_index < 0)
1546    *new_index = source_tab_strip->count() - 1;
1547
1548  if (*new_index != tab_index)
1549    source_tab_strip->MoveWebContentsAt(tab_index, *new_index, false);
1550
1551  if (has_callback()) {
1552    tab_values->Append(ExtensionTabUtil::CreateTabValue(
1553        contents, source_tab_strip, *new_index, GetExtension()));
1554  }
1555
1556  return true;
1557}
1558
1559bool TabsReloadFunction::RunImpl() {
1560  scoped_ptr<tabs::Reload::Params> params(
1561      tabs::Reload::Params::Create(*args_));
1562  EXTENSION_FUNCTION_VALIDATE(params.get());
1563
1564  bool bypass_cache = false;
1565  if (params->reload_properties.get() &&
1566      params->reload_properties->bypass_cache.get()) {
1567    bypass_cache = *params->reload_properties->bypass_cache;
1568  }
1569
1570  content::WebContents* web_contents = NULL;
1571
1572  // If |tab_id| is specified, look for it. Otherwise default to selected tab
1573  // in the current window.
1574  if (!params->tab_id.get()) {
1575    Browser* browser = GetCurrentBrowser();
1576    if (!browser) {
1577      error_ = keys::kNoCurrentWindowError;
1578      return false;
1579    }
1580
1581    if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL))
1582      return false;
1583  } else {
1584    int tab_id = *params->tab_id;
1585
1586    Browser* browser = NULL;
1587    if (!GetTabById(tab_id,
1588                    GetProfile(),
1589                    include_incognito(),
1590                    &browser,
1591                    NULL,
1592                    &web_contents,
1593                    NULL,
1594                    &error_))
1595    return false;
1596  }
1597
1598  if (web_contents->ShowingInterstitialPage()) {
1599    // This does as same as Browser::ReloadInternal.
1600    NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
1601    OpenURLParams params(entry->GetURL(), Referrer(), CURRENT_TAB,
1602                         content::PAGE_TRANSITION_RELOAD, false);
1603    GetCurrentBrowser()->OpenURL(params);
1604  } else if (bypass_cache) {
1605    web_contents->GetController().ReloadIgnoringCache(true);
1606  } else {
1607    web_contents->GetController().Reload(true);
1608  }
1609
1610  return true;
1611}
1612
1613bool TabsRemoveFunction::RunImpl() {
1614  scoped_ptr<tabs::Remove::Params> params(tabs::Remove::Params::Create(*args_));
1615  EXTENSION_FUNCTION_VALIDATE(params.get());
1616
1617  if (params->tab_ids.as_integers) {
1618    std::vector<int>& tab_ids = *params->tab_ids.as_integers;
1619    for (size_t i = 0; i < tab_ids.size(); ++i) {
1620      if (!RemoveTab(tab_ids[i]))
1621        return false;
1622    }
1623  } else {
1624    EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1625    if (!RemoveTab(*params->tab_ids.as_integer.get()))
1626      return false;
1627  }
1628  return true;
1629}
1630
1631bool TabsRemoveFunction::RemoveTab(int tab_id) {
1632  Browser* browser = NULL;
1633  WebContents* contents = NULL;
1634  if (!GetTabById(tab_id,
1635                  GetProfile(),
1636                  include_incognito(),
1637                  &browser,
1638                  NULL,
1639                  &contents,
1640                  NULL,
1641                  &error_)) {
1642    return false;
1643  }
1644
1645  // Don't let the extension remove a tab if the user is dragging tabs around.
1646  if (!browser->window()->IsTabStripEditable()) {
1647    error_ = keys::kTabStripNotEditableError;
1648    return false;
1649  }
1650  // There's a chance that the tab is being dragged, or we're in some other
1651  // nested event loop. This code path ensures that the tab is safely closed
1652  // under such circumstances, whereas |TabStripModel::CloseWebContentsAt()|
1653  // does not.
1654  contents->Close();
1655  return true;
1656}
1657
1658bool TabsCaptureVisibleTabFunction::IsScreenshotEnabled() {
1659  PrefService* service = GetProfile()->GetPrefs();
1660  if (service->GetBoolean(prefs::kDisableScreenshots)) {
1661    error_ = keys::kScreenshotsDisabled;
1662    return false;
1663  }
1664  return true;
1665}
1666
1667WebContents* TabsCaptureVisibleTabFunction::GetWebContentsForID(int window_id) {
1668  Browser* browser = NULL;
1669  if (!GetBrowserFromWindowID(this, window_id, &browser))
1670    return NULL;
1671
1672  WebContents* contents = browser->tab_strip_model()->GetActiveWebContents();
1673  if (!contents) {
1674    error_ = keys::kInternalVisibleTabCaptureError;
1675    return NULL;
1676  }
1677
1678  if (!PermissionsData::CanCaptureVisiblePage(GetExtension(),
1679                                              SessionID::IdForTab(contents),
1680                                              &error_)) {
1681    return NULL;
1682  }
1683  return contents;
1684}
1685
1686void TabsCaptureVisibleTabFunction::OnCaptureFailure(FailureReason reason) {
1687  error_ = keys::kInternalVisibleTabCaptureError;
1688  SendResponse(false);
1689}
1690
1691void TabsCaptureVisibleTabFunction::RegisterProfilePrefs(
1692    user_prefs::PrefRegistrySyncable* registry) {
1693  registry->RegisterBooleanPref(
1694      prefs::kDisableScreenshots,
1695      false,
1696      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1697}
1698
1699bool TabsDetectLanguageFunction::RunImpl() {
1700  scoped_ptr<tabs::DetectLanguage::Params> params(
1701      tabs::DetectLanguage::Params::Create(*args_));
1702  EXTENSION_FUNCTION_VALIDATE(params.get());
1703
1704  int tab_id = 0;
1705  Browser* browser = NULL;
1706  WebContents* contents = NULL;
1707
1708  // If |tab_id| is specified, look for it. Otherwise default to selected tab
1709  // in the current window.
1710  if (params->tab_id.get()) {
1711    tab_id = *params->tab_id;
1712    if (!GetTabById(tab_id,
1713                    GetProfile(),
1714                    include_incognito(),
1715                    &browser,
1716                    NULL,
1717                    &contents,
1718                    NULL,
1719                    &error_)) {
1720      return false;
1721    }
1722    if (!browser || !contents)
1723      return false;
1724  } else {
1725    browser = GetCurrentBrowser();
1726    if (!browser)
1727      return false;
1728    contents = browser->tab_strip_model()->GetActiveWebContents();
1729    if (!contents)
1730      return false;
1731  }
1732
1733  if (contents->GetController().NeedsReload()) {
1734    // If the tab hasn't been loaded, don't wait for the tab to load.
1735    error_ = keys::kCannotDetermineLanguageOfUnloadedTab;
1736    return false;
1737  }
1738
1739  AddRef();  // Balanced in GotLanguage().
1740
1741  TranslateTabHelper* translate_tab_helper =
1742      TranslateTabHelper::FromWebContents(contents);
1743  if (!translate_tab_helper->GetLanguageState().original_language().empty()) {
1744    // Delay the callback invocation until after the current JS call has
1745    // returned.
1746    base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
1747        &TabsDetectLanguageFunction::GotLanguage, this,
1748        translate_tab_helper->GetLanguageState().original_language()));
1749    return true;
1750  }
1751  // The tab contents does not know its language yet.  Let's wait until it
1752  // receives it, or until the tab is closed/navigates to some other page.
1753  registrar_.Add(this, chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED,
1754                 content::Source<WebContents>(contents));
1755  registrar_.Add(
1756      this, chrome::NOTIFICATION_TAB_CLOSING,
1757      content::Source<NavigationController>(&(contents->GetController())));
1758  registrar_.Add(
1759      this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
1760      content::Source<NavigationController>(&(contents->GetController())));
1761  return true;
1762}
1763
1764void TabsDetectLanguageFunction::Observe(
1765    int type,
1766    const content::NotificationSource& source,
1767    const content::NotificationDetails& details) {
1768  std::string language;
1769  if (type == chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED) {
1770    const LanguageDetectionDetails* lang_det_details =
1771        content::Details<const LanguageDetectionDetails>(details).ptr();
1772    language = lang_det_details->adopted_language;
1773  }
1774
1775  registrar_.RemoveAll();
1776
1777  // Call GotLanguage in all cases as we want to guarantee the callback is
1778  // called for every API call the extension made.
1779  GotLanguage(language);
1780}
1781
1782void TabsDetectLanguageFunction::GotLanguage(const std::string& language) {
1783  SetResult(new base::StringValue(language.c_str()));
1784  SendResponse(true);
1785
1786  Release();  // Balanced in Run()
1787}
1788
1789ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
1790    : execute_tab_id_(-1) {
1791}
1792
1793ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
1794
1795bool ExecuteCodeInTabFunction::HasPermission() {
1796  if (Init() && PermissionsData::HasAPIPermissionForTab(
1797                    extension_.get(), execute_tab_id_, APIPermission::kTab)) {
1798    return true;
1799  }
1800  return ExtensionFunction::HasPermission();
1801}
1802
1803bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage() {
1804  content::WebContents* contents = NULL;
1805
1806  // If |tab_id| is specified, look for the tab. Otherwise default to selected
1807  // tab in the current window.
1808  CHECK_GE(execute_tab_id_, 0);
1809  if (!GetTabById(execute_tab_id_,
1810                  GetProfile(),
1811                  include_incognito(),
1812                  NULL,
1813                  NULL,
1814                  &contents,
1815                  NULL,
1816                  &error_)) {
1817    return false;
1818  }
1819
1820  CHECK(contents);
1821
1822  // NOTE: This can give the wrong answer due to race conditions, but it is OK,
1823  // we check again in the renderer.
1824  content::RenderProcessHost* process = contents->GetRenderProcessHost();
1825  if (!PermissionsData::CanExecuteScriptOnPage(
1826          GetExtension(),
1827          contents->GetURL(),
1828          contents->GetURL(),
1829          execute_tab_id_,
1830          NULL,
1831          process ? process->GetID() : -1,
1832          &error_)) {
1833    return false;
1834  }
1835
1836  return true;
1837}
1838
1839ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor() {
1840  Browser* browser = NULL;
1841  content::WebContents* contents = NULL;
1842
1843  bool success = GetTabById(execute_tab_id_,
1844                            GetProfile(),
1845                            include_incognito(),
1846                            &browser,
1847                            NULL,
1848                            &contents,
1849                            NULL,
1850                            &error_) &&
1851                 contents && browser;
1852
1853  if (!success)
1854    return NULL;
1855
1856  return TabHelper::FromWebContents(contents)->script_executor();
1857}
1858
1859bool ExecuteCodeInTabFunction::IsWebView() const {
1860  return false;
1861}
1862
1863bool TabsExecuteScriptFunction::ShouldInsertCSS() const {
1864  return false;
1865}
1866
1867void TabsExecuteScriptFunction::OnExecuteCodeFinished(
1868    const std::string& error,
1869    int32 on_page_id,
1870    const GURL& on_url,
1871    const base::ListValue& result) {
1872  if (error.empty())
1873    SetResult(result.DeepCopy());
1874  ExecuteCodeInTabFunction::OnExecuteCodeFinished(error, on_page_id, on_url,
1875                                                  result);
1876}
1877
1878bool ExecuteCodeInTabFunction::Init() {
1879  if (details_.get())
1880    return true;
1881
1882  // |tab_id| is optional so it's ok if it's not there.
1883  int tab_id = -1;
1884  if (args_->GetInteger(0, &tab_id))
1885    EXTENSION_FUNCTION_VALIDATE(tab_id >= 0);
1886
1887  // |details| are not optional.
1888  base::DictionaryValue* details_value = NULL;
1889  if (!args_->GetDictionary(1, &details_value))
1890    return false;
1891  scoped_ptr<InjectDetails> details(new InjectDetails());
1892  if (!InjectDetails::Populate(*details_value, details.get()))
1893    return false;
1894
1895  // If the tab ID wasn't given then it needs to be converted to the
1896  // currently active tab's ID.
1897  if (tab_id == -1) {
1898    Browser* browser = GetCurrentBrowser();
1899    if (!browser)
1900      return false;
1901    content::WebContents* web_contents = NULL;
1902    if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id))
1903      return false;
1904  }
1905
1906  execute_tab_id_ = tab_id;
1907  details_ = details.Pass();
1908  return true;
1909}
1910
1911bool TabsInsertCSSFunction::ShouldInsertCSS() const {
1912  return true;
1913}
1914
1915}  // namespace extensions
1916