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

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

Issue 2868114: Rework FileWatcher to avoid race condition upon deletion. (Closed) Base URL: http://src.chromium.org/git/chromium.git
Patch Set: Add missing return... Created 10 years, 4 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
« no previous file with comments | « chrome/browser/user_style_sheet_watcher.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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/user_style_sheet_watcher.h" 5 #include "chrome/browser/user_style_sheet_watcher.h"
6 6
7 #include "base/base64.h" 7 #include "base/base64.h"
8 #include "base/file_util.h" 8 #include "base/file_util.h"
9 #include "chrome/common/notification_service.h" 9 #include "chrome/common/notification_service.h"
10 #include "chrome/common/notification_type.h" 10 #include "chrome/common/notification_type.h"
11 11
12 namespace { 12 namespace {
13 13
14 // The subdirectory of the profile that contains the style sheet. 14 // The subdirectory of the profile that contains the style sheet.
15 const char kStyleSheetDir[] = "User StyleSheets"; 15 const char kStyleSheetDir[] = "User StyleSheets";
16 // The filename of the stylesheet. 16 // The filename of the stylesheet.
17 const char kUserStyleSheetFile[] = "Custom.css"; 17 const char kUserStyleSheetFile[] = "Custom.css";
18 18
19 } // namespace 19 } // namespace
20 20
21 UserStyleSheetWatcher::UserStyleSheetWatcher(const FilePath& profile_path) 21 // UserStyleSheetLoader is responsible for loading the user style sheet on the
22 : profile_path_(profile_path), 22 // file thread and sends a notification when the style sheet is loaded. It is
23 has_loaded_(false) { 23 // a helper to UserStyleSheetWatcher. The reference graph is as follows:
24 // Listen for when the first render view host is created. If we load 24 //
25 // too fast, the first tab won't hear the notification and won't get 25 // .-----------------------. owns .-------------.
26 // the user style sheet. 26 // | UserStyleSheetWatcher |----------->| FileWatcher |
27 registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB, 27 // '-----------------------' '-------------'
28 NotificationService::AllSources()); 28 // | |
29 // V |
30 // .----------------------. |
31 // | UserStyleSheetLoader |<------------------'
32 // '----------------------'
33 //
34 // FileWatcher's reference to UserStyleSheetLoader is used for delivering the
35 // change notifications. Since they happen asynchronously, UserStyleSheetWatcher
36 // and its FileWatcher may be destroyed while a callback to UserStyleSheetLoader
37 // is in progress, in which case the UserStyleSheetLoader object outlives the
38 // watchers.
39 class UserStyleSheetLoader : public FileWatcher::Delegate {
40 public:
41 UserStyleSheetLoader();
42 virtual ~UserStyleSheetLoader() {}
43
44 GURL user_style_sheet() const {
45 return user_style_sheet_;
46 }
47
48 // Load the user style sheet on the file thread and convert it to a
49 // base64 URL. Posts the base64 URL back to the UI thread.
50 void LoadStyleSheet(const FilePath& style_sheet_file);
51
52 // Send out a notification if the stylesheet has already been loaded.
53 void NotifyLoaded();
54
55 // FileWatcher::Delegate interface
56 virtual void OnFileChanged(const FilePath& path);
57
58 private:
59 // Called on the UI thread after the stylesheet has loaded.
60 void SetStyleSheet(const GURL& url);
61
62 // The user style sheet as a base64 data:// URL.
63 GURL user_style_sheet_;
64
65 // Whether the stylesheet has been loaded.
66 bool has_loaded_;
67
68 DISALLOW_COPY_AND_ASSIGN(UserStyleSheetLoader);
69 };
70
71 UserStyleSheetLoader::UserStyleSheetLoader()
72 : has_loaded_(false) {
29 } 73 }
30 74
31 void UserStyleSheetWatcher::Observe(NotificationType type, 75 void UserStyleSheetLoader::NotifyLoaded() {
32 const NotificationSource& source, const NotificationDetails& details) {
33 DCHECK(type == NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB);
34
35 if (has_loaded_) { 76 if (has_loaded_) {
36 NotificationService::current()->Notify( 77 NotificationService::current()->Notify(
37 NotificationType::USER_STYLE_SHEET_UPDATED, 78 NotificationType::USER_STYLE_SHEET_UPDATED,
38 Source<UserStyleSheetWatcher>(this), 79 Source<UserStyleSheetLoader>(this),
39 NotificationService::NoDetails()); 80 NotificationService::NoDetails());
40 } 81 }
41
42 registrar_.RemoveAll();
43 } 82 }
44 83
45 void UserStyleSheetWatcher::OnFileChanged(const FilePath& path) { 84 void UserStyleSheetLoader::OnFileChanged(const FilePath& path) {
46 ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, 85 LoadStyleSheet(path);
47 NewRunnableMethod(this, &UserStyleSheetWatcher::LoadStyleSheet,
48 profile_path_));
49 } 86 }
50 87
51 void UserStyleSheetWatcher::Init() { 88 void UserStyleSheetLoader::LoadStyleSheet(const FilePath& style_sheet_file) {
52 ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
53 NewRunnableMethod(this, &UserStyleSheetWatcher::LoadStyleSheet,
54 profile_path_));
55 }
56
57 void UserStyleSheetWatcher::LoadStyleSheet(const FilePath& profile_path) {
58 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); 89 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
59 // We keep the user style sheet in a subdir so we can watch for changes 90 // We keep the user style sheet in a subdir so we can watch for changes
60 // to the file. 91 // to the file.
61 FilePath style_sheet_dir = profile_path.AppendASCII(kStyleSheetDir); 92 FilePath style_sheet_dir = style_sheet_file.DirName();
62 if (!file_util::DirectoryExists(style_sheet_dir)) { 93 if (!file_util::DirectoryExists(style_sheet_dir)) {
63 if (!file_util::CreateDirectory(style_sheet_dir)) 94 if (!file_util::CreateDirectory(style_sheet_dir))
64 return; 95 return;
65 } 96 }
66 // Create the file if it doesn't exist. 97 // Create the file if it doesn't exist.
67 FilePath css_file = style_sheet_dir.AppendASCII(kUserStyleSheetFile); 98 if (!file_util::PathExists(style_sheet_file))
68 if (!file_util::PathExists(css_file)) 99 file_util::WriteFile(style_sheet_file, "", 0);
69 file_util::WriteFile(css_file, "", 0);
70 100
71 std::string css; 101 std::string css;
72 bool rv = file_util::ReadFileToString(css_file, &css); 102 bool rv = file_util::ReadFileToString(style_sheet_file, &css);
73 GURL style_sheet_url; 103 GURL style_sheet_url;
74 if (rv && !css.empty()) { 104 if (rv && !css.empty()) {
75 std::string css_base64; 105 std::string css_base64;
76 rv = base::Base64Encode(css, &css_base64); 106 rv = base::Base64Encode(css, &css_base64);
77 if (rv) { 107 if (rv) {
78 // WebKit knows about data urls, so convert the file to a data url. 108 // WebKit knows about data urls, so convert the file to a data url.
79 const char kDataUrlPrefix[] = "data:text/css;charset=utf-8;base64,"; 109 const char kDataUrlPrefix[] = "data:text/css;charset=utf-8;base64,";
80 style_sheet_url = GURL(kDataUrlPrefix + css_base64); 110 style_sheet_url = GURL(kDataUrlPrefix + css_base64);
81 } 111 }
82 } 112 }
83 ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, 113 ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
84 NewRunnableMethod(this, &UserStyleSheetWatcher::SetStyleSheet, 114 NewRunnableMethod(this, &UserStyleSheetLoader::SetStyleSheet,
85 style_sheet_url)); 115 style_sheet_url));
116 }
117
118 void UserStyleSheetLoader::SetStyleSheet(const GURL& url) {
119 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
120
121 has_loaded_ = true;
122 user_style_sheet_ = url;
123 NotifyLoaded();
124 }
125
126 UserStyleSheetWatcher::UserStyleSheetWatcher(const FilePath& profile_path)
127 : profile_path_(profile_path),
128 loader_(new UserStyleSheetLoader) {
129 // Listen for when the first render view host is created. If we load
130 // too fast, the first tab won't hear the notification and won't get
131 // the user style sheet.
132 registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB,
133 NotificationService::AllSources());
134 }
135
136 UserStyleSheetWatcher::~UserStyleSheetWatcher() {
137 }
138
139 void UserStyleSheetWatcher::Init() {
140 // Make sure we run on the file thread.
141 if (!ChromeThread::CurrentlyOn(ChromeThread::FILE)) {
142 ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
143 NewRunnableMethod(this, &UserStyleSheetWatcher::Init));
144 return;
145 }
86 146
87 if (!file_watcher_.get()) { 147 if (!file_watcher_.get()) {
88 file_watcher_.reset(new FileWatcher); 148 file_watcher_.reset(new FileWatcher);
89 file_watcher_->Watch(profile_path_.AppendASCII(kStyleSheetDir) 149 file_watcher_->Watch(profile_path_.AppendASCII(kStyleSheetDir)
90 .AppendASCII(kUserStyleSheetFile), this); 150 .AppendASCII(kUserStyleSheetFile), loader_.get());
151 FilePath style_sheet_file = profile_path_.AppendASCII(kStyleSheetDir)
152 .AppendASCII(kUserStyleSheetFile);
153 loader_->LoadStyleSheet(style_sheet_file);
91 } 154 }
92 } 155 }
93 156
94 void UserStyleSheetWatcher::SetStyleSheet(const GURL& url) { 157 GURL UserStyleSheetWatcher::user_style_sheet() const {
95 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); 158 return loader_->user_style_sheet();
159 }
96 160
97 has_loaded_ = true; 161 void UserStyleSheetWatcher::Observe(NotificationType type,
98 user_style_sheet_ = url; 162 const NotificationSource& source, const NotificationDetails& details) {
99 NotificationService::current()->Notify( 163 DCHECK(type == NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB);
100 NotificationType::USER_STYLE_SHEET_UPDATED, 164 loader_->NotifyLoaded();
101 Source<UserStyleSheetWatcher>(this), 165 registrar_.RemoveAll();
102 NotificationService::NoDetails());
103 } 166 }
OLDNEW
« no previous file with comments | « chrome/browser/user_style_sheet_watcher.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698