extension_loading_browsertest.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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// This file contains tests for extension loading, reloading, and
6// unloading behavior.
7
8#include "base/json/json_writer.h"
9#include "base/run_loop.h"
10#include "base/safe_numerics.h"
11#include "base/test/values_test_util.h"
12#include "chrome/browser/extensions/extension_browsertest.h"
13#include "chrome/browser/extensions/extension_creator.h"
14#include "chrome/browser/extensions/extension_service.h"
15#include "chrome/browser/extensions/extension_system.h"
16#include "chrome/browser/ui/tabs/tab_strip_model.h"
17#include "chrome/test/base/in_process_browser_test.h"
18#include "chrome/test/base/ui_test_utils.h"
19#include "net/test/embedded_test_server/embedded_test_server.h"
20#include "testing/gmock/include/gmock/gmock.h"
21
22namespace extensions {
23namespace {
24
25// Provides a temporary directory to build an extension into.  This lets all of
26// an extension's code live inside the test instead of in a separate directory.
27class TestExtensionDir {
28 public:
29  TestExtensionDir() {
30    EXPECT_TRUE(dir_.CreateUniqueTempDir());
31    EXPECT_TRUE(crx_dir_.CreateUniqueTempDir());
32  }
33
34  // Writes |contents| to path()/filename, overwriting anything that was already
35  // there.
36  void WriteFile(base::FilePath::StringType filename,
37                 base::StringPiece contents) {
38    EXPECT_EQ(
39        base::checked_numeric_cast<int>(contents.size()),
40        file_util::WriteFile(
41            dir_.path().Append(filename), contents.data(), contents.size()));
42  }
43
44  // Converts |value| to JSON, and then writes it to path()/filename with
45  // WriteFile().
46  void WriteJson(base::FilePath::StringType filename, const Value& value) {
47    std::string json;
48    base::JSONWriter::Write(&value, &json);
49    WriteFile(filename, json);
50  }
51
52  // This function packs the extension into a .crx, and returns the path to that
53  // .crx. Multiple calls to Pack() will produce extensions with the same ID.
54  base::FilePath Pack() {
55    ExtensionCreator creator;
56    base::FilePath crx_path =
57        crx_dir_.path().Append(FILE_PATH_LITERAL("ext.crx"));
58    base::FilePath pem_path =
59        crx_dir_.path().Append(FILE_PATH_LITERAL("ext.pem"));
60    base::FilePath pem_in_path, pem_out_path;
61    if (file_util::PathExists(pem_path))
62      pem_in_path = pem_path;
63    else
64      pem_out_path = pem_path;
65    if (!creator.Run(dir_.path(),
66                     crx_path,
67                     pem_in_path,
68                     pem_out_path,
69                     ExtensionCreator::kOverwriteCRX)) {
70      ADD_FAILURE()
71          << "ExtensionCreator::Run() failed: " << creator.error_message();
72      return base::FilePath();
73    }
74    if (!file_util::PathExists(crx_path)) {
75      ADD_FAILURE() << crx_path.value() << " was not created.";
76      return base::FilePath();
77    }
78    return crx_path;
79  }
80
81 private:
82  // Stores files that make up the extension.
83  base::ScopedTempDir dir_;
84  // Stores the generated .crx and .pem.
85  base::ScopedTempDir crx_dir_;
86};
87
88class ExtensionLoadingTest : public ExtensionBrowserTest {
89};
90
91// Check the fix for http://crbug.com/178542.
92IN_PROC_BROWSER_TEST_F(ExtensionLoadingTest,
93                       UpgradeAfterNavigatingFromOverriddenNewTabPage) {
94  embedded_test_server()->ServeFilesFromDirectory(
95      base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
96  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
97
98  TestExtensionDir extension_dir;
99  scoped_ptr<base::Value> manifest =
100      base::test::ParseJson("{"
101                            "  \"name\": \"Overrides New Tab\","
102                            "  \"version\": \"1\","
103                            "  \"description\": \"Overrides New Tab\","
104                            "  \"manifest_version\": 2,"
105                            "  \"background\": {"
106                            "    \"persistent\": false,"
107                            "    \"scripts\": [\"event.js\"]"
108                            "  },"
109                            "  \"chrome_url_overrides\": {"
110                            "    \"newtab\": \"newtab.html\""
111                            "  }"
112                            "}");
113  extension_dir.WriteJson(FILE_PATH_LITERAL("manifest.json"), *manifest);
114  extension_dir.WriteFile(FILE_PATH_LITERAL("event.js"), "");
115  extension_dir.WriteFile(FILE_PATH_LITERAL("newtab.html"),
116                          "<h1>Overridden New Tab Page</h1>");
117
118  const Extension* new_tab_extension =
119      InstallExtension(extension_dir.Pack(), 1 /*new install*/);
120  ASSERT_TRUE(new_tab_extension);
121
122  // Visit the New Tab Page to get a renderer using the extension into history.
123  ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab"));
124
125  // Navigate that tab to a non-extension URL to swap out the extension's
126  // renderer.
127  const GURL test_link_from_NTP =
128      embedded_test_server()->GetURL("/README.chromium");
129  EXPECT_THAT(test_link_from_NTP.spec(), testing::EndsWith("/README.chromium"))
130      << "Check that the test server started.";
131  NavigateInRenderer(browser()->tab_strip_model()->GetActiveWebContents(),
132                     test_link_from_NTP);
133
134  // Increase the extension's version.
135  static_cast<base::DictionaryValue&>(*manifest).SetString("version", "2");
136  extension_dir.WriteJson(FILE_PATH_LITERAL("manifest.json"), *manifest);
137
138  // Upgrade the extension.
139  new_tab_extension = UpdateExtension(
140      new_tab_extension->id(), extension_dir.Pack(), 0 /*expected upgrade*/);
141  EXPECT_THAT(new_tab_extension->version()->components(),
142              testing::ElementsAre(2));
143
144  // The extension takes a couple round-trips to the renderer in order
145  // to crash, so open a new tab to wait long enough.
146  AddTabAtIndex(browser()->tab_strip_model()->count(),
147                GURL("http://www.google.com/"),
148                content::PAGE_TRANSITION_TYPED);
149
150  // Check that the extension hasn't crashed.
151  ExtensionService* service =
152      ExtensionSystem::Get(profile())->extension_service();
153  EXPECT_EQ(0U, service->terminated_extensions()->size());
154  EXPECT_TRUE(service->extensions()->Contains(new_tab_extension->id()));
155}
156
157}  // namespace
158}  // namespace extensions
159