Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(232)

Side by Side Diff: chrome/browser/extensions/api/bookmarks/bookmarks_api.cc

Issue 171813010: Move ProfileKeyedAPI implementations to take BrowserContext in the constructor (part 1). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: browser_context_ Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/api/bookmarks/bookmarks_api.h" 5 #include "chrome/browser/extensions/api/bookmarks/bookmarks_api.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/files/file_path.h" 8 #include "base/files/file_path.h"
9 #include "base/i18n/file_util_icu.h" 9 #include "base/i18n/file_util_icu.h"
10 #include "base/i18n/time_formatting.h" 10 #include "base/i18n/time_formatting.h"
(...skipping 21 matching lines...) Expand all
32 #include "chrome/browser/importer/importer_uma.h" 32 #include "chrome/browser/importer/importer_uma.h"
33 #include "chrome/browser/platform_util.h" 33 #include "chrome/browser/platform_util.h"
34 #include "chrome/browser/profiles/profile.h" 34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/ui/chrome_select_file_policy.h" 35 #include "chrome/browser/ui/chrome_select_file_policy.h"
36 #include "chrome/browser/ui/host_desktop.h" 36 #include "chrome/browser/ui/host_desktop.h"
37 #include "chrome/common/chrome_paths.h" 37 #include "chrome/common/chrome_paths.h"
38 #include "chrome/common/extensions/api/bookmarks.h" 38 #include "chrome/common/extensions/api/bookmarks.h"
39 #include "chrome/common/importer/importer_data_types.h" 39 #include "chrome/common/importer/importer_data_types.h"
40 #include "chrome/common/pref_names.h" 40 #include "chrome/common/pref_names.h"
41 #include "components/user_prefs/user_prefs.h" 41 #include "components/user_prefs/user_prefs.h"
42 #include "content/public/browser/browser_context.h"
42 #include "content/public/browser/notification_service.h" 43 #include "content/public/browser/notification_service.h"
43 #include "content/public/browser/web_contents.h" 44 #include "content/public/browser/web_contents.h"
44 #include "content/public/browser/web_contents_view.h" 45 #include "content/public/browser/web_contents_view.h"
45 #include "extensions/browser/event_router.h" 46 #include "extensions/browser/event_router.h"
46 #include "extensions/browser/extension_system.h" 47 #include "extensions/browser/extension_system.h"
47 #include "extensions/browser/quota_service.h" 48 #include "extensions/browser/quota_service.h"
48 #include "grit/generated_resources.h" 49 #include "grit/generated_resources.h"
49 #include "ui/base/l10n/l10n_util.h" 50 #include "ui/base/l10n/l10n_util.h"
50 51
51 #if defined(OS_WIN) 52 #if defined(OS_WIN)
52 #include "ui/aura/remote_window_tree_host_win.h" 53 #include "ui/aura/remote_window_tree_host_win.h"
53 #endif 54 #endif
54 55
55 namespace extensions { 56 namespace extensions {
56 57
57 namespace keys = bookmark_api_constants; 58 namespace keys = bookmark_api_constants;
58 namespace bookmarks = api::bookmarks; 59 namespace bookmarks = api::bookmarks;
59 60
60 using base::TimeDelta; 61 using base::TimeDelta;
61 using bookmarks::BookmarkTreeNode; 62 using bookmarks::BookmarkTreeNode;
63 using content::BrowserContext;
62 using content::BrowserThread; 64 using content::BrowserThread;
63 using content::WebContents; 65 using content::WebContents;
64 66
65 typedef QuotaLimitHeuristic::Bucket Bucket; 67 typedef QuotaLimitHeuristic::Bucket Bucket;
66 typedef QuotaLimitHeuristic::Config Config; 68 typedef QuotaLimitHeuristic::Config Config;
67 typedef QuotaLimitHeuristic::BucketList BucketList; 69 typedef QuotaLimitHeuristic::BucketList BucketList;
68 typedef QuotaService::TimedLimit TimedLimit; 70 typedef QuotaService::TimedLimit TimedLimit;
69 typedef QuotaService::SustainedLimit SustainedLimit; 71 typedef QuotaService::SustainedLimit SustainedLimit;
70 typedef QuotaLimitHeuristic::BucketMapper BucketMapper; 72 typedef QuotaLimitHeuristic::BucketMapper BucketMapper;
71 73
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
149 void BookmarksFunction::BookmarkModelChanged() { 151 void BookmarksFunction::BookmarkModelChanged() {
150 } 152 }
151 153
152 void BookmarksFunction::BookmarkModelLoaded(BookmarkModel* model, 154 void BookmarksFunction::BookmarkModelLoaded(BookmarkModel* model,
153 bool ids_reassigned) { 155 bool ids_reassigned) {
154 model->RemoveObserver(this); 156 model->RemoveObserver(this);
155 Run(); 157 Run();
156 Release(); // Balanced in Run(). 158 Release(); // Balanced in Run().
157 } 159 }
158 160
159 BookmarkEventRouter::BookmarkEventRouter(Profile* profile, BookmarkModel* model) 161 BookmarkEventRouter::BookmarkEventRouter(BrowserContext* context,
160 : profile_(profile), 162 BookmarkModel* model)
161 model_(model) { 163 : browser_context_(context), model_(model) {
162 model_->AddObserver(this); 164 model_->AddObserver(this);
163 } 165 }
164 166
165 BookmarkEventRouter::~BookmarkEventRouter() { 167 BookmarkEventRouter::~BookmarkEventRouter() {
166 if (model_) { 168 if (model_) {
167 model_->RemoveObserver(this); 169 model_->RemoveObserver(this);
168 } 170 }
169 } 171 }
170 172
171 void BookmarkEventRouter::DispatchEvent( 173 void BookmarkEventRouter::DispatchEvent(
172 const std::string& event_name, 174 const std::string& event_name,
173 scoped_ptr<base::ListValue> event_args) { 175 scoped_ptr<base::ListValue> event_args) {
174 if (extensions::ExtensionSystem::Get(profile_)->event_router()) { 176 if (extensions::ExtensionSystem::Get(browser_context_)->event_router()) {
175 extensions::ExtensionSystem::Get(profile_)->event_router()->BroadcastEvent( 177 extensions::ExtensionSystem::Get(browser_context_)
176 make_scoped_ptr(new extensions::Event(event_name, event_args.Pass()))); 178 ->event_router()
179 ->BroadcastEvent(make_scoped_ptr(
180 new extensions::Event(event_name, event_args.Pass())));
177 } 181 }
178 } 182 }
179 183
180 void BookmarkEventRouter::BookmarkModelLoaded(BookmarkModel* model, 184 void BookmarkEventRouter::BookmarkModelLoaded(BookmarkModel* model,
181 bool ids_reassigned) { 185 bool ids_reassigned) {
182 // TODO(erikkay): Perhaps we should send this event down to the extension 186 // TODO(erikkay): Perhaps we should send this event down to the extension
183 // so they know when it's safe to use the API? 187 // so they know when it's safe to use the API?
184 } 188 }
185 189
186 void BookmarkEventRouter::BookmarkModelBeingDeleted(BookmarkModel* model) { 190 void BookmarkEventRouter::BookmarkModelBeingDeleted(BookmarkModel* model) {
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
290 BookmarkModel* model) { 294 BookmarkModel* model) {
291 scoped_ptr<base::ListValue> args(new base::ListValue()); 295 scoped_ptr<base::ListValue> args(new base::ListValue());
292 DispatchEvent(bookmarks::OnImportBegan::kEventName, args.Pass()); 296 DispatchEvent(bookmarks::OnImportBegan::kEventName, args.Pass());
293 } 297 }
294 298
295 void BookmarkEventRouter::ExtensiveBookmarkChangesEnded(BookmarkModel* model) { 299 void BookmarkEventRouter::ExtensiveBookmarkChangesEnded(BookmarkModel* model) {
296 scoped_ptr<base::ListValue> args(new base::ListValue()); 300 scoped_ptr<base::ListValue> args(new base::ListValue());
297 DispatchEvent(bookmarks::OnImportEnded::kEventName, args.Pass()); 301 DispatchEvent(bookmarks::OnImportEnded::kEventName, args.Pass());
298 } 302 }
299 303
300 BookmarksAPI::BookmarksAPI(Profile* profile) : profile_(profile) { 304 BookmarksAPI::BookmarksAPI(BrowserContext* context)
301 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( 305 : browser_context_(context) {
302 this, bookmarks::OnCreated::kEventName); 306 EventRouter* event_router =
303 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( 307 ExtensionSystem::Get(browser_context_)->event_router();
304 this, bookmarks::OnRemoved::kEventName); 308 event_router->RegisterObserver(this, bookmarks::OnCreated::kEventName);
305 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( 309 event_router->RegisterObserver(this, bookmarks::OnRemoved::kEventName);
306 this, bookmarks::OnChanged::kEventName); 310 event_router->RegisterObserver(this, bookmarks::OnChanged::kEventName);
307 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( 311 event_router->RegisterObserver(this, bookmarks::OnMoved::kEventName);
308 this, bookmarks::OnMoved::kEventName); 312 event_router->RegisterObserver(this,
309 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( 313 bookmarks::OnChildrenReordered::kEventName);
310 this, bookmarks::OnChildrenReordered::kEventName); 314 event_router->RegisterObserver(this, bookmarks::OnImportBegan::kEventName);
311 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( 315 event_router->RegisterObserver(this, bookmarks::OnImportEnded::kEventName);
312 this, bookmarks::OnImportBegan::kEventName);
313 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
314 this, bookmarks::OnImportEnded::kEventName);
315 } 316 }
316 317
317 BookmarksAPI::~BookmarksAPI() { 318 BookmarksAPI::~BookmarksAPI() {
318 } 319 }
319 320
320 void BookmarksAPI::Shutdown() { 321 void BookmarksAPI::Shutdown() {
321 ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this); 322 ExtensionSystem::Get(browser_context_)->event_router()->UnregisterObserver(
323 this);
322 } 324 }
323 325
324 static base::LazyInstance<ProfileKeyedAPIFactory<BookmarksAPI> > 326 static base::LazyInstance<ProfileKeyedAPIFactory<BookmarksAPI> >
325 g_factory = LAZY_INSTANCE_INITIALIZER; 327 g_factory = LAZY_INSTANCE_INITIALIZER;
326 328
327 // static 329 // static
328 ProfileKeyedAPIFactory<BookmarksAPI>* BookmarksAPI::GetFactoryInstance() { 330 ProfileKeyedAPIFactory<BookmarksAPI>* BookmarksAPI::GetFactoryInstance() {
329 return g_factory.Pointer(); 331 return g_factory.Pointer();
330 } 332 }
331 333
332 void BookmarksAPI::OnListenerAdded(const EventListenerInfo& details) { 334 void BookmarksAPI::OnListenerAdded(const EventListenerInfo& details) {
333 bookmark_event_router_.reset(new BookmarkEventRouter(profile_, 335 bookmark_event_router_.reset(new BookmarkEventRouter(
334 BookmarkModelFactory::GetForProfile(profile_))); 336 browser_context_,
335 ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this); 337 BookmarkModelFactory::GetForProfile(
338 Profile::FromBrowserContext(browser_context_))));
339 ExtensionSystem::Get(browser_context_)->event_router()->UnregisterObserver(
340 this);
336 } 341 }
337 342
338 bool BookmarksGetFunction::RunImpl() { 343 bool BookmarksGetFunction::RunImpl() {
339 scoped_ptr<bookmarks::Get::Params> params( 344 scoped_ptr<bookmarks::Get::Params> params(
340 bookmarks::Get::Params::Create(*args_)); 345 bookmarks::Get::Params::Create(*args_));
341 EXTENSION_FUNCTION_VALIDATE(params.get()); 346 EXTENSION_FUNCTION_VALIDATE(params.get());
342 347
343 std::vector<linked_ptr<BookmarkTreeNode> > nodes; 348 std::vector<linked_ptr<BookmarkTreeNode> > nodes;
344 if (params->id_or_id_list.as_strings) { 349 if (params->id_or_id_list.as_strings) {
345 std::vector<std::string>& ids = *params->id_or_id_list.as_strings; 350 std::vector<std::string>& ids = *params->id_or_id_list.as_strings;
(...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after
725 return b; 730 return b;
726 } 731 }
727 private: 732 private:
728 std::map<BucketIdType, Bucket*> buckets_; 733 std::map<BucketIdType, Bucket*> buckets_;
729 }; 734 };
730 735
731 // Mapper for 'bookmarks.create'. Maps "same input to bookmarks.create" to a 736 // Mapper for 'bookmarks.create'. Maps "same input to bookmarks.create" to a
732 // unique bucket. 737 // unique bucket.
733 class CreateBookmarkBucketMapper : public BookmarkBucketMapper<std::string> { 738 class CreateBookmarkBucketMapper : public BookmarkBucketMapper<std::string> {
734 public: 739 public:
735 explicit CreateBookmarkBucketMapper(Profile* profile) : profile_(profile) {} 740 explicit CreateBookmarkBucketMapper(BrowserContext* context)
741 : browser_context_(context) {}
736 // TODO(tim): This should share code with BookmarksCreateFunction::RunImpl, 742 // TODO(tim): This should share code with BookmarksCreateFunction::RunImpl,
737 // but I can't figure out a good way to do that with all the macros. 743 // but I can't figure out a good way to do that with all the macros.
738 virtual void GetBucketsForArgs(const base::ListValue* args, 744 virtual void GetBucketsForArgs(const base::ListValue* args,
739 BucketList* buckets) OVERRIDE { 745 BucketList* buckets) OVERRIDE {
740 const base::DictionaryValue* json; 746 const base::DictionaryValue* json;
741 if (!args->GetDictionary(0, &json)) 747 if (!args->GetDictionary(0, &json))
742 return; 748 return;
743 749
744 std::string parent_id; 750 std::string parent_id;
745 if (json->HasKey(keys::kParentIdKey)) { 751 if (json->HasKey(keys::kParentIdKey)) {
746 if (!json->GetString(keys::kParentIdKey, &parent_id)) 752 if (!json->GetString(keys::kParentIdKey, &parent_id))
747 return; 753 return;
748 } 754 }
749 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_); 755 BookmarkModel* model = BookmarkModelFactory::GetForProfile(
756 Profile::FromBrowserContext(browser_context_));
750 757
751 int64 parent_id_int64; 758 int64 parent_id_int64;
752 base::StringToInt64(parent_id, &parent_id_int64); 759 base::StringToInt64(parent_id, &parent_id_int64);
753 const BookmarkNode* parent = model->GetNodeByID(parent_id_int64); 760 const BookmarkNode* parent = model->GetNodeByID(parent_id_int64);
754 if (!parent) 761 if (!parent)
755 return; 762 return;
756 763
757 std::string bucket_id = base::UTF16ToUTF8(parent->GetTitle()); 764 std::string bucket_id = base::UTF16ToUTF8(parent->GetTitle());
758 std::string title; 765 std::string title;
759 json->GetString(keys::kTitleKey, &title); 766 json->GetString(keys::kTitleKey, &title);
760 std::string url_string; 767 std::string url_string;
761 json->GetString(keys::kUrlKey, &url_string); 768 json->GetString(keys::kUrlKey, &url_string);
762 769
763 bucket_id += title; 770 bucket_id += title;
764 bucket_id += url_string; 771 bucket_id += url_string;
765 // 20 bytes (SHA1 hash length) is very likely less than most of the 772 // 20 bytes (SHA1 hash length) is very likely less than most of the
766 // |bucket_id| strings we construct here, so we hash it to save space. 773 // |bucket_id| strings we construct here, so we hash it to save space.
767 buckets->push_back(GetBucket(base::SHA1HashString(bucket_id))); 774 buckets->push_back(GetBucket(base::SHA1HashString(bucket_id)));
768 } 775 }
769 private: 776 private:
770 Profile* profile_; 777 BrowserContext* browser_context_;
771 }; 778 };
772 779
773 // Mapper for 'bookmarks.remove'. 780 // Mapper for 'bookmarks.remove'.
774 class RemoveBookmarksBucketMapper : public BookmarkBucketMapper<std::string> { 781 class RemoveBookmarksBucketMapper : public BookmarkBucketMapper<std::string> {
775 public: 782 public:
776 explicit RemoveBookmarksBucketMapper(Profile* profile) : profile_(profile) {} 783 explicit RemoveBookmarksBucketMapper(BrowserContext* context)
784 : browser_context_(context) {}
777 virtual void GetBucketsForArgs(const base::ListValue* args, 785 virtual void GetBucketsForArgs(const base::ListValue* args,
778 BucketList* buckets) OVERRIDE { 786 BucketList* buckets) OVERRIDE {
779 typedef std::list<int64> IdList; 787 typedef std::list<int64> IdList;
780 IdList ids; 788 IdList ids;
781 bool invalid_id = false; 789 bool invalid_id = false;
782 if (!BookmarksRemoveFunction::ExtractIds(args, &ids, &invalid_id) || 790 if (!BookmarksRemoveFunction::ExtractIds(args, &ids, &invalid_id) ||
783 invalid_id) { 791 invalid_id) {
784 return; 792 return;
785 } 793 }
786 794
787 for (IdList::iterator it = ids.begin(); it != ids.end(); ++it) { 795 for (IdList::iterator it = ids.begin(); it != ids.end(); ++it) {
788 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_); 796 BookmarkModel* model = BookmarkModelFactory::GetForProfile(
797 Profile::FromBrowserContext(browser_context_));
789 const BookmarkNode* node = model->GetNodeByID(*it); 798 const BookmarkNode* node = model->GetNodeByID(*it);
790 if (!node || node->is_root()) 799 if (!node || node->is_root())
791 return; 800 return;
792 801
793 std::string bucket_id; 802 std::string bucket_id;
794 bucket_id += base::UTF16ToUTF8(node->parent()->GetTitle()); 803 bucket_id += base::UTF16ToUTF8(node->parent()->GetTitle());
795 bucket_id += base::UTF16ToUTF8(node->GetTitle()); 804 bucket_id += base::UTF16ToUTF8(node->GetTitle());
796 bucket_id += node->url().spec(); 805 bucket_id += node->url().spec();
797 buckets->push_back(GetBucket(base::SHA1HashString(bucket_id))); 806 buckets->push_back(GetBucket(base::SHA1HashString(bucket_id)));
798 } 807 }
799 } 808 }
800 private: 809 private:
801 Profile* profile_; 810 BrowserContext* browser_context_;
802 }; 811 };
803 812
804 // Mapper for any bookmark function accepting bookmark IDs as parameters, where 813 // Mapper for any bookmark function accepting bookmark IDs as parameters, where
805 // a distinct ID corresponds to a single item in terms of quota limiting. This 814 // a distinct ID corresponds to a single item in terms of quota limiting. This
806 // is inappropriate for bookmarks.remove, for example, since repeated removals 815 // is inappropriate for bookmarks.remove, for example, since repeated removals
807 // of the same item will actually have a different ID each time. 816 // of the same item will actually have a different ID each time.
808 template <class FunctionType> 817 template <class FunctionType>
809 class BookmarkIdMapper : public BookmarkBucketMapper<int64> { 818 class BookmarkIdMapper : public BookmarkBucketMapper<int64> {
810 public: 819 public:
811 typedef std::list<int64> IdList; 820 typedef std::list<int64> IdList;
(...skipping 13 matching lines...) Expand all
825 public: 834 public:
826 // For id-based bookmark functions. 835 // For id-based bookmark functions.
827 template <class FunctionType> 836 template <class FunctionType>
828 static void Build(QuotaLimitHeuristics* heuristics) { 837 static void Build(QuotaLimitHeuristics* heuristics) {
829 BuildWithMappers(heuristics, new BookmarkIdMapper<FunctionType>(), 838 BuildWithMappers(heuristics, new BookmarkIdMapper<FunctionType>(),
830 new BookmarkIdMapper<FunctionType>()); 839 new BookmarkIdMapper<FunctionType>());
831 } 840 }
832 841
833 // For bookmarks.create. 842 // For bookmarks.create.
834 static void BuildForCreate(QuotaLimitHeuristics* heuristics, 843 static void BuildForCreate(QuotaLimitHeuristics* heuristics,
835 Profile* profile) { 844 BrowserContext* context) {
836 BuildWithMappers(heuristics, new CreateBookmarkBucketMapper(profile), 845 BuildWithMappers(heuristics,
837 new CreateBookmarkBucketMapper(profile)); 846 new CreateBookmarkBucketMapper(context),
847 new CreateBookmarkBucketMapper(context));
838 } 848 }
839 849
840 // For bookmarks.remove. 850 // For bookmarks.remove.
841 static void BuildForRemove(QuotaLimitHeuristics* heuristics, 851 static void BuildForRemove(QuotaLimitHeuristics* heuristics,
842 Profile* profile) { 852 BrowserContext* context) {
843 BuildWithMappers(heuristics, new RemoveBookmarksBucketMapper(profile), 853 BuildWithMappers(heuristics,
844 new RemoveBookmarksBucketMapper(profile)); 854 new RemoveBookmarksBucketMapper(context),
855 new RemoveBookmarksBucketMapper(context));
845 } 856 }
846 857
847 private: 858 private:
848 static void BuildWithMappers(QuotaLimitHeuristics* heuristics, 859 static void BuildWithMappers(QuotaLimitHeuristics* heuristics,
849 BucketMapper* short_mapper, BucketMapper* long_mapper) { 860 BucketMapper* short_mapper, BucketMapper* long_mapper) {
850 const Config kSustainedLimitConfig = { 861 const Config kSustainedLimitConfig = {
851 // See bookmarks.json for current value. 862 // See bookmarks.json for current value.
852 bookmarks::MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE, 863 bookmarks::MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE,
853 TimeDelta::FromMinutes(1) 864 TimeDelta::FromMinutes(1)
854 }; 865 };
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
1015 #if !defined(OS_ANDROID) 1026 #if !defined(OS_ANDROID)
1016 // Android does not have support for the standard exporter. 1027 // Android does not have support for the standard exporter.
1017 // TODO(jgreenwald): remove ifdef once extensions are no longer built on 1028 // TODO(jgreenwald): remove ifdef once extensions are no longer built on
1018 // Android. 1029 // Android.
1019 bookmark_html_writer::WriteBookmarks(GetProfile(), path, NULL); 1030 bookmark_html_writer::WriteBookmarks(GetProfile(), path, NULL);
1020 #endif 1031 #endif
1021 Release(); // Balanced in BookmarksIOFunction::SelectFile() 1032 Release(); // Balanced in BookmarksIOFunction::SelectFile()
1022 } 1033 }
1023 1034
1024 } // namespace extensions 1035 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698