1a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 2a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// found in the LICENSE file. 4a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 5a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "ash/wm/mru_window_tracker.h" 6a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 7a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include <algorithm> 8a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "ash/session/session_state_delegate.h" 10a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "ash/shell.h" 11a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "ash/shell_window_ids.h" 12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ash/switchable_windows.h" 135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/window_state.h" 14a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "ash/wm/window_util.h" 15a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "ash/wm/workspace_controller.h" 16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/aura/window_event_dispatcher.h" 17d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event.h" 18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event_handler.h" 19effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "ui/wm/public/activation_client.h" 20a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 21a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)namespace ash { 22a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 23a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)namespace { 24a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 25a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// Adds the windows that can be cycled through for the specified window id to 26a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// |windows|. 271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)void AddTrackedWindows(aura::Window* root, 281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) int container_id, 291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) MruWindowTracker::WindowList* windows) { 30a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) aura::Window* container = Shell::GetContainer(root, container_id); 312385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch const MruWindowTracker::WindowList& children(container->children()); 322385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch windows->insert(windows->end(), children.begin(), children.end()); 33a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} 34a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Adds windows being dragged in the docked container to |windows| list. 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AddDraggedWindows(aura::Window* root, 375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) MruWindowTracker::WindowList* windows) { 38c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch aura::Window* container = 39c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch Shell::GetContainer(root, kShellWindowId_DockedContainer); 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const MruWindowTracker::WindowList& children = container->children(); 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for (MruWindowTracker::WindowList::const_iterator iter = children.begin(); 425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) iter != children.end(); ++iter) { 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (wm::GetWindowState(*iter)->is_dragged()) 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) windows->insert(windows->end(), *iter); 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Returns whether |w1| should be considered less recently used than |w2|. This 494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// is used for a stable sort to move minimized windows to the LRU end of the 504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// list. 514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)bool CompareWindowState(aura::Window* w1, aura::Window* w2) { 524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return ash::wm::IsWindowMinimized(w1) && !ash::wm::IsWindowMinimized(w2); 534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 55a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// Returns a list of windows ordered by their stacking order. 56a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// If |mru_windows| is passed, these windows are moved to the front of the list. 57a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// If |top_most_at_end|, the list is returned in descending (bottom-most / least 58a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// recently used) order. 59a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)MruWindowTracker::WindowList BuildWindowListInternal( 60a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) const std::list<aura::Window*>* mru_windows, 61a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) bool top_most_at_end) { 62a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) MruWindowTracker::WindowList windows; 63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 64a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) aura::Window* active_root = Shell::GetTargetRootWindow(); 66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for (aura::Window::Windows::const_iterator iter = root_windows.begin(); 67a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) iter != root_windows.end(); ++iter) { 68a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) if (*iter == active_root) 69a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) continue; 7058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) 7158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) AddTrackedWindows(*iter, kSwitchableWindowContainerIds[i], &windows); 72a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 73a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 74a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // Add windows in the active root windows last so that the topmost window 75a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // in the active root window becomes the front of the list. 7658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) 7758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) AddTrackedWindows(active_root, kSwitchableWindowContainerIds[i], &windows); 78a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Dragged windows are temporarily parented in docked container. Include them 805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // in the in tracked list. 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) AddDraggedWindows(active_root, &windows); 825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 83a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // Removes unfocusable windows. 84a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) MruWindowTracker::WindowList::iterator last = 85a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) std::remove_if( 86a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) windows.begin(), 87a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) windows.end(), 88a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) std::not1(std::ptr_fun(ash::wm::CanActivateWindow))); 89a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) windows.erase(last, windows.end()); 90a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 91a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // Put the windows in the mru_windows list at the head, if it's available. 92a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) if (mru_windows) { 93a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // Iterate through the list backwards, so that we can move each window to 94a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // the front of the windows list as we find them. 95a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) for (std::list<aura::Window*>::const_reverse_iterator ix = 96a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) mru_windows->rbegin(); 97a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) ix != mru_windows->rend(); ++ix) { 9868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // Exclude windows in non-switchable containers and those which cannot 9968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // be activated. 10068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) if (!IsSwitchableContainer((*ix)->parent()) || 10168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) !ash::wm::CanActivateWindow(*ix)) { 10268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) continue; 10368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) } 10468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) 105a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) MruWindowTracker::WindowList::iterator window = 106a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) std::find(windows.begin(), windows.end(), *ix); 107a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) if (window != windows.end()) { 108a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) windows.erase(window); 109a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) windows.push_back(*ix); 110a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 111a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 112a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 113a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 1144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Move minimized windows to the beginning (LRU end) of the list. 1154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) std::stable_sort(windows.begin(), windows.end(), CompareWindowState); 1164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 117a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // Window cycling expects the topmost window at the front of the list. 118a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) if (!top_most_at_end) 119a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) std::reverse(windows.begin(), windows.end()); 120a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 121a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) return windows; 122a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} 123a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 124a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} // namespace 125a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 126a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////// 127a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// MruWindowTracker, public: 128a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 129a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)MruWindowTracker::MruWindowTracker( 130a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) aura::client::ActivationClient* activation_client) 131a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) : activation_client_(activation_client), 132a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) ignore_window_activations_(false) { 133a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) activation_client_->AddObserver(this); 134a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} 135a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 136a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)MruWindowTracker::~MruWindowTracker() { 13768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) for (std::list<aura::Window*>::iterator iter = mru_windows_.begin(); 13868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) iter != mru_windows_.end(); ++iter) { 13968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) (*iter)->RemoveObserver(this); 140a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 141a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 142a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) activation_client_->RemoveObserver(this); 143a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} 144a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 145a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// static 146a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)MruWindowTracker::WindowList MruWindowTracker::BuildWindowList( 147a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) bool top_most_at_end) { 148a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) return BuildWindowListInternal(NULL, top_most_at_end); 149a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} 150a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 151a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)MruWindowTracker::WindowList MruWindowTracker::BuildMruWindowList() { 152a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) return BuildWindowListInternal(&mru_windows_, false); 153a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} 154a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 155a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)void MruWindowTracker::SetIgnoreActivations(bool ignore) { 156a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) ignore_window_activations_ = ignore; 157a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 158a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // If no longer ignoring window activations, move currently active window 159a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // to front. 16068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) if (!ignore) 16168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) SetActiveWindow(wm::GetActiveWindow()); 162a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} 163a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 164a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////// 165a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// MruWindowTracker, private: 166a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 16768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void MruWindowTracker::SetActiveWindow(aura::Window* active_window) { 16868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) if (!active_window) 16968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) return; 17068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) 17168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) std::list<aura::Window*>::iterator iter = 17268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) std::find(mru_windows_.begin(), mru_windows_.end(), active_window); 17368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // Observe all newly tracked windows. 17468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) if (iter == mru_windows_.end()) 17568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) active_window->AddObserver(this); 17668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) else 17768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) mru_windows_.erase(iter); 17868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // TODO(flackr): Remove this check if this doesn't fire for a while. This 17968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // should verify that all tracked windows start with a layer, see 18068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // http://crbug.com/291354. 18168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) CHECK(active_window->layer()); 18268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) mru_windows_.push_front(active_window); 183a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} 184a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 185a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)void MruWindowTracker::OnWindowActivated(aura::Window* gained_active, 186a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) aura::Window* lost_active) { 18768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) if (!ignore_window_activations_) 18868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) SetActiveWindow(gained_active); 189a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} 190a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MruWindowTracker::OnWindowDestroyed(aura::Window* window) { 1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // It's possible for OnWindowActivated() to be called after 1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // OnWindowDestroying(). This means we need to override OnWindowDestroyed() 1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // else we may end up with a deleted window in |mru_windows_|. 19568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) mru_windows_.remove(window); 196a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) window->RemoveObserver(this); 197a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} 198a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 199a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} // namespace ash 200