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/file_system_util.h"
14#include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
15#include "chrome/browser/chromeos/fileapi/external_file_url_util.h"
16#include "chrome/browser/drive/drive_api_util.h"
17#include "chrome/browser/plugins/plugin_prefs.h"
18#include "chrome/browser/profiles/profile_manager.h"
19#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
20#include "chrome/browser/ui/browser.h"
21#include "chrome/browser/ui/browser_tabstrip.h"
22#include "chrome/browser/ui/browser_window.h"
23#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
24#include "chrome/common/chrome_paths.h"
25#include "chrome/common/chrome_switches.h"
26#include "content/public/browser/browser_thread.h"
27#include "content/public/browser/plugin_service.h"
28#include "content/public/common/pepper_plugin_info.h"
29#include "net/base/filename_util.h"
30
31using content::BrowserThread;
32using content::PluginService;
33
34namespace file_manager {
35namespace util {
36namespace {
37
38const base::FilePath::CharType kPdfExtension[] = FILE_PATH_LITERAL(".pdf");
39const base::FilePath::CharType kSwfExtension[] = FILE_PATH_LITERAL(".swf");
40
41// List of file extensions viewable in the browser.
42const base::FilePath::CharType* kFileExtensionsViewableInBrowser[] = {
43  FILE_PATH_LITERAL(".bmp"),
44  FILE_PATH_LITERAL(".ico"),
45  FILE_PATH_LITERAL(".jpg"),
46  FILE_PATH_LITERAL(".jpeg"),
47  FILE_PATH_LITERAL(".png"),
48  FILE_PATH_LITERAL(".webp"),
49  FILE_PATH_LITERAL(".gif"),
50  FILE_PATH_LITERAL(".txt"),
51  FILE_PATH_LITERAL(".html"),
52  FILE_PATH_LITERAL(".htm"),
53  FILE_PATH_LITERAL(".mhtml"),
54  FILE_PATH_LITERAL(".mht"),
55  FILE_PATH_LITERAL(".svg"),
56};
57
58// Returns true if |file_path| is viewable in the browser (ex. HTML file).
59bool IsViewableInBrowser(const base::FilePath& file_path) {
60  for (size_t i = 0; i < arraysize(kFileExtensionsViewableInBrowser); i++) {
61    if (file_path.MatchesExtension(kFileExtensionsViewableInBrowser[i]))
62      return true;
63  }
64  return false;
65}
66
67bool IsPepperPluginEnabled(Profile* profile,
68                           const base::FilePath& plugin_path) {
69  DCHECK(profile);
70
71  content::PepperPluginInfo* pepper_info =
72      PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(plugin_path);
73  if (!pepper_info)
74    return false;
75
76  scoped_refptr<PluginPrefs> plugin_prefs = PluginPrefs::GetForProfile(profile);
77  if (!plugin_prefs.get())
78    return false;
79
80  return plugin_prefs->IsPluginEnabled(pepper_info->ToWebPluginInfo());
81}
82
83bool IsPdfPluginEnabled(Profile* profile) {
84  DCHECK(profile);
85
86  base::FilePath plugin_path;
87  PathService::Get(chrome::FILE_PDF_PLUGIN, &plugin_path);
88  return IsPepperPluginEnabled(profile, plugin_path);
89}
90
91bool IsFlashPluginEnabled(Profile* profile) {
92  DCHECK(profile);
93
94  base::FilePath plugin_path(
95      CommandLine::ForCurrentProcess()->GetSwitchValueNative(
96          switches::kPpapiFlashPath));
97  if (plugin_path.empty())
98    PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN, &plugin_path);
99  return IsPepperPluginEnabled(profile, plugin_path);
100}
101
102void OpenNewTab(Profile* profile, const GURL& url) {
103  DCHECK_CURRENTLY_ON(BrowserThread::UI);
104
105  // Check the validity of the pointer so that the closure from
106  // base::Bind(&OpenNewTab, profile) can be passed between threads.
107  if (!g_browser_process->profile_manager()->IsValidProfile(profile))
108    return;
109
110  chrome::ScopedTabbedBrowserDisplayer displayer(
111      profile, chrome::HOST_DESKTOP_TYPE_ASH);
112  chrome::AddSelectedTabWithURL(displayer.browser(), url,
113      ui::PAGE_TRANSITION_LINK);
114
115  // Since the ScopedTabbedBrowserDisplayer does not guarantee that the
116  // browser will be shown on the active desktop, we ensure the visibility.
117  multi_user_util::MoveWindowToCurrentDesktop(
118      displayer.browser()->window()->GetNativeWindow());
119}
120
121// Reads the alternate URL from a GDoc file. When it fails, returns a file URL
122// for |file_path| as fallback.
123// Note that an alternate url is a URL to open a hosted document.
124GURL ReadUrlFromGDocOnBlockingPool(const base::FilePath& file_path) {
125  GURL url = drive::util::ReadUrlFromGDocFile(file_path);
126  if (url.is_empty())
127    url = net::FilePathToFileURL(file_path);
128  return url;
129}
130
131}  // namespace
132
133bool OpenFileWithBrowser(Profile* profile,
134                         const storage::FileSystemURL& file_system_url) {
135  DCHECK_CURRENTLY_ON(BrowserThread::UI);
136  DCHECK(profile);
137
138  const base::FilePath file_path = file_system_url.path();
139
140  // For things supported natively by the browser, we should open it
141  // in a tab.
142  if (IsViewableInBrowser(file_path) ||
143      ShouldBeOpenedWithPlugin(profile, file_path.Extension())) {
144    // Use external file URL if it is provided for the file system.
145    GURL page_url = chromeos::FileSystemURLToExternalFileURL(file_system_url);
146    if (page_url.is_empty())
147      page_url = net::FilePathToFileURL(file_path);
148
149    OpenNewTab(profile, page_url);
150    return true;
151  }
152
153  if (drive::util::HasHostedDocumentExtension(file_path)) {
154    if (file_manager::util::IsUnderNonNativeLocalPath(profile, file_path)) {
155      // The file is on a non-native volume. Use external file URL. If the file
156      // is on the drive volume, ExternalFileURLRequestJob redirects the URL to
157      // drive's web interface. Otherwise (e.g. MTP, FSP), the file is just
158      // downloaded in a browser tab.
159      const GURL url =
160          chromeos::FileSystemURLToExternalFileURL(file_system_url);
161      DCHECK(!url.is_empty());
162      OpenNewTab(profile, url);
163    } else {
164      // The file is local (downloaded from an attachment or otherwise copied).
165      // Parse the file to extract the Docs url and open this url.
166      base::PostTaskAndReplyWithResult(
167          BrowserThread::GetBlockingPool(),
168          FROM_HERE,
169          base::Bind(&ReadUrlFromGDocOnBlockingPool, file_path),
170          base::Bind(&OpenNewTab, profile));
171    }
172    return true;
173  }
174
175  // Failed to open the file of unknown type.
176  LOG(WARNING) << "Unknown file type: " << file_path.value();
177  return false;
178}
179
180// If a bundled plugin is enabled, we should open pdf/swf files in a tab.
181bool ShouldBeOpenedWithPlugin(
182    Profile* profile,
183    const base::FilePath::StringType& file_extension) {
184  DCHECK(profile);
185
186  const base::FilePath file_path =
187      base::FilePath::FromUTF8Unsafe("dummy").AddExtension(file_extension);
188  if (file_path.MatchesExtension(kPdfExtension))
189    return IsPdfPluginEnabled(profile);
190  if (file_path.MatchesExtension(kSwfExtension))
191    return IsFlashPluginEnabled(profile);
192  return false;
193}
194
195}  // namespace util
196}  // namespace file_manager
197