open_with_browser.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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/chromeos/file_manager/open_with_browser.h"
6
7#include "base/bind.h"
8#include "base/command_line.h"
9#include "base/logging.h"
10#include "base/path_service.h"
11#include "base/threading/sequenced_worker_pool.h"
12#include "chrome/browser/browser_process.h"
13#include "chrome/browser/chromeos/drive/drive.pb.h"
14#include "chrome/browser/chromeos/drive/file_system.h"
15#include "chrome/browser/chromeos/drive/file_system_util.h"
16#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
17#include "chrome/browser/extensions/crx_installer.h"
18#include "chrome/browser/extensions/extension_install_prompt.h"
19#include "chrome/browser/extensions/extension_service.h"
20#include "chrome/browser/extensions/extension_system.h"
21#include "chrome/browser/plugins/plugin_prefs.h"
22#include "chrome/browser/profiles/profile.h"
23#include "chrome/browser/profiles/profile_manager.h"
24#include "chrome/browser/ui/browser.h"
25#include "chrome/browser/ui/browser_finder.h"
26#include "chrome/browser/ui/browser_tabstrip.h"
27#include "chrome/browser/ui/browser_window.h"
28#include "chrome/browser/ui/simple_message_box.h"
29#include "chrome/common/chrome_paths.h"
30#include "chrome/common/chrome_switches.h"
31#include "chromeos/chromeos_switches.h"
32#include "content/public/browser/browser_thread.h"
33#include "content/public/browser/plugin_service.h"
34#include "content/public/common/pepper_plugin_info.h"
35#include "content/public/common/webplugininfo.h"
36#include "net/base/net_util.h"
37
38using content::BrowserThread;
39using content::PluginService;
40
41namespace file_manager {
42namespace util {
43namespace {
44
45const base::FilePath::CharType kCRXExtension[] = FILE_PATH_LITERAL(".crx");
46const base::FilePath::CharType kPdfExtension[] = FILE_PATH_LITERAL(".pdf");
47const base::FilePath::CharType kSwfExtension[] = FILE_PATH_LITERAL(".swf");
48
49// List of file extensions viewable in the browser.
50const base::FilePath::CharType* kFileExtensionsViewableInBrowser[] = {
51#if defined(GOOGLE_CHROME_BUILD)
52  FILE_PATH_LITERAL(".pdf"),
53  FILE_PATH_LITERAL(".swf"),
54#endif
55  FILE_PATH_LITERAL(".bmp"),
56  FILE_PATH_LITERAL(".jpg"),
57  FILE_PATH_LITERAL(".jpeg"),
58  FILE_PATH_LITERAL(".png"),
59  FILE_PATH_LITERAL(".webp"),
60  FILE_PATH_LITERAL(".gif"),
61  FILE_PATH_LITERAL(".txt"),
62  FILE_PATH_LITERAL(".html"),
63  FILE_PATH_LITERAL(".htm"),
64  FILE_PATH_LITERAL(".mhtml"),
65  FILE_PATH_LITERAL(".mht"),
66  FILE_PATH_LITERAL(".svg"),
67};
68
69// Returns true if |file_path| is viewable in the browser (ex. HTML file).
70bool IsViewableInBrowser(const base::FilePath& file_path) {
71  for (size_t i = 0; i < arraysize(kFileExtensionsViewableInBrowser); i++) {
72    if (file_path.MatchesExtension(kFileExtensionsViewableInBrowser[i]))
73      return true;
74  }
75  return false;
76}
77
78bool IsPepperPluginEnabled(Profile* profile,
79                           const base::FilePath& plugin_path) {
80  DCHECK(profile);
81
82  content::PepperPluginInfo* pepper_info =
83      PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(plugin_path);
84  if (!pepper_info)
85    return false;
86
87  scoped_refptr<PluginPrefs> plugin_prefs = PluginPrefs::GetForProfile(profile);
88  if (!plugin_prefs.get())
89    return false;
90
91  return plugin_prefs->IsPluginEnabled(pepper_info->ToWebPluginInfo());
92}
93
94bool IsPdfPluginEnabled(Profile* profile) {
95  DCHECK(profile);
96
97  base::FilePath plugin_path;
98  PathService::Get(chrome::FILE_PDF_PLUGIN, &plugin_path);
99  return IsPepperPluginEnabled(profile, plugin_path);
100}
101
102bool IsFlashPluginEnabled(Profile* profile) {
103  DCHECK(profile);
104
105  base::FilePath plugin_path(
106      CommandLine::ForCurrentProcess()->GetSwitchValueNative(
107          switches::kPpapiFlashPath));
108  if (plugin_path.empty())
109    PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN, &plugin_path);
110  return IsPepperPluginEnabled(profile, plugin_path);
111}
112
113void OpenNewTab(Profile* profile, const GURL& url) {
114  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
115
116  // Check the validity of the pointer so that the closure from
117  // base::Bind(&OpenNewTab, profile) can be passed between threads.
118  if (!g_browser_process->profile_manager()->IsValidProfile(profile))
119    return;
120
121  Browser* browser = chrome::FindOrCreateTabbedBrowser(
122      profile, chrome::HOST_DESKTOP_TYPE_ASH);
123  chrome::AddSelectedTabWithURL(browser, url, content::PAGE_TRANSITION_LINK);
124  // If the current browser is not tabbed then the new tab will be created
125  // in a different browser. Make sure it is visible.
126  browser->window()->Show();
127}
128
129void InstallCRX(Profile* profile, const base::FilePath& file_path) {
130  DCHECK(profile);
131
132  ExtensionService* service =
133      extensions::ExtensionSystem::Get(profile)->extension_service();
134  CHECK(service);
135
136  scoped_refptr<extensions::CrxInstaller> installer(
137      extensions::CrxInstaller::Create(
138          service,
139          scoped_ptr<ExtensionInstallPrompt>(new ExtensionInstallPrompt(
140              profile, NULL, NULL))));
141  installer->set_error_on_unsupported_requirements(true);
142  installer->set_is_gallery_install(false);
143  installer->set_allow_silent_install(false);
144  installer->InstallCrx(file_path);
145}
146
147// Called when a crx file on Drive was downloaded.
148void OnCRXDownloadCallback(Profile* profile,
149                           drive::FileError error,
150                           const base::FilePath& file,
151                           scoped_ptr<drive::ResourceEntry> entry) {
152  DCHECK(profile);
153
154  if (error != drive::FILE_ERROR_OK)
155    return;
156  InstallCRX(profile, file);
157}
158
159// Reads the alternate URL from a GDoc file. When it fails, returns a file URL
160// for |file_path| as fallback.
161// Note that an alternate url is a URL to open a hosted document.
162GURL ReadUrlFromGDocOnBlockingPool(const base::FilePath& file_path) {
163  GURL url = drive::util::ReadUrlFromGDocFile(file_path);
164  if (url.is_empty())
165    url = net::FilePathToFileURL(file_path);
166  return url;
167}
168
169}  // namespace
170
171bool OpenFileWithBrowser(Profile* profile, const base::FilePath& file_path) {
172  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
173  DCHECK(profile);
174
175  // For things supported natively by the browser, we should open it
176  // in a tab.
177  if (IsViewableInBrowser(file_path) ||
178      ShouldBeOpenedWithPlugin(profile, file_path.Extension())) {
179    GURL page_url = net::FilePathToFileURL(file_path);
180    // Override drive resource to point to internal handler instead of file URL.
181    if (drive::util::IsUnderDriveMountPoint(file_path)) {
182      page_url = drive::util::FilePathToDriveURL(
183          drive::util::ExtractDrivePath(file_path));
184    }
185    OpenNewTab(profile, page_url);
186    return true;
187  }
188
189  if (drive::util::HasGDocFileExtension(file_path)) {
190    if (drive::util::IsUnderDriveMountPoint(file_path)) {
191      // The file is on Google Docs. Open with drive URL.
192      GURL url = drive::util::FilePathToDriveURL(
193          drive::util::ExtractDrivePath(file_path));
194      OpenNewTab(profile, url);
195    } else {
196      // The file is local (downloaded from an attachment or otherwise copied).
197      // Parse the file to extract the Docs url and open this url.
198      base::PostTaskAndReplyWithResult(
199          BrowserThread::GetBlockingPool(),
200          FROM_HERE,
201          base::Bind(&ReadUrlFromGDocOnBlockingPool, file_path),
202          base::Bind(&OpenNewTab, profile));
203    }
204    return true;
205  }
206
207  if (file_path.MatchesExtension(kCRXExtension)) {
208    if (drive::util::IsUnderDriveMountPoint(file_path)) {
209      drive::FileSystemInterface* file_system =
210          drive::util::GetFileSystemByProfile(profile);
211      if (!file_system)
212        return false;
213      file_system->GetFileByPath(
214          drive::util::ExtractDrivePath(file_path),
215          base::Bind(&OnCRXDownloadCallback, profile));
216    } else {
217      InstallCRX(profile, file_path);
218    }
219    return true;
220  }
221
222  // Failed to open the file of unknown type.
223  LOG(WARNING) << "Unknown file type: " << file_path.value();
224  return false;
225}
226
227// If a bundled plugin is enabled, we should open pdf/swf files in a tab.
228bool ShouldBeOpenedWithPlugin(
229    Profile* profile,
230    const base::FilePath::StringType& file_extension) {
231  DCHECK(profile);
232
233  const base::FilePath file_path =
234      base::FilePath::FromUTF8Unsafe("dummy").AddExtension(file_extension);
235  if (file_path.MatchesExtension(kPdfExtension))
236    return IsPdfPluginEnabled(profile);
237  if (file_path.MatchesExtension(kSwfExtension))
238    return IsFlashPluginEnabled(profile);
239  return false;
240}
241
242}  // namespace util
243}  // namespace file_manager
244