1// Copyright (c) 2011 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/webui/chrome_url_data_manager.h"
6
7#include <vector>
8
9#include "base/i18n/rtl.h"
10#include "base/memory/ref_counted_memory.h"
11#include "base/message_loop.h"
12#include "base/string_util.h"
13#include "base/synchronization/lock.h"
14#include "base/values.h"
15#include "chrome/browser/net/chrome_url_request_context.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/browser/ui/webui/chrome_url_data_manager_backend.h"
18#include "content/browser/browser_thread.h"
19#include "grit/platform_locale_settings.h"
20#include "ui/base/l10n/l10n_util.h"
21
22#if defined(OS_WIN)
23#include "base/win/windows_version.h"
24#endif
25
26// static
27base::Lock ChromeURLDataManager::delete_lock_;
28
29// static
30ChromeURLDataManager::DataSources* ChromeURLDataManager::data_sources_ = NULL;
31
32// Invoked on the IO thread to do the actual adding of the DataSource.
33static void AddDataSourceOnIOThread(
34    scoped_refptr<net::URLRequestContextGetter> context_getter,
35    scoped_refptr<ChromeURLDataManager::DataSource> data_source) {
36  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
37  static_cast<ChromeURLRequestContext*>(
38      context_getter->GetURLRequestContext())->
39      GetChromeURLDataManagerBackend()->AddDataSource(data_source.get());
40}
41
42ChromeURLDataManager::ChromeURLDataManager(Profile* profile)
43    : profile_(profile) {
44}
45
46ChromeURLDataManager::~ChromeURLDataManager() {
47}
48
49void ChromeURLDataManager::AddDataSource(DataSource* source) {
50  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
51  BrowserThread::PostTask(
52      BrowserThread::IO, FROM_HERE,
53      NewRunnableFunction(AddDataSourceOnIOThread,
54                          make_scoped_refptr(profile_->GetRequestContext()),
55                          make_scoped_refptr(source)));
56}
57
58// static
59void ChromeURLDataManager::DeleteDataSources() {
60  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
61  DataSources sources;
62  {
63    base::AutoLock lock(delete_lock_);
64    if (!data_sources_)
65      return;
66    data_sources_->swap(sources);
67  }
68  for (size_t i = 0; i < sources.size(); ++i)
69    delete sources[i];
70}
71
72// static
73void ChromeURLDataManager::DeleteDataSource(const DataSource* data_source) {
74  // Invoked when a DataSource is no longer referenced and needs to be deleted.
75  if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
76    // We're on the UI thread, delete right away.
77    delete data_source;
78    return;
79  }
80
81  // We're not on the UI thread, add the DataSource to the list of DataSources
82  // to delete.
83  bool schedule_delete = false;
84  {
85    base::AutoLock lock(delete_lock_);
86    if (!data_sources_)
87      data_sources_ = new DataSources();
88    schedule_delete = data_sources_->empty();
89    data_sources_->push_back(data_source);
90  }
91  if (schedule_delete) {
92    // Schedule a task to delete the DataSource back on the UI thread.
93    BrowserThread::PostTask(BrowserThread::UI,
94                            FROM_HERE,
95                            NewRunnableFunction(
96                                &ChromeURLDataManager::DeleteDataSources));
97  }
98}
99
100// static
101bool ChromeURLDataManager::IsScheduledForDeletion(
102    const DataSource* data_source) {
103  base::AutoLock lock(delete_lock_);
104  if (!data_sources_)
105    return false;
106  return std::find(data_sources_->begin(), data_sources_->end(), data_source) !=
107      data_sources_->end();
108}
109
110ChromeURLDataManager::DataSource::DataSource(const std::string& source_name,
111                                             MessageLoop* message_loop)
112    : source_name_(source_name),
113      message_loop_(message_loop),
114      backend_(NULL) {
115}
116
117ChromeURLDataManager::DataSource::~DataSource() {
118}
119
120void ChromeURLDataManager::DataSource::SendResponse(int request_id,
121                                                    RefCountedMemory* bytes) {
122  if (IsScheduledForDeletion(this)) {
123    // We're scheduled for deletion. Servicing the request would result in
124    // this->AddRef being invoked, even though the ref count is 0 and 'this' is
125    // about to be deleted. If the AddRef were allowed through, when 'this' is
126    // released it would be deleted again.
127    //
128    // This scenario occurs with DataSources that make history requests. Such
129    // DataSources do a history query in |StartDataRequest| and the request is
130    // live until the object is deleted (history requests don't up the ref
131    // count). This means it's entirely possible for the DataSource to invoke
132    // |SendResponse| between the time when there are no more refs and the time
133    // when the object is deleted.
134    return;
135  }
136  BrowserThread::PostTask(
137      BrowserThread::IO, FROM_HERE,
138      NewRunnableMethod(this, &DataSource::SendResponseOnIOThread,
139                        request_id, make_scoped_refptr(bytes)));
140}
141
142MessageLoop* ChromeURLDataManager::DataSource::MessageLoopForRequestPath(
143    const std::string& path) const {
144  return message_loop_;
145}
146
147bool ChromeURLDataManager::DataSource::ShouldReplaceExistingSource() const {
148  return true;
149}
150
151// static
152void ChromeURLDataManager::DataSource::SetFontAndTextDirection(
153    DictionaryValue* localized_strings) {
154  localized_strings->SetString("fontfamily",
155      l10n_util::GetStringUTF16(IDS_WEB_FONT_FAMILY));
156
157  int web_font_size_id = IDS_WEB_FONT_SIZE;
158#if defined(OS_WIN)
159  // Some fonts used for some languages changed a lot in terms of the font
160  // metric in Vista. So, we need to use different size before Vista.
161  if (base::win::GetVersion() < base::win::VERSION_VISTA)
162    web_font_size_id = IDS_WEB_FONT_SIZE_XP;
163#endif
164  localized_strings->SetString("fontsize",
165      l10n_util::GetStringUTF16(web_font_size_id));
166
167  localized_strings->SetString("textdirection",
168      base::i18n::IsRTL() ? "rtl" : "ltr");
169}
170
171void ChromeURLDataManager::DataSource::SendResponseOnIOThread(
172    int request_id,
173    scoped_refptr<RefCountedMemory> bytes) {
174  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
175  if (backend_)
176    backend_->DataAvailable(request_id, bytes);
177}
178