1// Copyright 2013 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 "chrome/browser/extensions/error_console/error_console.h"
6
7#include "base/logging.h"
8#include "base/memory/ref_counted.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/prefs/pref_service.h"
11#include "base/strings/string_number_conversions.h"
12#include "chrome/common/extensions/features/feature_channel.h"
13#include "chrome/common/pref_names.h"
14#include "chrome/test/base/testing_profile.h"
15#include "components/crx_file/id_util.h"
16#include "extensions/browser/extension_error.h"
17#include "extensions/browser/extension_error_test_util.h"
18#include "extensions/browser/extension_registry.h"
19#include "extensions/common/constants.h"
20#include "extensions/common/extension_builder.h"
21#include "extensions/common/feature_switch.h"
22#include "extensions/common/value_builder.h"
23#include "testing/gtest/include/gtest/gtest.h"
24
25namespace extensions {
26
27using error_test_util::CreateNewManifestError;
28using error_test_util::CreateNewRuntimeError;
29
30class ErrorConsoleUnitTest : public testing::Test {
31 public:
32  ErrorConsoleUnitTest() : error_console_(NULL) { }
33  virtual ~ErrorConsoleUnitTest() { }
34
35  virtual void SetUp() OVERRIDE {
36    testing::Test::SetUp();
37
38    // Errors are only kept if we have the FeatureSwitch and have Developer Mode
39    // enabled.
40    FeatureSwitch::error_console()->SetOverrideValue(
41        FeatureSwitch::OVERRIDE_ENABLED);
42    profile_.reset(new TestingProfile);
43    profile_->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
44    error_console_ = ErrorConsole::Get(profile_.get());
45  }
46
47 protected:
48  scoped_ptr<TestingProfile> profile_;
49  ErrorConsole* error_console_;
50};
51
52// Test that the error console is enabled/disabled appropriately.
53TEST_F(ErrorConsoleUnitTest, EnableAndDisableErrorConsole) {
54  // Start in Dev Channel, without the feature switch.
55  scoped_ptr<ScopedCurrentChannel> channel_override(
56      new ScopedCurrentChannel(chrome::VersionInfo::CHANNEL_DEV));
57  ASSERT_EQ(chrome::VersionInfo::CHANNEL_DEV, GetCurrentChannel());
58  FeatureSwitch::error_console()->SetOverrideValue(
59      FeatureSwitch::OVERRIDE_DISABLED);
60
61  // At the start, the error console should be enabled, and specifically
62  // enabled for the chrome:extensions page.
63  EXPECT_TRUE(error_console_->enabled());
64  EXPECT_TRUE(error_console_->IsEnabledForChromeExtensionsPage());
65  EXPECT_FALSE(error_console_->IsEnabledForAppsDeveloperTools());
66
67  // If we disable developer mode, we should disable error console.
68  profile_->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, false);
69  EXPECT_FALSE(error_console_->enabled());
70  EXPECT_FALSE(error_console_->IsEnabledForChromeExtensionsPage());
71  EXPECT_FALSE(error_console_->IsEnabledForAppsDeveloperTools());
72
73  // Similarly, if we change the current to less fun than Dev, ErrorConsole
74  // should be disabled.
75  channel_override.reset();
76  channel_override.reset(
77      new ScopedCurrentChannel(chrome::VersionInfo::CHANNEL_BETA));
78  profile_->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
79  EXPECT_FALSE(error_console_->enabled());
80  EXPECT_FALSE(error_console_->IsEnabledForChromeExtensionsPage());
81  EXPECT_FALSE(error_console_->IsEnabledForAppsDeveloperTools());
82
83  // But if we add the feature switch, that should override the channel.
84  FeatureSwitch::error_console()->SetOverrideValue(
85      FeatureSwitch::OVERRIDE_ENABLED);
86  ASSERT_TRUE(FeatureSwitch::error_console()->IsEnabled());
87  // We use a pref mod to "poke" the ErrorConsole, because it needs an
88  // indication that something changed (FeatureSwitches don't change in a real
89  // environment, so ErrorConsole doesn't listen for them).
90  profile_->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, false);
91  profile_->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
92  EXPECT_TRUE(error_console_->enabled());
93  EXPECT_TRUE(error_console_->IsEnabledForChromeExtensionsPage());
94  EXPECT_FALSE(error_console_->IsEnabledForAppsDeveloperTools());
95
96  // Next, remove the feature switch (turning error console off), and install
97  // the Apps Developer Tools. If we have Apps Developer Tools, Error Console
98  // should be enabled by default.
99  FeatureSwitch::error_console()->SetOverrideValue(
100      FeatureSwitch::OVERRIDE_DISABLED);
101  const char kAppsDeveloperToolsExtensionId[] =
102      "ohmmkhmmmpcnpikjeljgnaoabkaalbgc";
103  scoped_refptr<Extension> adt =
104      ExtensionBuilder()
105          .SetManifest(
106              DictionaryBuilder().Set("name", "apps dev tools")
107                                 .Set("version", "0.2.0")
108                                 .Set("manifest_version", 2)
109                                 .Build())
110          .SetID(kAppsDeveloperToolsExtensionId)
111          .Build();
112  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_.get());
113  registry->AddEnabled(adt);
114  registry->TriggerOnLoaded(adt.get());
115  EXPECT_TRUE(error_console_->enabled());
116  EXPECT_FALSE(error_console_->IsEnabledForChromeExtensionsPage());
117  EXPECT_TRUE(error_console_->IsEnabledForAppsDeveloperTools());
118
119  // Unloading the Apps Developer Tools should disable error console.
120  registry->RemoveEnabled(adt->id());
121  registry->TriggerOnUnloaded(adt.get(), UnloadedExtensionInfo::REASON_DISABLE);
122  EXPECT_FALSE(error_console_->enabled());
123  EXPECT_FALSE(error_console_->IsEnabledForChromeExtensionsPage());
124  EXPECT_FALSE(error_console_->IsEnabledForAppsDeveloperTools());
125}
126
127// Test that errors are successfully reported. This is a simple test, since it
128// is tested more thoroughly in extensions/browser/error_map_unittest.cc
129TEST_F(ErrorConsoleUnitTest, ReportErrors) {
130  const size_t kNumTotalErrors = 6;
131  const std::string kId = crx_file::id_util::GenerateId("id");
132  error_console_->set_default_reporting_for_test(ExtensionError::MANIFEST_ERROR,
133                                                 true);
134  ASSERT_EQ(0u, error_console_->GetErrorsForExtension(kId).size());
135
136  for (size_t i = 0; i < kNumTotalErrors; ++i) {
137    error_console_->ReportError(
138        CreateNewManifestError(kId, base::UintToString(i)));
139  }
140
141  ASSERT_EQ(kNumTotalErrors, error_console_->GetErrorsForExtension(kId).size());
142}
143
144TEST_F(ErrorConsoleUnitTest, DontStoreErrorsWithoutEnablingType) {
145  // Disable default runtime error reporting, and enable default manifest error
146  // reporting.
147  error_console_->set_default_reporting_for_test(ExtensionError::RUNTIME_ERROR,
148                                                 false);
149  error_console_->set_default_reporting_for_test(ExtensionError::MANIFEST_ERROR,
150                                                 true);
151
152  const std::string kId = crx_file::id_util::GenerateId("id");
153
154  // Try to report a runtime error - it should be ignored.
155  error_console_->ReportError(CreateNewRuntimeError(kId, "a"));
156  ASSERT_EQ(0u, error_console_->GetErrorsForExtension(kId).size());
157
158  // Check that manifest errors are reported successfully.
159  error_console_->ReportError(CreateNewManifestError(kId, "b"));
160  ASSERT_EQ(1u, error_console_->GetErrorsForExtension(kId).size());
161
162  // We should still ignore runtime errors.
163  error_console_->ReportError(CreateNewRuntimeError(kId, "c"));
164  ASSERT_EQ(1u, error_console_->GetErrorsForExtension(kId).size());
165
166  // Enable runtime errors specifically for this extension, and disable the use
167  // of the default mask.
168  error_console_->SetReportingForExtension(
169      kId, ExtensionError::RUNTIME_ERROR, true);
170
171  // We should now accept runtime and manifest errors.
172  error_console_->ReportError(CreateNewManifestError(kId, "d"));
173  ASSERT_EQ(2u, error_console_->GetErrorsForExtension(kId).size());
174  error_console_->ReportError(CreateNewRuntimeError(kId, "e"));
175  ASSERT_EQ(3u, error_console_->GetErrorsForExtension(kId).size());
176
177  // All other extensions should still use the default mask, and ignore runtime
178  // errors but report manifest errors.
179  const std::string kId2 = crx_file::id_util::GenerateId("id2");
180  error_console_->ReportError(CreateNewRuntimeError(kId2, "f"));
181  ASSERT_EQ(0u, error_console_->GetErrorsForExtension(kId2).size());
182  error_console_->ReportError(CreateNewManifestError(kId2, "g"));
183  ASSERT_EQ(1u, error_console_->GetErrorsForExtension(kId2).size());
184
185  // By reverting to default reporting, we should again allow manifest errors,
186  // but not runtime errors.
187  error_console_->UseDefaultReportingForExtension(kId);
188  error_console_->ReportError(CreateNewManifestError(kId, "h"));
189  ASSERT_EQ(4u, error_console_->GetErrorsForExtension(kId).size());
190  error_console_->ReportError(CreateNewRuntimeError(kId, "i"));
191  ASSERT_EQ(4u, error_console_->GetErrorsForExtension(kId).size());
192}
193
194// Test that we only store errors by default for unpacked extensions, and that
195// assigning a preference to any extension overrides the defaults.
196TEST_F(ErrorConsoleUnitTest, TestDefaultStoringPrefs) {
197  // For this, we need actual extensions.
198  scoped_refptr<const Extension> unpacked_extension =
199      ExtensionBuilder()
200          .SetManifest(DictionaryBuilder()
201                           .Set("name", "unpacked")
202                           .Set("version", "0.0.1")
203                           .Set("manifest_version", 2)
204                           .Build())
205          .SetLocation(Manifest::UNPACKED)
206          .SetID(crx_file::id_util::GenerateId("unpacked"))
207          .Build();
208  scoped_refptr<const Extension> packed_extension =
209      ExtensionBuilder()
210          .SetManifest(DictionaryBuilder()
211                           .Set("name", "packed")
212                           .Set("version", "0.0.1")
213                           .Set("manifest_version", 2)
214                           .Build())
215          .SetLocation(Manifest::INTERNAL)
216          .SetID(crx_file::id_util::GenerateId("packed"))
217          .Build();
218
219  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_.get());
220  registry->AddEnabled(unpacked_extension);
221  registry->AddEnabled(packed_extension);
222
223  // We should start with a clean slate.
224  EXPECT_EQ(0u, error_console_->GetErrorsForExtension(
225      unpacked_extension->id()).size());
226  EXPECT_EQ(0u, error_console_->GetErrorsForExtension(
227      packed_extension->id()).size());
228
229  // Errors should be ignored by default for the packed extension.
230  error_console_->ReportError(
231      CreateNewManifestError(packed_extension->id(), "manifest error 1"));
232  error_console_->ReportError(
233      CreateNewRuntimeError(packed_extension->id(), "runtime error 1"));
234  EXPECT_EQ(0u, error_console_->GetErrorsForExtension(
235      packed_extension->id()).size());
236  // Also check that reporting settings are correctly returned.
237  EXPECT_FALSE(error_console_->IsReportingEnabledForExtension(
238      packed_extension->id()));
239
240  // Errors should be reported by default for the unpacked extension.
241  error_console_->ReportError(
242      CreateNewManifestError(unpacked_extension->id(), "manifest error 2"));
243  error_console_->ReportError(
244      CreateNewRuntimeError(unpacked_extension->id(), "runtime error 2"));
245  EXPECT_EQ(2u, error_console_->GetErrorsForExtension(
246      unpacked_extension->id()).size());
247  // Also check that reporting settings are correctly returned.
248  EXPECT_TRUE(error_console_->IsReportingEnabledForExtension(
249      unpacked_extension->id()));
250
251  // Registering a preference should override this for both types of extensions
252  // (should be able to enable errors for packed, or disable errors for
253  // unpacked).
254  error_console_->SetReportingForExtension(packed_extension->id(),
255                                           ExtensionError::RUNTIME_ERROR,
256                                           true);
257  error_console_->ReportError(
258      CreateNewRuntimeError(packed_extension->id(), "runtime error 3"));
259  EXPECT_EQ(1u, error_console_->GetErrorsForExtension(
260      packed_extension->id()).size());
261  EXPECT_TRUE(error_console_->IsReportingEnabledForExtension(
262      packed_extension->id()));
263
264  error_console_->SetReportingForExtension(unpacked_extension->id(),
265                                           ExtensionError::RUNTIME_ERROR,
266                                           false);
267  error_console_->ReportError(
268      CreateNewRuntimeError(packed_extension->id(), "runtime error 4"));
269  EXPECT_EQ(2u,  // We should still have the first two errors.
270            error_console_->GetErrorsForExtension(
271                unpacked_extension->id()).size());
272  EXPECT_FALSE(error_console_->IsReportingEnabledForExtension(
273      unpacked_extension->id()));
274}
275
276}  // namespace extensions
277