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.
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/infobars/infobar_service.h"
15 #include "chrome/browser/sessions/session_tab_helper.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "chrome/common/chrome_paths.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/test/base/ui_test_utils.h"
20 #include "components/infobars/core/infobar.h"
21 #include "components/infobars/core/infobar_delegate.h"
22 #include "extensions/browser/extension_function.h"
23 #include "extensions/common/extension.h"
24 #include "extensions/common/extension_builder.h"
25 #include "extensions/common/manifest_constants.h"
26 #include "extensions/common/switches.h"
27 #include "extensions/common/value_builder.h"
29 namespace extensions
{
31 class DebuggerApiTest
: public ExtensionApiTest
{
33 ~DebuggerApiTest() override
{}
35 void SetUpCommandLine(base::CommandLine
* command_line
) override
;
36 void SetUpOnMainThread() override
;
38 // Run the attach function. If |expected_error| is not empty, then the
39 // function should fail with the error. Otherwise, the function is expected
41 testing::AssertionResult
RunAttachFunction(const GURL
& url
,
42 const std::string
& expected_error
);
44 const Extension
* extension() const { return extension_
.get(); }
45 base::CommandLine
* command_line() const { return command_line_
; }
48 testing::AssertionResult
RunAttachFunctionOnTarget(
49 const std::string
& debuggee_target
, const std::string
& expected_error
);
51 // The command-line for the test process, preserved in order to modify
53 base::CommandLine
* command_line_
;
55 // A basic extension with the debugger permission.
56 scoped_refptr
<const Extension
> extension_
;
59 void DebuggerApiTest::SetUpCommandLine(base::CommandLine
* command_line
) {
60 ExtensionApiTest::SetUpCommandLine(command_line
);
61 // We need to hold onto |command_line| in order to modify it during the test.
62 command_line_
= command_line
;
65 void DebuggerApiTest::SetUpOnMainThread() {
66 ExtensionApiTest::SetUpOnMainThread();
68 ExtensionBuilder().SetManifest(
69 DictionaryBuilder().Set("name", "debugger")
70 .Set("version", "0.1")
71 .Set("manifest_version", 2)
73 ListBuilder().Append("debugger"))).Build();
76 testing::AssertionResult
DebuggerApiTest::RunAttachFunction(
77 const GURL
& url
, const std::string
& expected_error
) {
78 ui_test_utils::NavigateToURL(browser(), url
);
79 content::WebContents
* web_contents
=
80 browser()->tab_strip_model()->GetActiveWebContents();
83 int tab_id
= SessionTabHelper::IdForTab(web_contents
);
84 std::string debugee_by_tab
= base::StringPrintf("{\"tabId\": %d}", tab_id
);
85 testing::AssertionResult result
=
86 RunAttachFunctionOnTarget(debugee_by_tab
, expected_error
);
90 // Attach by targetId.
91 scoped_refptr
<DebuggerGetTargetsFunction
> get_targets_function
=
92 new DebuggerGetTargetsFunction();
93 scoped_ptr
<base::Value
> value(
94 extension_function_test_utils::RunFunctionAndReturnSingleResult(
95 get_targets_function
.get(), "[]", browser()));
96 base::ListValue
* targets
= nullptr;
97 EXPECT_TRUE(value
->GetAsList(&targets
));
99 std::string debugger_target_id
;
100 for (size_t i
= 0; i
< targets
->GetSize(); ++i
) {
101 base::DictionaryValue
* target_dict
= nullptr;
102 EXPECT_TRUE(targets
->GetDictionary(i
, &target_dict
));
104 if (target_dict
->GetInteger("tabId", &id
) && id
== tab_id
) {
105 EXPECT_TRUE(target_dict
->GetString("id", &debugger_target_id
));
109 EXPECT_TRUE(!debugger_target_id
.empty());
111 std::string debugee_by_target_id
=
112 base::StringPrintf("{\"targetId\": \"%s\"}", debugger_target_id
.c_str());
113 return RunAttachFunctionOnTarget(debugee_by_target_id
, expected_error
);
116 testing::AssertionResult
DebuggerApiTest::RunAttachFunctionOnTarget(
117 const std::string
& debuggee_target
, const std::string
& expected_error
) {
118 scoped_refptr
<DebuggerAttachFunction
> attach_function
=
119 new DebuggerAttachFunction();
120 attach_function
->set_extension(extension_
.get());
122 std::string actual_error
;
123 if (!RunFunction(attach_function
.get(),
124 base::StringPrintf("[%s, \"1.1\"]", debuggee_target
.c_str()),
126 extension_function_test_utils::NONE
)) {
127 actual_error
= attach_function
->GetError();
129 // Clean up and detach.
130 scoped_refptr
<DebuggerDetachFunction
> detach_function
=
131 new DebuggerDetachFunction();
132 detach_function
->set_extension(extension_
.get());
133 if (!RunFunction(detach_function
.get(),
134 base::StringPrintf("[%s]", debuggee_target
.c_str()),
136 extension_function_test_utils::NONE
)) {
137 return testing::AssertionFailure() << "Could not detach from "
138 << debuggee_target
<< " : " << detach_function
->GetError();
142 if (expected_error
.empty() && !actual_error
.empty()) {
143 return testing::AssertionFailure() << "Could not attach to "
144 << debuggee_target
<< " : " << actual_error
;
145 } else if (actual_error
!= expected_error
) {
146 return testing::AssertionFailure() << "Did not get correct error upon "
147 << "attach to " << debuggee_target
<< " : "
148 << "expected: " << expected_error
<< ", found: " << actual_error
;
150 return testing::AssertionSuccess();
153 IN_PROC_BROWSER_TEST_F(ExtensionApiTest
, Debugger
) {
154 ASSERT_TRUE(RunExtensionTest("debugger")) << message_
;
157 IN_PROC_BROWSER_TEST_F(DebuggerApiTest
,
158 DebuggerNotAllowedOnOtherExtensionPages
) {
159 // Load another arbitrary extension with an associated resource (popup.html).
161 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA
, &path
));
162 path
= path
.AppendASCII("extensions").AppendASCII("good_unpacked");
163 const Extension
* another_extension
= LoadExtension(path
);
164 ASSERT_TRUE(another_extension
);
167 GURL(base::StringPrintf("chrome-extension://%s/popup.html",
168 another_extension
->id().c_str()));
170 // This extension should not be able to access another extension.
171 EXPECT_TRUE(RunAttachFunction(
172 other_ext_url
, manifest_errors::kCannotAccessExtensionUrl
));
174 // This extension *should* be able to debug itself.
175 EXPECT_TRUE(RunAttachFunction(
176 GURL(base::StringPrintf("chrome-extension://%s/foo.html",
177 extension()->id().c_str())),
180 // Append extensions on chrome urls switch. The extension should now be able
181 // to debug any extension.
182 command_line()->AppendSwitch(switches::kExtensionsOnChromeURLs
);
183 EXPECT_TRUE(RunAttachFunction(other_ext_url
, std::string()));
186 IN_PROC_BROWSER_TEST_F(DebuggerApiTest
, InfoBar
) {
187 int tab_id
= SessionTabHelper::IdForTab(
188 browser()->tab_strip_model()->GetActiveWebContents());
189 scoped_refptr
<DebuggerAttachFunction
> attach_function
;
190 scoped_refptr
<DebuggerDetachFunction
> detach_function
;
192 Browser
* another_browser
=
193 new Browser(Browser::CreateParams(profile(), chrome::GetActiveDesktop()));
194 AddBlankTabAndShow(another_browser
);
195 AddBlankTabAndShow(another_browser
);
197 InfoBarService
* service1
= InfoBarService::FromWebContents(
198 browser()->tab_strip_model()->GetActiveWebContents());
199 InfoBarService
* service2
= InfoBarService::FromWebContents(
200 another_browser
->tab_strip_model()->GetWebContentsAt(0));
201 InfoBarService
* service3
= InfoBarService::FromWebContents(
202 another_browser
->tab_strip_model()->GetWebContentsAt(1));
204 // Attach should create infobars in both browsers.
205 attach_function
= new DebuggerAttachFunction();
206 attach_function
->set_extension(extension());
208 RunFunction(attach_function
.get(),
209 base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id
),
210 browser(), extension_function_test_utils::NONE
));
211 EXPECT_EQ(1u, service1
->infobar_count());
212 EXPECT_EQ(1u, service2
->infobar_count());
213 EXPECT_EQ(1u, service3
->infobar_count());
215 // Detach should remove all infobars.
216 detach_function
= new DebuggerDetachFunction();
217 detach_function
->set_extension(extension());
218 ASSERT_TRUE(RunFunction(detach_function
.get(),
219 base::StringPrintf("[{\"tabId\": %d}]", tab_id
),
220 browser(), extension_function_test_utils::NONE
));
221 EXPECT_EQ(0u, service1
->infobar_count());
222 EXPECT_EQ(0u, service2
->infobar_count());
223 EXPECT_EQ(0u, service3
->infobar_count());
226 attach_function
= new DebuggerAttachFunction();
227 attach_function
->set_extension(extension());
229 RunFunction(attach_function
.get(),
230 base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id
),
231 browser(), extension_function_test_utils::NONE
));
232 EXPECT_EQ(1u, service1
->infobar_count());
233 EXPECT_EQ(1u, service2
->infobar_count());
234 EXPECT_EQ(1u, service3
->infobar_count());
236 // Closing infobar should cause detach and remove all infobars.
237 service2
->infobar_at(0)->delegate()->InfoBarDismissed();
238 EXPECT_EQ(0u, service1
->infobar_count());
239 EXPECT_EQ(0u, service2
->infobar_count());
240 EXPECT_EQ(0u, service3
->infobar_count());
241 detach_function
= new DebuggerDetachFunction();
242 detach_function
->set_extension(extension());
243 // Cannot detach again.
244 ASSERT_FALSE(RunFunction(detach_function
.get(),
245 base::StringPrintf("[{\"tabId\": %d}]", tab_id
),
246 browser(), extension_function_test_utils::NONE
));
249 attach_function
= new DebuggerAttachFunction();
250 attach_function
->set_extension(extension());
252 RunFunction(attach_function
.get(),
253 base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id
),
254 browser(), extension_function_test_utils::NONE
));
255 EXPECT_EQ(1u, service1
->infobar_count());
256 EXPECT_EQ(1u, service2
->infobar_count());
257 EXPECT_EQ(1u, service3
->infobar_count());
259 // Closing tab should not affect anything.
260 ASSERT_TRUE(another_browser
->tab_strip_model()->CloseWebContentsAt(1, 0));
262 EXPECT_EQ(1u, service1
->infobar_count());
263 EXPECT_EQ(1u, service2
->infobar_count());
265 // Closing browser should not affect anything.
266 CloseBrowserSynchronously(another_browser
);
268 another_browser
= nullptr;
269 EXPECT_EQ(1u, service1
->infobar_count());
271 // Detach should remove the remaining infobar.
272 detach_function
= new DebuggerDetachFunction();
273 detach_function
->set_extension(extension());
274 ASSERT_TRUE(RunFunction(detach_function
.get(),
275 base::StringPrintf("[{\"tabId\": %d}]", tab_id
),
276 browser(), extension_function_test_utils::NONE
));
277 EXPECT_EQ(0u, service1
->infobar_count());
280 } // namespace extensions