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 <string>
6
7#include "base/command_line.h"
8#include "base/memory/ref_counted.h"
9#include "base/path_service.h"
10#include "base/strings/stringprintf.h"
11#include "chrome/browser/extensions/api/debugger/debugger_api.h"
12#include "chrome/browser/extensions/extension_apitest.h"
13#include "chrome/browser/extensions/extension_function_test_utils.h"
14#include "chrome/browser/sessions/session_tab_helper.h"
15#include "chrome/browser/ui/tabs/tab_strip_model.h"
16#include "chrome/common/chrome_paths.h"
17#include "chrome/common/chrome_switches.h"
18#include "chrome/test/base/ui_test_utils.h"
19#include "extensions/browser/extension_function.h"
20#include "extensions/common/extension.h"
21#include "extensions/common/extension_builder.h"
22#include "extensions/common/manifest_constants.h"
23#include "extensions/common/switches.h"
24#include "extensions/common/value_builder.h"
25
26namespace extensions {
27
28class DebuggerApiTest : public ExtensionApiTest {
29 protected:
30  virtual ~DebuggerApiTest() {}
31
32  virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE;
33  virtual void SetUpOnMainThread() OVERRIDE;
34
35  // Run the attach function. If |expected_error| is not empty, then the
36  // function should fail with the error. Otherwise, the function is expected
37  // to succeed.
38  testing::AssertionResult RunAttachFunction(const GURL& url,
39                                             const std::string& expected_error);
40
41  const Extension* extension() const { return extension_.get(); }
42  base::CommandLine* command_line() const { return command_line_; }
43
44 private:
45  // The command-line for the test process, preserved in order to modify
46  // mid-test.
47  base::CommandLine* command_line_;
48
49  // A basic extension with the debugger permission.
50  scoped_refptr<const Extension> extension_;
51};
52
53void DebuggerApiTest::SetUpCommandLine(base::CommandLine* command_line) {
54  ExtensionApiTest::SetUpCommandLine(command_line);
55  // We need to hold onto |command_line| in order to modify it during the test.
56  command_line_ = command_line;
57}
58
59void DebuggerApiTest::SetUpOnMainThread() {
60  ExtensionApiTest::SetUpOnMainThread();
61  extension_ =
62      ExtensionBuilder().SetManifest(
63          DictionaryBuilder().Set("name", "debugger")
64                             .Set("version", "0.1")
65                             .Set("manifest_version", 2)
66                             .Set("permissions",
67                                  ListBuilder().Append("debugger"))).Build();
68}
69
70testing::AssertionResult DebuggerApiTest::RunAttachFunction(
71    const GURL& url, const std::string& expected_error) {
72  ui_test_utils::NavigateToURL(browser(), url);
73  content::WebContents* web_contents =
74      browser()->tab_strip_model()->GetActiveWebContents();
75  int tab_id = SessionTabHelper::IdForTab(web_contents);
76  scoped_refptr<DebuggerAttachFunction> attach_function =
77      new DebuggerAttachFunction();
78  attach_function->set_extension(extension_.get());
79  std::string args = base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id);
80
81  if (!expected_error.empty()) {
82    std::string actual_error =
83        extension_function_test_utils::RunFunctionAndReturnError(
84            attach_function.get(), args, browser());
85    if (actual_error != expected_error) {
86      return testing::AssertionFailure() << "Did not get correct error: "
87          << "expected: " << expected_error << ", found: " << actual_error;
88    }
89  } else {
90    if (!RunFunction(attach_function.get(),
91                     args,
92                     browser(),
93                     extension_function_test_utils::NONE)) {
94      return testing::AssertionFailure() << "Could not run function: "
95          << attach_function->GetError();
96    }
97
98    // Clean up and detach.
99    scoped_refptr<DebuggerDetachFunction> detach_function =
100        new DebuggerDetachFunction();
101    detach_function->set_extension(extension_.get());
102    if (!RunFunction(detach_function.get(),
103                     base::StringPrintf("[{\"tabId\": %d}]", tab_id),
104                     browser(),
105                     extension_function_test_utils::NONE)) {
106      return testing::AssertionFailure() << "Could not detach: "
107          << detach_function->GetError();
108    }
109  }
110  return testing::AssertionSuccess();
111}
112
113IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Debugger) {
114  ASSERT_TRUE(RunExtensionTest("debugger")) << message_;
115}
116
117IN_PROC_BROWSER_TEST_F(DebuggerApiTest,
118                       DebuggerNotAllowedOnOtherExtensionPages) {
119  // Load another arbitrary extension with an associated resource (popup.html).
120  base::FilePath path;
121  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
122  path = path.AppendASCII("extensions").AppendASCII("good_unpacked");
123  const Extension* another_extension = LoadExtension(path);
124  ASSERT_TRUE(another_extension);
125
126  GURL other_ext_url =
127      GURL(base::StringPrintf("chrome-extension://%s/popup.html",
128                              another_extension->id().c_str()));
129
130  // This extension should not be able to access another extension.
131  EXPECT_TRUE(RunAttachFunction(
132      other_ext_url, manifest_errors::kCannotAccessExtensionUrl));
133
134  // This extension *should* be able to debug itself.
135  EXPECT_TRUE(RunAttachFunction(
136                  GURL(base::StringPrintf("chrome-extension://%s/foo.html",
137                                          extension()->id().c_str())),
138                  std::string()));
139
140  // Append extensions on chrome urls switch. The extension should now be able
141  // to debug any extension.
142  command_line()->AppendSwitch(switches::kExtensionsOnChromeURLs);
143  EXPECT_TRUE(RunAttachFunction(other_ext_url, std::string()));
144}
145
146}  // namespace extensions
147