15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "extensions/browser/app_window/app_window_geometry_cache.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "components/keyed_service/content/browser_context_dependency_manager.h"
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_prefs.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_prefs_factory.h"
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "extensions/browser/extension_registry.h"
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extensions_browser_client.h"
15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/extension.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The timeout in milliseconds before we'll persist window geometry to the
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// StateStore.
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kSyncTimeoutMilliseconds = 1000;
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)namespace extensions {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)AppWindowGeometryCache::AppWindowGeometryCache(content::BrowserContext* context,
2803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                                               ExtensionPrefs* prefs)
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : prefs_(prefs),
30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      sync_delay_(base::TimeDelta::FromMilliseconds(kSyncTimeoutMilliseconds)),
31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      extension_registry_observer_(this) {
3203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  extension_registry_observer_.Add(ExtensionRegistry::Get(context));
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppWindowGeometryCache::~AppWindowGeometryCache() {}
3690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// static
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppWindowGeometryCache* AppWindowGeometryCache::Get(
3990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    content::BrowserContext* context) {
4090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return Factory::GetForContext(context, true /* create */);
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppWindowGeometryCache::SaveGeometry(const std::string& extension_id,
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                          const std::string& window_id,
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                          const gfx::Rect& bounds,
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                          const gfx::Rect& screen_bounds,
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                          ui::WindowShowState window_state) {
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionData& extension_data = cache_[extension_id];
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we don't have any unsynced changes and this is a duplicate of what's
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // already in the cache, just ignore it.
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (extension_data[window_id].bounds == bounds &&
53b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      extension_data[window_id].window_state == window_state &&
54eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      extension_data[window_id].screen_bounds == screen_bounds &&
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !ContainsKey(unsynced_extensions_, extension_id))
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::Time now = base::Time::Now();
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  extension_data[window_id].bounds = bounds;
61eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  extension_data[window_id].screen_bounds = screen_bounds;
62b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  extension_data[window_id].window_state = window_state;
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  extension_data[window_id].last_change = now;
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (extension_data.size() > kMaxCachedWindows) {
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ExtensionData::iterator oldest = extension_data.end();
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Too many windows in the cache, find the oldest one to remove.
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (ExtensionData::iterator it = extension_data.begin();
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)         it != extension_data.end();
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)         ++it) {
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Don't expunge the window that was just added.
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (it->first == window_id)
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        continue;
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // If time is in the future, reset it to now to minimize weirdness.
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (it->second.last_change > now)
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        it->second.last_change = now;
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (oldest == extension_data.end() ||
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          it->second.last_change < oldest->second.last_change)
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        oldest = it;
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    extension_data.erase(oldest);
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsynced_extensions_.insert(extension_id);
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't use Reset() because the timer may not yet be running.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // (In that case Stop() is a no-op.)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_timer_.Stop();
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_timer_.Start(
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      FROM_HERE, sync_delay_, this, &AppWindowGeometryCache::SyncToStorage);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppWindowGeometryCache::SyncToStorage() {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::set<std::string> tosync;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tosync.swap(unsynced_extensions_);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (std::set<std::string>::const_iterator it = tosync.begin(),
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                             eit = tosync.end();
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       it != eit;
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       ++it) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id = *it;
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ExtensionData& extension_data = cache_[extension_id];
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (ExtensionData::const_iterator it = extension_data.begin(),
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                       eit = extension_data.end();
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)         it != eit;
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)         ++it) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::DictionaryValue* value = new base::DictionaryValue;
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const gfx::Rect& bounds = it->second.bounds;
112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      const gfx::Rect& screen_bounds = it->second.screen_bounds;
1133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      DCHECK(!bounds.IsEmpty());
1143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      DCHECK(!screen_bounds.IsEmpty());
1153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      DCHECK(it->second.window_state != ui::SHOW_STATE_DEFAULT);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      value->SetInteger("x", bounds.x());
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      value->SetInteger("y", bounds.y());
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      value->SetInteger("w", bounds.width());
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      value->SetInteger("h", bounds.height());
120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      value->SetInteger("screen_bounds_x", screen_bounds.x());
121eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      value->SetInteger("screen_bounds_y", screen_bounds.y());
122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      value->SetInteger("screen_bounds_w", screen_bounds.width());
123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      value->SetInteger("screen_bounds_h", screen_bounds.height());
124b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      value->SetInteger("state", it->second.window_state);
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      value->SetString(
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "ts", base::Int64ToString(it->second.last_change.ToInternalValue()));
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dict->SetWithoutPathExpansion(it->first, value);
128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      FOR_EACH_OBSERVER(
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          Observer,
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          observers_,
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          OnGeometryCacheChanged(extension_id, it->first, bounds));
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    prefs_->SetGeometryCache(extension_id, dict.Pass());
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool AppWindowGeometryCache::GetGeometry(const std::string& extension_id,
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                         const std::string& window_id,
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                         gfx::Rect* bounds,
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                         gfx::Rect* screen_bounds,
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                         ui::WindowShowState* window_state) {
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::map<std::string, ExtensionData>::const_iterator extension_data_it =
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      cache_.find(extension_id);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1477d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // Not in the map means loading data for the extension didn't finish yet or
1487d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // the cache was not constructed until after the extension was loaded.
1497d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // Attempt to load from sync to address the latter case.
1507d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (extension_data_it == cache_.end()) {
1517d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    LoadGeometryFromStorage(extension_id);
1527d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    extension_data_it = cache_.find(extension_id);
1537d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    DCHECK(extension_data_it != cache_.end());
1547d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  }
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ExtensionData::const_iterator window_data_it =
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      extension_data_it->second.find(window_id);
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
159a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (window_data_it == extension_data_it->second.end())
160a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    return false;
161a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
162a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  const WindowData& window_data = window_data_it->second;
163a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
164a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // Check for and do not return corrupt data.
165a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if ((bounds && window_data.bounds.IsEmpty()) ||
166a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      (screen_bounds && window_data.screen_bounds.IsEmpty()) ||
167a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      (window_state && window_data.window_state == ui::SHOW_STATE_DEFAULT))
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
170b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (bounds)
171a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    *bounds = window_data.bounds;
172eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (screen_bounds)
173a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    *screen_bounds = window_data.screen_bounds;
174b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (window_state)
175a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    *window_state = window_data.window_state;
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppWindowGeometryCache::Shutdown() { SyncToStorage(); }
18090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppWindowGeometryCache::WindowData::WindowData()
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : window_state(ui::SHOW_STATE_DEFAULT) {}
183eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppWindowGeometryCache::WindowData::~WindowData() {}
185eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void AppWindowGeometryCache::OnExtensionLoaded(
187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    content::BrowserContext* browser_context,
18803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    const Extension* extension) {
189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  LoadGeometryFromStorage(extension->id());
190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void AppWindowGeometryCache::OnExtensionUnloaded(
193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    content::BrowserContext* browser_context,
19403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    const Extension* extension,
19503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    UnloadedExtensionInfo::Reason reason) {
196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  SyncToStorage();
197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  cache_.erase(extension->id());
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppWindowGeometryCache::SetSyncDelayForTests(int timeout_ms) {
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_delay_ = base::TimeDelta::FromMilliseconds(timeout_ms);
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppWindowGeometryCache::LoadGeometryFromStorage(
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& extension_id) {
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionData& extension_data = cache_[extension_id];
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::DictionaryValue* stored_windows =
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      prefs_->GetGeometryCache(extension_id);
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!stored_windows)
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
213c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (base::DictionaryValue::Iterator it(*stored_windows); !it.IsAtEnd();
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it.Advance()) {
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // If the cache already contains geometry for this window, don't
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // overwrite that information since it is probably the result of an
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // application starting up very quickly.
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& window_id = it.key();
219b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    ExtensionData::iterator cached_window = extension_data.find(window_id);
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (cached_window == extension_data.end()) {
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const base::DictionaryValue* stored_window;
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (it.value().GetAsDictionary(&stored_window)) {
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        WindowData& window_data = extension_data[it.key()];
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        int i;
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (stored_window->GetInteger("x", &i))
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          window_data.bounds.set_x(i);
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (stored_window->GetInteger("y", &i))
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          window_data.bounds.set_y(i);
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (stored_window->GetInteger("w", &i))
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          window_data.bounds.set_width(i);
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (stored_window->GetInteger("h", &i))
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          window_data.bounds.set_height(i);
234eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        if (stored_window->GetInteger("screen_bounds_x", &i))
235eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          window_data.screen_bounds.set_x(i);
236eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        if (stored_window->GetInteger("screen_bounds_y", &i))
237eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          window_data.screen_bounds.set_y(i);
238eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        if (stored_window->GetInteger("screen_bounds_w", &i))
239eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          window_data.screen_bounds.set_width(i);
240eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        if (stored_window->GetInteger("screen_bounds_h", &i))
241eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          window_data.screen_bounds.set_height(i);
242b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        if (stored_window->GetInteger("state", &i)) {
2435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          window_data.window_state = static_cast<ui::WindowShowState>(i);
244b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        }
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        std::string ts_as_string;
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (stored_window->GetString("ts", &ts_as_string)) {
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          int64 ts;
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (base::StringToInt64(ts_as_string, &ts)) {
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            window_data.last_change = base::Time::FromInternalValue(ts);
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
25890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Factory boilerplate
25990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
26090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// static
2615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppWindowGeometryCache* AppWindowGeometryCache::Factory::GetForContext(
2625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    content::BrowserContext* context,
2635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    bool create) {
2645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return static_cast<AppWindowGeometryCache*>(
26590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      GetInstance()->GetServiceForBrowserContext(context, create));
26690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
26790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppWindowGeometryCache::Factory*
2695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppWindowGeometryCache::Factory::GetInstance() {
2705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return Singleton<AppWindowGeometryCache::Factory>::get();
27190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
27290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppWindowGeometryCache::Factory::Factory()
27490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    : BrowserContextKeyedServiceFactory(
2755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          "AppWindowGeometryCache",
2765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          BrowserContextDependencyManager::GetInstance()) {
27703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  DependsOn(ExtensionPrefsFactory::GetInstance());
27890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
27990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppWindowGeometryCache::Factory::~Factory() {}
28190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
282a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)KeyedService* AppWindowGeometryCache::Factory::BuildServiceInstanceFor(
28390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    content::BrowserContext* context) const {
28403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return new AppWindowGeometryCache(context, ExtensionPrefs::Get(context));
28590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
28690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool AppWindowGeometryCache::Factory::ServiceIsNULLWhileTesting() const {
28890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return false;
28990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
29090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
29190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)content::BrowserContext*
2925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppWindowGeometryCache::Factory::GetBrowserContextToUse(
29390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    content::BrowserContext* context) const {
29403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return ExtensionsBrowserClient::Get()->GetOriginalContext(context);
29590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
29690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppWindowGeometryCache::AddObserver(Observer* observer) {
298a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  observers_.AddObserver(observer);
299a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
300a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppWindowGeometryCache::RemoveObserver(Observer* observer) {
302a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  observers_.RemoveObserver(observer);
303a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
304a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
30503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}  // namespace extensions
306