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/extensions/api/media_galleries_private/media_galleries_private_api.h"
6
7#include "base/basictypes.h"
8#include "base/bind.h"
9#include "base/files/file_path.h"
10#include "base/lazy_instance.h"
11#include "base/location.h"
12#include "base/prefs/pref_service.h"
13#include "base/strings/string_number_conversions.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/browser/extensions/api/media_galleries_private/gallery_watch_manager.h"
16#include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_event_router.h"
17#include "chrome/browser/extensions/extension_util.h"
18#include "chrome/browser/media_galleries/media_file_system_registry.h"
19#include "chrome/browser/media_galleries/media_galleries_preferences.h"
20#include "chrome/browser/profiles/profile.h"
21#include "content/public/browser/browser_thread.h"
22#include "content/public/browser/render_view_host.h"
23#include "extensions/browser/event_router.h"
24#include "extensions/browser/extension_function.h"
25#include "extensions/browser/extension_system.h"
26
27using base::DictionaryValue;
28using base::ListValue;
29
30namespace extensions {
31
32namespace AddGalleryWatch =
33    extensions::api::media_galleries_private::AddGalleryWatch;
34namespace RemoveGalleryWatch =
35    extensions::api::media_galleries_private::RemoveGalleryWatch;
36namespace GetAllGalleryWatch =
37    extensions::api::media_galleries_private::GetAllGalleryWatch;
38namespace media_galleries_private =
39    api::media_galleries_private;
40
41namespace {
42
43const char kInvalidGalleryIDError[] = "Invalid gallery ID";
44
45// Handles the profile shutdown event on the file thread to clean up
46// GalleryWatchManager.
47void HandleProfileShutdownOnFileThread(void* profile_id) {
48  DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
49  GalleryWatchManager::OnProfileShutdown(profile_id);
50}
51
52// Returns true and sets |gallery_file_path| and |gallery_pref_id| if the
53// |gallery_id| is valid and returns false otherwise.
54bool GetGalleryFilePathAndId(const std::string& gallery_id,
55                             Profile* profile,
56                             const Extension* extension,
57                             base::FilePath* gallery_file_path,
58                             MediaGalleryPrefId* gallery_pref_id) {
59  MediaGalleryPrefId pref_id;
60  if (!base::StringToUint64(gallery_id, &pref_id))
61    return false;
62  MediaGalleriesPreferences* preferences =
63      g_browser_process->media_file_system_registry()->GetPreferences(profile);
64  base::FilePath file_path(
65      preferences->LookUpGalleryPathForExtension(pref_id, extension, false));
66  if (file_path.empty())
67    return false;
68  *gallery_pref_id = pref_id;
69  *gallery_file_path = file_path;
70  return true;
71}
72
73}  // namespace
74
75
76///////////////////////////////////////////////////////////////////////////////
77//                      MediaGalleriesPrivateAPI                             //
78///////////////////////////////////////////////////////////////////////////////
79
80MediaGalleriesPrivateAPI::MediaGalleriesPrivateAPI(
81    content::BrowserContext* context)
82    : profile_(Profile::FromBrowserContext(context)), weak_ptr_factory_(this) {
83  DCHECK(profile_);
84  EventRouter* event_router = EventRouter::Get(profile_);
85  event_router->RegisterObserver(
86      this, media_galleries_private::OnGalleryChanged::kEventName);
87}
88
89MediaGalleriesPrivateAPI::~MediaGalleriesPrivateAPI() {
90}
91
92void MediaGalleriesPrivateAPI::Shutdown() {
93  EventRouter::Get(profile_)->UnregisterObserver(this);
94  weak_ptr_factory_.InvalidateWeakPtrs();
95  content::BrowserThread::PostTask(
96      content::BrowserThread::FILE, FROM_HERE,
97      base::Bind(&HandleProfileShutdownOnFileThread, profile_));
98}
99
100static base::LazyInstance<
101    BrowserContextKeyedAPIFactory<MediaGalleriesPrivateAPI> > g_factory =
102    LAZY_INSTANCE_INITIALIZER;
103
104// static
105BrowserContextKeyedAPIFactory<MediaGalleriesPrivateAPI>*
106MediaGalleriesPrivateAPI::GetFactoryInstance() {
107  return g_factory.Pointer();
108}
109
110// static
111MediaGalleriesPrivateAPI* MediaGalleriesPrivateAPI::Get(
112    content::BrowserContext* context) {
113  return BrowserContextKeyedAPIFactory<MediaGalleriesPrivateAPI>::Get(context);
114}
115
116void MediaGalleriesPrivateAPI::OnListenerAdded(
117    const EventListenerInfo& details) {
118  // Make sure MediaGalleriesPreferences is initialized. After that,
119  // try to initialize the event router for the listener.
120  // This method is called synchronously with the message handler for the
121  // JS invocation.
122
123  MediaGalleriesPreferences* preferences =
124      g_browser_process->media_file_system_registry()->GetPreferences(profile_);
125  preferences->EnsureInitialized(base::Bind(
126      &MediaGalleriesPrivateAPI::MaybeInitializeEventRouterAndTracker,
127      weak_ptr_factory_.GetWeakPtr()));
128}
129
130MediaGalleriesPrivateEventRouter* MediaGalleriesPrivateAPI::GetEventRouter() {
131  MaybeInitializeEventRouterAndTracker();
132  return media_galleries_private_event_router_.get();
133}
134
135GalleryWatchStateTracker*
136MediaGalleriesPrivateAPI::GetGalleryWatchStateTracker() {
137  MaybeInitializeEventRouterAndTracker();
138  return tracker_.get();
139}
140
141void MediaGalleriesPrivateAPI::MaybeInitializeEventRouterAndTracker() {
142  if (media_galleries_private_event_router_.get())
143    return;
144  media_galleries_private_event_router_.reset(
145      new MediaGalleriesPrivateEventRouter(profile_));
146  DCHECK(g_browser_process->media_file_system_registry()->
147             GetPreferences(profile_)->IsInitialized());
148  tracker_.reset(
149      new GalleryWatchStateTracker(profile_));
150}
151
152///////////////////////////////////////////////////////////////////////////////
153//              MediaGalleriesPrivateAddGalleryWatchFunction                 //
154///////////////////////////////////////////////////////////////////////////////
155MediaGalleriesPrivateAddGalleryWatchFunction::
156~MediaGalleriesPrivateAddGalleryWatchFunction() {
157}
158
159bool MediaGalleriesPrivateAddGalleryWatchFunction::RunAsync() {
160  DCHECK(GetProfile());
161  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
162  if (!render_view_host() || !render_view_host()->GetProcess())
163    return false;
164
165  scoped_ptr<AddGalleryWatch::Params> params(
166      AddGalleryWatch::Params::Create(*args_));
167  EXTENSION_FUNCTION_VALIDATE(params.get());
168
169  MediaGalleriesPreferences* preferences =
170      g_browser_process->media_file_system_registry()->GetPreferences(
171          GetProfile());
172  preferences->EnsureInitialized(base::Bind(
173      &MediaGalleriesPrivateAddGalleryWatchFunction::OnPreferencesInit,
174      this,
175      params->gallery_id));
176
177  return true;
178}
179
180void MediaGalleriesPrivateAddGalleryWatchFunction::OnPreferencesInit(
181    const std::string& pref_id) {
182  base::FilePath gallery_file_path;
183  MediaGalleryPrefId gallery_pref_id = 0;
184  if (!GetGalleryFilePathAndId(pref_id,
185                               GetProfile(),
186                               extension(),
187                               &gallery_file_path,
188                               &gallery_pref_id)) {
189    error_ = kInvalidGalleryIDError;
190    HandleResponse(gallery_pref_id, false);
191    return;
192  }
193
194  MediaGalleriesPrivateEventRouter* router =
195      MediaGalleriesPrivateAPI::Get(GetProfile())->GetEventRouter();
196
197  // TODO(tommycli): The new GalleryWatchManager no longer checks that there is
198  // an event listener attached. There should be a check for that here.
199
200  DCHECK(router);
201  content::BrowserThread::PostTaskAndReplyWithResult(
202      content::BrowserThread::FILE,
203      FROM_HERE,
204      base::Bind(&GalleryWatchManager::SetupGalleryWatch,
205                 GetProfile(),
206                 gallery_pref_id,
207                 gallery_file_path,
208                 extension_id(),
209                 router->AsWeakPtr()),
210      base::Bind(&MediaGalleriesPrivateAddGalleryWatchFunction::HandleResponse,
211                 this,
212                 gallery_pref_id));
213}
214
215void MediaGalleriesPrivateAddGalleryWatchFunction::HandleResponse(
216    MediaGalleryPrefId gallery_id,
217    bool success) {
218  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
219  media_galleries_private::AddGalleryWatchResult result;
220  result.gallery_id = base::Uint64ToString(gallery_id);
221  result.success = success;
222  SetResult(result.ToValue().release());
223  if (success) {
224    DCHECK(g_browser_process->media_file_system_registry()
225               ->GetPreferences(GetProfile())
226               ->IsInitialized());
227    GalleryWatchStateTracker* state_tracker = MediaGalleriesPrivateAPI::Get(
228        GetProfile())->GetGalleryWatchStateTracker();
229    state_tracker->OnGalleryWatchAdded(extension_id(), gallery_id);
230  }
231  SendResponse(true);
232}
233
234
235///////////////////////////////////////////////////////////////////////////////
236//              MediaGalleriesPrivateRemoveGalleryWatchFunction              //
237///////////////////////////////////////////////////////////////////////////////
238
239MediaGalleriesPrivateRemoveGalleryWatchFunction::
240~MediaGalleriesPrivateRemoveGalleryWatchFunction() {
241}
242
243bool MediaGalleriesPrivateRemoveGalleryWatchFunction::RunAsync() {
244  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
245  if (!render_view_host() || !render_view_host()->GetProcess())
246    return false;
247
248  scoped_ptr<RemoveGalleryWatch::Params> params(
249      RemoveGalleryWatch::Params::Create(*args_));
250  EXTENSION_FUNCTION_VALIDATE(params.get());
251
252  MediaGalleriesPreferences* preferences =
253      g_browser_process->media_file_system_registry()->GetPreferences(
254          GetProfile());
255  preferences->EnsureInitialized(base::Bind(
256      &MediaGalleriesPrivateRemoveGalleryWatchFunction::OnPreferencesInit,
257      this,
258      params->gallery_id));
259  return true;
260}
261
262void MediaGalleriesPrivateRemoveGalleryWatchFunction::OnPreferencesInit(
263    const std::string& pref_id) {
264  base::FilePath gallery_file_path;
265  MediaGalleryPrefId gallery_pref_id = 0;
266  if (!GetGalleryFilePathAndId(pref_id,
267                               GetProfile(),
268                               extension(),
269                               &gallery_file_path,
270                               &gallery_pref_id)) {
271    error_ = kInvalidGalleryIDError;
272    SendResponse(false);
273    return;
274  }
275
276  content::BrowserThread::PostTask(
277      content::BrowserThread::FILE,
278      FROM_HERE,
279      base::Bind(&GalleryWatchManager::RemoveGalleryWatch,
280                 GetProfile(),
281                 gallery_file_path,
282                 extension_id()));
283
284  GalleryWatchStateTracker* state_tracker = MediaGalleriesPrivateAPI::Get(
285      GetProfile())->GetGalleryWatchStateTracker();
286  state_tracker->OnGalleryWatchRemoved(extension_id(), gallery_pref_id);
287  SendResponse(true);
288}
289
290///////////////////////////////////////////////////////////////////////////////
291//              MediaGalleriesPrivateGetAllGalleryWatchFunction              //
292///////////////////////////////////////////////////////////////////////////////
293
294MediaGalleriesPrivateGetAllGalleryWatchFunction::
295~MediaGalleriesPrivateGetAllGalleryWatchFunction() {
296}
297
298bool MediaGalleriesPrivateGetAllGalleryWatchFunction::RunAsync() {
299  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
300  if (!render_view_host() || !render_view_host()->GetProcess())
301    return false;
302
303  MediaGalleriesPreferences* preferences =
304      g_browser_process->media_file_system_registry()->GetPreferences(
305          GetProfile());
306  preferences->EnsureInitialized(base::Bind(
307      &MediaGalleriesPrivateGetAllGalleryWatchFunction::OnPreferencesInit,
308      this));
309  return true;
310}
311
312void MediaGalleriesPrivateGetAllGalleryWatchFunction::OnPreferencesInit() {
313  std::vector<std::string> result;
314  GalleryWatchStateTracker* state_tracker = MediaGalleriesPrivateAPI::Get(
315      GetProfile())->GetGalleryWatchStateTracker();
316  MediaGalleryPrefIdSet gallery_ids =
317      state_tracker->GetAllWatchedGalleryIDsForExtension(extension_id());
318  for (MediaGalleryPrefIdSet::const_iterator iter = gallery_ids.begin();
319       iter != gallery_ids.end(); ++iter) {
320    result.push_back(base::Uint64ToString(*iter));
321  }
322  results_ = GetAllGalleryWatch::Results::Create(result);
323  SendResponse(true);
324}
325
326///////////////////////////////////////////////////////////////////////////////
327//              MediaGalleriesPrivateRemoveAllGalleryWatchFunction           //
328///////////////////////////////////////////////////////////////////////////////
329
330MediaGalleriesPrivateRemoveAllGalleryWatchFunction::
331~MediaGalleriesPrivateRemoveAllGalleryWatchFunction() {
332}
333
334bool MediaGalleriesPrivateRemoveAllGalleryWatchFunction::RunAsync() {
335  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
336  if (!render_view_host() || !render_view_host()->GetProcess())
337    return false;
338
339  MediaGalleriesPreferences* preferences =
340      g_browser_process->media_file_system_registry()->GetPreferences(
341          GetProfile());
342  preferences->EnsureInitialized(base::Bind(
343      &MediaGalleriesPrivateRemoveAllGalleryWatchFunction::OnPreferencesInit,
344      this));
345  return true;
346}
347
348void MediaGalleriesPrivateRemoveAllGalleryWatchFunction::OnPreferencesInit() {
349  MediaGalleriesPreferences* preferences =
350      g_browser_process->media_file_system_registry()->GetPreferences(
351          GetProfile());
352  GalleryWatchStateTracker* state_tracker = MediaGalleriesPrivateAPI::Get(
353      GetProfile())->GetGalleryWatchStateTracker();
354  state_tracker->RemoveAllGalleryWatchersForExtension(
355      extension_id(), preferences);
356  SendResponse(true);
357}
358
359}  // namespace extensions
360