1// Copyright (c) 2012 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/ui/webui/extensions/install_extension_handler.h"
6
7#include "base/bind.h"
8#include "base/strings/string_util.h"
9#include "base/strings/utf_string_conversions.h"
10#include "chrome/browser/extensions/crx_installer.h"
11#include "chrome/browser/extensions/extension_install_prompt.h"
12#include "chrome/browser/extensions/extension_service.h"
13#include "chrome/browser/extensions/unpacked_installer.h"
14#include "chrome/browser/extensions/zipfile_installer.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/grit/generated_resources.h"
17#include "content/public/browser/web_contents.h"
18#include "content/public/browser/web_ui.h"
19#include "content/public/browser/web_ui_data_source.h"
20#include "content/public/common/drop_data.h"
21#include "extensions/browser/extension_system.h"
22#include "extensions/common/feature_switch.h"
23#include "net/base/filename_util.h"
24#include "ui/base/l10n/l10n_util.h"
25
26namespace extensions {
27
28InstallExtensionHandler::InstallExtensionHandler() {
29}
30
31InstallExtensionHandler::~InstallExtensionHandler() {
32}
33
34void InstallExtensionHandler::GetLocalizedValues(
35    content::WebUIDataSource* source) {
36  source->AddString(
37      "extensionSettingsInstallDropTarget",
38      l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALL_DROP_TARGET));
39  source->AddBoolean(
40      "offStoreInstallEnabled",
41      FeatureSwitch::easy_off_store_install()->IsEnabled());
42}
43
44void InstallExtensionHandler::RegisterMessages() {
45  web_ui()->RegisterMessageCallback(
46      "startDrag",
47      base::Bind(&InstallExtensionHandler::HandleStartDragMessage,
48                 base::Unretained(this)));
49  web_ui()->RegisterMessageCallback(
50      "stopDrag",
51      base::Bind(&InstallExtensionHandler::HandleStopDragMessage,
52                 base::Unretained(this)));
53  web_ui()->RegisterMessageCallback(
54      "installDroppedFile",
55      base::Bind(&InstallExtensionHandler::HandleInstallMessage,
56                 base::Unretained(this)));
57  web_ui()->RegisterMessageCallback(
58      "installDroppedDirectory",
59      base::Bind(&InstallExtensionHandler::HandleInstallDirectoryMessage,
60                 base::Unretained(this)));
61}
62
63void InstallExtensionHandler::HandleStartDragMessage(
64    const base::ListValue* args) {
65  content::DropData* drop_data =
66      web_ui()->GetWebContents()->GetDropData();
67  if (!drop_data) {
68    DLOG(ERROR) << "No current drop data.";
69    return;
70  }
71
72  if (drop_data->filenames.empty()) {
73    DLOG(ERROR) << "Current drop data contains no files.";
74    return;
75  }
76
77  const ui::FileInfo& file_info = drop_data->filenames.front();
78
79  file_to_install_ = file_info.path;
80  // Use the display name if provided, for checking file names
81  // (.path is likely a random hash value in that case).
82  // TODO(dcheng): It would be nice to make this a FilePath too.
83  file_display_name_ = file_info.display_name.empty()
84                           ? file_info.path.AsUTF16Unsafe()
85                           : file_info.display_name.AsUTF16Unsafe();
86}
87
88void InstallExtensionHandler::HandleStopDragMessage(
89    const base::ListValue* args) {
90  file_to_install_.clear();
91  file_display_name_.clear();
92}
93
94void InstallExtensionHandler::HandleInstallMessage(
95    const base::ListValue* args) {
96  if (file_to_install_.empty()) {
97    LOG(ERROR) << "No file captured to install.";
98    return;
99  }
100
101  Profile* profile = Profile::FromBrowserContext(
102      web_ui()->GetWebContents()->GetBrowserContext());
103
104  const bool kCaseSensitive = false;
105
106  if (EndsWith(
107          file_display_name_, base::ASCIIToUTF16(".zip"), kCaseSensitive)) {
108    ZipFileInstaller::Create(ExtensionSystem::Get(profile)->extension_service())
109        ->LoadFromZipFile(file_to_install_);
110  } else {
111    scoped_ptr<ExtensionInstallPrompt> prompt(
112        new ExtensionInstallPrompt(web_ui()->GetWebContents()));
113    scoped_refptr<CrxInstaller> crx_installer(CrxInstaller::Create(
114        ExtensionSystem::Get(profile)->extension_service(), prompt.Pass()));
115    crx_installer->set_error_on_unsupported_requirements(true);
116    crx_installer->set_off_store_install_allow_reason(
117        CrxInstaller::OffStoreInstallAllowedFromSettingsPage);
118    crx_installer->set_install_immediately(true);
119
120    if (EndsWith(file_display_name_,
121                 base::ASCIIToUTF16(".user.js"),
122                 kCaseSensitive)) {
123      crx_installer->InstallUserScript(
124          file_to_install_, net::FilePathToFileURL(file_to_install_));
125    } else if (EndsWith(file_display_name_,
126                        base::ASCIIToUTF16(".crx"),
127                        kCaseSensitive)) {
128      crx_installer->InstallCrx(file_to_install_);
129    } else {
130      CHECK(false);
131    }
132  }
133
134  file_to_install_.clear();
135  file_display_name_.clear();
136}
137
138void InstallExtensionHandler::HandleInstallDirectoryMessage(
139    const base::ListValue* args) {
140  Profile* profile = Profile::FromBrowserContext(
141      web_ui()->GetWebContents()->GetBrowserContext());
142  UnpackedInstaller::Create(
143      ExtensionSystem::Get(profile)->
144          extension_service())->Load(file_to_install_);
145}
146
147}  // namespace extensions
148