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.
5 #include "base/command_line.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/run_loop.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
11 #include "chrome/browser/extensions/activity_log/activity_log.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/test_extension_system.h"
14 #include "chrome/browser/prerender/prerender_handle.h"
15 #include "chrome/browser/prerender/prerender_manager.h"
16 #include "chrome/browser/prerender/prerender_manager_factory.h"
17 #include "chrome/common/chrome_constants.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
20 #include "chrome/test/base/testing_profile.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/test/test_browser_thread_bundle.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/uninstall_reason.h"
25 #include "extensions/common/dom_action_types.h"
26 #include "extensions/common/extension_builder.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 #if defined(OS_CHROMEOS)
30 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
31 #include "chrome/browser/chromeos/settings/cros_settings.h"
32 #include "chrome/browser/chromeos/settings/device_settings_service.h"
37 const char kExtensionId
[] = "abc";
39 const char* const kUrlApiCalls
[] = {
40 "HTMLButtonElement.formAction", "HTMLEmbedElement.src",
41 "HTMLFormElement.action", "HTMLFrameElement.src",
42 "HTMLHtmlElement.manifest", "HTMLIFrameElement.src",
43 "HTMLImageElement.longDesc", "HTMLImageElement.src",
44 "HTMLImageElement.lowsrc", "HTMLInputElement.formAction",
45 "HTMLInputElement.src", "HTMLLinkElement.href",
46 "HTMLMediaElement.src", "HTMLMediaElement.currentSrc",
47 "HTMLModElement.cite", "HTMLObjectElement.data",
48 "HTMLQuoteElement.cite", "HTMLScriptElement.src",
49 "HTMLSourceElement.src", "HTMLTrackElement.src",
50 "HTMLVideoElement.poster"};
54 namespace extensions
{
56 class ActivityLogTest
: public ChromeRenderViewHostTestHarness
{
58 void SetUp() override
{
59 ChromeRenderViewHostTestHarness::SetUp();
60 #if defined OS_CHROMEOS
61 test_user_manager_
.reset(new chromeos::ScopedTestUserManager());
63 base::CommandLine
command_line(base::CommandLine::NO_PROGRAM
);
64 base::CommandLine::ForCurrentProcess()->AppendSwitch(
65 switches::kEnableExtensionActivityLogging
);
66 base::CommandLine::ForCurrentProcess()->AppendSwitch(
67 switches::kEnableExtensionActivityLogTesting
);
68 extension_service_
= static_cast<TestExtensionSystem
*>(
69 ExtensionSystem::Get(profile()))->CreateExtensionService
70 (&command_line
, base::FilePath(), false);
71 base::RunLoop().RunUntilIdle();
74 void TearDown() override
{
75 #if defined OS_CHROMEOS
76 test_user_manager_
.reset();
78 base::RunLoop().RunUntilIdle();
79 ChromeRenderViewHostTestHarness::TearDown();
82 static void RetrieveActions_LogAndFetchActions0(
83 scoped_ptr
<std::vector
<scoped_refptr
<Action
> > > i
) {
84 ASSERT_EQ(0, static_cast<int>(i
->size()));
87 static void RetrieveActions_LogAndFetchActions2(
88 scoped_ptr
<std::vector
<scoped_refptr
<Action
> > > i
) {
89 ASSERT_EQ(2, static_cast<int>(i
->size()));
92 void SetPolicy(bool log_arguments
) {
93 ActivityLog
* activity_log
= ActivityLog::GetInstance(profile());
95 activity_log
->SetDatabasePolicy(ActivityLogPolicy::POLICY_FULLSTREAM
);
97 activity_log
->SetDatabasePolicy(ActivityLogPolicy::POLICY_COUNTS
);
100 bool GetDatabaseEnabled() {
101 ActivityLog
* activity_log
= ActivityLog::GetInstance(profile());
102 return activity_log
->IsDatabaseEnabled();
105 bool GetWatchdogActive() {
106 ActivityLog
* activity_log
= ActivityLog::GetInstance(profile());
107 return activity_log
->IsWatchdogAppActive();
110 static void Arguments_Prerender(
111 scoped_ptr
<std::vector
<scoped_refptr
<Action
> > > i
) {
112 ASSERT_EQ(1U, i
->size());
113 scoped_refptr
<Action
> last
= i
->front();
115 ASSERT_EQ("odlameecjipmbmbejkplpemijjgpljce", last
->extension_id());
116 ASSERT_EQ(Action::ACTION_CONTENT_SCRIPT
, last
->action_type());
117 ASSERT_EQ("[\"script\"]",
118 ActivityLogPolicy::Util::Serialize(last
->args()));
119 ASSERT_EQ("http://www.google.com/", last
->SerializePageUrl());
120 ASSERT_EQ("{\"prerender\":true}",
121 ActivityLogPolicy::Util::Serialize(last
->other()));
122 ASSERT_EQ("", last
->api_name());
123 ASSERT_EQ("", last
->page_title());
124 ASSERT_EQ("", last
->SerializeArgUrl());
127 static void RetrieveActions_ArgUrlExtraction(
128 scoped_ptr
<std::vector
<scoped_refptr
<Action
> > > i
) {
129 const base::DictionaryValue
* other
= NULL
;
132 ASSERT_EQ(4U, i
->size());
133 scoped_refptr
<Action
> action
= i
->at(0);
134 ASSERT_EQ("XMLHttpRequest.open", action
->api_name());
135 ASSERT_EQ("[\"POST\",\"\\u003Carg_url>\"]",
136 ActivityLogPolicy::Util::Serialize(action
->args()));
137 ASSERT_EQ("http://api.google.com/", action
->arg_url().spec());
138 // Test that the dom_verb field was changed to XHR (from METHOD). This
139 // could be tested on all retrieved XHR actions but it would be redundant,
140 // so just test once.
141 other
= action
->other();
143 ASSERT_TRUE(other
->GetInteger(activity_log_constants::kActionDomVerb
,
145 ASSERT_EQ(DomActionType::XHR
, dom_verb
);
148 ASSERT_EQ("XMLHttpRequest.open", action
->api_name());
149 ASSERT_EQ("[\"POST\",\"\\u003Carg_url>\"]",
150 ActivityLogPolicy::Util::Serialize(action
->args()));
151 ASSERT_EQ("http://www.google.com/api/", action
->arg_url().spec());
154 ASSERT_EQ("XMLHttpRequest.open", action
->api_name());
155 ASSERT_EQ("[\"POST\",\"/api/\"]",
156 ActivityLogPolicy::Util::Serialize(action
->args()));
157 ASSERT_FALSE(action
->arg_url().is_valid());
160 ASSERT_EQ("windows.create", action
->api_name());
161 ASSERT_EQ("[{\"url\":\"\\u003Carg_url>\"}]",
162 ActivityLogPolicy::Util::Serialize(action
->args()));
163 ASSERT_EQ("http://www.google.co.uk/", action
->arg_url().spec());
166 static void RetrieveActions_ArgUrlApiCalls(
167 scoped_ptr
<std::vector
<scoped_refptr
<Action
> > > actions
) {
168 size_t api_calls_size
= arraysize(kUrlApiCalls
);
169 const base::DictionaryValue
* other
= NULL
;
172 ASSERT_EQ(api_calls_size
, actions
->size());
174 for (size_t i
= 0; i
< actions
->size(); i
++) {
175 scoped_refptr
<Action
> action
= actions
->at(i
);
176 ASSERT_EQ(kExtensionId
, action
->extension_id());
177 ASSERT_EQ(Action::ACTION_DOM_ACCESS
, action
->action_type());
178 ASSERT_EQ(kUrlApiCalls
[i
], action
->api_name());
179 ASSERT_EQ("[\"\\u003Carg_url>\"]",
180 ActivityLogPolicy::Util::Serialize(action
->args()));
181 ASSERT_EQ("http://www.google.co.uk/", action
->arg_url().spec());
182 other
= action
->other();
185 other
->GetInteger(activity_log_constants::kActionDomVerb
, &dom_verb
));
186 ASSERT_EQ(DomActionType::SETTER
, dom_verb
);
190 ExtensionService
* extension_service_
;
192 #if defined OS_CHROMEOS
193 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_
;
194 chromeos::ScopedTestCrosSettings test_cros_settings_
;
195 scoped_ptr
<chromeos::ScopedTestUserManager
> test_user_manager_
;
199 TEST_F(ActivityLogTest
, Construct
) {
200 ASSERT_TRUE(GetDatabaseEnabled());
201 ASSERT_FALSE(GetWatchdogActive());
204 TEST_F(ActivityLogTest
, LogAndFetchActions
) {
205 ActivityLog
* activity_log
= ActivityLog::GetInstance(profile());
206 scoped_ptr
<base::ListValue
> args(new base::ListValue());
207 ASSERT_TRUE(GetDatabaseEnabled());
209 // Write some API calls
210 scoped_refptr
<Action
> action
= new Action(kExtensionId
,
212 Action::ACTION_API_CALL
,
214 activity_log
->LogAction(action
);
215 action
= new Action(kExtensionId
,
217 Action::ACTION_DOM_ACCESS
,
219 action
->set_page_url(GURL("http://www.google.com"));
220 activity_log
->LogAction(action
);
222 activity_log
->GetFilteredActions(
229 base::Bind(ActivityLogTest::RetrieveActions_LogAndFetchActions2
));
232 TEST_F(ActivityLogTest
, LogPrerender
) {
233 scoped_refptr
<const Extension
> extension
=
235 .SetManifest(DictionaryBuilder()
236 .Set("name", "Test extension")
237 .Set("version", "1.0.0")
238 .Set("manifest_version", 2))
240 extension_service_
->AddExtension(extension
.get());
241 ActivityLog
* activity_log
= ActivityLog::GetInstance(profile());
242 ASSERT_TRUE(GetDatabaseEnabled());
243 GURL
url("http://www.google.com");
245 prerender::PrerenderManager
* prerender_manager
=
246 prerender::PrerenderManagerFactory::GetForProfile(
247 Profile::FromBrowserContext(profile()));
249 const gfx::Size
kSize(640, 480);
250 scoped_ptr
<prerender::PrerenderHandle
> prerender_handle(
251 prerender_manager
->AddPrerenderFromOmnibox(
253 web_contents()->GetController().GetDefaultSessionStorageNamespace(),
256 const std::vector
<content::WebContents
*> contentses
=
257 prerender_manager
->GetAllPrerenderingContents();
258 ASSERT_EQ(1U, contentses
.size());
259 content::WebContents
*contents
= contentses
[0];
260 ASSERT_TRUE(prerender_manager
->IsWebContentsPrerendering(contents
, NULL
));
262 ScriptExecutionObserver::ExecutingScriptsMap executing_scripts
;
263 executing_scripts
[extension
->id()].insert("script");
265 static_cast<ScriptExecutionObserver
*>(activity_log
)
266 ->OnScriptsExecuted(contents
, executing_scripts
, url
);
268 activity_log
->GetFilteredActions(
275 base::Bind(ActivityLogTest::Arguments_Prerender
));
277 prerender_manager
->CancelAllPrerenders();
280 TEST_F(ActivityLogTest
, ArgUrlExtraction
) {
281 ActivityLog
* activity_log
= ActivityLog::GetInstance(profile());
282 scoped_ptr
<base::ListValue
> args(new base::ListValue());
284 base::Time now
= base::Time::Now();
286 // Submit a DOM API call which should have its URL extracted into the arg_url
288 scoped_refptr
<Action
> action
= new Action(kExtensionId
,
290 Action::ACTION_DOM_ACCESS
,
291 "XMLHttpRequest.open");
292 action
->set_page_url(GURL("http://www.google.com/"));
293 action
->mutable_args()->AppendString("POST");
294 action
->mutable_args()->AppendString("http://api.google.com/");
295 action
->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb
,
296 DomActionType::METHOD
);
297 activity_log
->LogAction(action
);
299 // Submit a DOM API call with a relative URL in the argument, which should be
300 // resolved relative to the page URL.
301 action
= new Action(kExtensionId
,
302 now
- base::TimeDelta::FromSeconds(1),
303 Action::ACTION_DOM_ACCESS
,
304 "XMLHttpRequest.open");
305 action
->set_page_url(GURL("http://www.google.com/"));
306 action
->mutable_args()->AppendString("POST");
307 action
->mutable_args()->AppendString("/api/");
308 action
->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb
,
309 DomActionType::METHOD
);
310 activity_log
->LogAction(action
);
312 // Submit a DOM API call with a relative URL but no base page URL against
314 action
= new Action(kExtensionId
,
315 now
- base::TimeDelta::FromSeconds(2),
316 Action::ACTION_DOM_ACCESS
,
317 "XMLHttpRequest.open");
318 action
->mutable_args()->AppendString("POST");
319 action
->mutable_args()->AppendString("/api/");
320 action
->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb
,
321 DomActionType::METHOD
);
322 activity_log
->LogAction(action
);
324 // Submit an API call with an embedded URL.
325 action
= new Action(kExtensionId
,
326 now
- base::TimeDelta::FromSeconds(3),
327 Action::ACTION_API_CALL
,
331 .Append(DictionaryBuilder().Set("url", "http://www.google.co.uk"))
333 activity_log
->LogAction(action
);
335 activity_log
->GetFilteredActions(
342 base::Bind(ActivityLogTest::RetrieveActions_ArgUrlExtraction
));
345 TEST_F(ActivityLogTest
, UninstalledExtension
) {
346 scoped_refptr
<const Extension
> extension
=
348 .SetManifest(DictionaryBuilder()
349 .Set("name", "Test extension")
350 .Set("version", "1.0.0")
351 .Set("manifest_version", 2))
354 ActivityLog
* activity_log
= ActivityLog::GetInstance(profile());
355 scoped_ptr
<base::ListValue
> args(new base::ListValue());
356 ASSERT_TRUE(GetDatabaseEnabled());
358 // Write some API calls
359 scoped_refptr
<Action
> action
= new Action(extension
->id(),
361 Action::ACTION_API_CALL
,
363 activity_log
->LogAction(action
);
364 action
= new Action(extension
->id(),
366 Action::ACTION_DOM_ACCESS
,
368 action
->set_page_url(GURL("http://www.google.com"));
370 activity_log
->OnExtensionUninstalled(
371 NULL
, extension
.get(), extensions::UNINSTALL_REASON_FOR_TESTING
);
372 activity_log
->GetFilteredActions(
379 base::Bind(ActivityLogTest::RetrieveActions_LogAndFetchActions0
));
382 TEST_F(ActivityLogTest
, ArgUrlApiCalls
) {
383 ActivityLog
* activity_log
= ActivityLog::GetInstance(profile());
384 scoped_ptr
<base::ListValue
> args(new base::ListValue());
385 base::Time now
= base::Time::Now();
386 int api_calls_size
= arraysize(kUrlApiCalls
);
387 scoped_refptr
<Action
> action
;
389 for (int i
= 0; i
< api_calls_size
; i
++) {
390 action
= new Action(kExtensionId
,
391 now
- base::TimeDelta::FromSeconds(i
),
392 Action::ACTION_DOM_ACCESS
,
394 action
->mutable_args()->AppendString("http://www.google.co.uk");
395 action
->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb
,
396 DomActionType::SETTER
);
397 activity_log
->LogAction(action
);
400 activity_log
->GetFilteredActions(
407 base::Bind(ActivityLogTest::RetrieveActions_ArgUrlApiCalls
));
410 } // namespace extensions