OLD | NEW |
| (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/extensions/shell_window_geometry_cache.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/stl_util.h" | |
9 #include "base/strings/string_number_conversions.h" | |
10 #include "chrome/browser/extensions/extension_prefs.h" | |
11 #include "chrome/common/chrome_notification_types.h" | |
12 #include "chrome/common/extensions/extension.h" | |
13 #include "content/public/browser/notification_service.h" | |
14 #include "content/public/browser/notification_types.h" | |
15 | |
16 namespace { | |
17 | |
18 // The timeout in milliseconds before we'll persist window geometry to the | |
19 // StateStore. | |
20 const int kSyncTimeoutMilliseconds = 1000; | |
21 | |
22 } // namespace | |
23 | |
24 namespace extensions { | |
25 | |
26 ShellWindowGeometryCache::ShellWindowGeometryCache(Profile* profile, | |
27 ExtensionPrefs* prefs) | |
28 : prefs_(prefs), | |
29 sync_delay_(base::TimeDelta::FromMilliseconds(kSyncTimeoutMilliseconds)) { | |
30 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, | |
31 content::Source<Profile>(profile)); | |
32 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, | |
33 content::Source<Profile>(profile)); | |
34 } | |
35 | |
36 ShellWindowGeometryCache::~ShellWindowGeometryCache() { | |
37 SyncToStorage(); | |
38 } | |
39 | |
40 void ShellWindowGeometryCache::SaveGeometry( | |
41 const std::string& extension_id, | |
42 const std::string& window_id, | |
43 const gfx::Rect& bounds, | |
44 ui::WindowShowState window_state) { | |
45 ExtensionData& extension_data = cache_[extension_id]; | |
46 | |
47 // If we don't have any unsynced changes and this is a duplicate of what's | |
48 // already in the cache, just ignore it. | |
49 if (extension_data[window_id].bounds == bounds && | |
50 extension_data[window_id].window_state == window_state && | |
51 !ContainsKey(unsynced_extensions_, extension_id)) | |
52 return; | |
53 | |
54 base::Time now = base::Time::Now(); | |
55 | |
56 extension_data[window_id].bounds = bounds; | |
57 extension_data[window_id].window_state = window_state; | |
58 extension_data[window_id].last_change = now; | |
59 | |
60 if (extension_data.size() > kMaxCachedWindows) { | |
61 ExtensionData::iterator oldest = extension_data.end(); | |
62 // Too many windows in the cache, find the oldest one to remove. | |
63 for (ExtensionData::iterator it = extension_data.begin(); | |
64 it != extension_data.end(); ++it) { | |
65 // Don't expunge the window that was just added. | |
66 if (it->first == window_id) continue; | |
67 | |
68 // If time is in the future, reset it to now to minimize weirdness. | |
69 if (it->second.last_change > now) | |
70 it->second.last_change = now; | |
71 | |
72 if (oldest == extension_data.end() || | |
73 it->second.last_change < oldest->second.last_change) | |
74 oldest = it; | |
75 } | |
76 extension_data.erase(oldest); | |
77 } | |
78 | |
79 unsynced_extensions_.insert(extension_id); | |
80 | |
81 // We don't use Reset() because the timer may not yet be running. | |
82 // (In that case Stop() is a no-op.) | |
83 sync_timer_.Stop(); | |
84 sync_timer_.Start(FROM_HERE, sync_delay_, this, | |
85 &ShellWindowGeometryCache::SyncToStorage); | |
86 } | |
87 | |
88 void ShellWindowGeometryCache::SyncToStorage() { | |
89 std::set<std::string> tosync; | |
90 tosync.swap(unsynced_extensions_); | |
91 for (std::set<std::string>::const_iterator it = tosync.begin(), | |
92 eit = tosync.end(); it != eit; ++it) { | |
93 const std::string& extension_id = *it; | |
94 const ExtensionData& extension_data = cache_[extension_id]; | |
95 | |
96 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue); | |
97 for (ExtensionData::const_iterator it = extension_data.begin(), | |
98 eit = extension_data.end(); it != eit; ++it) { | |
99 base::DictionaryValue* value = new base::DictionaryValue; | |
100 const gfx::Rect& bounds = it->second.bounds; | |
101 value->SetInteger("x", bounds.x()); | |
102 value->SetInteger("y", bounds.y()); | |
103 value->SetInteger("w", bounds.width()); | |
104 value->SetInteger("h", bounds.height()); | |
105 value->SetInteger("state", it->second.window_state); | |
106 value->SetString( | |
107 "ts", base::Int64ToString(it->second.last_change.ToInternalValue())); | |
108 dict->SetWithoutPathExpansion(it->first, value); | |
109 } | |
110 prefs_->SetGeometryCache(extension_id, dict.Pass()); | |
111 } | |
112 } | |
113 | |
114 bool ShellWindowGeometryCache::GetGeometry( | |
115 const std::string& extension_id, | |
116 const std::string& window_id, | |
117 gfx::Rect* bounds, | |
118 ui::WindowShowState* window_state) const { | |
119 | |
120 std::map<std::string, ExtensionData>::const_iterator | |
121 extension_data_it = cache_.find(extension_id); | |
122 | |
123 // Not in the map means loading data for the extension didn't finish yet. | |
124 if (extension_data_it == cache_.end()) | |
125 return false; | |
126 | |
127 ExtensionData::const_iterator window_data = extension_data_it->second.find( | |
128 window_id); | |
129 | |
130 if (window_data == extension_data_it->second.end()) | |
131 return false; | |
132 | |
133 if (bounds) | |
134 *bounds = window_data->second.bounds; | |
135 if (window_state) | |
136 *window_state = window_data->second.window_state; | |
137 return true; | |
138 } | |
139 | |
140 void ShellWindowGeometryCache::Observe( | |
141 int type, const content::NotificationSource& source, | |
142 const content::NotificationDetails& details) { | |
143 switch (type) { | |
144 case chrome::NOTIFICATION_EXTENSION_LOADED: { | |
145 std::string extension_id = | |
146 content::Details<const Extension>(details).ptr()->id(); | |
147 OnExtensionLoaded(extension_id); | |
148 break; | |
149 } | |
150 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { | |
151 std::string extension_id = | |
152 content::Details<const UnloadedExtensionInfo>(details). | |
153 ptr()->extension->id(); | |
154 OnExtensionUnloaded(extension_id); | |
155 break; | |
156 } | |
157 default: | |
158 NOTREACHED(); | |
159 return; | |
160 } | |
161 } | |
162 | |
163 void ShellWindowGeometryCache::SetSyncDelayForTests(int timeout_ms) { | |
164 sync_delay_ = base::TimeDelta::FromMilliseconds(timeout_ms); | |
165 } | |
166 | |
167 void ShellWindowGeometryCache::OnExtensionLoaded( | |
168 const std::string& extension_id) { | |
169 ExtensionData& extension_data = cache_[extension_id]; | |
170 | |
171 const base::DictionaryValue* stored_windows = | |
172 prefs_->GetGeometryCache(extension_id); | |
173 if (!stored_windows) | |
174 return; | |
175 | |
176 for (base::DictionaryValue::Iterator it(*stored_windows); !it.IsAtEnd(); | |
177 it.Advance()) { | |
178 // If the cache already contains geometry for this window, don't | |
179 // overwrite that information since it is probably the result of an | |
180 // application starting up very quickly. | |
181 const std::string& window_id = it.key(); | |
182 ExtensionData::iterator cached_window = extension_data.find(window_id); | |
183 if (cached_window == extension_data.end()) { | |
184 const base::DictionaryValue* stored_window; | |
185 if (it.value().GetAsDictionary(&stored_window)) { | |
186 WindowData& window_data = extension_data[it.key()]; | |
187 | |
188 int i; | |
189 if (stored_window->GetInteger("x", &i)) | |
190 window_data.bounds.set_x(i); | |
191 if (stored_window->GetInteger("y", &i)) | |
192 window_data.bounds.set_y(i); | |
193 if (stored_window->GetInteger("w", &i)) | |
194 window_data.bounds.set_width(i); | |
195 if (stored_window->GetInteger("h", &i)) | |
196 window_data.bounds.set_height(i); | |
197 if (stored_window->GetInteger("state", &i)) { | |
198 window_data.window_state = | |
199 static_cast<ui::WindowShowState>(i); | |
200 } | |
201 std::string ts_as_string; | |
202 if (stored_window->GetString("ts", &ts_as_string)) { | |
203 int64 ts; | |
204 if (base::StringToInt64(ts_as_string, &ts)) { | |
205 window_data.last_change = base::Time::FromInternalValue(ts); | |
206 } | |
207 } | |
208 } | |
209 } | |
210 } | |
211 } | |
212 | |
213 void ShellWindowGeometryCache::OnExtensionUnloaded( | |
214 const std::string& extension_id) { | |
215 SyncToStorage(); | |
216 cache_.erase(extension_id); | |
217 } | |
218 | |
219 } // namespace extensions | |
OLD | NEW |