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 "base/files/file_path.h"
6#include "chrome/common/extensions/api/plugins/plugins_handler.h"
7#include "chrome/common/extensions/sync_helper.h"
8#include "extensions/common/error_utils.h"
9#include "extensions/common/extension.h"
10#include "extensions/common/manifest.h"
11#include "extensions/common/manifest_constants.h"
12#include "testing/gtest/include/gtest/gtest.h"
13#include "url/gurl.h"
14
15namespace extensions {
16
17namespace keys = manifest_keys;
18namespace errors = manifest_errors;
19
20class ExtensionSyncTypeTest : public testing::Test {
21 protected:
22  enum SyncTestExtensionType {
23    EXTENSION,
24    APP,
25    USER_SCRIPT,
26    THEME
27  };
28
29  static scoped_refptr<Extension> MakeSyncTestExtensionWithPluginPermission(
30      SyncTestExtensionType type,
31      const GURL& update_url,
32      const GURL& launch_url,
33      Manifest::Location location,
34      const base::FilePath& extension_path,
35      int creation_flags,
36      int num_plugins,
37      bool has_plugin_permission,
38      const std::string& expected_error) {
39    base::DictionaryValue source;
40    source.SetString(keys::kName, "PossiblySyncableExtension");
41    source.SetString(keys::kVersion, "0.0.0.0");
42    if (type == APP)
43      source.SetString(keys::kApp, "true");
44    if (type == THEME)
45      source.Set(keys::kTheme, new base::DictionaryValue());
46    if (!update_url.is_empty()) {
47      source.SetString(keys::kUpdateURL, update_url.spec());
48    }
49    if (!launch_url.is_empty()) {
50      source.SetString(keys::kLaunchWebURL, launch_url.spec());
51    }
52    if (type != THEME) {
53      source.SetBoolean(keys::kConvertedFromUserScript, type == USER_SCRIPT);
54      if (num_plugins >= 0) {
55        base::ListValue* plugins = new base::ListValue();
56        for (int i = 0; i < num_plugins; ++i) {
57          base::DictionaryValue* plugin = new base::DictionaryValue();
58          plugin->SetString(keys::kPluginsPath, std::string());
59          plugins->Set(i, plugin);
60        }
61        source.Set(keys::kPlugins, plugins);
62      }
63    }
64    if (has_plugin_permission) {
65      base::ListValue* plugins = new base::ListValue();
66      plugins->Set(0, new base::StringValue("plugin"));
67      source.Set(keys::kPermissions, plugins);
68    }
69
70    std::string error;
71    scoped_refptr<Extension> extension = Extension::Create(
72        extension_path, location, source, creation_flags, &error);
73    if (expected_error == "")
74      EXPECT_TRUE(extension.get());
75    else
76      EXPECT_FALSE(extension.get());
77    EXPECT_EQ(expected_error, error);
78    return extension;
79  }
80
81  static scoped_refptr<Extension> MakeSyncTestExtension(
82      SyncTestExtensionType type,
83      const GURL& update_url,
84      const GURL& launch_url,
85      Manifest::Location location,
86      const base::FilePath& extension_path,
87      int creation_flags) {
88    return MakeSyncTestExtensionWithPluginPermission(
89        type, update_url, launch_url, location, extension_path,
90        creation_flags, -1, false, "");
91  }
92
93  static const char kValidUpdateUrl1[];
94  static const char kValidUpdateUrl2[];
95};
96
97const char ExtensionSyncTypeTest::kValidUpdateUrl1[] =
98    "http://clients2.google.com/service/update2/crx";
99const char ExtensionSyncTypeTest::kValidUpdateUrl2[] =
100    "https://clients2.google.com/service/update2/crx";
101
102TEST_F(ExtensionSyncTypeTest, NormalExtensionNoUpdateUrl) {
103  scoped_refptr<Extension> extension(
104      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
105                            Manifest::INTERNAL, base::FilePath(),
106                            Extension::NO_FLAGS));
107  EXPECT_TRUE(sync_helper::IsSyncableExtension(extension.get()));
108}
109
110TEST_F(ExtensionSyncTypeTest, UserScriptValidUpdateUrl) {
111  scoped_refptr<Extension> extension(
112      MakeSyncTestExtension(USER_SCRIPT, GURL(kValidUpdateUrl1), GURL(),
113                            Manifest::INTERNAL, base::FilePath(),
114                            Extension::NO_FLAGS));
115  EXPECT_TRUE(sync_helper::IsSyncableExtension(extension.get()));
116}
117
118TEST_F(ExtensionSyncTypeTest, UserScriptNoUpdateUrl) {
119  scoped_refptr<Extension> extension(
120      MakeSyncTestExtension(USER_SCRIPT, GURL(), GURL(),
121                            Manifest::INTERNAL, base::FilePath(),
122                            Extension::NO_FLAGS));
123  EXPECT_FALSE(sync_helper::IsSyncableExtension(extension.get()));
124}
125
126TEST_F(ExtensionSyncTypeTest, ThemeNoUpdateUrl) {
127  scoped_refptr<Extension> extension(
128      MakeSyncTestExtension(THEME, GURL(), GURL(),
129                            Manifest::INTERNAL, base::FilePath(),
130                            Extension::NO_FLAGS));
131  EXPECT_FALSE(sync_helper::IsSyncableExtension(extension.get()));
132  EXPECT_FALSE(sync_helper::IsSyncableApp(extension.get()));
133}
134
135TEST_F(ExtensionSyncTypeTest, AppWithLaunchUrl) {
136  scoped_refptr<Extension> extension(
137      MakeSyncTestExtension(EXTENSION, GURL(), GURL("http://www.google.com"),
138                            Manifest::INTERNAL, base::FilePath(),
139                            Extension::NO_FLAGS));
140  EXPECT_TRUE(sync_helper::IsSyncableApp(extension.get()));
141}
142
143TEST_F(ExtensionSyncTypeTest, ExtensionExternal) {
144  scoped_refptr<Extension> extension(
145      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
146                            Manifest::EXTERNAL_PREF, base::FilePath(),
147                            Extension::NO_FLAGS));
148  EXPECT_FALSE(sync_helper::IsSyncableExtension(extension.get()));
149}
150
151TEST_F(ExtensionSyncTypeTest, UserScriptThirdPartyUpdateUrl) {
152  scoped_refptr<Extension> extension(
153      MakeSyncTestExtension(
154          USER_SCRIPT, GURL("http://third-party.update_url.com"), GURL(),
155          Manifest::INTERNAL, base::FilePath(), Extension::NO_FLAGS));
156  EXPECT_FALSE(sync_helper::IsSyncableExtension(extension.get()));
157}
158
159TEST_F(ExtensionSyncTypeTest, OnlyDisplayAppsInLauncher) {
160  scoped_refptr<Extension> extension(
161      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
162                            Manifest::INTERNAL, base::FilePath(),
163                            Extension::NO_FLAGS));
164
165  EXPECT_FALSE(extension->ShouldDisplayInAppLauncher());
166  EXPECT_FALSE(extension->ShouldDisplayInNewTabPage());
167
168  scoped_refptr<Extension> app(
169      MakeSyncTestExtension(APP, GURL(), GURL("http://www.google.com"),
170                            Manifest::INTERNAL, base::FilePath(),
171                            Extension::NO_FLAGS));
172  EXPECT_TRUE(app->ShouldDisplayInAppLauncher());
173  EXPECT_TRUE(app->ShouldDisplayInNewTabPage());
174}
175
176TEST_F(ExtensionSyncTypeTest, DisplayInXManifestProperties) {
177  base::DictionaryValue manifest;
178  manifest.SetString(keys::kName, "TestComponentApp");
179  manifest.SetString(keys::kVersion, "0.0.0.0");
180  manifest.SetString(keys::kApp, "true");
181  manifest.SetString(keys::kPlatformAppBackgroundPage, std::string());
182
183  std::string error;
184  scoped_refptr<Extension> app;
185
186  // Default to true.
187  app = Extension::Create(
188      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
189  EXPECT_EQ(error, std::string());
190  EXPECT_TRUE(app->ShouldDisplayInAppLauncher());
191  EXPECT_TRUE(app->ShouldDisplayInNewTabPage());
192
193  // Value display_in_NTP defaults to display_in_launcher.
194  manifest.SetBoolean(keys::kDisplayInLauncher, false);
195  app = Extension::Create(
196      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
197  EXPECT_EQ(error, std::string());
198  EXPECT_FALSE(app->ShouldDisplayInAppLauncher());
199  EXPECT_FALSE(app->ShouldDisplayInNewTabPage());
200
201  // Value display_in_NTP = true overriding display_in_launcher = false.
202  manifest.SetBoolean(keys::kDisplayInNewTabPage, true);
203  app = Extension::Create(
204      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
205  EXPECT_EQ(error, std::string());
206  EXPECT_FALSE(app->ShouldDisplayInAppLauncher());
207  EXPECT_TRUE(app->ShouldDisplayInNewTabPage());
208
209  // Value display_in_NTP = false only, overrides default = true.
210  manifest.Remove(keys::kDisplayInLauncher, NULL);
211  manifest.SetBoolean(keys::kDisplayInNewTabPage, false);
212  app = Extension::Create(
213      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
214  EXPECT_EQ(error, std::string());
215  EXPECT_TRUE(app->ShouldDisplayInAppLauncher());
216  EXPECT_FALSE(app->ShouldDisplayInNewTabPage());
217
218  // Error checking.
219  manifest.SetString(keys::kDisplayInNewTabPage, "invalid");
220  app = Extension::Create(
221      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
222  EXPECT_EQ(error, std::string(errors::kInvalidDisplayInNewTabPage));
223}
224
225TEST_F(ExtensionSyncTypeTest, OnlySyncInternal) {
226  scoped_refptr<Extension> extension_internal(
227      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
228                            Manifest::INTERNAL, base::FilePath(),
229                            Extension::NO_FLAGS));
230  EXPECT_TRUE(sync_helper::IsSyncable(extension_internal.get()));
231
232  scoped_refptr<Extension> extension_noninternal(
233      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
234                            Manifest::COMPONENT, base::FilePath(),
235                            Extension::NO_FLAGS));
236  EXPECT_FALSE(sync_helper::IsSyncable(extension_noninternal.get()));
237}
238
239TEST_F(ExtensionSyncTypeTest, DontSyncDefault) {
240  scoped_refptr<Extension> extension_default(
241      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
242                            Manifest::INTERNAL, base::FilePath(),
243                            Extension::WAS_INSTALLED_BY_DEFAULT));
244  EXPECT_FALSE(sync_helper::IsSyncable(extension_default.get()));
245}
246
247// These plugin tests don't make sense on Chrome OS, where extension plugins
248// are not allowed.
249#if !defined(OS_CHROMEOS)
250TEST_F(ExtensionSyncTypeTest, ExtensionWithEmptyPlugins) {
251  scoped_refptr<Extension> extension(
252      MakeSyncTestExtensionWithPluginPermission(
253          EXTENSION, GURL(), GURL(),
254          Manifest::INTERNAL, base::FilePath(),
255          Extension::NO_FLAGS, 0, false, ""));
256  if (extension.get())
257    EXPECT_TRUE(sync_helper::IsSyncableExtension(extension.get()));
258}
259
260TEST_F(ExtensionSyncTypeTest, ExtensionWithPlugin) {
261  scoped_refptr<Extension> extension(
262      MakeSyncTestExtensionWithPluginPermission(
263          EXTENSION, GURL(), GURL(),
264          Manifest::INTERNAL, base::FilePath(),
265          Extension::NO_FLAGS, 1, false, ""));
266  if (extension.get())
267    EXPECT_FALSE(sync_helper::IsSyncableExtension(extension.get()));
268}
269
270TEST_F(ExtensionSyncTypeTest, ExtensionWithTwoPlugins) {
271  scoped_refptr<Extension> extension(
272      MakeSyncTestExtensionWithPluginPermission(
273          EXTENSION, GURL(), GURL(),
274          Manifest::INTERNAL, base::FilePath(),
275          Extension::NO_FLAGS, 2, false, ""));
276  if (extension.get())
277    EXPECT_FALSE(sync_helper::IsSyncableExtension(extension.get()));
278}
279
280TEST_F(ExtensionSyncTypeTest, ExtensionWithPluginPermission) {
281  // Specifying the "plugin" permission in the manifest is an error.
282  scoped_refptr<Extension> extension(
283      MakeSyncTestExtensionWithPluginPermission(
284          EXTENSION, GURL(), GURL(),
285          Manifest::INTERNAL, base::FilePath(),
286          Extension::NO_FLAGS, 0, true,
287          ErrorUtils::FormatErrorMessage(
288              errors::kPermissionNotAllowedInManifest, "plugin")));
289}
290
291#endif // !defined(OS_CHROMEOS)
292
293}  // namespace extensions
294