OLD | NEW |
(Empty) | |
| 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/views/select_file_dialog_extension_views.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/callback.h" |
| 9 #include "base/logging.h" |
| 10 #include "base/memory/ref_counted.h" |
| 11 #include "base/memory/singleton.h" |
| 12 #include "base/message_loop.h" |
| 13 #include "chrome/browser/browser_process.h" |
| 14 #include "chrome/browser/chromeos/extensions/file_browser_private_api.h" |
| 15 #include "chrome/browser/chromeos/extensions/file_manager_util.h" |
| 16 #include "chrome/browser/extensions/extension_host.h" |
| 17 #include "chrome/browser/extensions/extension_service.h" |
| 18 #include "chrome/browser/extensions/shell_window_registry.h" |
| 19 #include "chrome/browser/profiles/profile_manager.h" |
| 20 #include "chrome/browser/sessions/restore_tab_helper.h" |
| 21 #include "chrome/browser/ui/base_window.h" |
| 22 #include "chrome/browser/ui/browser.h" |
| 23 #include "chrome/browser/ui/browser_finder.h" |
| 24 #include "chrome/browser/ui/browser_list.h" |
| 25 #include "chrome/browser/ui/browser_tabstrip.h" |
| 26 #include "chrome/browser/ui/browser_window.h" |
| 27 #include "chrome/browser/ui/chrome_select_file_policy.h" |
| 28 #include "chrome/browser/ui/extensions/shell_window.h" |
| 29 #include "chrome/browser/ui/select_file_dialog_extension.h" |
| 30 #include "chrome/browser/ui/tab_contents/tab_contents.h" |
| 31 #include "chrome/browser/ui/views/extensions/extension_dialog.h" |
| 32 #include "content/public/browser/browser_thread.h" |
| 33 #include "ui/base/dialogs/selected_file_info.h" |
| 34 |
| 35 using content::BrowserThread; |
| 36 |
| 37 namespace { |
| 38 |
| 39 const int kFileManagerWidth = 954; // pixels |
| 40 const int kFileManagerHeight = 640; // pixels |
| 41 |
| 42 const int kFileManagerMinimumWidth = kFileManagerWidth * 2 / 3; // pixels |
| 43 const int kFileManagerMinimumHeight = kFileManagerHeight * 2 / 3; // pixels |
| 44 |
| 45 // Holds references to file manager dialogs that have callbacks pending |
| 46 // to their listeners. |
| 47 class PendingDialog { |
| 48 public: |
| 49 static PendingDialog* GetInstance(); |
| 50 void Add(int32 tab_id, scoped_refptr<SelectFileDialogExtensionViews> dialog); |
| 51 void Remove(int32 tab_id); |
| 52 scoped_refptr<SelectFileDialogExtensionViews> Find(int32 tab_id); |
| 53 |
| 54 private: |
| 55 friend struct DefaultSingletonTraits<PendingDialog>; |
| 56 typedef std::map<int32, scoped_refptr<SelectFileDialogExtensionViews> > Map; |
| 57 Map map_; |
| 58 }; |
| 59 |
| 60 // static |
| 61 PendingDialog* PendingDialog::GetInstance() { |
| 62 return Singleton<PendingDialog>::get(); |
| 63 } |
| 64 |
| 65 void PendingDialog::Add(int32 tab_id, |
| 66 scoped_refptr<SelectFileDialogExtensionViews> dialog) { |
| 67 DCHECK(dialog); |
| 68 if (map_.find(tab_id) == map_.end()) |
| 69 map_.insert(std::make_pair(tab_id, dialog)); |
| 70 else |
| 71 DLOG(WARNING) << "Duplicate pending dialog " << tab_id; |
| 72 } |
| 73 |
| 74 void PendingDialog::Remove(int32 tab_id) { |
| 75 map_.erase(tab_id); |
| 76 } |
| 77 |
| 78 scoped_refptr<SelectFileDialogExtensionViews> PendingDialog::Find( |
| 79 int32 tab_id) { |
| 80 Map::const_iterator it = map_.find(tab_id); |
| 81 if (it == map_.end()) |
| 82 return NULL; |
| 83 return it->second; |
| 84 } |
| 85 |
| 86 } // namespace |
| 87 |
| 88 ///////////////////////////////////////////////////////////////////////////// |
| 89 |
| 90 // TODO(jamescook): Move this into a new file shell_dialogs_chromeos.cc |
| 91 // TODO(jamescook): Change all instances of SelectFileDialog::Create to return |
| 92 // scoped_refptr<SelectFileDialog> as object is ref-counted. |
| 93 // static |
| 94 SelectFileDialogExtension* SelectFileDialogExtension::Create( |
| 95 Listener* listener, |
| 96 ui::SelectFilePolicy* policy) { |
| 97 return new SelectFileDialogExtension(listener, policy); |
| 98 } |
| 99 |
| 100 // static |
| 101 void SelectFileDialogExtensionViews::OnFileSelected( |
| 102 int32 tab_id, |
| 103 const ui::SelectedFileInfo& file, |
| 104 int index) { |
| 105 scoped_refptr<SelectFileDialogExtensionViews> dialog = |
| 106 PendingDialog::GetInstance()->Find(tab_id); |
| 107 if (!dialog) |
| 108 return; |
| 109 dialog->selection_type_ = SINGLE_FILE; |
| 110 dialog->selection_files_.clear(); |
| 111 dialog->selection_files_.push_back(file); |
| 112 dialog->selection_index_ = index; |
| 113 } |
| 114 |
| 115 // static |
| 116 void SelectFileDialogExtensionViews::OnMultiFilesSelected( |
| 117 int32 tab_id, |
| 118 const std::vector<ui::SelectedFileInfo>& files) { |
| 119 scoped_refptr<SelectFileDialogExtensionViews> dialog = |
| 120 PendingDialog::GetInstance()->Find(tab_id); |
| 121 if (!dialog) |
| 122 return; |
| 123 dialog->selection_type_ = MULTIPLE_FILES; |
| 124 dialog->selection_files_ = files; |
| 125 dialog->selection_index_ = 0; |
| 126 } |
| 127 |
| 128 // static |
| 129 void SelectFileDialogExtensionViews::OnFileSelectionCanceled(int32 tab_id) { |
| 130 scoped_refptr<SelectFileDialogExtensionViews> dialog = |
| 131 PendingDialog::GetInstance()->Find(tab_id); |
| 132 if (!dialog) |
| 133 return; |
| 134 dialog->selection_type_ = CANCEL; |
| 135 dialog->selection_files_.clear(); |
| 136 dialog->selection_index_ = 0; |
| 137 } |
| 138 |
| 139 bool SelectFileDialogExtensionViews::IsRunning( |
| 140 gfx::NativeWindow owner_window) const { |
| 141 return owner_window_ == owner_window; |
| 142 } |
| 143 |
| 144 void SelectFileDialogExtensionViews::ListenerDestroyed() { |
| 145 listener_ = NULL; |
| 146 params_ = NULL; |
| 147 PendingDialog::GetInstance()->Remove(tab_id_); |
| 148 } |
| 149 |
| 150 void SelectFileDialogExtensionViews::ExtensionDialogClosing( |
| 151 ExtensionDialog* dialog) { |
| 152 profile_ = NULL; |
| 153 owner_window_ = NULL; |
| 154 // Release our reference to the dialog to allow it to close. |
| 155 extension_dialog_ = NULL; |
| 156 PendingDialog::GetInstance()->Remove(tab_id_); |
| 157 // Actually invoke the appropriate callback on our listener. |
| 158 NotifyListener(); |
| 159 } |
| 160 |
| 161 void SelectFileDialogExtensionViews::ExtensionTerminated( |
| 162 ExtensionDialog* dialog) { |
| 163 // The extension would have been unloaded because of the termination, |
| 164 // reload it. |
| 165 std::string extension_id = dialog->host()->extension()->id(); |
| 166 // Reload the extension after a bit; the extension may not have been unloaded |
| 167 // yet. We don't want to try to reload the extension only to have the Unload |
| 168 // code execute after us and re-unload the extension. |
| 169 // |
| 170 // TODO(rkc): This is ugly. The ideal solution is that we shouldn't need to |
| 171 // reload the extension at all - when we try to open the extension the next |
| 172 // time, the extension subsystem would automatically reload it for us. At |
| 173 // this time though this is broken because of some faulty wiring in |
| 174 // ExtensionProcessManager::CreateViewHost. Once that is fixed, remove this. |
| 175 if (profile_) { |
| 176 MessageLoop::current()->PostTask(FROM_HERE, |
| 177 base::Bind(&ExtensionService::ReloadExtension, |
| 178 base::Unretained(profile_->GetExtensionService()), |
| 179 extension_id)); |
| 180 } |
| 181 |
| 182 dialog->Close(); |
| 183 } |
| 184 |
| 185 content::RenderViewHost* SelectFileDialogExtensionViews::GetRenderViewHost() { |
| 186 if (extension_dialog_) |
| 187 return extension_dialog_->host()->render_view_host(); |
| 188 return NULL; |
| 189 } |
| 190 |
| 191 void SelectFileDialogExtensionViews::NotifyListener() { |
| 192 if (!listener_) |
| 193 return; |
| 194 switch (selection_type_) { |
| 195 case CANCEL: |
| 196 listener_->FileSelectionCanceled(params_); |
| 197 break; |
| 198 case SINGLE_FILE: |
| 199 listener_->FileSelectedWithExtraInfo(selection_files_[0], |
| 200 selection_index_, |
| 201 params_); |
| 202 break; |
| 203 case MULTIPLE_FILES: |
| 204 listener_->MultiFilesSelectedWithExtraInfo(selection_files_, params_); |
| 205 break; |
| 206 default: |
| 207 NOTREACHED(); |
| 208 break; |
| 209 } |
| 210 } |
| 211 |
| 212 void SelectFileDialogExtensionViews::AddPending(int32 tab_id) { |
| 213 PendingDialog::GetInstance()->Add(tab_id, this); |
| 214 } |
| 215 |
| 216 // static |
| 217 bool SelectFileDialogExtensionViews::PendingExists(int32 tab_id) { |
| 218 return PendingDialog::GetInstance()->Find(tab_id) != NULL; |
| 219 } |
| 220 |
| 221 bool SelectFileDialogExtensionViews::HasMultipleFileTypeChoicesImpl() { |
| 222 return has_multiple_file_type_choices_; |
| 223 } |
| 224 |
| 225 void SelectFileDialogExtensionViews::SelectFileImpl( |
| 226 Type type, |
| 227 const string16& title, |
| 228 const FilePath& default_path, |
| 229 const FileTypeInfo* file_types, |
| 230 int file_type_index, |
| 231 const FilePath::StringType& default_extension, |
| 232 gfx::NativeWindow owner_window, |
| 233 void* params) { |
| 234 if (owner_window_) { |
| 235 LOG(ERROR) << "File dialog already in use!"; |
| 236 return; |
| 237 } |
| 238 |
| 239 // The base window to associate the dialog with. |
| 240 BaseWindow* base_window = NULL; |
| 241 |
| 242 // The tab contents to associate the dialog with. |
| 243 const TabContents* tab = NULL; |
| 244 |
| 245 // First try to find a Browser using the supplied owner_window. If no owner |
| 246 // window has been supplied, this is running from a background page and should |
| 247 // be associated with the last active browser. |
| 248 Browser* owner_browser = (owner_window ? |
| 249 browser::FindBrowserWithWindow(owner_window) : |
| 250 BrowserList::GetLastActive()); |
| 251 if (owner_browser) { |
| 252 base_window = owner_browser->window(); |
| 253 tab = chrome::GetActiveTabContents(owner_browser); |
| 254 profile_ = tab->profile(); |
| 255 } else if (owner_window) { |
| 256 // If an owner_window was supplied but we couldn't find a browser, this |
| 257 // could be for a shell window. |
| 258 // TODO(benwells): Find a better way to get a shell window from a native |
| 259 // window. |
| 260 std::vector<Profile*> profiles = |
| 261 g_browser_process->profile_manager()->GetLoadedProfiles(); |
| 262 for (std::vector<Profile*>::const_iterator i(profiles.begin()); |
| 263 i < profiles.end(); ++i) { |
| 264 ShellWindowRegistry* registry = ShellWindowRegistry::Get(*i); |
| 265 DCHECK(registry); |
| 266 ShellWindow* shell_window = registry->GetShellWindowForNativeWindow( |
| 267 owner_window); |
| 268 if (shell_window) { |
| 269 base_window = shell_window; |
| 270 tab = shell_window->tab_contents(); |
| 271 profile_ = *i; |
| 272 break; |
| 273 } |
| 274 } |
| 275 } |
| 276 |
| 277 if (!base_window) { |
| 278 NOTREACHED() << "Can't find owning window."; |
| 279 return; |
| 280 } |
| 281 DCHECK(profile_); |
| 282 |
| 283 // Check if we have another dialog opened in the tab. It's unlikely, but |
| 284 // possible. If there is no tab contents use a tab_id of 0. A dialog without |
| 285 // an associated tab contents will be shown fully screen; only one at a time |
| 286 // is allowed in this state. |
| 287 int32 tab_id = tab ? tab->restore_tab_helper()->session_id().id() : 0; |
| 288 if (PendingExists(tab_id)) { |
| 289 DLOG(WARNING) << "Pending dialog exists with id " << tab_id; |
| 290 return; |
| 291 } |
| 292 |
| 293 FilePath virtual_path; |
| 294 if (file_manager_util::ConvertFileToRelativeFileSystemPath( |
| 295 profile_, default_path, &virtual_path)) { |
| 296 virtual_path = FilePath("/").Append(virtual_path); |
| 297 } else { |
| 298 virtual_path = default_path.BaseName(); |
| 299 } |
| 300 |
| 301 has_multiple_file_type_choices_ = |
| 302 file_types ? file_types->extensions.size() > 1 : true; |
| 303 |
| 304 GURL file_browser_url = file_manager_util::GetFileBrowserUrlWithParams( |
| 305 type, title, virtual_path, file_types, file_type_index, |
| 306 default_extension); |
| 307 |
| 308 ExtensionDialog* dialog = ExtensionDialog::Show(file_browser_url, |
| 309 base_window, profile_, tab->web_contents(), |
| 310 kFileManagerWidth, kFileManagerHeight, |
| 311 #if defined(USE_AURA) |
| 312 file_manager_util::GetTitleFromType(type), |
| 313 #else |
| 314 // HTML-based header used. |
| 315 string16(), |
| 316 #endif |
| 317 this /* ExtensionDialog::Observer */); |
| 318 if (!dialog) { |
| 319 LOG(ERROR) << "Unable to create extension dialog"; |
| 320 return; |
| 321 } |
| 322 |
| 323 dialog->SetMinimumContentsSize(kFileManagerMinimumWidth, |
| 324 kFileManagerMinimumHeight); |
| 325 |
| 326 // Connect our listener to FileDialogFunction's per-tab callbacks. |
| 327 AddPending(tab_id); |
| 328 |
| 329 extension_dialog_ = dialog; |
| 330 params_ = params; |
| 331 tab_id_ = tab_id; |
| 332 owner_window_ = owner_window; |
| 333 } |
| 334 |
| 335 SelectFileDialogExtensionViews::SelectFileDialogExtensionViews( |
| 336 Listener* listener, |
| 337 ui::SelectFilePolicy* policy) |
| 338 : SelectFileDialog(listener, policy), |
| 339 has_multiple_file_type_choices_(false), |
| 340 tab_id_(0), |
| 341 profile_(NULL), |
| 342 owner_window_(NULL), |
| 343 selection_type_(CANCEL), |
| 344 selection_index_(0), |
| 345 params_(NULL) { |
| 346 } |
| 347 |
| 348 SelectFileDialogExtensionViews::~SelectFileDialogExtensionViews() { |
| 349 if (extension_dialog_) |
| 350 extension_dialog_->ObserverDestroyed(); |
| 351 } |
| 352 |
OLD | NEW |