// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/user_style_sheet_watcher.h" #include "base/base64.h" #include "base/file_util.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" namespace { // The subdirectory of the profile that contains the style sheet. const char kStyleSheetDir[] = "User StyleSheets"; // The filename of the stylesheet. const char kUserStyleSheetFile[] = "Custom.css"; } // namespace UserStyleSheetWatcher::UserStyleSheetWatcher(const FilePath& profile_path) : profile_path_(profile_path), has_loaded_(false) { // Listen for when the first render view host is created. If we load // too fast, the first tab won't hear the notification and won't get // the user style sheet. registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB, NotificationService::AllSources()); } void UserStyleSheetWatcher::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { DCHECK(type == NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB); if (has_loaded_) { NotificationService::current()->Notify( NotificationType::USER_STYLE_SHEET_UPDATED, Source(this), NotificationService::NoDetails()); } registrar_.RemoveAll(); } void UserStyleSheetWatcher::OnFileChanged(const FilePath& path) { ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, NewRunnableMethod(this, &UserStyleSheetWatcher::LoadStyleSheet, profile_path_)); } void UserStyleSheetWatcher::Init() { ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, NewRunnableMethod(this, &UserStyleSheetWatcher::LoadStyleSheet, profile_path_)); } void UserStyleSheetWatcher::LoadStyleSheet(const FilePath& profile_path) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); // We keep the user style sheet in a subdir so we can watch for changes // to the file. FilePath style_sheet_dir = profile_path.AppendASCII(kStyleSheetDir); if (!file_util::DirectoryExists(style_sheet_dir)) { if (!file_util::CreateDirectory(style_sheet_dir)) return; } // Create the file if it doesn't exist. FilePath css_file = style_sheet_dir.AppendASCII(kUserStyleSheetFile); if (!file_util::PathExists(css_file)) file_util::WriteFile(css_file, "", 0); std::string css; bool rv = file_util::ReadFileToString(css_file, &css); GURL style_sheet_url; if (rv && !css.empty()) { std::string css_base64; rv = base::Base64Encode(css, &css_base64); if (rv) { // WebKit knows about data urls, so convert the file to a data url. const char kDataUrlPrefix[] = "data:text/css;charset=utf-8;base64,"; style_sheet_url = GURL(kDataUrlPrefix + css_base64); } } ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, NewRunnableMethod(this, &UserStyleSheetWatcher::SetStyleSheet, style_sheet_url)); if (!file_watcher_.get()) { file_watcher_.reset(new FileWatcher); file_watcher_->Watch(profile_path_.AppendASCII(kStyleSheetDir) .AppendASCII(kUserStyleSheetFile), this); } } void UserStyleSheetWatcher::SetStyleSheet(const GURL& url) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); has_loaded_ = true; user_style_sheet_ = url; NotificationService::current()->Notify( NotificationType::USER_STYLE_SHEET_UPDATED, Source(this), NotificationService::NoDetails()); }