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

Side by Side Diff: chrome/browser/user_style_sheet_watcher.cc

Issue 64843004: Get rid of user-level styles. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: merge to ToT Created 7 years, 1 month 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
(Empty)
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/user_style_sheet_watcher.h"
6
7 #include "base/base64.h"
8 #include "base/bind.h"
9 #include "base/file_util.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "content/public/browser/notification_service.h"
12 #include "content/public/browser/notification_types.h"
13 #include "content/public/browser/web_contents.h"
14
15 using ::base::FilePathWatcher;
16 using content::BrowserThread;
17 using content::WebContents;
18
19 namespace {
20
21 // The subdirectory of the profile that contains the style sheet.
22 const char kStyleSheetDir[] = "User StyleSheets";
23 // The filename of the stylesheet.
24 const char kUserStyleSheetFile[] = "Custom.css";
25
26 } // namespace
27
28 // UserStyleSheetLoader is responsible for loading the user style sheet on the
29 // file thread and sends a notification when the style sheet is loaded. It is
30 // a helper to UserStyleSheetWatcher. The reference graph is as follows:
31 //
32 // .-----------------------. owns .-----------------.
33 // | UserStyleSheetWatcher |----------->| FilePathWatcher |
34 // '-----------------------' '-----------------'
35 // | |
36 // V |
37 // .----------------------. |
38 // | UserStyleSheetLoader |<--------------------'
39 // '----------------------'
40 //
41 // FilePathWatcher's reference to UserStyleSheetLoader is used for delivering
42 // the change notifications. Since they happen asynchronously,
43 // UserStyleSheetWatcher and its FilePathWatcher may be destroyed while a
44 // callback to UserStyleSheetLoader is in progress, in which case the
45 // UserStyleSheetLoader object outlives the watchers.
46 class UserStyleSheetLoader
47 : public base::RefCountedThreadSafe<UserStyleSheetLoader> {
48 public:
49 UserStyleSheetLoader();
50
51 GURL user_style_sheet() const {
52 return user_style_sheet_;
53 }
54
55 // Load the user style sheet on the file thread and convert it to a
56 // base64 URL. Posts the base64 URL back to the UI thread.
57 void LoadStyleSheet(const base::FilePath& style_sheet_file);
58
59 // Register a callback to be called whenever the stylesheet gets updated.
60 scoped_ptr<base::CallbackList<void(void)>::Subscription>
61 RegisterOnStyleSheetUpdatedCallback(const base::Closure& callback);
62
63 // Send out a notification if the stylesheet has already been loaded.
64 void NotifyLoaded();
65
66 // FilePathWatcher::Callback method:
67 void NotifyPathChanged(const base::FilePath& path, bool error);
68
69 private:
70 friend class base::RefCountedThreadSafe<UserStyleSheetLoader>;
71 ~UserStyleSheetLoader();
72
73 // Called on the UI thread after the stylesheet has loaded.
74 void SetStyleSheet(const GURL& url);
75
76 // The user style sheet as a base64 data:// URL.
77 GURL user_style_sheet_;
78
79 // Whether the stylesheet has been loaded.
80 bool has_loaded_;
81
82 base::CallbackList<void(void)> style_sheet_updated_callbacks_;
83
84 DISALLOW_COPY_AND_ASSIGN(UserStyleSheetLoader);
85 };
86
87 UserStyleSheetLoader::UserStyleSheetLoader()
88 : has_loaded_(false) {
89 }
90
91 UserStyleSheetLoader::~UserStyleSheetLoader() {
92 }
93
94 scoped_ptr<base::CallbackList<void(void)>::Subscription>
95 UserStyleSheetLoader::RegisterOnStyleSheetUpdatedCallback(
96 const base::Closure& callback) {
97 return style_sheet_updated_callbacks_.Add(callback);
98 }
99
100 void UserStyleSheetLoader::NotifyLoaded() {
101 if (has_loaded_) {
102 style_sheet_updated_callbacks_.Notify();
103 }
104 }
105
106 void UserStyleSheetLoader::NotifyPathChanged(const base::FilePath& path,
107 bool error) {
108 if (!error)
109 LoadStyleSheet(path);
110 }
111
112 void UserStyleSheetLoader::LoadStyleSheet(
113 const base::FilePath& style_sheet_file) {
114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
115 // We keep the user style sheet in a subdir so we can watch for changes
116 // to the file.
117 base::FilePath style_sheet_dir = style_sheet_file.DirName();
118 if (!base::DirectoryExists(style_sheet_dir)) {
119 if (!file_util::CreateDirectory(style_sheet_dir))
120 return;
121 }
122 // Create the file if it doesn't exist.
123 if (!base::PathExists(style_sheet_file))
124 file_util::WriteFile(style_sheet_file, "", 0);
125
126 std::string css;
127 bool rv = base::ReadFileToString(style_sheet_file, &css);
128 GURL style_sheet_url;
129 if (rv && !css.empty()) {
130 std::string css_base64;
131 rv = base::Base64Encode(css, &css_base64);
132 if (rv) {
133 // WebKit knows about data urls, so convert the file to a data url.
134 const char kDataUrlPrefix[] = "data:text/css;charset=utf-8;base64,";
135 style_sheet_url = GURL(kDataUrlPrefix + css_base64);
136 }
137 }
138 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
139 base::Bind(&UserStyleSheetLoader::SetStyleSheet, this,
140 style_sheet_url));
141 }
142
143 void UserStyleSheetLoader::SetStyleSheet(const GURL& url) {
144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
145
146 has_loaded_ = true;
147 user_style_sheet_ = url;
148 NotifyLoaded();
149 }
150
151 UserStyleSheetWatcher::UserStyleSheetWatcher(Profile* profile,
152 const base::FilePath& profile_path)
153 : RefcountedBrowserContextKeyedService(content::BrowserThread::UI),
154 profile_(profile),
155 profile_path_(profile_path),
156 loader_(new UserStyleSheetLoader) {
157 // Listen for when the first render view host is created. If we load
158 // too fast, the first tab won't hear the notification and won't get
159 // the user style sheet.
160 registrar_.Add(this,
161 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
162 content::NotificationService::AllBrowserContextsAndSources());
163 }
164
165 UserStyleSheetWatcher::~UserStyleSheetWatcher() {
166 }
167
168 void UserStyleSheetWatcher::Init() {
169 // Make sure we run on the file thread.
170 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
171 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
172 base::Bind(&UserStyleSheetWatcher::Init, this));
173 return;
174 }
175
176 if (!file_watcher_.get()) {
177 file_watcher_.reset(new FilePathWatcher);
178 base::FilePath style_sheet_file = profile_path_.AppendASCII(kStyleSheetDir)
179 .AppendASCII(kUserStyleSheetFile);
180 if (!file_watcher_->Watch(
181 style_sheet_file,
182 false,
183 base::Bind(&UserStyleSheetLoader::NotifyPathChanged,
184 loader_.get()))) {
185 LOG(ERROR) << "Failed to setup watch for " << style_sheet_file.value();
186 }
187 loader_->LoadStyleSheet(style_sheet_file);
188 }
189 }
190
191 GURL UserStyleSheetWatcher::user_style_sheet() const {
192 return loader_->user_style_sheet();
193 }
194
195 scoped_ptr<base::CallbackList<void(void)>::Subscription>
196 UserStyleSheetWatcher::RegisterOnStyleSheetUpdatedCallback(
197 const base::Closure& callback) {
198 return loader_->RegisterOnStyleSheetUpdatedCallback(callback);
199 }
200
201 void UserStyleSheetWatcher::Observe(int type,
202 const content::NotificationSource& source,
203 const content::NotificationDetails& details) {
204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
205 DCHECK(type == content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED);
206 if (profile_->IsSameProfile(Profile::FromBrowserContext(
207 content::Source<WebContents>(source)->GetBrowserContext()))) {
208 loader_->NotifyLoaded();
209 registrar_.RemoveAll();
210 }
211 }
212
213 void UserStyleSheetWatcher::ShutdownOnUIThread() {
214 registrar_.RemoveAll();
215 }
OLDNEW
« no previous file with comments | « chrome/browser/user_style_sheet_watcher.h ('k') | chrome/browser/user_style_sheet_watcher_factory.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698