Commit 63e2b237 authored by skuhne@chromium.org's avatar skuhne@chromium.org

Additions to Activities to allow resource management

This is by no means finished. There are plenty of things to do - but I send it out now so that you can have a look at it as it is to see what my plans are.

Note: The activities management functions are not yet called. So there should be no impact on anything running yet. 

Please have a look! If you generally agree I will try to get it in before I go on vacation, otherwise it will have to wait until I get back. I might even work tomorrow instead of taking off as planned if needed.

BUG=388085
TEST=not yet done
TBR=rsesek

Review URL: https://codereview.chromium.org/358003002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282841 0039d316-1c4b-4281-b951-d872f2087c98
parent 25244a93
......@@ -13,12 +13,65 @@ namespace athena {
class ActivityViewModel;
// This class is a high level abstraction of an activity (which could be either
// a web page or a V1/V2 app/extension). Through this class the activity can
// be controlled (e.g. loaded / unloaded).
// An Activity gets created with state |ACTIVITY_UNLOADED|.
// Requesting |ACTIVITY_VISIBLE| or |ACTIVITY_INVISIBLE| will load it.
// Once an activity was |ACTIVITY_INVISIBLE| for a while it can be transitioned
// into |ACTIVITY_BACKGROUND_LOW_PRIORITY| to surrender more resources. After
// more time it can be transitions to |ACTIVITY_PERSISTENT| in which it only
// has it's runtime state left. At any time it can be transitioned back to one
// of the higher levels or unloaded via |ACTIVITY_UNLOADED|.
// Note that the resource manager will also query the media state before
// deciding if an activity can put into a lower state then |ACTIVITY_INVISIBLE|.
class ATHENA_EXPORT Activity {
public:
// The state of an activity which could either be set or requested by e.g. the
// resource management system.
enum ActivityState {
// The activity is allowed to have gpu compositor layers and can be visible.
ACTIVITY_VISIBLE,
// The activity does not have gpu compositing layers, will not be visible
// and will be treated as a background priority task.
ACTIVITY_INVISIBLE,
// The activity should surrender additional resources. This has only an
// effect when the activity is in a loaded state (Visible, Active, Hidden).
ACTIVITY_BACKGROUND_LOW_PRIORITY,
// The activity will only keep a minimum set of resources to get back to the
// running state. It will get stalled however. Note that it is not possible
// to get into this state from the |ACTIVITY_UNLOADED| state.
ACTIVITY_PERSISTENT,
// Unloads the activity and can be called in any state - but unloaded.
ACTIVITY_UNLOADED
};
// This enum declares the media state the activity is in.
// TODO(skuhne): Move the |TabMediaState| out of chrome and combine it in a
// media library within content and then use that enum instead.
enum ActivityMediaState {
ACTIVITY_MEDIA_STATE_NONE,
ACTIVITY_MEDIA_STATE_RECORDING, // Audio/Video being recorded by activity.
ACTIVITY_MEDIA_STATE_CAPTURING, // Activity is being captured.
ACTIVITY_MEDIA_STATE_AUDIO_PLAYING // Audible audio is playing in activity.
};
virtual ~Activity();
// The Activity retains ownership of the returned view-model.
virtual ActivityViewModel* GetActivityViewModel() = 0;
// Transition the activity into a new state.
virtual void SetCurrentState(ActivityState state) = 0;
// Returns the current state of the activity.
virtual ActivityState GetCurrentState() = 0;
// Returns if the activity is visible or not.
virtual bool IsVisible() = 0;
// Returns the current media state.
virtual ActivityMediaState GetMediaState() = 0;
};
} // namespace athena
......
......@@ -10,17 +10,22 @@
typedef unsigned int SkColor;
namespace gfx {
class ImageSkia;
}
namespace views {
class View;
}
namespace athena {
// The view model for the representation of the activity.
class ATHENA_EXPORT ActivityViewModel {
public:
virtual ~ActivityViewModel() {}
// Called after the view model is attaced to the widget/window tree.
// Called after the view model is attached to the widget/window tree.
virtual void Init() = 0;
// Returns a color most representative of this activity.
......@@ -33,8 +38,22 @@ class ATHENA_EXPORT ActivityViewModel {
// draws its own frame.
virtual bool UsesFrame() const = 0;
// Returns the contents view.
// Returns the contents view which might be NULL if the activity is not
// loaded. Note that the caller should not hold on to the view since it can
// be deleted by the resource manager.
virtual views::View* GetContentsView() = 0;
// This gets called before the Activity gets (partially) thrown out of memory
// to create a preview image of the activity. Note that even if this function
// gets called, |GetOverviewModeImage()| could still return an empty image.
virtual void CreateOverviewModeImage() = 0;
// Returns an image which can be used to represent the activity in e.g. the
// overview mode. The returned image can have no size if either a view exists
// or the activity has not yet been loaded. In that case
// GetRepresentativeColor() should be used to clear the preview area.
// Note: We intentionally do not use a layer / view for this.
virtual gfx::ImageSkia GetOverviewModeImage() = 0;
};
} // namespace athena
......
......@@ -7,5 +7,6 @@ include_rules = [
"+extensions/browser",
"+extensions/common",
"+ui/app_list",
"+ui/gfx",
"+ui/views",
]
......@@ -13,17 +13,75 @@ namespace athena {
// TODO(mukai): specifies the same accelerators of WebActivity.
AppActivity::AppActivity(apps::ShellAppWindow* app_window)
: app_window_(app_window), web_view_(NULL) {
: app_window_(app_window),
web_view_(NULL),
current_state_(ACTIVITY_UNLOADED) {
DCHECK(app_window_);
}
AppActivity::~AppActivity() {
if (GetCurrentState() != ACTIVITY_UNLOADED)
SetCurrentState(ACTIVITY_UNLOADED);
}
ActivityViewModel* AppActivity::GetActivityViewModel() {
return this;
}
void AppActivity::SetCurrentState(Activity::ActivityState state) {
switch (state) {
case ACTIVITY_VISIBLE:
// Fall through (for the moment).
case ACTIVITY_INVISIBLE:
// By clearing the overview mode image we allow the content to be shown.
overview_mode_image_ = gfx::ImageSkia();
// TODO(skuhne): Find out how to reload an app from the extension system.
break;
case ACTIVITY_BACKGROUND_LOW_PRIORITY:
DCHECK(ACTIVITY_VISIBLE == current_state_ ||
ACTIVITY_INVISIBLE == current_state_);
// TODO(skuhne): Do this.
break;
case ACTIVITY_PERSISTENT:
DCHECK_EQ(ACTIVITY_BACKGROUND_LOW_PRIORITY, current_state_);
// TODO(skuhne): Do this.
break;
case ACTIVITY_UNLOADED:
DCHECK_NE(ACTIVITY_UNLOADED, current_state_);
// TODO(skuhne): Find out how to evict an app from the extension system.
// web_view_->EvictContent();
break;
}
// Remember the last requested state.
current_state_ = state;
}
Activity::ActivityState AppActivity::GetCurrentState() {
// TODO(skuhne): Check here also eviction status.
if (!web_view_) {
DCHECK_EQ(ACTIVITY_UNLOADED, current_state_);
return ACTIVITY_UNLOADED;
}
// TODO(skuhne): This should be controlled by an observer and should not
// reside here.
if (IsVisible() && current_state_ != ACTIVITY_VISIBLE)
SetCurrentState(ACTIVITY_VISIBLE);
// Note: If the activity is not visible it does not necessarily mean that it
// does not have GPU compositor resources (yet).
return current_state_;
}
bool AppActivity::IsVisible() {
return web_view_ && web_view_->IsDrawn();
}
Activity::ActivityMediaState AppActivity::GetMediaState() {
// TODO(skuhne): The function GetTabMediaStateForContents(WebContents),
// and the AudioStreamMonitor needs to be moved from Chrome into contents to
// make it more modular and so that we can use it from here.
return Activity::ACTIVITY_MEDIA_STATE_NONE;
}
void AppActivity::Init() {
}
......@@ -47,11 +105,21 @@ views::View* AppActivity::GetContentsView() {
app_window_->GetAssociatedWebContents();
web_view_ = new views::WebView(web_contents->GetBrowserContext());
web_view_->SetWebContents(web_contents);
SetCurrentState(ACTIVITY_INVISIBLE);
Observe(web_contents);
overview_mode_image_ = gfx::ImageSkia();
}
return web_view_;
}
void AppActivity::CreateOverviewModeImage() {
// TODO(skuhne): Implement this!
}
gfx::ImageSkia AppActivity::GetOverviewModeImage() {
return overview_mode_image_;
}
void AppActivity::TitleWasSet(content::NavigationEntry* entry,
bool explicit_set) {
ActivityManager::Get()->UpdateActivity(this);
......
......@@ -8,6 +8,7 @@
#include "athena/activity/public/activity.h"
#include "athena/activity/public/activity_view_model.h"
#include "content/public/browser/web_contents_observer.h"
#include "ui/gfx/image/image_skia.h"
namespace apps {
class ShellAppWindow;
......@@ -29,6 +30,10 @@ class AppActivity : public Activity,
protected:
// Activity:
virtual athena::ActivityViewModel* GetActivityViewModel() OVERRIDE;
virtual void SetCurrentState(Activity::ActivityState state) OVERRIDE;
virtual ActivityState GetCurrentState() OVERRIDE;
virtual bool IsVisible() OVERRIDE;
virtual ActivityMediaState GetMediaState() OVERRIDE;
// ActivityViewModel:
virtual void Init() OVERRIDE;
......@@ -36,6 +41,8 @@ class AppActivity : public Activity,
virtual base::string16 GetTitle() const OVERRIDE;
virtual bool UsesFrame() const OVERRIDE;
virtual views::View* GetContentsView() OVERRIDE;
virtual void CreateOverviewModeImage() OVERRIDE;
virtual gfx::ImageSkia GetOverviewModeImage() OVERRIDE;
// content::WebContentsObserver:
virtual void TitleWasSet(content::NavigationEntry* entry,
......@@ -47,6 +54,12 @@ class AppActivity : public Activity,
scoped_ptr<apps::ShellAppWindow> app_window_;
views::WebView* web_view_;
// The current state for this activity.
ActivityState current_state_;
// The image which will be used in overview mode.
gfx::ImageSkia overview_mode_image_;
DISALLOW_COPY_AND_ASSIGN(AppActivity);
};
......
......@@ -116,15 +116,57 @@ class WebActivityController : public AcceleratorHandler {
DISALLOW_COPY_AND_ASSIGN(WebActivityController);
};
// A web view for athena's web activity.
} // namespace
// A web view for athena's web activity. Note that AthenaWebView will create its
// own content so that it can eject and reload it.
class AthenaWebView : public views::WebView {
public:
AthenaWebView(content::BrowserContext* context)
: views::WebView(context), controller_(new WebActivityController(this)) {}
virtual ~AthenaWebView() {}
: views::WebView(context), controller_(new WebActivityController(this)) {
// We create the first web contents ourselves to allow us to replace it
// later on.
SetWebContents(content::WebContents::Create(
content::WebContents::CreateParams(context)));
// TODO(skuhne): Add content observer to detect renderer crash and set
// content status to unloaded if that happens.
}
virtual ~AthenaWebView() {
// |WebView| does not own the content, so we need to destroy it here.
content::WebContents* current_contents = GetWebContents();
SetWebContents(NULL);
delete current_contents;
}
void InstallAccelerators() { controller_->InstallAccelerators(); }
void EvictContent() {
content::WebContents* old_contents = GetWebContents();
evicted_web_contents_.reset(
content::WebContents::Create(content::WebContents::CreateParams(
old_contents->GetBrowserContext())));
evicted_web_contents_->GetController().CopyStateFrom(
old_contents->GetController());
SetWebContents(content::WebContents::Create(
content::WebContents::CreateParams(old_contents->GetBrowserContext())));
delete old_contents;
// As soon as the new contents becomes visible, it should reload.
// TODO(skuhne): This breaks script connections with other activities.
// Even though this is the same technique as used by the TabStripModel,
// we might want to address this cleaner since we are more likely to
// run into this state. by unloading.
}
void ReloadContent() {
CHECK(evicted_web_contents_.get());
content::WebContents* null_contents = GetWebContents();
SetWebContents(evicted_web_contents_.release());
delete null_contents;
}
// Check if the content got evicted.
const bool IsContentEvicted() { return !!evicted_web_contents_.get(); }
private:
// WebContentsDelegate:
virtual bool PreHandleKeyboardEvent(
......@@ -143,28 +185,92 @@ class AthenaWebView : public views::WebView {
scoped_ptr<WebActivityController> controller_;
// If the activity got evicted, this is the web content which holds the known
// state of the content before eviction.
scoped_ptr<content::WebContents> evicted_web_contents_;
DISALLOW_COPY_AND_ASSIGN(AthenaWebView);
};
} // namespace
WebActivity::WebActivity(content::BrowserContext* browser_context,
const GURL& url)
: browser_context_(browser_context),
url_(url),
web_view_(NULL) {
web_view_(NULL),
current_state_(ACTIVITY_UNLOADED) {
}
WebActivity::~WebActivity() {
// It is not required to change the activity state to UNLOADED - unless we
// would add state observers.
}
ActivityViewModel* WebActivity::GetActivityViewModel() {
return this;
}
void WebActivity::SetCurrentState(Activity::ActivityState state) {
switch (state) {
case ACTIVITY_VISIBLE:
// Fall through (for the moment).
case ACTIVITY_INVISIBLE:
// By clearing the overview mode image we allow the content to be shown.
overview_mode_image_ = gfx::ImageSkia();
if (web_view_->IsContentEvicted()) {
DCHECK_EQ(ACTIVITY_UNLOADED, current_state_);
web_view_->ReloadContent();
}
Observe(web_view_->GetWebContents());
break;
case ACTIVITY_BACKGROUND_LOW_PRIORITY:
DCHECK(ACTIVITY_VISIBLE == current_state_ ||
ACTIVITY_INVISIBLE == current_state_);
// TODO(skuhne): Do this.
break;
case ACTIVITY_PERSISTENT:
DCHECK_EQ(ACTIVITY_BACKGROUND_LOW_PRIORITY, current_state_);
// TODO(skuhne): Do this. As soon as the new resource management is
// agreed upon - or remove otherwise.
break;
case ACTIVITY_UNLOADED:
DCHECK_NE(ACTIVITY_UNLOADED, current_state_);
Observe(NULL);
web_view_->EvictContent();
break;
}
// Remember the last requested state.
current_state_ = state;
}
Activity::ActivityState WebActivity::GetCurrentState() {
if (!web_view_ || web_view_->IsContentEvicted()) {
DCHECK_EQ(ACTIVITY_UNLOADED, current_state_);
return ACTIVITY_UNLOADED;
}
// TODO(skuhne): This should be controlled by an observer and should not
// reside here.
if (IsVisible() && current_state_ != ACTIVITY_VISIBLE)
SetCurrentState(ACTIVITY_VISIBLE);
// Note: If the activity is not visible it does not necessarily mean that it
// does not have GPU compositor resources (yet).
return current_state_;
}
bool WebActivity::IsVisible() {
return web_view_ && web_view_->IsDrawn();
}
Activity::ActivityMediaState WebActivity::GetMediaState() {
// TODO(skuhne): The function GetTabMediaStateForContents(WebContents),
// and the AudioStreamMonitor needs to be moved from Chrome into contents to
// make it more modular and so that we can use it from here.
return Activity::ACTIVITY_MEDIA_STATE_NONE;
}
void WebActivity::Init() {
DCHECK(web_view_);
static_cast<AthenaWebView*>(web_view_)->InstallAccelerators();
web_view_->InstallAccelerators();
}
SkColor WebActivity::GetRepresentativeColor() const {
......@@ -184,11 +290,21 @@ views::View* WebActivity::GetContentsView() {
if (!web_view_) {
web_view_ = new AthenaWebView(browser_context_);
web_view_->LoadInitialURL(url_);
Observe(web_view_->GetWebContents());
SetCurrentState(ACTIVITY_INVISIBLE);
// Reset the overview mode image.
overview_mode_image_ = gfx::ImageSkia();
}
return web_view_;
}
void WebActivity::CreateOverviewModeImage() {
// TODO(skuhne): Create an overview.
}
gfx::ImageSkia WebActivity::GetOverviewModeImage() {
return overview_mode_image_;
}
void WebActivity::TitleWasSet(content::NavigationEntry* entry,
bool explicit_set) {
ActivityManager::Get()->UpdateActivity(this);
......
......@@ -8,6 +8,7 @@
#include "athena/activity/public/activity.h"
#include "athena/activity/public/activity_view_model.h"
#include "content/public/browser/web_contents_observer.h"
#include "ui/gfx/image/image_skia.h"
namespace content {
class BrowserContext;
......@@ -21,6 +22,8 @@ class WidgetDelegate;
namespace athena {
class AthenaWebView;
class WebActivity : public Activity,
public ActivityViewModel,
public content::WebContentsObserver {
......@@ -31,6 +34,10 @@ class WebActivity : public Activity,
protected:
// Activity:
virtual athena::ActivityViewModel* GetActivityViewModel() OVERRIDE;
virtual void SetCurrentState(ActivityState state) OVERRIDE;
virtual ActivityState GetCurrentState() OVERRIDE;
virtual bool IsVisible() OVERRIDE;
virtual ActivityMediaState GetMediaState() OVERRIDE;
// ActivityViewModel:
virtual void Init() OVERRIDE;
......@@ -38,6 +45,8 @@ class WebActivity : public Activity,
virtual base::string16 GetTitle() const OVERRIDE;
virtual bool UsesFrame() const OVERRIDE;
virtual views::View* GetContentsView() OVERRIDE;
virtual void CreateOverviewModeImage() OVERRIDE;
virtual gfx::ImageSkia GetOverviewModeImage() OVERRIDE;
// content::WebContentsObserver:
virtual void TitleWasSet(content::NavigationEntry* entry,
......@@ -48,7 +57,13 @@ class WebActivity : public Activity,
private:
content::BrowserContext* browser_context_;
const GURL url_;
views::WebView* web_view_;
AthenaWebView* web_view_;
// The current state for this activity.
ActivityState current_state_;
// The image which will be used in overview mode.
gfx::ImageSkia overview_mode_image_;
DISALLOW_COPY_AND_ASSIGN(WebActivity);
};
......
......@@ -16,7 +16,8 @@ SampleActivity::SampleActivity(SkColor color,
: color_(color),
contents_color_(contents_color),
title_(title),
contents_view_(NULL) {
contents_view_(NULL),
current_state_(ACTIVITY_UNLOADED) {
}
SampleActivity::~SampleActivity() {
......@@ -26,6 +27,22 @@ athena::ActivityViewModel* SampleActivity::GetActivityViewModel() {
return this;
}
void SampleActivity::SetCurrentState(Activity::ActivityState state) {
current_state_ = state;
}
Activity::ActivityState SampleActivity::GetCurrentState() {
return current_state_;
}
bool SampleActivity::IsVisible() {
return contents_view_ && contents_view_->IsDrawn();
}
Activity::ActivityMediaState SampleActivity::GetMediaState() {
return Activity::ACTIVITY_MEDIA_STATE_NONE;
}
void SampleActivity::Init() {
}
......@@ -50,5 +67,12 @@ views::View* SampleActivity::GetContentsView() {
return contents_view_;
}
void SampleActivity::CreateOverviewModeImage() {
}
gfx::ImageSkia SampleActivity::GetOverviewModeImage() {
return gfx::ImageSkia();
}
} // namespace test
} // namespace athena
......@@ -8,6 +8,7 @@
#include "athena/activity/public/activity.h"
#include "athena/activity/public/activity_view_model.h"
#include "base/memory/scoped_ptr.h"
#include "ui/gfx/image/image_skia.h"
namespace athena {
namespace test {
......@@ -23,6 +24,10 @@ class SampleActivity : public Activity,
private:
// athena::Activity:
virtual athena::ActivityViewModel* GetActivityViewModel() OVERRIDE;
virtual void SetCurrentState(Activity::ActivityState state) OVERRIDE;
virtual ActivityState GetCurrentState() OVERRIDE;
virtual bool IsVisible() OVERRIDE;
virtual ActivityMediaState GetMediaState() OVERRIDE;
// athena::ActivityViewModel:
virtual void Init() OVERRIDE;
......@@ -30,12 +35,17 @@ class SampleActivity : public Activity,
virtual base::string16 GetTitle() const OVERRIDE;
virtual bool UsesFrame() const OVERRIDE;
virtual views::View* GetContentsView() OVERRIDE;
virtual void CreateOverviewModeImage() OVERRIDE;
virtual gfx::ImageSkia GetOverviewModeImage() OVERRIDE;
SkColor color_;
SkColor contents_color_;
base::string16 title_;
views::View* contents_view_;
// The current state for this activity.
ActivityState current_state_;
DISALLOW_COPY_AND_ASSIGN(SampleActivity);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment