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 14namespace extensions { 15 16namespace { 17 18// Helper for observing extension unloads. 19class 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. 53class 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 92class 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 142IN_PROC_BROWSER_TEST_F(ContentVerifierTest, FailOnRead) { 143 delegate_.fail_next_read(); 144 OpenPageAndWaitForUnload(); 145} 146 147IN_PROC_BROWSER_TEST_F(ContentVerifierTest, FailOnDone) { 148 delegate_.fail_next_done(); 149 OpenPageAndWaitForUnload(); 150} 151 152} // namespace extensions 153