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/managed_bookmark_service_factory.h"
25 #include "chrome/browser/extensions/api/bookmarks/bookmark_api_constants.h"
26 #include "chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.h"
27 #include "chrome/browser/importer/external_process_importer_host.h"
28 #include "chrome/browser/importer/importer_uma.h"
29 #include "chrome/browser/platform_util.h"
30 #include "chrome/browser/profiles/profile.h"
31 #include "chrome/browser/ui/chrome_select_file_policy.h"
32 #include "chrome/browser/ui/host_desktop.h"
33 #include "chrome/common/chrome_paths.h"
34 #include "chrome/common/extensions/api/bookmarks.h"
35 #include "chrome/common/importer/importer_data_types.h"
36 #include "chrome/common/pref_names.h"
37 #include "chrome/grit/generated_resources.h"
38 #include "components/bookmarks/browser/bookmark_model.h"
39 #include "components/bookmarks/browser/bookmark_utils.h"
40 #include "components/bookmarks/managed/managed_bookmark_service.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 using bookmarks::BookmarkModel
;
55 using bookmarks::BookmarkNode
;
56 using bookmarks::ManagedBookmarkService
;
58 namespace extensions
{
60 namespace keys
= bookmark_api_constants
;
61 namespace bookmarks
= api::bookmarks
;
63 using bookmarks::BookmarkTreeNode
;
64 using bookmarks::CreateDetails
;
65 using content::BrowserContext
;
66 using content::BrowserThread
;
67 using content::WebContents
;
71 // Generates a default path (including a default filename) that will be
72 // used for pre-populating the "Export Bookmarks" file chooser dialog box.
73 base::FilePath
GetDefaultFilepathForBookmarkExport() {
74 base::Time time
= base::Time::Now();
76 // Concatenate a date stamp to the filename.
78 base::FilePath::StringType filename
=
79 l10n_util::GetStringFUTF8(IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME
,
80 base::TimeFormatShortDateNumeric(time
));
82 base::FilePath::StringType filename
=
83 l10n_util::GetStringFUTF16(IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME
,
84 base::TimeFormatShortDateNumeric(time
));
87 base::i18n::ReplaceIllegalCharactersInPath(&filename
, '_');
89 base::FilePath default_path
;
90 PathService::Get(chrome::DIR_USER_DOCUMENTS
, &default_path
);
91 return default_path
.Append(filename
);
96 bool BookmarksFunction::RunAsync() {
97 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(GetProfile());
98 if (!model
->loaded()) {
99 // Bookmarks are not ready yet. We'll wait.
100 model
->AddObserver(this);
101 AddRef(); // Balanced in Loaded().
105 RunAndSendResponse();
109 BookmarkModel
* BookmarksFunction::GetBookmarkModel() {
110 return BookmarkModelFactory::GetForProfile(GetProfile());
113 ManagedBookmarkService
* BookmarksFunction::GetManagedBookmarkService() {
114 return ManagedBookmarkServiceFactory::GetForProfile(GetProfile());
117 bool BookmarksFunction::GetBookmarkIdAsInt64(const std::string
& id_string
,
119 if (base::StringToInt64(id_string
, id
))
122 error_
= keys::kInvalidIdError
;
126 const BookmarkNode
* BookmarksFunction::GetBookmarkNodeFromId(
127 const std::string
& id_string
) {
129 if (!GetBookmarkIdAsInt64(id_string
, &id
))
132 const BookmarkNode
* node
= ::bookmarks::GetBookmarkNodeByID(
133 BookmarkModelFactory::GetForProfile(GetProfile()), id
);
135 error_
= keys::kNoNodeError
;
140 const BookmarkNode
* BookmarksFunction::CreateBookmarkNode(
141 BookmarkModel
* model
,
142 const CreateDetails
& details
,
143 const BookmarkNode::MetaInfoMap
* meta_info
) {
146 if (!details
.parent_id
.get()) {
147 // Optional, default to "other bookmarks".
148 parentId
= model
->other_node()->id();
150 if (!GetBookmarkIdAsInt64(*details
.parent_id
, &parentId
))
153 const BookmarkNode
* parent
=
154 ::bookmarks::GetBookmarkNodeByID(model
, parentId
);
155 if (!CanBeModified(parent
))
159 if (!details
.index
.get()) { // Optional (defaults to end).
160 index
= parent
->child_count();
162 index
= *details
.index
;
163 if (index
> parent
->child_count() || index
< 0) {
164 error_
= keys::kInvalidIndexError
;
169 base::string16 title
; // Optional.
170 if (details
.title
.get())
171 title
= base::UTF8ToUTF16(*details
.title
.get());
173 std::string url_string
; // Optional.
174 if (details
.url
.get())
175 url_string
= *details
.url
.get();
177 GURL
url(url_string
);
178 if (!url_string
.empty() && !url
.is_valid()) {
179 error_
= keys::kInvalidUrlError
;
183 const BookmarkNode
* node
;
184 if (url_string
.length()) {
185 node
= model
->AddURLWithCreationTimeAndMetaInfo(
186 parent
, index
, title
, url
, base::Time::Now(), meta_info
);
188 node
= model
->AddFolderWithMetaInfo(parent
, index
, title
, meta_info
);
189 model
->SetDateFolderModified(parent
, base::Time::Now());
197 bool BookmarksFunction::EditBookmarksEnabled() {
198 PrefService
* prefs
= user_prefs::UserPrefs::Get(GetProfile());
199 if (prefs
->GetBoolean(::bookmarks::prefs::kEditBookmarksEnabled
))
201 error_
= keys::kEditBookmarksDisabled
;
205 bool BookmarksFunction::CanBeModified(const BookmarkNode
* node
) {
207 error_
= keys::kNoParentError
;
210 if (node
->is_root()) {
211 error_
= keys::kModifySpecialError
;
214 ManagedBookmarkService
* managed
= GetManagedBookmarkService();
215 if (::bookmarks::IsDescendantOf(node
, managed
->managed_node()) ||
216 ::bookmarks::IsDescendantOf(node
, managed
->supervised_node())) {
217 error_
= keys::kModifyManagedError
;
223 void BookmarksFunction::BookmarkModelChanged() {
226 void BookmarksFunction::BookmarkModelLoaded(BookmarkModel
* model
,
227 bool ids_reassigned
) {
228 model
->RemoveObserver(this);
229 RunAndSendResponse();
230 Release(); // Balanced in RunOnReady().
233 void BookmarksFunction::RunAndSendResponse() {
234 bool success
= RunOnReady();
236 content::NotificationService::current()->Notify(
237 extensions::NOTIFICATION_EXTENSION_BOOKMARKS_API_INVOKED
,
238 content::Source
<const Extension
>(extension()),
239 content::Details
<const BookmarksFunction
>(this));
241 SendResponse(success
);
244 BookmarkEventRouter::BookmarkEventRouter(Profile
* profile
)
245 : browser_context_(profile
),
246 model_(BookmarkModelFactory::GetForProfile(profile
)),
247 managed_(ManagedBookmarkServiceFactory::GetForProfile(profile
)) {
248 model_
->AddObserver(this);
251 BookmarkEventRouter::~BookmarkEventRouter() {
253 model_
->RemoveObserver(this);
257 void BookmarkEventRouter::DispatchEvent(
258 events::HistogramValue histogram_value
,
259 const std::string
& event_name
,
260 scoped_ptr
<base::ListValue
> event_args
) {
261 EventRouter
* event_router
= EventRouter::Get(browser_context_
);
263 event_router
->BroadcastEvent(make_scoped_ptr(
264 new extensions::Event(histogram_value
, event_name
, event_args
.Pass())));
268 void BookmarkEventRouter::BookmarkModelLoaded(BookmarkModel
* model
,
269 bool ids_reassigned
) {
270 // TODO(erikkay): Perhaps we should send this event down to the extension
271 // so they know when it's safe to use the API?
274 void BookmarkEventRouter::BookmarkModelBeingDeleted(BookmarkModel
* model
) {
278 void BookmarkEventRouter::BookmarkNodeMoved(BookmarkModel
* model
,
279 const BookmarkNode
* old_parent
,
281 const BookmarkNode
* new_parent
,
283 const BookmarkNode
* node
= new_parent
->GetChild(new_index
);
284 bookmarks::OnMoved::MoveInfo move_info
;
285 move_info
.parent_id
= base::Int64ToString(new_parent
->id());
286 move_info
.index
= new_index
;
287 move_info
.old_parent_id
= base::Int64ToString(old_parent
->id());
288 move_info
.old_index
= old_index
;
291 events::BOOKMARKS_ON_MOVED
, bookmarks::OnMoved::kEventName
,
292 bookmarks::OnMoved::Create(base::Int64ToString(node
->id()), move_info
));
295 void BookmarkEventRouter::BookmarkNodeAdded(BookmarkModel
* model
,
296 const BookmarkNode
* parent
,
298 const BookmarkNode
* node
= parent
->GetChild(index
);
299 scoped_ptr
<BookmarkTreeNode
> tree_node(
300 bookmark_api_helpers::GetBookmarkTreeNode(managed_
, node
, false, false));
301 DispatchEvent(events::BOOKMARKS_ON_CREATED
, bookmarks::OnCreated::kEventName
,
302 bookmarks::OnCreated::Create(base::Int64ToString(node
->id()),
306 void BookmarkEventRouter::BookmarkNodeRemoved(
307 BookmarkModel
* model
,
308 const BookmarkNode
* parent
,
310 const BookmarkNode
* node
,
311 const std::set
<GURL
>& removed_urls
) {
312 bookmarks::OnRemoved::RemoveInfo remove_info
;
313 remove_info
.parent_id
= base::Int64ToString(parent
->id());
314 remove_info
.index
= index
;
316 DispatchEvent(events::BOOKMARKS_ON_REMOVED
, bookmarks::OnRemoved::kEventName
,
317 bookmarks::OnRemoved::Create(base::Int64ToString(node
->id()),
321 void BookmarkEventRouter::BookmarkAllUserNodesRemoved(
322 BookmarkModel
* model
,
323 const std::set
<GURL
>& removed_urls
) {
325 // TODO(shashishekhar) Currently this notification is only used on Android,
326 // which does not support extensions. If Desktop needs to support this, add
327 // a new event to the extensions api.
330 void BookmarkEventRouter::BookmarkNodeChanged(BookmarkModel
* model
,
331 const BookmarkNode
* node
) {
332 // TODO(erikkay) The only three things that BookmarkModel sends this
333 // notification for are title, url and favicon. Since we're currently
334 // ignoring favicon and since the notification doesn't say which one anyway,
335 // for now we only include title and url. The ideal thing would be to change
336 // BookmarkModel to indicate what changed.
337 bookmarks::OnChanged::ChangeInfo change_info
;
338 change_info
.title
= base::UTF16ToUTF8(node
->GetTitle());
340 change_info
.url
.reset(new std::string(node
->url().spec()));
342 DispatchEvent(events::BOOKMARKS_ON_CHANGED
, bookmarks::OnChanged::kEventName
,
343 bookmarks::OnChanged::Create(base::Int64ToString(node
->id()),
347 void BookmarkEventRouter::BookmarkNodeFaviconChanged(BookmarkModel
* model
,
348 const BookmarkNode
* node
) {
349 // TODO(erikkay) anything we should do here?
352 void BookmarkEventRouter::BookmarkNodeChildrenReordered(
353 BookmarkModel
* model
,
354 const BookmarkNode
* node
) {
355 bookmarks::OnChildrenReordered::ReorderInfo reorder_info
;
356 int childCount
= node
->child_count();
357 for (int i
= 0; i
< childCount
; ++i
) {
358 const BookmarkNode
* child
= node
->GetChild(i
);
359 reorder_info
.child_ids
.push_back(base::Int64ToString(child
->id()));
362 DispatchEvent(events::BOOKMARKS_ON_CHILDREN_REORDERED
,
363 bookmarks::OnChildrenReordered::kEventName
,
364 bookmarks::OnChildrenReordered::Create(
365 base::Int64ToString(node
->id()), reorder_info
));
368 void BookmarkEventRouter::ExtensiveBookmarkChangesBeginning(
369 BookmarkModel
* model
) {
370 DispatchEvent(events::BOOKMARKS_ON_IMPORT_BEGAN
,
371 bookmarks::OnImportBegan::kEventName
,
372 bookmarks::OnImportBegan::Create());
375 void BookmarkEventRouter::ExtensiveBookmarkChangesEnded(BookmarkModel
* model
) {
376 DispatchEvent(events::BOOKMARKS_ON_IMPORT_ENDED
,
377 bookmarks::OnImportEnded::kEventName
,
378 bookmarks::OnImportEnded::Create());
381 BookmarksAPI::BookmarksAPI(BrowserContext
* context
)
382 : browser_context_(context
) {
383 EventRouter
* event_router
= EventRouter::Get(browser_context_
);
384 event_router
->RegisterObserver(this, bookmarks::OnCreated::kEventName
);
385 event_router
->RegisterObserver(this, bookmarks::OnRemoved::kEventName
);
386 event_router
->RegisterObserver(this, bookmarks::OnChanged::kEventName
);
387 event_router
->RegisterObserver(this, bookmarks::OnMoved::kEventName
);
388 event_router
->RegisterObserver(this,
389 bookmarks::OnChildrenReordered::kEventName
);
390 event_router
->RegisterObserver(this, bookmarks::OnImportBegan::kEventName
);
391 event_router
->RegisterObserver(this, bookmarks::OnImportEnded::kEventName
);
394 BookmarksAPI::~BookmarksAPI() {
397 void BookmarksAPI::Shutdown() {
398 EventRouter::Get(browser_context_
)->UnregisterObserver(this);
401 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<BookmarksAPI
> >
402 g_factory
= LAZY_INSTANCE_INITIALIZER
;
405 BrowserContextKeyedAPIFactory
<BookmarksAPI
>*
406 BookmarksAPI::GetFactoryInstance() {
407 return g_factory
.Pointer();
410 void BookmarksAPI::OnListenerAdded(const EventListenerInfo
& details
) {
411 bookmark_event_router_
.reset(
412 new BookmarkEventRouter(Profile::FromBrowserContext(browser_context_
)));
413 EventRouter::Get(browser_context_
)->UnregisterObserver(this);
416 bool BookmarksGetFunction::RunOnReady() {
417 scoped_ptr
<bookmarks::Get::Params
> params(
418 bookmarks::Get::Params::Create(*args_
));
419 EXTENSION_FUNCTION_VALIDATE(params
.get());
421 std::vector
<linked_ptr
<BookmarkTreeNode
> > nodes
;
422 ManagedBookmarkService
* managed
= GetManagedBookmarkService();
423 if (params
->id_or_id_list
.as_strings
) {
424 std::vector
<std::string
>& ids
= *params
->id_or_id_list
.as_strings
;
425 size_t count
= ids
.size();
426 EXTENSION_FUNCTION_VALIDATE(count
> 0);
427 for (size_t i
= 0; i
< count
; ++i
) {
428 const BookmarkNode
* node
= GetBookmarkNodeFromId(ids
[i
]);
431 bookmark_api_helpers::AddNode(managed
, node
, &nodes
, false);
434 const BookmarkNode
* node
=
435 GetBookmarkNodeFromId(*params
->id_or_id_list
.as_string
);
438 bookmark_api_helpers::AddNode(managed
, node
, &nodes
, false);
441 results_
= bookmarks::Get::Results::Create(nodes
);
445 bool BookmarksGetChildrenFunction::RunOnReady() {
446 scoped_ptr
<bookmarks::GetChildren::Params
> params(
447 bookmarks::GetChildren::Params::Create(*args_
));
448 EXTENSION_FUNCTION_VALIDATE(params
.get());
450 const BookmarkNode
* node
= GetBookmarkNodeFromId(params
->id
);
454 std::vector
<linked_ptr
<BookmarkTreeNode
> > nodes
;
455 int child_count
= node
->child_count();
456 for (int i
= 0; i
< child_count
; ++i
) {
457 const BookmarkNode
* child
= node
->GetChild(i
);
458 bookmark_api_helpers::AddNode(GetManagedBookmarkService(), child
, &nodes
,
462 results_
= bookmarks::GetChildren::Results::Create(nodes
);
466 bool BookmarksGetRecentFunction::RunOnReady() {
467 scoped_ptr
<bookmarks::GetRecent::Params
> params(
468 bookmarks::GetRecent::Params::Create(*args_
));
469 EXTENSION_FUNCTION_VALIDATE(params
.get());
470 if (params
->number_of_items
< 1)
473 std::vector
<const BookmarkNode
*> nodes
;
474 ::bookmarks::GetMostRecentlyAddedEntries(
475 BookmarkModelFactory::GetForProfile(GetProfile()),
476 params
->number_of_items
,
479 std::vector
<linked_ptr
<BookmarkTreeNode
> > tree_nodes
;
480 std::vector
<const BookmarkNode
*>::iterator i
= nodes
.begin();
481 for (; i
!= nodes
.end(); ++i
) {
482 const BookmarkNode
* node
= *i
;
483 bookmark_api_helpers::AddNode(GetManagedBookmarkService(), node
,
487 results_
= bookmarks::GetRecent::Results::Create(tree_nodes
);
491 bool BookmarksGetTreeFunction::RunOnReady() {
492 std::vector
<linked_ptr
<BookmarkTreeNode
> > nodes
;
493 const BookmarkNode
* node
=
494 BookmarkModelFactory::GetForProfile(GetProfile())->root_node();
495 bookmark_api_helpers::AddNode(GetManagedBookmarkService(), node
, &nodes
,
497 results_
= bookmarks::GetTree::Results::Create(nodes
);
501 bool BookmarksGetSubTreeFunction::RunOnReady() {
502 scoped_ptr
<bookmarks::GetSubTree::Params
> params(
503 bookmarks::GetSubTree::Params::Create(*args_
));
504 EXTENSION_FUNCTION_VALIDATE(params
.get());
506 const BookmarkNode
* node
= GetBookmarkNodeFromId(params
->id
);
510 std::vector
<linked_ptr
<BookmarkTreeNode
> > nodes
;
511 bookmark_api_helpers::AddNode(GetManagedBookmarkService(), node
, &nodes
,
513 results_
= bookmarks::GetSubTree::Results::Create(nodes
);
517 bool BookmarksSearchFunction::RunOnReady() {
518 scoped_ptr
<bookmarks::Search::Params
> params(
519 bookmarks::Search::Params::Create(*args_
));
520 EXTENSION_FUNCTION_VALIDATE(params
.get());
522 PrefService
* prefs
= user_prefs::UserPrefs::Get(GetProfile());
523 std::string lang
= prefs
->GetString(prefs::kAcceptLanguages
);
524 std::vector
<const BookmarkNode
*> nodes
;
525 if (params
->query
.as_string
) {
526 ::bookmarks::QueryFields query
;
527 query
.word_phrase_query
.reset(
528 new base::string16(base::UTF8ToUTF16(*params
->query
.as_string
)));
529 ::bookmarks::GetBookmarksMatchingProperties(
530 BookmarkModelFactory::GetForProfile(GetProfile()),
532 std::numeric_limits
<int>::max(),
536 DCHECK(params
->query
.as_object
);
537 const bookmarks::Search::Params::Query::Object
& object
=
538 *params
->query
.as_object
;
539 ::bookmarks::QueryFields query
;
541 query
.word_phrase_query
.reset(
542 new base::string16(base::UTF8ToUTF16(*object
.query
)));
545 query
.url
.reset(new base::string16(base::UTF8ToUTF16(*object
.url
)));
547 query
.title
.reset(new base::string16(base::UTF8ToUTF16(*object
.title
)));
548 ::bookmarks::GetBookmarksMatchingProperties(
549 BookmarkModelFactory::GetForProfile(GetProfile()),
551 std::numeric_limits
<int>::max(),
556 std::vector
<linked_ptr
<BookmarkTreeNode
> > tree_nodes
;
557 ManagedBookmarkService
* managed
= GetManagedBookmarkService();
558 for (std::vector
<const BookmarkNode
*>::iterator node_iter
= nodes
.begin();
559 node_iter
!= nodes
.end(); ++node_iter
) {
560 bookmark_api_helpers::AddNode(managed
, *node_iter
, &tree_nodes
, false);
563 results_
= bookmarks::Search::Results::Create(tree_nodes
);
568 bool BookmarksRemoveFunction::ExtractIds(const base::ListValue
* args
,
569 std::list
<int64
>* ids
,
571 std::string id_string
;
572 if (!args
->GetString(0, &id_string
))
575 if (base::StringToInt64(id_string
, &id
))
582 bool BookmarksRemoveFunction::RunOnReady() {
583 if (!EditBookmarksEnabled())
586 scoped_ptr
<bookmarks::Remove::Params
> params(
587 bookmarks::Remove::Params::Create(*args_
));
588 EXTENSION_FUNCTION_VALIDATE(params
.get());
591 if (!GetBookmarkIdAsInt64(params
->id
, &id
))
594 bool recursive
= false;
595 if (name() == BookmarksRemoveTreeFunction::function_name())
598 BookmarkModel
* model
= GetBookmarkModel();
599 ManagedBookmarkService
* managed
= GetManagedBookmarkService();
600 if (!bookmark_api_helpers::RemoveNode(model
, managed
, id
, recursive
, &error_
))
606 bool BookmarksCreateFunction::RunOnReady() {
607 if (!EditBookmarksEnabled())
610 scoped_ptr
<bookmarks::Create::Params
> params(
611 bookmarks::Create::Params::Create(*args_
));
612 EXTENSION_FUNCTION_VALIDATE(params
.get());
614 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(GetProfile());
615 const BookmarkNode
* node
= CreateBookmarkNode(model
, params
->bookmark
, NULL
);
619 scoped_ptr
<BookmarkTreeNode
> ret(bookmark_api_helpers::GetBookmarkTreeNode(
620 GetManagedBookmarkService(), node
, false, false));
621 results_
= bookmarks::Create::Results::Create(*ret
);
627 bool BookmarksMoveFunction::ExtractIds(const base::ListValue
* args
,
628 std::list
<int64
>* ids
,
630 // For now, Move accepts ID parameters in the same way as an Update.
631 return BookmarksUpdateFunction::ExtractIds(args
, ids
, invalid_id
);
634 bool BookmarksMoveFunction::RunOnReady() {
635 if (!EditBookmarksEnabled())
638 scoped_ptr
<bookmarks::Move::Params
> params(
639 bookmarks::Move::Params::Create(*args_
));
640 EXTENSION_FUNCTION_VALIDATE(params
.get());
642 const BookmarkNode
* node
= GetBookmarkNodeFromId(params
->id
);
646 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(GetProfile());
647 if (model
->is_permanent_node(node
)) {
648 error_
= keys::kModifySpecialError
;
652 const BookmarkNode
* parent
= NULL
;
653 if (!params
->destination
.parent_id
.get()) {
654 // Optional, defaults to current parent.
655 parent
= node
->parent();
658 if (!GetBookmarkIdAsInt64(*params
->destination
.parent_id
, &parentId
))
661 parent
= ::bookmarks::GetBookmarkNodeByID(model
, parentId
);
663 if (!CanBeModified(parent
) || !CanBeModified(node
))
667 if (params
->destination
.index
.get()) { // Optional (defaults to end).
668 index
= *params
->destination
.index
;
669 if (index
> parent
->child_count() || index
< 0) {
670 error_
= keys::kInvalidIndexError
;
674 index
= parent
->child_count();
677 model
->Move(node
, parent
, index
);
679 scoped_ptr
<BookmarkTreeNode
> tree_node(
680 bookmark_api_helpers::GetBookmarkTreeNode(GetManagedBookmarkService(),
681 node
, false, false));
682 results_
= bookmarks::Move::Results::Create(*tree_node
);
688 bool BookmarksUpdateFunction::ExtractIds(const base::ListValue
* args
,
689 std::list
<int64
>* ids
,
691 // For now, Update accepts ID parameters in the same way as an Remove.
692 return BookmarksRemoveFunction::ExtractIds(args
, ids
, invalid_id
);
695 bool BookmarksUpdateFunction::RunOnReady() {
696 if (!EditBookmarksEnabled())
699 scoped_ptr
<bookmarks::Update::Params
> params(
700 bookmarks::Update::Params::Create(*args_
));
701 EXTENSION_FUNCTION_VALIDATE(params
.get());
703 // Optional but we need to distinguish non present from an empty title.
704 base::string16 title
;
705 bool has_title
= false;
706 if (params
->changes
.title
.get()) {
707 title
= base::UTF8ToUTF16(*params
->changes
.title
);
712 std::string url_string
;
713 if (params
->changes
.url
.get())
714 url_string
= *params
->changes
.url
;
715 GURL
url(url_string
);
716 if (!url_string
.empty() && !url
.is_valid()) {
717 error_
= keys::kInvalidUrlError
;
721 const BookmarkNode
* node
= GetBookmarkNodeFromId(params
->id
);
722 if (!CanBeModified(node
))
725 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(GetProfile());
726 if (model
->is_permanent_node(node
)) {
727 error_
= keys::kModifySpecialError
;
731 model
->SetTitle(node
, title
);
733 model
->SetURL(node
, url
);
735 scoped_ptr
<BookmarkTreeNode
> tree_node(
736 bookmark_api_helpers::GetBookmarkTreeNode(GetManagedBookmarkService(),
737 node
, false, false));
738 results_
= bookmarks::Update::Results::Create(*tree_node
);
742 BookmarksIOFunction::BookmarksIOFunction() {}
744 BookmarksIOFunction::~BookmarksIOFunction() {
745 // There may be pending file dialogs, we need to tell them that we've gone
746 // away so they don't try and call back to us.
747 if (select_file_dialog_
.get())
748 select_file_dialog_
->ListenerDestroyed();
751 void BookmarksIOFunction::SelectFile(ui::SelectFileDialog::Type type
) {
752 // GetDefaultFilepathForBookmarkExport() might have to touch the filesystem
753 // (stat or access, for example), so this requires a thread with IO allowed.
754 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
755 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
,
756 base::Bind(&BookmarksIOFunction::SelectFile
, this, type
));
760 // Pre-populating the filename field in case this is a SELECT_SAVEAS_FILE
761 // dialog. If not, there is no filename field in the dialog box.
762 base::FilePath default_path
;
763 if (type
== ui::SelectFileDialog::SELECT_SAVEAS_FILE
)
764 default_path
= GetDefaultFilepathForBookmarkExport();
766 DCHECK(type
== ui::SelectFileDialog::SELECT_OPEN_FILE
);
768 // After getting the |default_path|, ask the UI to display the file dialog.
769 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
770 base::Bind(&BookmarksIOFunction::ShowSelectFileDialog
, this,
771 type
, default_path
));
774 void BookmarksIOFunction::ShowSelectFileDialog(
775 ui::SelectFileDialog::Type type
,
776 const base::FilePath
& default_path
) {
778 return; // Extension was unloaded.
780 // Balanced in one of the three callbacks of SelectFileDialog:
781 // either FileSelectionCanceled, MultiFilesSelected, or FileSelected
784 WebContents
* web_contents
= GetAssociatedWebContents();
786 select_file_dialog_
= ui::SelectFileDialog::Create(
787 this, new ChromeSelectFilePolicy(web_contents
));
788 ui::SelectFileDialog::FileTypeInfo file_type_info
;
789 file_type_info
.extensions
.resize(1);
790 file_type_info
.extensions
[0].push_back(FILE_PATH_LITERAL("html"));
791 gfx::NativeWindow owning_window
= web_contents
?
792 platform_util::GetTopLevel(web_contents
->GetNativeView())
795 if (!owning_window
&&
796 chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH
)
797 owning_window
= aura::RemoteWindowTreeHostWin::Instance()->GetAshWindow();
799 // |web_contents| can be NULL (for background pages), which is fine. In such
800 // a case if file-selection dialogs are forbidden by policy, we will not
801 // show an InfoBar, which is better than letting one appear out of the blue.
802 select_file_dialog_
->SelectFile(type
,
807 base::FilePath::StringType(),
812 void BookmarksIOFunction::FileSelectionCanceled(void* params
) {
813 Release(); // Balanced in BookmarksIOFunction::SelectFile()
816 void BookmarksIOFunction::MultiFilesSelected(
817 const std::vector
<base::FilePath
>& files
, void* params
) {
818 Release(); // Balanced in BookmarsIOFunction::SelectFile()
819 NOTREACHED() << "Should not be able to select multiple files";
822 bool BookmarksImportFunction::RunOnReady() {
823 if (!EditBookmarksEnabled())
825 SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE
);
829 void BookmarksImportFunction::FileSelected(const base::FilePath
& path
,
833 ExternalProcessImporterHost
* importer_host
= new ExternalProcessImporterHost
;
834 importer::SourceProfile source_profile
;
835 source_profile
.importer_type
= importer::TYPE_BOOKMARKS_FILE
;
836 source_profile
.source_path
= path
;
837 importer_host
->StartImportSettings(source_profile
,
840 new ProfileWriter(GetProfile()));
842 importer::LogImporterUseToMetrics("BookmarksAPI",
843 importer::TYPE_BOOKMARKS_FILE
);
844 Release(); // Balanced in BookmarksIOFunction::SelectFile()
847 bool BookmarksExportFunction::RunOnReady() {
848 SelectFile(ui::SelectFileDialog::SELECT_SAVEAS_FILE
);
852 void BookmarksExportFunction::FileSelected(const base::FilePath
& path
,
855 bookmark_html_writer::WriteBookmarks(GetProfile(), path
, NULL
);
856 Release(); // Balanced in BookmarksIOFunction::SelectFile()
859 } // namespace extensions