1// Copyright 2014 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 "content/browser/time_zone_monitor.h" 6 7#include <stdlib.h> 8 9#include <vector> 10 11#include "base/basictypes.h" 12#include "base/bind.h" 13#include "base/files/file_path.h" 14#include "base/files/file_path_watcher.h" 15#include "base/memory/ref_counted.h" 16#include "base/stl_util.h" 17#include "content/public/browser/browser_thread.h" 18 19#if !defined(OS_CHROMEOS) 20 21namespace content { 22 23namespace { 24class TimeZoneMonitorLinuxImpl; 25} // namespace 26 27class TimeZoneMonitorLinux : public TimeZoneMonitor { 28 public: 29 TimeZoneMonitorLinux(); 30 virtual ~TimeZoneMonitorLinux(); 31 32 void NotifyRenderersFromImpl() { 33 NotifyRenderers(); 34 } 35 36 private: 37 scoped_refptr<TimeZoneMonitorLinuxImpl> impl_; 38 39 DISALLOW_COPY_AND_ASSIGN(TimeZoneMonitorLinux); 40}; 41 42namespace { 43 44// FilePathWatcher needs to run on the FILE thread, but TimeZoneMonitor runs 45// on the UI thread. TimeZoneMonitorLinuxImpl is the bridge between these 46// threads. 47class TimeZoneMonitorLinuxImpl 48 : public base::RefCountedThreadSafe<TimeZoneMonitorLinuxImpl> { 49 public: 50 explicit TimeZoneMonitorLinuxImpl(TimeZoneMonitorLinux* owner) 51 : base::RefCountedThreadSafe<TimeZoneMonitorLinuxImpl>(), 52 file_path_watchers_(), 53 owner_(owner) { 54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 55 BrowserThread::PostTask( 56 BrowserThread::FILE, 57 FROM_HERE, 58 base::Bind(&TimeZoneMonitorLinuxImpl::StartWatchingOnFileThread, this)); 59 } 60 61 void StopWatching() { 62 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 63 owner_ = NULL; 64 BrowserThread::PostTask( 65 BrowserThread::FILE, 66 FROM_HERE, 67 base::Bind(&TimeZoneMonitorLinuxImpl::StopWatchingOnFileThread, this)); 68 } 69 70 private: 71 friend class base::RefCountedThreadSafe<TimeZoneMonitorLinuxImpl>; 72 73 ~TimeZoneMonitorLinuxImpl() { 74 DCHECK(!owner_); 75 STLDeleteElements(&file_path_watchers_); 76 } 77 78 void StartWatchingOnFileThread() { 79 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 80 81 // There is no true standard for where time zone information is actually 82 // stored. glibc uses /etc/localtime, uClibc uses /etc/TZ, and some older 83 // systems store the name of the time zone file within /usr/share/zoneinfo 84 // in /etc/timezone. Different libraries and custom builds may mean that 85 // still more paths are used. Just watch all three of these paths, because 86 // false positives are harmless, assuming the false positive rate is 87 // reasonable. 88 const char* kFilesToWatch[] = { 89 "/etc/localtime", 90 "/etc/timezone", 91 "/etc/TZ", 92 }; 93 94 for (size_t index = 0; index < arraysize(kFilesToWatch); ++index) { 95 file_path_watchers_.push_back(new base::FilePathWatcher()); 96 file_path_watchers_.back()->Watch( 97 base::FilePath(kFilesToWatch[index]), 98 false, 99 base::Bind(&TimeZoneMonitorLinuxImpl::OnTimeZoneFileChanged, this)); 100 } 101 } 102 103 void StopWatchingOnFileThread() { 104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 105 STLDeleteElements(&file_path_watchers_); 106 } 107 108 void OnTimeZoneFileChanged(const base::FilePath& path, bool error) { 109 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 110 BrowserThread::PostTask( 111 BrowserThread::UI, 112 FROM_HERE, 113 base::Bind(&TimeZoneMonitorLinuxImpl::OnTimeZoneFileChangedOnUIThread, 114 this)); 115 } 116 117 void OnTimeZoneFileChangedOnUIThread() { 118 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 119 if (owner_) { 120 owner_->NotifyRenderersFromImpl(); 121 } 122 } 123 124 std::vector<base::FilePathWatcher*> file_path_watchers_; 125 TimeZoneMonitorLinux* owner_; 126 127 DISALLOW_COPY_AND_ASSIGN(TimeZoneMonitorLinuxImpl); 128}; 129 130} // namespace 131 132TimeZoneMonitorLinux::TimeZoneMonitorLinux() 133 : TimeZoneMonitor(), 134 impl_() { 135 // If the TZ environment variable is set, its value specifies the time zone 136 // specification, and it's pointless to monitor any files in /etc for 137 // changes because such changes would have no effect on the TZ environment 138 // variable and thus the interpretation of the local time zone in the 139 // or renderer processes. 140 // 141 // The system-specific format for the TZ environment variable beginning with 142 // a colon is implemented by glibc as the path to a time zone data file, and 143 // it would be possible to monitor this file for changes if a TZ variable of 144 // this format was encountered, but this is not necessary: when loading a 145 // time zone specification in this way, glibc does not reload the file when 146 // it changes, so it's pointless to respond to a notification that it has 147 // changed. 148 if (!getenv("TZ")) { 149 impl_ = new TimeZoneMonitorLinuxImpl(this); 150 } 151} 152 153TimeZoneMonitorLinux::~TimeZoneMonitorLinux() { 154 if (impl_.get()) { 155 impl_->StopWatching(); 156 } 157} 158 159// static 160scoped_ptr<TimeZoneMonitor> TimeZoneMonitor::Create() { 161 return scoped_ptr<TimeZoneMonitor>(new TimeZoneMonitorLinux()); 162} 163 164} // namespace content 165 166#endif // !OS_CHROMEOS 167