summaryrefslogtreecommitdiffstats
path: root/chrome/browser/chromeos
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/chromeos')
-rw-r--r--chrome/browser/chromeos/extensions/media_player_event_router.cc29
-rw-r--r--chrome/browser/chromeos/extensions/media_player_event_router.h31
-rw-r--r--chrome/browser/chromeos/media/media_player.cc311
-rw-r--r--chrome/browser/chromeos/media/media_player.h161
-rw-r--r--chrome/browser/chromeos/media/media_player_browsertest.cc78
5 files changed, 610 insertions, 0 deletions
diff --git a/chrome/browser/chromeos/extensions/media_player_event_router.cc b/chrome/browser/chromeos/extensions/media_player_event_router.cc
new file mode 100644
index 0000000..ea8f70b
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/media_player_event_router.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 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/chromeos/extensions/media_player_event_router.h"
+
+#include "base/memory/singleton.h"
+#include "chrome/browser/extensions/extension_event_router.h"
+#include "chrome/browser/profiles/profile.h"
+
+ExtensionMediaPlayerEventRouter::ExtensionMediaPlayerEventRouter()
+ : profile_(NULL) {
+}
+
+ExtensionMediaPlayerEventRouter*
+ ExtensionMediaPlayerEventRouter::GetInstance() {
+ return Singleton<ExtensionMediaPlayerEventRouter>::get();
+}
+
+void ExtensionMediaPlayerEventRouter::Init(Profile* profile) {
+ profile_ = profile;
+}
+
+void ExtensionMediaPlayerEventRouter::NotifyPlaylistChanged() {
+ if (profile_ && profile_->GetExtensionEventRouter()) {
+ profile_->GetExtensionEventRouter()->DispatchEventToRenderers(
+ "mediaPlayerPrivate.onPlaylistChanged", "[]", NULL, GURL());
+ }
+}
diff --git a/chrome/browser/chromeos/extensions/media_player_event_router.h b/chrome/browser/chromeos/extensions/media_player_event_router.h
new file mode 100644
index 0000000..5095de1
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/media_player_event_router.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_MEDIA_PLAYER_EVENT_ROUTER_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_MEDIA_PLAYER_EVENT_ROUTER_H_
+#pragma once
+
+#include "base/basictypes.h"
+
+class Profile;
+template <typename T> struct DefaultSingletonTraits;
+
+// Event router class for events related to Mediaplayer.
+class ExtensionMediaPlayerEventRouter {
+ public:
+ static ExtensionMediaPlayerEventRouter* GetInstance();
+
+ void Init(Profile* profile);
+
+ void NotifyPlaylistChanged();
+
+ private:
+ Profile* profile_;
+
+ ExtensionMediaPlayerEventRouter();
+ friend struct DefaultSingletonTraits<ExtensionMediaPlayerEventRouter>;
+ DISALLOW_COPY_AND_ASSIGN(ExtensionMediaPlayerEventRouter);
+};
+
+#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_MEDIA_PLAYER_EVENT_ROUTER_H_
diff --git a/chrome/browser/chromeos/media/media_player.cc b/chrome/browser/chromeos/media/media_player.cc
new file mode 100644
index 0000000..d320cfa
--- /dev/null
+++ b/chrome/browser/chromeos/media/media_player.cc
@@ -0,0 +1,311 @@
+// Copyright (c) 2011 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/chromeos/media/media_player.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "base/threading/thread.h"
+#include "base/time.h"
+#include "base/values.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/chromeos/extensions/media_player_event_router.h"
+#include "chrome/browser/download/download_manager.h"
+#include "chrome/browser/download/download_util.h"
+#include "chrome/browser/extensions/file_manager_util.h"
+#include "chrome/browser/history/history_types.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/webui/favicon_source.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/jstemplate_builder.h"
+#include "chrome/common/net/url_fetcher.h"
+#include "chrome/common/time_format.h"
+#include "chrome/common/url_constants.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "content/browser/user_metrics.h"
+#include "grit/browser_resources.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "grit/locale_settings.h"
+#include "net/base/escape.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_request_job.h"
+#include "ui/base/resource/resource_bundle.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/frame/panel_browser_view.h"
+#endif
+
+static const char* kMediaPlayerAppName = "mediaplayer";
+static const int kPopupLeft = 0;
+static const int kPopupTop = 0;
+static const int kPopupWidth = 350;
+static const int kPopupHeight = 300;
+
+const MediaPlayer::UrlVector& MediaPlayer::GetPlaylist() const {
+ return current_playlist_;
+}
+
+int MediaPlayer::GetPlaylistPosition() const {
+ return current_position_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Mediaplayer
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// Allows InvokeLater without adding refcounting. This class is a Singleton and
+// won't be deleted until it's last InvokeLater is run.
+DISABLE_RUNNABLE_METHOD_REFCOUNT(MediaPlayer);
+
+MediaPlayer::~MediaPlayer() {
+}
+
+// static
+MediaPlayer* MediaPlayer::GetInstance() {
+ return Singleton<MediaPlayer>::get();
+}
+
+void MediaPlayer::EnqueueMediaFile(Profile* profile, const FilePath& file_path,
+ Browser* creator) {
+ GURL url;
+ if (!FileManagerUtil::ConvertFileToFileSystemUrl(profile, file_path,
+ GetOriginUrl(), &url)) {
+ }
+ EnqueueMediaFileUrl(url, creator);
+}
+
+void MediaPlayer::EnqueueMediaFileUrl(const GURL& url, Browser* creator) {
+ if (mediaplayer_browser_ == NULL) {
+ PopupMediaPlayer(creator);
+ }
+ EnqueueMediaFileUrl(url);
+}
+
+void MediaPlayer::EnqueueMediaFileUrl(const GURL& url) {
+ current_playlist_.push_back(MediaUrl(url));
+ NotifyPlaylistChanged();
+}
+
+void MediaPlayer::ForcePlayMediaFile(Profile* profile,
+ const FilePath& file_path,
+ Browser* creator) {
+ GURL url;
+ if (!FileManagerUtil::ConvertFileToFileSystemUrl(profile, file_path,
+ GetOriginUrl(), &url)) {
+ return;
+ }
+ ForcePlayMediaURL(url, creator);
+}
+
+void MediaPlayer::ForcePlayMediaURL(const GURL& url, Browser* creator) {
+ if (mediaplayer_browser_ == NULL) {
+ PopupMediaPlayer(creator);
+ }
+ current_playlist_.push_back(MediaUrl(url));
+ current_position_ = current_playlist_.size() - 1;
+ pending_playback_request_ = true;
+ NotifyPlaylistChanged();
+}
+
+void MediaPlayer::TogglePlaylistWindowVisible() {
+ if (playlist_browser_) {
+ ClosePlaylistWindow();
+ } else {
+ ShowPlaylistWindow();
+ }
+}
+
+void MediaPlayer::ShowPlaylistWindow() {
+ if (playlist_browser_ == NULL) {
+ PopupPlaylist(NULL);
+ }
+}
+
+void MediaPlayer::ClosePlaylistWindow() {
+ if (playlist_browser_ != NULL) {
+ playlist_browser_->window()->Close();
+ }
+}
+
+void MediaPlayer::SetPlaylistPosition(int position) {
+ const int playlist_size = current_playlist_.size();
+ if (current_position_ < 0 || current_position_ > playlist_size)
+ position = current_playlist_.size();
+ if (current_position_ != position) {
+ current_position_ = position;
+ NotifyPlaylistChanged();
+ }
+}
+
+void MediaPlayer::SetPlaybackError(GURL const& url) {
+ for (size_t x = 0; x < current_playlist_.size(); x++) {
+ if (current_playlist_[x].url == url) {
+ current_playlist_[x].haderror = true;
+ }
+ }
+ NotifyPlaylistChanged();
+}
+
+void MediaPlayer::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::BROWSER_CLOSING);
+ registrar_.Remove(this,
+ NotificationType::BROWSER_CLOSING,
+ source);
+ if (Source<Browser>(source).ptr() == mediaplayer_browser_) {
+ mediaplayer_browser_ = NULL;
+ } else if (Source<Browser>(source).ptr() == playlist_browser_) {
+ playlist_browser_ = NULL;
+ }
+}
+
+void MediaPlayer::NotifyPlaylistChanged() {
+ ExtensionMediaPlayerEventRouter::GetInstance()->NotifyPlaylistChanged();
+}
+
+bool MediaPlayer::GetPendingPlayRequestAndReset() {
+ bool result = pending_playback_request_;
+ pending_playback_request_ = false;
+ return result;
+}
+
+void MediaPlayer::SetPlaybackRequest() {
+ pending_playback_request_ = true;
+}
+
+void MediaPlayer::ToggleFullscreen() {
+ if (mediaplayer_browser_) {
+ mediaplayer_browser_->ToggleFullscreenMode();
+ }
+}
+
+void MediaPlayer::PopupPlaylist(Browser* creator) {
+ Profile* profile = BrowserList::GetLastActive()->profile();
+ playlist_browser_ = Browser::CreateForApp(Browser::TYPE_PANEL,
+ kMediaPlayerAppName,
+ gfx::Size(),
+ profile);
+ registrar_.Add(this,
+ NotificationType::BROWSER_CLOSING,
+ Source<Browser>(playlist_browser_));
+ playlist_browser_->AddSelectedTabWithURL(GetMediaplayerPlaylistUrl(),
+ PageTransition::LINK);
+ playlist_browser_->window()->SetBounds(gfx::Rect(kPopupLeft,
+ kPopupTop,
+ kPopupWidth,
+ kPopupHeight));
+ playlist_browser_->window()->Show();
+}
+
+void MediaPlayer::PopupMediaPlayer(Browser* creator) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &MediaPlayer::PopupMediaPlayer,
+ static_cast<Browser*>(NULL)));
+ return;
+ }
+ Profile* profile = BrowserList::GetLastActive()->profile();
+ mediaplayer_browser_ = Browser::CreateForApp(Browser::TYPE_PANEL,
+ kMediaPlayerAppName,
+ gfx::Size(),
+ profile);
+ registrar_.Add(this,
+ NotificationType::BROWSER_CLOSING,
+ Source<Browser>(mediaplayer_browser_));
+
+#if defined(OS_CHROMEOS)
+ // Since we are on chromeos, popups should be a PanelBrowserView,
+ // so we can just cast it.
+ if (creator) {
+ chromeos::PanelBrowserView* creatorview =
+ static_cast<chromeos::PanelBrowserView*>(creator->window());
+ chromeos::PanelBrowserView* view =
+ static_cast<chromeos::PanelBrowserView*>(
+ mediaplayer_browser_->window());
+ view->SetCreatorView(creatorview);
+ }
+#endif
+ mediaplayer_browser_->AddSelectedTabWithURL(GetMediaPlayerUrl(),
+ PageTransition::LINK);
+ mediaplayer_browser_->window()->SetBounds(gfx::Rect(kPopupLeft,
+ kPopupTop,
+ kPopupWidth,
+ kPopupHeight));
+ mediaplayer_browser_->window()->Show();
+}
+
+net::URLRequestJob* MediaPlayer::MaybeIntercept(net::URLRequest* request) {
+ // Don't attempt to intercept here as we want to wait until the mime
+ // type is fully determined.
+ return NULL;
+}
+
+// This is the list of mime types currently supported by the Google
+// Document Viewer.
+static const char* const supported_mime_type_list[] = {
+ "audio/mpeg",
+ "video/mp4",
+ "audio/mp3"
+};
+
+net::URLRequestJob* MediaPlayer::MaybeInterceptResponse(
+ net::URLRequest* request) {
+ // Do not intercept this request if it is a download.
+ if (request->load_flags() & net::LOAD_IS_DOWNLOAD) {
+ return NULL;
+ }
+
+ std::string mime_type;
+ request->GetMimeType(&mime_type);
+ // If it is in our list of known URLs, enqueue the url then
+ // Cancel the request so the mediaplayer can handle it when
+ // it hits it in the playlist.
+ if (supported_mime_types_.find(mime_type) != supported_mime_types_.end()) {
+ if (request->referrer() != chrome::kChromeUIMediaplayerURL &&
+ !request->referrer().empty()) {
+ EnqueueMediaFileUrl(request->url(), NULL);
+ request->Cancel();
+ }
+ }
+ return NULL;
+}
+
+GURL MediaPlayer::GetOriginUrl() const {
+ return FileManagerUtil::GetMediaPlayerUrl().GetOrigin();
+}
+
+GURL MediaPlayer::GetMediaplayerPlaylistUrl() const {
+ return FileManagerUtil::GetMediaPlayerPlaylistUrl();
+}
+
+GURL MediaPlayer::GetMediaPlayerUrl() const {
+ return FileManagerUtil::GetMediaPlayerUrl();
+}
+
+MediaPlayer::MediaPlayer()
+ : current_position_(0),
+ pending_playback_request_(false),
+ playlist_browser_(NULL),
+ mediaplayer_browser_(NULL) {
+ for (size_t i = 0; i < arraysize(supported_mime_type_list); ++i) {
+ supported_mime_types_.insert(supported_mime_type_list[i]);
+ }
+};
diff --git a/chrome/browser/chromeos/media/media_player.h b/chrome/browser/chromeos/media/media_player.h
new file mode 100644
index 0000000..e916ea2
--- /dev/null
+++ b/chrome/browser/chromeos/media/media_player.h
@@ -0,0 +1,161 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_MEDIA_MEDIA_PLAYER_H_
+#define CHROME_BROWSER_CHROMEOS_MEDIA_MEDIA_PLAYER_H_
+#pragma once
+
+#include <set>
+#include <vector>
+
+#include "base/memory/singleton.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/notification_source.h"
+#include "content/common/notification_type.h"
+
+#include "net/url_request/url_request.h"
+
+template <typename T> struct DefaultSingletonTraits;
+
+class Browser;
+class GURL;
+class Profile;
+
+class MediaPlayer : public NotificationObserver,
+ public net::URLRequest::Interceptor {
+ public:
+ struct MediaUrl;
+ typedef std::vector<MediaUrl> UrlVector;
+
+ virtual ~MediaPlayer();
+
+ // Enqueues this file into the current playlist. If the mediaplayer is
+ // not currently visible, show it, and play the given url.
+ void EnqueueMediaFile(Profile* profile, const FilePath& file_path,
+ Browser* creator);
+
+ // Enqueues this fileschema url into the current playlist. If the mediaplayer
+ // is not currently visible, show it, and play the given url.
+ void EnqueueMediaFileUrl(const GURL& url, Browser* creator);
+
+ // Clears out the current playlist, and start playback of the given
+ // |file_path|. If there is no mediaplayer currently, show it, and play the
+ // given |file_path|.
+ void ForcePlayMediaFile(Profile* profile, const FilePath& file_path,
+ Browser* creator);
+
+ // Clears out the current playlist, and start playback of the given url.
+ // If there is no mediaplayer currently, show it, and play the given url.
+ void ForcePlayMediaURL(const GURL& url, Browser* creator);
+
+ // Toggle the visibility of the playlist window.
+ void TogglePlaylistWindowVisible();
+
+ // Force the playlist window to be shown.
+ void ShowPlaylistWindow();
+
+ // Toggle the mediaplayer between fullscreen and windowed.
+ void ToggleFullscreen();
+
+ // Force the playlist window to be closed.
+ void ClosePlaylistWindow();
+
+ // Sets the currently playing element to the given positions.
+ void SetPlaylistPosition(int position);
+
+ // Returns current playlist.
+ const UrlVector& GetPlaylist() const;
+
+ // Returns current playlist position.
+ int GetPlaylistPosition() const;
+
+ // Set flag that error occuires while playing the url.
+ void SetPlaybackError(GURL const& url);
+
+ // Notfies the mediaplayer that the playlist changed. This could be
+ // called from the mediaplayer itself for example.
+ void NotifyPlaylistChanged();
+
+ // Retuen true if playback requested. Resets this flag.
+ bool GetPendingPlayRequestAndReset();
+
+ // Requests starting playback of the current playlist item when the
+ // mediaplayer get the playlist updated.
+ void SetPlaybackRequest();
+
+ // Always returns NULL because we don't want to attempt a redirect
+ // before seeing the detected mime type of the request.
+ // Implementation of net::URLRequest::Interceptor.
+ virtual net::URLRequestJob* MaybeIntercept(net::URLRequest* request);
+
+ // Determines if the requested document can be viewed by the
+ // MediaPlayer. If it can, returns a net::URLRequestJob that
+ // redirects the browser to the view URL.
+ // Implementation of net::URLRequest::Interceptor.
+ virtual net::URLRequestJob* MaybeInterceptResponse(net::URLRequest* request);
+
+ // Used to detect when the mediaplayer is closed.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Getter for the singleton.
+ static MediaPlayer* GetInstance();
+
+ private:
+ friend struct DefaultSingletonTraits<MediaPlayer>;
+
+ // The current playlist of urls.
+ UrlVector current_playlist_;
+ // The position into the current_playlist_ of the currently playing item.
+ int current_position_;
+
+ bool pending_playback_request_;
+
+ MediaPlayer();
+
+ GURL GetOriginUrl() const;
+ GURL GetMediaplayerPlaylistUrl() const;
+ GURL GetMediaPlayerUrl() const;
+
+ // Popup the mediaplayer, this shows the browser, and sets up its
+ // locations correctly.
+ void PopupMediaPlayer(Browser* creator);
+
+ // Popup the playlist. Shows the browser, sets it up to point at
+ // chrome://mediaplayer#playlist
+ void PopupPlaylist(Browser* creator);
+
+ void EnqueueMediaFileUrl(const GURL& url);
+
+ // Browser containing the playlist. Used to force closes. This is created
+ // By the PopupPlaylist call, and is NULLed out when the window is closed.
+ Browser* playlist_browser_;
+
+ // Browser containing the Mediaplayer. Used to force closes. This is
+ // created by the PopupMediaplayer call, and is NULLed out when the window
+ // is closed.
+ Browser* mediaplayer_browser_;
+
+ // Used to register for events on the windows, like to listen for closes.
+ NotificationRegistrar registrar_;
+
+ // List of mimetypes that the mediaplayer should listen to. Used for
+ // interceptions of url GETs.
+ std::set<std::string> supported_mime_types_;
+ friend class MediaPlayerBrowserTest;
+ DISALLOW_COPY_AND_ASSIGN(MediaPlayer);
+};
+
+struct MediaPlayer::MediaUrl {
+ MediaUrl() {}
+ explicit MediaUrl(const GURL& newurl)
+ : url(newurl),
+ haderror(false) {}
+ GURL url;
+ bool haderror;
+};
+
+#endif // CHROME_BROWSER_CHROMEOS_MEDIA_MEDIA_PLAYER_H_
diff --git a/chrome/browser/chromeos/media/media_player_browsertest.cc b/chrome/browser/chromeos/media/media_player_browsertest.cc
new file mode 100644
index 0000000..9afaff3
--- /dev/null
+++ b/chrome/browser/chromeos/media/media_player_browsertest.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 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 "base/command_line.h"
+#include "base/memory/ref_counted.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/chromeos/media/media_player.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/automation/dom_element_proxy.h"
+#include "chrome/test/in_process_browser_test.h"
+#include "chrome/test/ui_test_utils.h"
+#include "content/browser/tab_contents/tab_contents.h"
+
+class MediaPlayerBrowserTest : public InProcessBrowserTest {
+ public:
+ MediaPlayerBrowserTest() {}
+
+ GURL GetMusicTestURL() {
+ return GURL("http://localhost:1337/files/plugin/sample_mp3.mp3");
+ }
+
+ bool IsBrowserVisible(Browser* browser) {
+ if (browser == NULL)
+ return false;
+ for (BrowserList::const_iterator it = BrowserList::begin();
+ it != BrowserList::end(); ++it) {
+ if ((*it)->is_type_panel() && (*it)->is_app() && (*it) == browser)
+ return true;
+ }
+ return false;
+ }
+
+ bool IsPlayerVisible() {
+ return IsBrowserVisible(MediaPlayer::GetInstance()->mediaplayer_browser_);
+ }
+
+ bool IsPlaylistVisible() {
+ return IsBrowserVisible(MediaPlayer::GetInstance()->playlist_browser_);
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(MediaPlayerBrowserTest, Popup) {
+ ASSERT_TRUE(test_server()->Start());
+ // Doing this so we have a valid profile.
+ ui_test_utils::NavigateToURL(browser(),
+ GURL("chrome://downloads"));
+
+ MediaPlayer* player = MediaPlayer::GetInstance();
+ // Check that its not currently visible
+ ASSERT_FALSE(IsPlayerVisible());
+
+ player->EnqueueMediaFileUrl(GetMusicTestURL(), NULL);
+
+ ASSERT_TRUE(IsPlayerVisible());
+}
+
+IN_PROC_BROWSER_TEST_F(MediaPlayerBrowserTest, PopupPlaylist) {
+ ASSERT_TRUE(test_server()->Start());
+ // Doing this so we have a valid profile.
+ ui_test_utils::NavigateToURL(browser(),
+ GURL("chrome://downloads"));
+
+
+ MediaPlayer* player = MediaPlayer::GetInstance();
+
+ player->EnqueueMediaFileUrl(GetMusicTestURL(), NULL);
+
+ EXPECT_FALSE(IsPlaylistVisible());
+
+ player->TogglePlaylistWindowVisible();
+
+ EXPECT_TRUE(IsPlaylistVisible());
+}
+