blob: 2bc95688861f5f9621fceef47f0fe8e14e3a2738 [file] [log] [blame]
// 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<UserStyleSheetWatcher>(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<UserStyleSheetWatcher>(this),
NotificationService::NoDetails());
}