Commit 9f26807d authored by mlamouri@chromium.org's avatar mlamouri@chromium.org

Defer app window appearance until first paint.

In order to avoid an ugly flash of white pixels when a new app window is
shown, this patch defers showing a window until a non-empty backing
store update has completed.

The feature is behind the "enable-apps-show-on-first-paint" flag.

BUG=225843, 254152

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@233422 0039d316-1c4b-4281-b951-d872f2087c98
parent 11e70098
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "apps/shell_window_geometry_cache.h" #include "apps/shell_window_geometry_cache.h"
#include "apps/shell_window_registry.h" #include "apps/shell_window_registry.h"
#include "apps/ui/native_app_window.h" #include "apps/ui/native_app_window.h"
#include "base/command_line.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/values.h" #include "base/values.h"
...@@ -17,6 +18,7 @@ ...@@ -17,6 +18,7 @@
#include "chrome/browser/extensions/suggest_permission_util.h" #include "chrome/browser/extensions/suggest_permission_util.h"
#include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_messages.h" #include "chrome/common/extensions/extension_messages.h"
#include "chrome/common/extensions/manifest_handlers/icons_handler.h" #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
...@@ -30,6 +32,7 @@ ...@@ -30,6 +32,7 @@
#include "content/public/browser/render_view_host.h" #include "content/public/browser/render_view_host.h"
#include "content/public/browser/resource_dispatcher_host.h" #include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "content/public/common/media_stream_request.h" #include "content/public/common/media_stream_request.h"
#include "extensions/browser/view_type_utils.h" #include "extensions/browser/view_type_utils.h"
#include "third_party/skia/include/core/SkRegion.h" #include "third_party/skia/include/core/SkRegion.h"
...@@ -139,7 +142,9 @@ ShellWindow::ShellWindow(Profile* profile, ...@@ -139,7 +142,9 @@ ShellWindow::ShellWindow(Profile* profile,
delegate_(delegate), delegate_(delegate),
image_loader_ptr_factory_(this), image_loader_ptr_factory_(this),
fullscreen_for_window_api_(false), fullscreen_for_window_api_(false),
fullscreen_for_tab_(false) { fullscreen_for_tab_(false),
show_on_first_paint_(false),
first_paint_complete_(false) {
} }
void ShellWindow::Init(const GURL& url, void ShellWindow::Init(const GURL& url,
...@@ -149,6 +154,10 @@ void ShellWindow::Init(const GURL& url, ...@@ -149,6 +154,10 @@ void ShellWindow::Init(const GURL& url,
shell_window_contents_.reset(shell_window_contents); shell_window_contents_.reset(shell_window_contents);
shell_window_contents_->Initialize(profile(), url); shell_window_contents_->Initialize(profile(), url);
WebContents* web_contents = shell_window_contents_->GetWebContents(); WebContents* web_contents = shell_window_contents_->GetWebContents();
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableAppsShowOnFirstPaint)) {
content::WebContentsObserver::Observe(web_contents);
}
delegate_->InitWebContents(web_contents); delegate_->InitWebContents(web_contents);
WebContentsModalDialogManager::CreateForWebContents(web_contents); WebContentsModalDialogManager::CreateForWebContents(web_contents);
extensions::ExtensionWebContentsObserver::CreateForWebContents(web_contents); extensions::ExtensionWebContentsObserver::CreateForWebContents(web_contents);
...@@ -167,10 +176,8 @@ void ShellWindow::Init(const GURL& url, ...@@ -167,10 +176,8 @@ void ShellWindow::Init(const GURL& url,
native_app_window_.reset(delegate_->CreateNativeAppWindow(this, new_params)); native_app_window_.reset(delegate_->CreateNativeAppWindow(this, new_params));
if (!new_params.hidden) { if (!new_params.hidden) {
if (window_type_is_panel()) // Panels are not activated by default.
GetBaseWindow()->ShowInactive(); // Panels are not activated by default. Show(window_type_is_panel() ? SHOW_INACTIVE : SHOW_ACTIVE);
else
GetBaseWindow()->Show();
} }
if (new_params.state == ui::SHOW_STATE_FULLSCREEN) if (new_params.state == ui::SHOW_STATE_FULLSCREEN)
...@@ -194,6 +201,15 @@ void ShellWindow::Init(const GURL& url, ...@@ -194,6 +201,15 @@ void ShellWindow::Init(const GURL& url,
shell_window_contents_->LoadContents(new_params.creator_process_id); shell_window_contents_->LoadContents(new_params.creator_process_id);
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableAppsShowOnFirstPaint)) {
// We want to show the window only when the content has been painted. For
// that to happen, we need to define a size for the content, otherwise the
// layout will happen in a 0x0 area.
// Note: WebContents::GetView() is guaranteed to be non-null.
web_contents->GetView()->SizeContents(new_params.bounds.size());
}
// Prevent the browser process from shutting down while this window is open. // Prevent the browser process from shutting down while this window is open.
chrome::StartKeepAlive(); chrome::StartKeepAlive();
...@@ -284,6 +300,15 @@ void ShellWindow::RequestToLockMouse(WebContents* web_contents, ...@@ -284,6 +300,15 @@ void ShellWindow::RequestToLockMouse(WebContents* web_contents,
web_contents->GotResponseToLockMouseRequest(has_permission); web_contents->GotResponseToLockMouseRequest(has_permission);
} }
void ShellWindow::DidFirstVisuallyNonEmptyPaint(int32 page_id) {
first_paint_complete_ = true;
if (show_on_first_paint_) {
DCHECK(delayed_show_type_ == SHOW_ACTIVE ||
delayed_show_type_ == SHOW_INACTIVE);
Show(delayed_show_type_);
}
}
void ShellWindow::OnNativeClose() { void ShellWindow::OnNativeClose() {
ShellWindowRegistry::Get(profile_)->RemoveShellWindow(this); ShellWindowRegistry::Get(profile_)->RemoveShellWindow(this);
if (shell_window_contents_) { if (shell_window_contents_) {
...@@ -411,6 +436,36 @@ void ShellWindow::SetMaximumSize(const gfx::Size& max_size) { ...@@ -411,6 +436,36 @@ void ShellWindow::SetMaximumSize(const gfx::Size& max_size) {
OnSizeConstraintsChanged(); OnSizeConstraintsChanged();
} }
void ShellWindow::Show(ShowType show_type) {
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableAppsShowOnFirstPaint)) {
show_on_first_paint_ = true;
if (!first_paint_complete_) {
delayed_show_type_ = show_type;
return;
}
}
switch (show_type) {
case SHOW_ACTIVE:
GetBaseWindow()->Show();
break;
case SHOW_INACTIVE:
GetBaseWindow()->ShowInactive();
break;
}
}
void ShellWindow::Hide() {
// This is there to prevent race conditions with Hide() being called before
// there was a non-empty paint. It should have no effect in a non-racy
// scenario where the application is hiding then showing a window: the second
// show will not be delayed.
show_on_first_paint_ = false;
GetBaseWindow()->Hide();
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Private methods // Private methods
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_registrar.h"
#include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/console_message_level.h" #include "content/public/common/console_message_level.h"
#include "ui/base/ui_base_types.h" // WindowShowState #include "ui/base/ui_base_types.h" // WindowShowState
#include "ui/gfx/image/image.h" #include "ui/gfx/image/image.h"
...@@ -74,6 +75,7 @@ class ShellWindowContents { ...@@ -74,6 +75,7 @@ class ShellWindowContents {
// have a WebContents but none of the chrome of normal browser windows. // have a WebContents but none of the chrome of normal browser windows.
class ShellWindow : public content::NotificationObserver, class ShellWindow : public content::NotificationObserver,
public content::WebContentsDelegate, public content::WebContentsDelegate,
public content::WebContentsObserver,
public web_modal::WebContentsModalDialogManagerDelegate, public web_modal::WebContentsModalDialogManagerDelegate,
public extensions::ExtensionKeybindingRegistry::Delegate, public extensions::ExtensionKeybindingRegistry::Delegate,
public extensions::IconImage::Observer { public extensions::IconImage::Observer {
...@@ -286,6 +288,19 @@ class ShellWindow : public content::NotificationObserver, ...@@ -286,6 +288,19 @@ class ShellWindow : public content::NotificationObserver,
void SetMinimumSize(const gfx::Size& min_size); void SetMinimumSize(const gfx::Size& min_size);
void SetMaximumSize(const gfx::Size& max_size); void SetMaximumSize(const gfx::Size& max_size);
enum ShowType {
SHOW_ACTIVE,
SHOW_INACTIVE
};
// Shows the window if its contents have been painted; otherwise flags the
// window to be shown as soon as its contents are painted for the first time.
void Show(ShowType show_type);
// Hides the window. If the window was previously flagged to be shown on
// first paint, it will be unflagged.
void Hide();
ShellWindowContents* shell_window_contents_for_test() { ShellWindowContents* shell_window_contents_for_test() {
return shell_window_contents_.get(); return shell_window_contents_.get();
} }
...@@ -340,6 +355,9 @@ class ShellWindow : public content::NotificationObserver, ...@@ -340,6 +355,9 @@ class ShellWindow : public content::NotificationObserver,
bool user_gesture, bool user_gesture,
bool last_unlocked_by_target) OVERRIDE; bool last_unlocked_by_target) OVERRIDE;
// content::WebContentsObserver implementation.
virtual void DidFirstVisuallyNonEmptyPaint(int32 page_id) OVERRIDE;
// content::NotificationObserver implementation. // content::NotificationObserver implementation.
virtual void Observe(int type, virtual void Observe(int type,
const content::NotificationSource& source, const content::NotificationSource& source,
...@@ -434,6 +452,16 @@ class ShellWindow : public content::NotificationObserver, ...@@ -434,6 +452,16 @@ class ShellWindow : public content::NotificationObserver,
// Size constraints on the window. // Size constraints on the window.
SizeConstraints size_constraints_; SizeConstraints size_constraints_;
// Show has been called, so the window should be shown once the first visually
// non-empty paint occurs.
bool show_on_first_paint_;
// The first visually non-empty paint has completed.
bool first_paint_complete_;
// Whether the delayed Show() call was for an active or inactive window.
ShowType delayed_show_type_;
DISALLOW_COPY_AND_ASSIGN(ShellWindow); DISALLOW_COPY_AND_ASSIGN(ShellWindow);
}; };
......
...@@ -6961,6 +6961,12 @@ Keep your key file in a safe place. You will need it to create new versions of y ...@@ -6961,6 +6961,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_VIEWS_USE_RECT_BASED_TARGETING_DESCRIPTION" desc="Description of about:flags option to use rect-based targeting in views"> <message name="IDS_FLAGS_VIEWS_USE_RECT_BASED_TARGETING_DESCRIPTION" desc="Description of about:flags option to use rect-based targeting in views">
Use a heuristic to determine the most probable target of a gesture, where the touch region is represented by a rectangle. Use a heuristic to determine the most probable target of a gesture, where the touch region is represented by a rectangle.
</message> </message>
<message name="IDS_FLAGS_ENABLE_APPS_SHOW_ON_FIRST_PAINT_NAME" desc="Name of the flag to enable show-on-first-paint for apps.">
Enable show-on-first-paint for apps.
</message>
<message name="IDS_FLAGS_ENABLE_APPS_SHOW_ON_FIRST_PAINT_DESCRIPTION" desc="Description of flag to enable show-on-first-paint for apps.">
Show apps windows after the first paint. Windows will be shown significantly later for heavy apps loading resources synchronously but it will be insignificant for apps that load most of their resources asynchronously.
</message>
<!-- Crashes --> <!-- Crashes -->
<message name="IDS_CRASHES_TITLE" desc="Title for the chrome://crashes page."> <message name="IDS_CRASHES_TITLE" desc="Title for the chrome://crashes page.">
......
...@@ -1846,6 +1846,13 @@ const Experiment kExperiments[] = { ...@@ -1846,6 +1846,13 @@ const Experiment kExperiments[] = {
SINGLE_VALUE_TYPE(views::switches::kViewsUseRectBasedTargeting) SINGLE_VALUE_TYPE(views::switches::kViewsUseRectBasedTargeting)
}, },
#endif #endif
{
"enable-apps-show-on-first-paint",
IDS_FLAGS_ENABLE_APPS_SHOW_ON_FIRST_PAINT_NAME,
IDS_FLAGS_ENABLE_APPS_SHOW_ON_FIRST_PAINT_DESCRIPTION,
kOsDesktop,
SINGLE_VALUE_TYPE(switches::kEnableAppsShowOnFirstPaint)
},
}; };
const Experiment* experiments = kExperiments; const Experiment* experiments = kExperiments;
......
...@@ -108,13 +108,13 @@ bool AppCurrentWindowInternalClearAttentionFunction::RunWithWindow( ...@@ -108,13 +108,13 @@ bool AppCurrentWindowInternalClearAttentionFunction::RunWithWindow(
bool AppCurrentWindowInternalShowFunction::RunWithWindow( bool AppCurrentWindowInternalShowFunction::RunWithWindow(
ShellWindow* window) { ShellWindow* window) {
window->GetBaseWindow()->Show(); window->Show(ShellWindow::SHOW_ACTIVE);
return true; return true;
} }
bool AppCurrentWindowInternalHideFunction::RunWithWindow( bool AppCurrentWindowInternalHideFunction::RunWithWindow(
ShellWindow* window) { ShellWindow* window) {
window->GetBaseWindow()->Hide(); window->Hide();
return true; return true;
} }
......
...@@ -179,7 +179,7 @@ bool AppWindowCreateFunction::RunImpl() { ...@@ -179,7 +179,7 @@ bool AppWindowCreateFunction::RunImpl() {
view_id = created_view->GetRoutingID(); view_id = created_view->GetRoutingID();
} }
window->GetBaseWindow()->Show(); window->Show(ShellWindow::SHOW_ACTIVE);
base::DictionaryValue* result = new base::DictionaryValue; base::DictionaryValue* result = new base::DictionaryValue;
result->Set("viewId", new base::FundamentalValue(view_id)); result->Set("viewId", new base::FundamentalValue(view_id));
SetCreateResultFromShellWindow(window, result); SetCreateResultFromShellWindow(window, result);
......
...@@ -544,6 +544,11 @@ const char kEnableAdviewSrcAttribute[] = "enable-adview-src-attribute"; ...@@ -544,6 +544,11 @@ const char kEnableAdviewSrcAttribute[] = "enable-adview-src-attribute";
// Enables the <window-controls> tag in platform apps. // Enables the <window-controls> tag in platform apps.
const char kEnableAppWindowControls[] = "enable-app-window-controls"; const char kEnableAppWindowControls[] = "enable-app-window-controls";
// Show apps windows after the first paint. Windows will be shown significantly
// later for heavy apps loading resources synchronously but it will be
// insignificant for apps that load most of their resources asynchronously.
const char kEnableAppsShowOnFirstPaint[] = "enable-apps-show-on-first-paint";
// Enables the experimental asynchronous DNS client. // Enables the experimental asynchronous DNS client.
const char kEnableAsyncDns[] = "enable-async-dns"; const char kEnableAsyncDns[] = "enable-async-dns";
......
...@@ -164,6 +164,7 @@ extern const char kEnableAdview[]; ...@@ -164,6 +164,7 @@ extern const char kEnableAdview[];
extern const char kEnableAdviewSrcAttribute[]; extern const char kEnableAdviewSrcAttribute[];
extern const char kEnableAppList[]; extern const char kEnableAppList[];
extern const char kEnableAppWindowControls[]; extern const char kEnableAppWindowControls[];
extern const char kEnableAppsShowOnFirstPaint[];
extern const char kEnableAsyncDns[]; extern const char kEnableAsyncDns[];
extern const char kEnableAuthNegotiatePort[]; extern const char kEnableAuthNegotiatePort[];
extern const char kEnableAutologin[]; extern const char kEnableAutologin[];
......
...@@ -529,6 +529,8 @@ bool WebContentsImpl::OnMessageReceived(RenderViewHost* render_view_host, ...@@ -529,6 +529,8 @@ bool WebContentsImpl::OnMessageReceived(RenderViewHost* render_view_host,
OnJavaBridgeGetChannelHandle) OnJavaBridgeGetChannelHandle)
#endif #endif
IPC_MESSAGE_HANDLER(ViewHostMsg_MediaNotification, OnMediaNotification) IPC_MESSAGE_HANDLER(ViewHostMsg_MediaNotification, OnMediaNotification)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidFirstVisuallyNonEmptyPaint,
OnFirstVisuallyNonEmptyPaint)
IPC_MESSAGE_UNHANDLED(handled = false) IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP_EX() IPC_END_MESSAGE_MAP_EX()
message_source_ = NULL; message_source_ = NULL;
...@@ -2458,6 +2460,10 @@ void WebContentsImpl::OnMediaNotification(int64 player_cookie, ...@@ -2458,6 +2460,10 @@ void WebContentsImpl::OnMediaNotification(int64 player_cookie,
#endif // !defined(OS_CHROMEOS) #endif // !defined(OS_CHROMEOS)
} }
void WebContentsImpl::OnFirstVisuallyNonEmptyPaint(int32 page_id) {
FOR_EACH_OBSERVER(WebContentsObserver, observers_,
DidFirstVisuallyNonEmptyPaint(page_id));
}
void WebContentsImpl::DidChangeVisibleSSLState() { void WebContentsImpl::DidChangeVisibleSSLState() {
FOR_EACH_OBSERVER(WebContentsObserver, observers_, FOR_EACH_OBSERVER(WebContentsObserver, observers_,
......
...@@ -661,7 +661,7 @@ class CONTENT_EXPORT WebContentsImpl ...@@ -661,7 +661,7 @@ class CONTENT_EXPORT WebContentsImpl
const std::vector<gfx::Size>& original_bitmap_sizes); const std::vector<gfx::Size>& original_bitmap_sizes);
void OnUpdateFaviconURL(int32 page_id, void OnUpdateFaviconURL(int32 page_id,
const std::vector<FaviconURL>& candidates); const std::vector<FaviconURL>& candidates);
void OnFirstVisuallyNonEmptyPaint(int32 page_id);
void OnMediaNotification(int64 player_cookie, void OnMediaNotification(int64 player_cookie,
bool has_video, bool has_video,
bool has_audio, bool has_audio,
......
...@@ -218,6 +218,10 @@ class CONTENT_EXPORT WebContentsObserver : public IPC::Listener, ...@@ -218,6 +218,10 @@ class CONTENT_EXPORT WebContentsObserver : public IPC::Listener,
virtual void FrameDetached(RenderViewHost* render_view_host, virtual void FrameDetached(RenderViewHost* render_view_host,
int64 frame_id) {} int64 frame_id) {}
// This method is invoked when the renderer has completed its first paint
// after a non-empty layout.
virtual void DidFirstVisuallyNonEmptyPaint(int32 page_id) {}
// These two methods correspond to the points in time when the spinner of the // These two methods correspond to the points in time when the spinner of the
// tab starts and stops spinning. // tab starts and stops spinning.
virtual void DidStartLoading(RenderViewHost* render_view_host) {} virtual void DidStartLoading(RenderViewHost* render_view_host) {}
......
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