Home | History | Annotate | Download | only in activity_log
      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 "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/test/test_browser_thread_bundle.h"
     22 #include "extensions/browser/extension_registry.h"
     23 #include "extensions/common/dom_action_types.h"
     24 #include "extensions/common/extension_builder.h"
     25 #include "sql/statement.h"
     26 #include "testing/gtest/include/gtest/gtest.h"
     27 
     28 #if defined(OS_CHROMEOS)
     29 #include "chrome/browser/chromeos/login/users/user_manager.h"
     30 #include "chrome/browser/chromeos/settings/cros_settings.h"
     31 #include "chrome/browser/chromeos/settings/device_settings_service.h"
     32 #endif
     33 
     34 namespace {
     35 
     36 const char kExtensionId[] = "abc";
     37 
     38 const char* kUrlApiCalls[] = {
     39     "HTMLButtonElement.formAction", "HTMLEmbedElement.src",
     40     "HTMLFormElement.action",       "HTMLFrameElement.src",
     41     "HTMLHtmlElement.manifest",     "HTMLIFrameElement.src",
     42     "HTMLImageElement.longDesc",    "HTMLImageElement.src",
     43     "HTMLImageElement.lowsrc",      "HTMLInputElement.formAction",
     44     "HTMLInputElement.src",         "HTMLLinkElement.href",
     45     "HTMLMediaElement.src",         "HTMLMediaElement.currentSrc",
     46     "HTMLModElement.cite",          "HTMLObjectElement.data",
     47     "HTMLQuoteElement.cite",        "HTMLScriptElement.src",
     48     "HTMLSourceElement.src",        "HTMLTrackElement.src",
     49     "HTMLVideoElement.poster"};
     50 
     51 }  // namespace
     52 
     53 namespace extensions {
     54 
     55 class ActivityLogTest : public ChromeRenderViewHostTestHarness {
     56  protected:
     57   virtual void SetUp() OVERRIDE {
     58     ChromeRenderViewHostTestHarness::SetUp();
     59 #if defined OS_CHROMEOS
     60     test_user_manager_.reset(new chromeos::ScopedTestUserManager());
     61 #endif
     62     CommandLine command_line(CommandLine::NO_PROGRAM);
     63     CommandLine::ForCurrentProcess()->AppendSwitch(
     64         switches::kEnableExtensionActivityLogging);
     65     CommandLine::ForCurrentProcess()->AppendSwitch(
     66         switches::kEnableExtensionActivityLogTesting);
     67     extension_service_ = static_cast<TestExtensionSystem*>(
     68         ExtensionSystem::Get(profile()))->CreateExtensionService
     69             (&command_line, base::FilePath(), false);
     70     base::RunLoop().RunUntilIdle();
     71   }
     72 
     73   virtual void TearDown() OVERRIDE {
     74 #if defined OS_CHROMEOS
     75     test_user_manager_.reset();
     76 #endif
     77     base::RunLoop().RunUntilIdle();
     78     ChromeRenderViewHostTestHarness::TearDown();
     79   }
     80 
     81   static void RetrieveActions_LogAndFetchActions0(
     82       scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
     83     ASSERT_EQ(0, static_cast<int>(i->size()));
     84   }
     85 
     86   static void RetrieveActions_LogAndFetchActions2(
     87       scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
     88     ASSERT_EQ(2, static_cast<int>(i->size()));
     89   }
     90 
     91   void SetPolicy(bool log_arguments) {
     92     ActivityLog* activity_log = ActivityLog::GetInstance(profile());
     93     if (log_arguments)
     94       activity_log->SetDatabasePolicy(ActivityLogPolicy::POLICY_FULLSTREAM);
     95     else
     96       activity_log->SetDatabasePolicy(ActivityLogPolicy::POLICY_COUNTS);
     97   }
     98 
     99   bool GetDatabaseEnabled() {
    100     ActivityLog* activity_log = ActivityLog::GetInstance(profile());
    101     return activity_log->IsDatabaseEnabled();
    102   }
    103 
    104   bool GetWatchdogActive() {
    105     ActivityLog* activity_log = ActivityLog::GetInstance(profile());
    106     return activity_log->IsWatchdogAppActive();
    107   }
    108 
    109   static void Arguments_Prerender(
    110       scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
    111     ASSERT_EQ(1U, i->size());
    112     scoped_refptr<Action> last = i->front();
    113 
    114     ASSERT_EQ("odlameecjipmbmbejkplpemijjgpljce", last->extension_id());
    115     ASSERT_EQ(Action::ACTION_CONTENT_SCRIPT, last->action_type());
    116     ASSERT_EQ("[\"script\"]",
    117               ActivityLogPolicy::Util::Serialize(last->args()));
    118     ASSERT_EQ("http://www.google.com/", last->SerializePageUrl());
    119     ASSERT_EQ("{\"prerender\":true}",
    120               ActivityLogPolicy::Util::Serialize(last->other()));
    121     ASSERT_EQ("", last->api_name());
    122     ASSERT_EQ("", last->page_title());
    123     ASSERT_EQ("", last->SerializeArgUrl());
    124   }
    125 
    126   static void RetrieveActions_ArgUrlExtraction(
    127       scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
    128     const base::DictionaryValue* other = NULL;
    129     int dom_verb = -1;
    130 
    131     ASSERT_EQ(4U, i->size());
    132     scoped_refptr<Action> action = i->at(0);
    133     ASSERT_EQ("XMLHttpRequest.open", action->api_name());
    134     ASSERT_EQ("[\"POST\",\"\\u003Carg_url>\"]",
    135               ActivityLogPolicy::Util::Serialize(action->args()));
    136     ASSERT_EQ("http://api.google.com/", action->arg_url().spec());
    137     // Test that the dom_verb field was changed to XHR (from METHOD).  This
    138     // could be tested on all retrieved XHR actions but it would be redundant,
    139     // so just test once.
    140     other = action->other();
    141     ASSERT_TRUE(other);
    142     ASSERT_TRUE(other->GetInteger(activity_log_constants::kActionDomVerb,
    143                                   &dom_verb));
    144     ASSERT_EQ(DomActionType::XHR, dom_verb);
    145 
    146     action = i->at(1);
    147     ASSERT_EQ("XMLHttpRequest.open", action->api_name());
    148     ASSERT_EQ("[\"POST\",\"\\u003Carg_url>\"]",
    149               ActivityLogPolicy::Util::Serialize(action->args()));
    150     ASSERT_EQ("http://www.google.com/api/", action->arg_url().spec());
    151 
    152     action = i->at(2);
    153     ASSERT_EQ("XMLHttpRequest.open", action->api_name());
    154     ASSERT_EQ("[\"POST\",\"/api/\"]",
    155               ActivityLogPolicy::Util::Serialize(action->args()));
    156     ASSERT_FALSE(action->arg_url().is_valid());
    157 
    158     action = i->at(3);
    159     ASSERT_EQ("windows.create", action->api_name());
    160     ASSERT_EQ("[{\"url\":\"\\u003Carg_url>\"}]",
    161               ActivityLogPolicy::Util::Serialize(action->args()));
    162     ASSERT_EQ("http://www.google.co.uk/", action->arg_url().spec());
    163   }
    164 
    165   static void RetrieveActions_ArgUrlApiCalls(
    166       scoped_ptr<std::vector<scoped_refptr<Action> > > actions) {
    167     size_t api_calls_size = arraysize(kUrlApiCalls);
    168     const base::DictionaryValue* other = NULL;
    169     int dom_verb = -1;
    170 
    171     ASSERT_EQ(api_calls_size, actions->size());
    172 
    173     for (size_t i = 0; i < actions->size(); i++) {
    174       scoped_refptr<Action> action = actions->at(i);
    175       ASSERT_EQ(kExtensionId, action->extension_id());
    176       ASSERT_EQ(Action::ACTION_DOM_ACCESS, action->action_type());
    177       ASSERT_EQ(kUrlApiCalls[i], action->api_name());
    178       ASSERT_EQ("[\"\\u003Carg_url>\"]",
    179                 ActivityLogPolicy::Util::Serialize(action->args()));
    180       ASSERT_EQ("http://www.google.co.uk/", action->arg_url().spec());
    181       other = action->other();
    182       ASSERT_TRUE(other);
    183       ASSERT_TRUE(
    184           other->GetInteger(activity_log_constants::kActionDomVerb, &dom_verb));
    185       ASSERT_EQ(DomActionType::SETTER, dom_verb);
    186     }
    187   }
    188 
    189   ExtensionService* extension_service_;
    190 
    191 #if defined OS_CHROMEOS
    192   chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
    193   chromeos::ScopedTestCrosSettings test_cros_settings_;
    194   scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
    195 #endif
    196 };
    197 
    198 TEST_F(ActivityLogTest, Construct) {
    199   ASSERT_TRUE(GetDatabaseEnabled());
    200   ASSERT_FALSE(GetWatchdogActive());
    201 }
    202 
    203 TEST_F(ActivityLogTest, LogAndFetchActions) {
    204   ActivityLog* activity_log = ActivityLog::GetInstance(profile());
    205   scoped_ptr<base::ListValue> args(new base::ListValue());
    206   ASSERT_TRUE(GetDatabaseEnabled());
    207 
    208   // Write some API calls
    209   scoped_refptr<Action> action = new Action(kExtensionId,
    210                                             base::Time::Now(),
    211                                             Action::ACTION_API_CALL,
    212                                             "tabs.testMethod");
    213   activity_log->LogAction(action);
    214   action = new Action(kExtensionId,
    215                       base::Time::Now(),
    216                       Action::ACTION_DOM_ACCESS,
    217                       "document.write");
    218   action->set_page_url(GURL("http://www.google.com"));
    219   activity_log->LogAction(action);
    220 
    221   activity_log->GetFilteredActions(
    222       kExtensionId,
    223       Action::ACTION_ANY,
    224       "",
    225       "",
    226       "",
    227       0,
    228       base::Bind(ActivityLogTest::RetrieveActions_LogAndFetchActions2));
    229 }
    230 
    231 TEST_F(ActivityLogTest, LogPrerender) {
    232   scoped_refptr<const Extension> extension =
    233       ExtensionBuilder()
    234           .SetManifest(DictionaryBuilder()
    235                        .Set("name", "Test extension")
    236                        .Set("version", "1.0.0")
    237                        .Set("manifest_version", 2))
    238           .Build();
    239   extension_service_->AddExtension(extension.get());
    240   ActivityLog* activity_log = ActivityLog::GetInstance(profile());
    241   ASSERT_TRUE(GetDatabaseEnabled());
    242   GURL url("http://www.google.com");
    243 
    244   prerender::PrerenderManager* prerender_manager =
    245       prerender::PrerenderManagerFactory::GetForProfile(
    246           Profile::FromBrowserContext(profile()));
    247 
    248   prerender_manager->OnCookieStoreLoaded();
    249 
    250   const gfx::Size kSize(640, 480);
    251   scoped_ptr<prerender::PrerenderHandle> prerender_handle(
    252       prerender_manager->AddPrerenderFromLocalPredictor(
    253           url,
    254           web_contents()->GetController().GetDefaultSessionStorageNamespace(),
    255           kSize));
    256 
    257   const std::vector<content::WebContents*> contentses =
    258       prerender_manager->GetAllPrerenderingContents();
    259   ASSERT_EQ(1U, contentses.size());
    260   content::WebContents *contents = contentses[0];
    261   ASSERT_TRUE(prerender_manager->IsWebContentsPrerendering(contents, NULL));
    262 
    263   TabHelper::ScriptExecutionObserver::ExecutingScriptsMap executing_scripts;
    264   executing_scripts[extension->id()].insert("script");
    265 
    266   static_cast<TabHelper::ScriptExecutionObserver*>(activity_log)->
    267       OnScriptsExecuted(contents, executing_scripts, 0, url);
    268 
    269   activity_log->GetFilteredActions(
    270       extension->id(),
    271       Action::ACTION_ANY,
    272       "",
    273       "",
    274       "",
    275       0,
    276       base::Bind(ActivityLogTest::Arguments_Prerender));
    277 
    278   prerender_manager->CancelAllPrerenders();
    279 }
    280 
    281 TEST_F(ActivityLogTest, ArgUrlExtraction) {
    282   ActivityLog* activity_log = ActivityLog::GetInstance(profile());
    283   scoped_ptr<base::ListValue> args(new base::ListValue());
    284 
    285   base::Time now = base::Time::Now();
    286 
    287   // Submit a DOM API call which should have its URL extracted into the arg_url
    288   // field.
    289   scoped_refptr<Action> action = new Action(kExtensionId,
    290                                             now,
    291                                             Action::ACTION_DOM_ACCESS,
    292                                             "XMLHttpRequest.open");
    293   action->set_page_url(GURL("http://www.google.com/"));
    294   action->mutable_args()->AppendString("POST");
    295   action->mutable_args()->AppendString("http://api.google.com/");
    296   action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
    297                                       DomActionType::METHOD);
    298   activity_log->LogAction(action);
    299 
    300   // Submit a DOM API call with a relative URL in the argument, which should be
    301   // resolved relative to the page URL.
    302   action = new Action(kExtensionId,
    303                       now - base::TimeDelta::FromSeconds(1),
    304                       Action::ACTION_DOM_ACCESS,
    305                       "XMLHttpRequest.open");
    306   action->set_page_url(GURL("http://www.google.com/"));
    307   action->mutable_args()->AppendString("POST");
    308   action->mutable_args()->AppendString("/api/");
    309   action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
    310                                       DomActionType::METHOD);
    311   activity_log->LogAction(action);
    312 
    313   // Submit a DOM API call with a relative URL but no base page URL against
    314   // which to resolve.
    315   action = new Action(kExtensionId,
    316                       now - base::TimeDelta::FromSeconds(2),
    317                       Action::ACTION_DOM_ACCESS,
    318                       "XMLHttpRequest.open");
    319   action->mutable_args()->AppendString("POST");
    320   action->mutable_args()->AppendString("/api/");
    321   action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
    322                                       DomActionType::METHOD);
    323   activity_log->LogAction(action);
    324 
    325   // Submit an API call with an embedded URL.
    326   action = new Action(kExtensionId,
    327                       now - base::TimeDelta::FromSeconds(3),
    328                       Action::ACTION_API_CALL,
    329                       "windows.create");
    330   action->set_args(
    331       ListBuilder()
    332           .Append(DictionaryBuilder().Set("url", "http://www.google.co.uk"))
    333           .Build());
    334   activity_log->LogAction(action);
    335 
    336   activity_log->GetFilteredActions(
    337       kExtensionId,
    338       Action::ACTION_ANY,
    339       "",
    340       "",
    341       "",
    342       -1,
    343       base::Bind(ActivityLogTest::RetrieveActions_ArgUrlExtraction));
    344 }
    345 
    346 TEST_F(ActivityLogTest, UninstalledExtension) {
    347   scoped_refptr<const Extension> extension =
    348       ExtensionBuilder()
    349           .SetManifest(DictionaryBuilder()
    350                        .Set("name", "Test extension")
    351                        .Set("version", "1.0.0")
    352                        .Set("manifest_version", 2))
    353           .Build();
    354 
    355   ActivityLog* activity_log = ActivityLog::GetInstance(profile());
    356   scoped_ptr<base::ListValue> args(new base::ListValue());
    357   ASSERT_TRUE(GetDatabaseEnabled());
    358 
    359   // Write some API calls
    360   scoped_refptr<Action> action = new Action(extension->id(),
    361                                             base::Time::Now(),
    362                                             Action::ACTION_API_CALL,
    363                                             "tabs.testMethod");
    364   activity_log->LogAction(action);
    365   action = new Action(extension->id(),
    366                       base::Time::Now(),
    367                       Action::ACTION_DOM_ACCESS,
    368                       "document.write");
    369   action->set_page_url(GURL("http://www.google.com"));
    370 
    371   activity_log->OnExtensionUninstalled(NULL, extension);
    372   activity_log->GetFilteredActions(
    373       extension->id(),
    374       Action::ACTION_ANY,
    375       "",
    376       "",
    377       "",
    378       -1,
    379       base::Bind(ActivityLogTest::RetrieveActions_LogAndFetchActions0));
    380 }
    381 
    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;
    388 
    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,
    393                         kUrlApiCalls[i]);
    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);
    398   }
    399 
    400   activity_log->GetFilteredActions(
    401       kExtensionId,
    402       Action::ACTION_ANY,
    403       "",
    404       "",
    405       "",
    406       -1,
    407       base::Bind(ActivityLogTest::RetrieveActions_ArgUrlApiCalls));
    408 }
    409 
    410 }  // namespace extensions
    411