Home | History | Annotate | Download | only in extensions
      1 // Copyright 2014 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/scoped_observer.h"
      6 #include "chrome/browser/extensions/extension_browsertest.h"
      7 #include "content/public/test/test_utils.h"
      8 #include "extensions/browser/content_verify_job.h"
      9 #include "extensions/browser/extension_prefs.h"
     10 #include "extensions/browser/extension_registry.h"
     11 #include "extensions/browser/extension_registry_observer.h"
     12 #include "extensions/common/switches.h"
     13 
     14 namespace extensions {
     15 
     16 namespace {
     17 
     18 // Helper for observing extension unloads.
     19 class UnloadObserver : public ExtensionRegistryObserver {
     20  public:
     21   explicit UnloadObserver(ExtensionRegistry* registry) : observer_(this) {
     22     observer_.Add(registry);
     23   }
     24   virtual ~UnloadObserver() {}
     25 
     26   void WaitForUnload(const ExtensionId& id) {
     27     if (ContainsKey(observed_, id))
     28       return;
     29 
     30     ASSERT_TRUE(loop_runner_.get() == NULL);
     31     awaited_id_ = id;
     32     loop_runner_ = new content::MessageLoopRunner();
     33     loop_runner_->Run();
     34   }
     35 
     36   virtual void OnExtensionUnloaded(
     37       content::BrowserContext* browser_context,
     38       const Extension* extension,
     39       UnloadedExtensionInfo::Reason reason) OVERRIDE {
     40     observed_.insert(extension->id());
     41     if (awaited_id_ == extension->id())
     42       loop_runner_->Quit();
     43   }
     44 
     45  private:
     46   ExtensionId awaited_id_;
     47   std::set<ExtensionId> observed_;
     48   scoped_refptr<content::MessageLoopRunner> loop_runner_;
     49   ScopedObserver<ExtensionRegistry, UnloadObserver> observer_;
     50 };
     51 
     52 // Helper for forcing ContentVerifyJob's to return an error.
     53 class JobDelegate : public ContentVerifyJob::TestDelegate {
     54  public:
     55   JobDelegate() : fail_next_read_(false), fail_next_done_(false) {}
     56 
     57   virtual ~JobDelegate() {}
     58 
     59   void set_id(const ExtensionId& id) { id_ = id; }
     60   void fail_next_read() { fail_next_read_ = true; }
     61   void fail_next_done() { fail_next_done_ = true; }
     62 
     63   virtual ContentVerifyJob::FailureReason BytesRead(const ExtensionId& id,
     64                                                     int count,
     65                                                     const char* data) OVERRIDE {
     66     if (id == id_ && fail_next_read_) {
     67       fail_next_read_ = false;
     68       return ContentVerifyJob::HASH_MISMATCH;
     69     }
     70     return ContentVerifyJob::NONE;
     71   }
     72 
     73   virtual ContentVerifyJob::FailureReason DoneReading(
     74       const ExtensionId& id) OVERRIDE {
     75     if (id == id_ && fail_next_done_) {
     76       fail_next_done_ = false;
     77       return ContentVerifyJob::HASH_MISMATCH;
     78     }
     79     return ContentVerifyJob::NONE;
     80   }
     81 
     82  private:
     83   DISALLOW_COPY_AND_ASSIGN(JobDelegate);
     84 
     85   ExtensionId id_;
     86   bool fail_next_read_;
     87   bool fail_next_done_;
     88 };
     89 
     90 }  // namespace
     91 
     92 class ContentVerifierTest : public ExtensionBrowserTest {
     93  public:
     94   ContentVerifierTest() {}
     95   virtual ~ContentVerifierTest() {}
     96 
     97   virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE {
     98     ExtensionBrowserTest::SetUpCommandLine(command_line);
     99     command_line->AppendSwitchASCII(
    100         switches::kExtensionContentVerification,
    101         switches::kExtensionContentVerificationEnforce);
    102   }
    103 
    104   // Setup our unload observer and JobDelegate, and install a test extension.
    105   virtual void SetUpOnMainThread() OVERRIDE {
    106     ExtensionBrowserTest::SetUpOnMainThread();
    107     unload_observer_.reset(
    108         new UnloadObserver(ExtensionRegistry::Get(profile())));
    109     const Extension* extension = InstallExtensionFromWebstore(
    110         test_data_dir_.AppendASCII("content_verifier/v1.crx"), 1);
    111     ASSERT_TRUE(extension);
    112     id_ = extension->id();
    113     page_url_ = extension->GetResourceURL("page.html");
    114     delegate_.set_id(id_);
    115     ContentVerifyJob::SetDelegateForTests(&delegate_);
    116   }
    117 
    118   virtual void TearDownOnMainThread() OVERRIDE {
    119     ContentVerifyJob::SetDelegateForTests(NULL);
    120     ExtensionBrowserTest::TearDownOnMainThread();
    121   }
    122 
    123   virtual void OpenPageAndWaitForUnload() {
    124     AddTabAtIndex(1, page_url_, content::PAGE_TRANSITION_LINK);
    125     unload_observer_->WaitForUnload(id_);
    126     ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
    127     int reasons = prefs->GetDisableReasons(id_);
    128     EXPECT_TRUE(reasons & Extension::DISABLE_CORRUPTED);
    129 
    130     // This needs to happen before the ExtensionRegistry gets deleted, which
    131     // happens before TearDownOnMainThread is called.
    132     unload_observer_.reset();
    133   }
    134 
    135  protected:
    136   JobDelegate delegate_;
    137   scoped_ptr<UnloadObserver> unload_observer_;
    138   ExtensionId id_;
    139   GURL page_url_;
    140 };
    141 
    142 IN_PROC_BROWSER_TEST_F(ContentVerifierTest, FailOnRead) {
    143   delegate_.fail_next_read();
    144   OpenPageAndWaitForUnload();
    145 }
    146 
    147 IN_PROC_BROWSER_TEST_F(ContentVerifierTest, FailOnDone) {
    148   delegate_.fail_next_done();
    149   OpenPageAndWaitForUnload();
    150 }
    151 
    152 }  // namespace extensions
    153