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