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.
5 #include "chrome/browser/extensions/api/bookmarks/bookmarks_api.h"
8 #include "base/files/file_path.h"
9 #include "base/i18n/file_util_icu.h"
10 #include "base/i18n/time_formatting.h"
11 #include "base/lazy_instance.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/path_service.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/sha1.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string16.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/time/time.h"
22 #include "chrome/browser/bookmarks/bookmark_html_writer.h"
23 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
24 #include "chrome/browser/bookmarks/chrome_bookmark_client.h"
25 #include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h"
26 #include "chrome/browser/extensions/api/bookmarks/bookmark_api_constants.h"
27 #include "chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.h"
28 #include "chrome/browser/importer/external_process_importer_host.h"
29 #include "chrome/browser/importer/importer_uma.h"
30 #include "chrome/browser/platform_util.h"
31 #include "chrome/browser/profiles/profile.h"
32 #include "chrome/browser/ui/chrome_select_file_policy.h"
33 #include "chrome/browser/ui/host_desktop.h"
34 #include "chrome/common/chrome_paths.h"
35 #include "chrome/common/extensions/api/bookmarks.h"
36 #include "chrome/common/importer/importer_data_types.h"
37 #include "chrome/common/pref_names.h"
38 #include "chrome/grit/generated_resources.h"
39 #include "components/bookmarks/browser/bookmark_model.h"
40 #include "components/bookmarks/browser/bookmark_utils.h"
41 #include "components/user_prefs/user_prefs.h"
42 #include "content/public/browser/browser_context.h"
43 #include "content/public/browser/notification_service.h"
44 #include "content/public/browser/web_contents.h"
45 #include "extensions/browser/event_router.h"
46 #include "extensions/browser/extension_function_dispatcher.h"
47 #include "extensions/browser/notification_types.h"
48 #include "ui/base/l10n/l10n_util.h"
51 #include "ui/aura/remote_window_tree_host_win.h"
54 namespace extensions {
56 namespace keys = bookmark_api_constants;
57 namespace bookmarks = api::bookmarks;
59 using bookmarks::BookmarkTreeNode;
60 using bookmarks::CreateDetails;
61 using content::BrowserContext;
62 using content::BrowserThread;
63 using content::WebContents;
67 // Generates a default path (including a default filename) that will be
68 // used for pre-populating the "Export Bookmarks" file chooser dialog box.
69 base::FilePath GetDefaultFilepathForBookmarkExport() {
70 base::Time time = base::Time::Now();
72 // Concatenate a date stamp to the filename.
74 base::FilePath::StringType filename =
75 l10n_util::GetStringFUTF8(IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME,
76 base::TimeFormatShortDateNumeric(time));
78 base::FilePath::StringType filename =
79 l10n_util::GetStringFUTF16(IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME,
80 base::TimeFormatShortDateNumeric(time));
83 base::i18n::ReplaceIllegalCharactersInPath(&filename, '_');
85 base::FilePath default_path;
86 PathService::Get(chrome::DIR_USER_DOCUMENTS, &default_path);
87 return default_path.Append(filename);
92 bool BookmarksFunction::RunAsync() {
93 BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
94 if (!model->loaded()) {
95 // Bookmarks are not ready yet. We'll wait.
96 model->AddObserver(this);
97 AddRef(); // Balanced in Loaded().
101 RunAndSendResponse();
105 BookmarkModel* BookmarksFunction::GetBookmarkModel() {
106 return BookmarkModelFactory::GetForProfile(GetProfile());
109 ChromeBookmarkClient* BookmarksFunction::GetChromeBookmarkClient() {
110 return ChromeBookmarkClientFactory::GetForProfile(GetProfile());
113 bool BookmarksFunction::GetBookmarkIdAsInt64(const std::string& id_string,
115 if (base::StringToInt64(id_string, id))
118 error_ = keys::kInvalidIdError;
122 const BookmarkNode* BookmarksFunction::GetBookmarkNodeFromId(
123 const std::string& id_string) {
125 if (!GetBookmarkIdAsInt64(id_string, &id))
128 const BookmarkNode* node = ::bookmarks::GetBookmarkNodeByID(
129 BookmarkModelFactory::GetForProfile(GetProfile()), id);
131 error_ = keys::kNoNodeError;
136 const BookmarkNode* BookmarksFunction::CreateBookmarkNode(
137 BookmarkModel* model,
138 const CreateDetails& details,
139 const BookmarkNode::MetaInfoMap* meta_info) {
142 if (!details.parent_id.get()) {
143 // Optional, default to "other bookmarks".
144 parentId = model->other_node()->id();
146 if (!GetBookmarkIdAsInt64(*details.parent_id, &parentId))
149 const BookmarkNode* parent =
150 ::bookmarks::GetBookmarkNodeByID(model, parentId);
151 if (!CanBeModified(parent))
155 if (!details.index.get()) { // Optional (defaults to end).
156 index = parent->child_count();
158 index = *details.index;
159 if (index > parent->child_count() || index < 0) {
160 error_ = keys::kInvalidIndexError;
165 base::string16 title; // Optional.
166 if (details.title.get())
167 title = base::UTF8ToUTF16(*details.title.get());
169 std::string url_string; // Optional.
170 if (details.url.get())
171 url_string = *details.url.get();
173 GURL url(url_string);
174 if (!url_string.empty() && !url.is_valid()) {
175 error_ = keys::kInvalidUrlError;
179 const BookmarkNode* node;
180 if (url_string.length())
181 node = model->AddURLWithCreationTimeAndMetaInfo(
182 parent, index, title, url, base::Time::Now(), meta_info);
184 node = model->AddFolderWithMetaInfo(parent, index, title, meta_info);
187 error_ = keys::kNoNodeError;
194 bool BookmarksFunction::EditBookmarksEnabled() {
195 PrefService* prefs = user_prefs::UserPrefs::Get(GetProfile());
196 if (prefs->GetBoolean(::bookmarks::prefs::kEditBookmarksEnabled))
198 error_ = keys::kEditBookmarksDisabled;
202 bool BookmarksFunction::CanBeModified(const BookmarkNode* node) {
204 error_ = keys::kNoParentError;
207 if (node->is_root()) {
208 error_ = keys::kModifySpecialError;
211 ChromeBookmarkClient* client = GetChromeBookmarkClient();
212 if (client->IsDescendantOfManagedNode(node)) {
213 error_ = keys::kModifyManagedError;
219 void BookmarksFunction::BookmarkModelChanged() {
222 void BookmarksFunction::BookmarkModelLoaded(BookmarkModel* model,
223 bool ids_reassigned) {
224 model->RemoveObserver(this);
225 RunAndSendResponse();
226 Release(); // Balanced in RunOnReady().
229 void BookmarksFunction::RunAndSendResponse() {
230 bool success = RunOnReady();
232 content::NotificationService::current()->Notify(
233 extensions::NOTIFICATION_EXTENSION_BOOKMARKS_API_INVOKED,
234 content::Source<const Extension>(extension()),
235 content::Details<const BookmarksFunction>(this));
237 SendResponse(success);
240 BookmarkEventRouter::BookmarkEventRouter(Profile* profile)
241 : browser_context_(profile),
242 model_(BookmarkModelFactory::GetForProfile(profile)),
243 client_(ChromeBookmarkClientFactory::GetForProfile(profile)) {
244 model_->AddObserver(this);
247 BookmarkEventRouter::~BookmarkEventRouter() {
249 model_->RemoveObserver(this);
253 void BookmarkEventRouter::DispatchEvent(
254 const std::string& event_name,
255 scoped_ptr<base::ListValue> event_args) {
256 EventRouter* event_router = EventRouter::Get(browser_context_);
258 event_router->BroadcastEvent(
259 make_scoped_ptr(new extensions::Event(event_name, event_args.Pass())));
263 void BookmarkEventRouter::BookmarkModelLoaded(BookmarkModel* model,
264 bool ids_reassigned) {
265 // TODO(erikkay): Perhaps we should send this event down to the extension
266 // so they know when it's safe to use the API?
269 void BookmarkEventRouter::BookmarkModelBeingDeleted(BookmarkModel* model) {
273 void BookmarkEventRouter::BookmarkNodeMoved(BookmarkModel* model,
274 const BookmarkNode* old_parent,
276 const BookmarkNode* new_parent,
278 const BookmarkNode* node = new_parent->GetChild(new_index);
279 bookmarks::OnMoved::MoveInfo move_info;
280 move_info.parent_id = base::Int64ToString(new_parent->id());
281 move_info.index = new_index;
282 move_info.old_parent_id = base::Int64ToString(old_parent->id());
283 move_info.old_index = old_index;
286 bookmarks::OnMoved::kEventName,
287 bookmarks::OnMoved::Create(base::Int64ToString(node->id()), move_info));
290 void BookmarkEventRouter::BookmarkNodeAdded(BookmarkModel* model,
291 const BookmarkNode* parent,
293 const BookmarkNode* node = parent->GetChild(index);
294 scoped_ptr<BookmarkTreeNode> tree_node(
295 bookmark_api_helpers::GetBookmarkTreeNode(client_, node, false, false));
296 DispatchEvent(bookmarks::OnCreated::kEventName,
297 bookmarks::OnCreated::Create(base::Int64ToString(node->id()),
301 void BookmarkEventRouter::BookmarkNodeRemoved(
302 BookmarkModel* model,
303 const BookmarkNode* parent,
305 const BookmarkNode* node,
306 const std::set<GURL>& removed_urls) {
307 bookmarks::OnRemoved::RemoveInfo remove_info;
308 remove_info.parent_id = base::Int64ToString(parent->id());
309 remove_info.index = index;
311 DispatchEvent(bookmarks::OnRemoved::kEventName,
312 bookmarks::OnRemoved::Create(base::Int64ToString(node->id()),
316 void BookmarkEventRouter::BookmarkAllUserNodesRemoved(
317 BookmarkModel* model,
318 const std::set<GURL>& removed_urls) {
320 // TODO(shashishekhar) Currently this notification is only used on Android,
321 // which does not support extensions. If Desktop needs to support this, add
322 // a new event to the extensions api.
325 void BookmarkEventRouter::BookmarkNodeChanged(BookmarkModel* model,
326 const BookmarkNode* node) {
327 // TODO(erikkay) The only three things that BookmarkModel sends this
328 // notification for are title, url and favicon. Since we're currently
329 // ignoring favicon and since the notification doesn't say which one anyway,
330 // for now we only include title and url. The ideal thing would be to change
331 // BookmarkModel to indicate what changed.
332 bookmarks::OnChanged::ChangeInfo change_info;
333 change_info.title = base::UTF16ToUTF8(node->GetTitle());
335 change_info.url.reset(new std::string(node->url().spec()));
337 DispatchEvent(bookmarks::OnChanged::kEventName,
338 bookmarks::OnChanged::Create(base::Int64ToString(node->id()),
342 void BookmarkEventRouter::BookmarkNodeFaviconChanged(BookmarkModel* model,
343 const BookmarkNode* node) {
344 // TODO(erikkay) anything we should do here?
347 void BookmarkEventRouter::BookmarkNodeChildrenReordered(
348 BookmarkModel* model,
349 const BookmarkNode* node) {
350 bookmarks::OnChildrenReordered::ReorderInfo reorder_info;
351 int childCount = node->child_count();
352 for (int i = 0; i < childCount; ++i) {
353 const BookmarkNode* child = node->GetChild(i);
354 reorder_info.child_ids.push_back(base::Int64ToString(child->id()));
357 DispatchEvent(bookmarks::OnChildrenReordered::kEventName,
358 bookmarks::OnChildrenReordered::Create(
359 base::Int64ToString(node->id()), reorder_info));
362 void BookmarkEventRouter::ExtensiveBookmarkChangesBeginning(
363 BookmarkModel* model) {
364 DispatchEvent(bookmarks::OnImportBegan::kEventName,
365 bookmarks::OnImportBegan::Create());
368 void BookmarkEventRouter::ExtensiveBookmarkChangesEnded(BookmarkModel* model) {
369 DispatchEvent(bookmarks::OnImportEnded::kEventName,
370 bookmarks::OnImportEnded::Create());
373 BookmarksAPI::BookmarksAPI(BrowserContext* context)
374 : browser_context_(context) {
375 EventRouter* event_router = EventRouter::Get(browser_context_);
376 event_router->RegisterObserver(this, bookmarks::OnCreated::kEventName);
377 event_router->RegisterObserver(this, bookmarks::OnRemoved::kEventName);
378 event_router->RegisterObserver(this, bookmarks::OnChanged::kEventName);
379 event_router->RegisterObserver(this, bookmarks::OnMoved::kEventName);
380 event_router->RegisterObserver(this,
381 bookmarks::OnChildrenReordered::kEventName);
382 event_router->RegisterObserver(this, bookmarks::OnImportBegan::kEventName);
383 event_router->RegisterObserver(this, bookmarks::OnImportEnded::kEventName);
386 BookmarksAPI::~BookmarksAPI() {
389 void BookmarksAPI::Shutdown() {
390 EventRouter::Get(browser_context_)->UnregisterObserver(this);
393 static base::LazyInstance<BrowserContextKeyedAPIFactory<BookmarksAPI> >
394 g_factory = LAZY_INSTANCE_INITIALIZER;
397 BrowserContextKeyedAPIFactory<BookmarksAPI>*
398 BookmarksAPI::GetFactoryInstance() {
399 return g_factory.Pointer();
402 void BookmarksAPI::OnListenerAdded(const EventListenerInfo& details) {
403 bookmark_event_router_.reset(
404 new BookmarkEventRouter(Profile::FromBrowserContext(browser_context_)));
405 EventRouter::Get(browser_context_)->UnregisterObserver(this);
408 bool BookmarksGetFunction::RunOnReady() {
409 scoped_ptr<bookmarks::Get::Params> params(
410 bookmarks::Get::Params::Create(*args_));
411 EXTENSION_FUNCTION_VALIDATE(params.get());
413 std::vector<linked_ptr<BookmarkTreeNode> > nodes;
414 ChromeBookmarkClient* client = GetChromeBookmarkClient();
415 if (params->id_or_id_list.as_strings) {
416 std::vector<std::string>& ids = *params->id_or_id_list.as_strings;
417 size_t count = ids.size();
418 EXTENSION_FUNCTION_VALIDATE(count > 0);
419 for (size_t i = 0; i < count; ++i) {
420 const BookmarkNode* node = GetBookmarkNodeFromId(ids[i]);
423 bookmark_api_helpers::AddNode(client, node, &nodes, false);
426 const BookmarkNode* node =
427 GetBookmarkNodeFromId(*params->id_or_id_list.as_string);
430 bookmark_api_helpers::AddNode(client, node, &nodes, false);
433 results_ = bookmarks::Get::Results::Create(nodes);
437 bool BookmarksGetChildrenFunction::RunOnReady() {
438 scoped_ptr<bookmarks::GetChildren::Params> params(
439 bookmarks::GetChildren::Params::Create(*args_));
440 EXTENSION_FUNCTION_VALIDATE(params.get());
442 const BookmarkNode* node = GetBookmarkNodeFromId(params->id);
446 std::vector<linked_ptr<BookmarkTreeNode> > nodes;
447 int child_count = node->child_count();
448 for (int i = 0; i < child_count; ++i) {
449 const BookmarkNode* child = node->GetChild(i);
450 bookmark_api_helpers::AddNode(
451 GetChromeBookmarkClient(), child, &nodes, false);
454 results_ = bookmarks::GetChildren::Results::Create(nodes);
458 bool BookmarksGetRecentFunction::RunOnReady() {
459 scoped_ptr<bookmarks::GetRecent::Params> params(
460 bookmarks::GetRecent::Params::Create(*args_));
461 EXTENSION_FUNCTION_VALIDATE(params.get());
462 if (params->number_of_items < 1)
465 std::vector<const BookmarkNode*> nodes;
466 ::bookmarks::GetMostRecentlyAddedEntries(
467 BookmarkModelFactory::GetForProfile(GetProfile()),
468 params->number_of_items,
471 std::vector<linked_ptr<BookmarkTreeNode> > tree_nodes;
472 std::vector<const BookmarkNode*>::iterator i = nodes.begin();
473 for (; i != nodes.end(); ++i) {
474 const BookmarkNode* node = *i;
475 bookmark_api_helpers::AddNode(
476 GetChromeBookmarkClient(), node, &tree_nodes, false);
479 results_ = bookmarks::GetRecent::Results::Create(tree_nodes);
483 bool BookmarksGetTreeFunction::RunOnReady() {
484 std::vector<linked_ptr<BookmarkTreeNode> > nodes;
485 const BookmarkNode* node =
486 BookmarkModelFactory::GetForProfile(GetProfile())->root_node();
487 bookmark_api_helpers::AddNode(GetChromeBookmarkClient(), node, &nodes, true);
488 results_ = bookmarks::GetTree::Results::Create(nodes);
492 bool BookmarksGetSubTreeFunction::RunOnReady() {
493 scoped_ptr<bookmarks::GetSubTree::Params> params(
494 bookmarks::GetSubTree::Params::Create(*args_));
495 EXTENSION_FUNCTION_VALIDATE(params.get());
497 const BookmarkNode* node = GetBookmarkNodeFromId(params->id);
501 std::vector<linked_ptr<BookmarkTreeNode> > nodes;
502 bookmark_api_helpers::AddNode(GetChromeBookmarkClient(), node, &nodes, true);
503 results_ = bookmarks::GetSubTree::Results::Create(nodes);
507 bool BookmarksSearchFunction::RunOnReady() {
508 scoped_ptr<bookmarks::Search::Params> params(
509 bookmarks::Search::Params::Create(*args_));
510 EXTENSION_FUNCTION_VALIDATE(params.get());
512 PrefService* prefs = user_prefs::UserPrefs::Get(GetProfile());
513 std::string lang = prefs->GetString(prefs::kAcceptLanguages);
514 std::vector<const BookmarkNode*> nodes;
515 if (params->query.as_string) {
516 ::bookmarks::QueryFields query;
517 query.word_phrase_query.reset(
518 new base::string16(base::UTF8ToUTF16(*params->query.as_string)));
519 ::bookmarks::GetBookmarksMatchingProperties(
520 BookmarkModelFactory::GetForProfile(GetProfile()),
522 std::numeric_limits<int>::max(),
526 DCHECK(params->query.as_object);
527 const bookmarks::Search::Params::Query::Object& object =
528 *params->query.as_object;
529 ::bookmarks::QueryFields query;
531 query.word_phrase_query.reset(
532 new base::string16(base::UTF8ToUTF16(*object.query)));
535 query.url.reset(new base::string16(base::UTF8ToUTF16(*object.url)));
537 query.title.reset(new base::string16(base::UTF8ToUTF16(*object.title)));
538 ::bookmarks::GetBookmarksMatchingProperties(
539 BookmarkModelFactory::GetForProfile(GetProfile()),
541 std::numeric_limits<int>::max(),
546 std::vector<linked_ptr<BookmarkTreeNode> > tree_nodes;
547 ChromeBookmarkClient* client = GetChromeBookmarkClient();
548 for (std::vector<const BookmarkNode*>::iterator node_iter = nodes.begin();
549 node_iter != nodes.end(); ++node_iter) {
550 bookmark_api_helpers::AddNode(client, *node_iter, &tree_nodes, false);
553 results_ = bookmarks::Search::Results::Create(tree_nodes);
558 bool BookmarksRemoveFunction::ExtractIds(const base::ListValue* args,
559 std::list<int64>* ids,
561 std::string id_string;
562 if (!args->GetString(0, &id_string))
565 if (base::StringToInt64(id_string, &id))
572 bool BookmarksRemoveFunction::RunOnReady() {
573 if (!EditBookmarksEnabled())
576 scoped_ptr<bookmarks::Remove::Params> params(
577 bookmarks::Remove::Params::Create(*args_));
578 EXTENSION_FUNCTION_VALIDATE(params.get());
581 if (!GetBookmarkIdAsInt64(params->id, &id))
584 bool recursive = false;
585 if (name() == BookmarksRemoveTreeFunction::function_name())
588 BookmarkModel* model = GetBookmarkModel();
589 ChromeBookmarkClient* client = GetChromeBookmarkClient();
590 if (!bookmark_api_helpers::RemoveNode(model, client, id, recursive, &error_))
596 bool BookmarksCreateFunction::RunOnReady() {
597 if (!EditBookmarksEnabled())
600 scoped_ptr<bookmarks::Create::Params> params(
601 bookmarks::Create::Params::Create(*args_));
602 EXTENSION_FUNCTION_VALIDATE(params.get());
604 BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
605 const BookmarkNode* node = CreateBookmarkNode(model, params->bookmark, NULL);
609 scoped_ptr<BookmarkTreeNode> ret(bookmark_api_helpers::GetBookmarkTreeNode(
610 GetChromeBookmarkClient(), node, false, false));
611 results_ = bookmarks::Create::Results::Create(*ret);
617 bool BookmarksMoveFunction::ExtractIds(const base::ListValue* args,
618 std::list<int64>* ids,
620 // For now, Move accepts ID parameters in the same way as an Update.
621 return BookmarksUpdateFunction::ExtractIds(args, ids, invalid_id);
624 bool BookmarksMoveFunction::RunOnReady() {
625 if (!EditBookmarksEnabled())
628 scoped_ptr<bookmarks::Move::Params> params(
629 bookmarks::Move::Params::Create(*args_));
630 EXTENSION_FUNCTION_VALIDATE(params.get());
632 const BookmarkNode* node = GetBookmarkNodeFromId(params->id);
636 BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
637 if (model->is_permanent_node(node)) {
638 error_ = keys::kModifySpecialError;
642 const BookmarkNode* parent = NULL;
643 if (!params->destination.parent_id.get()) {
644 // Optional, defaults to current parent.
645 parent = node->parent();
648 if (!GetBookmarkIdAsInt64(*params->destination.parent_id, &parentId))
651 parent = ::bookmarks::GetBookmarkNodeByID(model, parentId);
653 if (!CanBeModified(parent) || !CanBeModified(node))
657 if (params->destination.index.get()) { // Optional (defaults to end).
658 index = *params->destination.index;
659 if (index > parent->child_count() || index < 0) {
660 error_ = keys::kInvalidIndexError;
664 index = parent->child_count();
667 model->Move(node, parent, index);
669 scoped_ptr<BookmarkTreeNode> tree_node(
670 bookmark_api_helpers::GetBookmarkTreeNode(
671 GetChromeBookmarkClient(), node, false, false));
672 results_ = bookmarks::Move::Results::Create(*tree_node);
678 bool BookmarksUpdateFunction::ExtractIds(const base::ListValue* args,
679 std::list<int64>* ids,
681 // For now, Update accepts ID parameters in the same way as an Remove.
682 return BookmarksRemoveFunction::ExtractIds(args, ids, invalid_id);
685 bool BookmarksUpdateFunction::RunOnReady() {
686 if (!EditBookmarksEnabled())
689 scoped_ptr<bookmarks::Update::Params> params(
690 bookmarks::Update::Params::Create(*args_));
691 EXTENSION_FUNCTION_VALIDATE(params.get());
693 // Optional but we need to distinguish non present from an empty title.
694 base::string16 title;
695 bool has_title = false;
696 if (params->changes.title.get()) {
697 title = base::UTF8ToUTF16(*params->changes.title);
702 std::string url_string;
703 if (params->changes.url.get())
704 url_string = *params->changes.url;
705 GURL url(url_string);
706 if (!url_string.empty() && !url.is_valid()) {
707 error_ = keys::kInvalidUrlError;
711 const BookmarkNode* node = GetBookmarkNodeFromId(params->id);
712 if (!CanBeModified(node))
715 BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
716 if (model->is_permanent_node(node)) {
717 error_ = keys::kModifySpecialError;
721 model->SetTitle(node, title);
723 model->SetURL(node, url);
725 scoped_ptr<BookmarkTreeNode> tree_node(
726 bookmark_api_helpers::GetBookmarkTreeNode(
727 GetChromeBookmarkClient(), node, false, false));
728 results_ = bookmarks::Update::Results::Create(*tree_node);
732 BookmarksIOFunction::BookmarksIOFunction() {}
734 BookmarksIOFunction::~BookmarksIOFunction() {
735 // There may be pending file dialogs, we need to tell them that we've gone
736 // away so they don't try and call back to us.
737 if (select_file_dialog_.get())
738 select_file_dialog_->ListenerDestroyed();
741 void BookmarksIOFunction::SelectFile(ui::SelectFileDialog::Type type) {
742 // GetDefaultFilepathForBookmarkExport() might have to touch the filesystem
743 // (stat or access, for example), so this requires a thread with IO allowed.
744 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
745 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
746 base::Bind(&BookmarksIOFunction::SelectFile, this, type));
750 // Pre-populating the filename field in case this is a SELECT_SAVEAS_FILE
751 // dialog. If not, there is no filename field in the dialog box.
752 base::FilePath default_path;
753 if (type == ui::SelectFileDialog::SELECT_SAVEAS_FILE)
754 default_path = GetDefaultFilepathForBookmarkExport();
756 DCHECK(type == ui::SelectFileDialog::SELECT_OPEN_FILE);
758 // After getting the |default_path|, ask the UI to display the file dialog.
759 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
760 base::Bind(&BookmarksIOFunction::ShowSelectFileDialog, this,
761 type, default_path));
764 void BookmarksIOFunction::ShowSelectFileDialog(
765 ui::SelectFileDialog::Type type,
766 const base::FilePath& default_path) {
768 return; // Extension was unloaded.
770 // Balanced in one of the three callbacks of SelectFileDialog:
771 // either FileSelectionCanceled, MultiFilesSelected, or FileSelected
774 WebContents* web_contents = dispatcher()->delegate()->
775 GetAssociatedWebContents();
777 select_file_dialog_ = ui::SelectFileDialog::Create(
778 this, new ChromeSelectFilePolicy(web_contents));
779 ui::SelectFileDialog::FileTypeInfo file_type_info;
780 file_type_info.extensions.resize(1);
781 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("html"));
782 gfx::NativeWindow owning_window = web_contents ?
783 platform_util::GetTopLevel(web_contents->GetNativeView())
786 if (!owning_window &&
787 chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH)
788 owning_window = aura::RemoteWindowTreeHostWin::Instance()->GetAshWindow();
790 // |web_contents| can be NULL (for background pages), which is fine. In such
791 // a case if file-selection dialogs are forbidden by policy, we will not
792 // show an InfoBar, which is better than letting one appear out of the blue.
793 select_file_dialog_->SelectFile(type,
798 base::FilePath::StringType(),
803 void BookmarksIOFunction::FileSelectionCanceled(void* params) {
804 Release(); // Balanced in BookmarksIOFunction::SelectFile()
807 void BookmarksIOFunction::MultiFilesSelected(
808 const std::vector<base::FilePath>& files, void* params) {
809 Release(); // Balanced in BookmarsIOFunction::SelectFile()
810 NOTREACHED() << "Should not be able to select multiple files";
813 bool BookmarksImportFunction::RunOnReady() {
814 if (!EditBookmarksEnabled())
816 SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE);
820 void BookmarksImportFunction::FileSelected(const base::FilePath& path,
824 ExternalProcessImporterHost* importer_host = new ExternalProcessImporterHost;
825 importer::SourceProfile source_profile;
826 source_profile.importer_type = importer::TYPE_BOOKMARKS_FILE;
827 source_profile.source_path = path;
828 importer_host->StartImportSettings(source_profile,
831 new ProfileWriter(GetProfile()));
833 importer::LogImporterUseToMetrics("BookmarksAPI",
834 importer::TYPE_BOOKMARKS_FILE);
835 Release(); // Balanced in BookmarksIOFunction::SelectFile()
838 bool BookmarksExportFunction::RunOnReady() {
839 SelectFile(ui::SelectFileDialog::SELECT_SAVEAS_FILE);
843 void BookmarksExportFunction::FileSelected(const base::FilePath& path,
846 bookmark_html_writer::WriteBookmarks(GetProfile(), path, NULL);
847 Release(); // Balanced in BookmarksIOFunction::SelectFile()
850 } // namespace extensions