Merge pull request #1811 from atom/new-webview

Change how webview is resized
This commit is contained in:
Cheng Zhao 2015-05-29 14:14:23 +08:00
commit 4a310ad630
12 changed files with 251 additions and 69 deletions

View file

@ -44,12 +44,42 @@
#include "atom/common/node_includes.h"
namespace mate {
template<>
struct Converter<atom::api::SetSizeParams> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
atom::api::SetSizeParams* out) {
mate::Dictionary params;
if (!ConvertFromV8(isolate, val, &params))
return false;
bool autosize;
if (params.Get("enableAutoSize", &autosize))
out->enable_auto_size.reset(new bool(true));
gfx::Size size;
if (params.Get("min", &size))
out->min_size.reset(new gfx::Size(size));
if (params.Get("max", &size))
out->max_size.reset(new gfx::Size(size));
if (params.Get("normal", &size))
out->normal_size.reset(new gfx::Size(size));
return true;
}
};
} // namespace mate
namespace atom {
namespace api {
namespace {
const int kDefaultWidth = 300;
const int kDefaultHeight = 300;
v8::Persistent<v8::ObjectTemplate> template_;
// Get the window that has the |guest| embedded.
@ -82,18 +112,18 @@ content::ServiceWorkerContext* GetServiceWorkerContext(
WebContents::WebContents(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
guest_instance_id_(-1),
element_instance_id_(-1),
guest_opaque_(true),
guest_host_(nullptr),
auto_size_enabled_(false) {
auto_size_enabled_(false),
is_full_page_plugin_(false) {
}
WebContents::WebContents(const mate::Dictionary& options)
: guest_instance_id_(-1),
element_instance_id_(-1),
guest_opaque_(true),
guest_host_(nullptr),
auto_size_enabled_(false) {
auto_size_enabled_(false),
is_full_page_plugin_(false) {
options.Get("guestInstanceId", &guest_instance_id_);
auto browser_context = AtomBrowserContext::Get();
@ -418,14 +448,6 @@ void WebContents::DidAttach(int guest_proxy_routing_id) {
Emit("did-attach");
}
void WebContents::ElementSizeChanged(const gfx::Size& size) {
element_size_ = size;
// Only resize if needed.
if (!size.IsEmpty())
guest_host_->SizeContents(size);
}
content::WebContents* WebContents::GetOwnerWebContents() const {
return embedder_web_contents_;
}
@ -445,7 +467,7 @@ void WebContents::WillAttach(content::WebContents* embedder_web_contents,
int element_instance_id,
bool is_full_page_plugin) {
embedder_web_contents_ = embedder_web_contents;
element_instance_id_ = element_instance_id;
is_full_page_plugin_ = is_full_page_plugin;
}
void WebContents::Destroy() {
@ -612,31 +634,57 @@ bool WebContents::SendIPCMessage(const base::string16& channel,
return Send(new AtomViewMsg_Message(routing_id(), channel, args));
}
void WebContents::SetAutoSize(bool enabled,
const gfx::Size& min_size,
const gfx::Size& max_size) {
void WebContents::SetSize(const SetSizeParams& params) {
bool enable_auto_size =
params.enable_auto_size ? *params.enable_auto_size : auto_size_enabled_;
gfx::Size min_size = params.min_size ? *params.min_size : min_auto_size_;
gfx::Size max_size = params.max_size ? *params.max_size : max_auto_size_;
if (params.normal_size)
normal_size_ = *params.normal_size;
min_auto_size_ = min_size;
min_auto_size_.SetToMin(max_size);
max_auto_size_ = max_size;
max_auto_size_.SetToMax(min_size);
enabled &= !min_auto_size_.IsEmpty() && !max_auto_size_.IsEmpty();
if (!enabled && !auto_size_enabled_)
return;
auto_size_enabled_ = enabled;
if (!attached())
return;
enable_auto_size &= !min_auto_size_.IsEmpty() && !max_auto_size_.IsEmpty();
content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
if (auto_size_enabled_) {
if (enable_auto_size) {
// Autosize is being enabled.
rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
normal_size_.SetSize(0, 0);
} else {
rvh->DisableAutoResize(element_size_);
guest_size_ = element_size_;
GuestSizeChangedDueToAutoSize(guest_size_, element_size_);
// Autosize is being disabled.
// Use default width/height if missing from partially defined normal size.
if (normal_size_.width() && !normal_size_.height())
normal_size_.set_height(GetDefaultSize().height());
if (!normal_size_.width() && normal_size_.height())
normal_size_.set_width(GetDefaultSize().width());
gfx::Size new_size;
if (!normal_size_.IsEmpty()) {
new_size = normal_size_;
} else if (!guest_size_.IsEmpty()) {
new_size = guest_size_;
} else {
new_size = GetDefaultSize();
}
if (auto_size_enabled_) {
// Autosize was previously enabled.
rvh->DisableAutoResize(new_size);
GuestSizeChangedDueToAutoSize(guest_size_, new_size);
} else {
// Autosize was already disabled.
guest_host_->SizeContents(new_size);
}
guest_size_ = new_size;
}
auto_size_enabled_ = enable_auto_size;
}
void WebContents::SetAllowTransparency(bool allow) {
@ -727,7 +775,7 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder(
.SetMethod("replace", &WebContents::Replace)
.SetMethod("replaceMisspelling", &WebContents::ReplaceMisspelling)
.SetMethod("_send", &WebContents::SendIPCMessage)
.SetMethod("setAutoSize", &WebContents::SetAutoSize)
.SetMethod("setSize", &WebContents::SetSize)
.SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency)
.SetMethod("isGuest", &WebContents::is_guest)
.SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
@ -760,6 +808,16 @@ void WebContents::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
new_size.width(), new_size.height());
}
gfx::Size WebContents::GetDefaultSize() const {
if (is_full_page_plugin_) {
// Full page plugins default to the size of the owner's viewport.
return embedder_web_contents_->GetRenderWidgetHostView()
->GetVisibleViewportSize();
} else {
return gfx::Size(kDefaultWidth, kDefaultHeight);
}
}
// static
mate::Handle<WebContents> WebContents::CreateFrom(
v8::Isolate* isolate, content::WebContents* web_contents) {

View file

@ -33,6 +33,22 @@ class WebDialogHelper;
namespace api {
// A struct of parameters for SetSize(). The parameters are all declared as
// scoped pointers since they are all optional. Null pointers indicate that the
// parameter has not been provided, and the last used value should be used. Note
// that when |enable_auto_size| is true, providing |normal_size| is not
// meaningful. This is because the normal size of the guestview is overridden
// whenever autosizing occurs.
struct SetSizeParams {
SetSizeParams() {}
~SetSizeParams() {}
scoped_ptr<bool> enable_auto_size;
scoped_ptr<gfx::Size> min_size;
scoped_ptr<gfx::Size> max_size;
scoped_ptr<gfx::Size> normal_size;
};
class WebContents : public mate::EventEmitter,
public content::BrowserPluginGuestDelegate,
public content::WebContentsDelegate,
@ -89,10 +105,9 @@ class WebContents : public mate::EventEmitter,
bool SendIPCMessage(const base::string16& channel,
const base::ListValue& args);
// Toggles autosize mode for corresponding <webview>.
void SetAutoSize(bool enabled,
const gfx::Size& min_size,
const gfx::Size& max_size);
// Used to toggle autosize mode for this GuestView, and set both the automatic
// and normal sizes.
void SetSize(const SetSizeParams& params);
// Sets the transparency of the guest.
void SetAllowTransparency(bool allow);
@ -196,7 +211,6 @@ class WebContents : public mate::EventEmitter,
// content::BrowserPluginGuestDelegate:
void DidAttach(int guest_proxy_routing_id) final;
void ElementSizeChanged(const gfx::Size& size) final;
content::WebContents* GetOwnerWebContents() const final;
void GuestSizeChanged(const gfx::Size& new_size) final;
void SetGuestHost(content::GuestHost* guest_host) final;
@ -217,19 +231,23 @@ class WebContents : public mate::EventEmitter,
const base::ListValue& args,
IPC::Message* message);
// This method is invoked when the contents auto-resized to give the container
// an opportunity to match it if it wishes.
//
// This gives the derived class an opportunity to inform its container element
// or perform other actions.
void GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
const gfx::Size& new_size);
// Returns the default size of the guestview.
gfx::Size GetDefaultSize() const;
scoped_ptr<WebDialogHelper> web_dialog_helper_;
scoped_ptr<AtomJavaScriptDialogManager> dialog_manager_;
// Unique ID for a guest WebContents.
int guest_instance_id_;
// |element_instance_id_| is an identifer that's unique to a particular
// element.
int element_instance_id_;
// Stores whether the contents of the guest can be transparent.
bool guest_opaque_;
@ -258,6 +276,12 @@ class WebContents : public mate::EventEmitter,
// The minimum size constraints of the container element in autosize mode.
gfx::Size min_auto_size_;
// The size that will be used when autosize mode is disabled.
gfx::Size normal_size_;
// Whether the guest view is inside a plugin document.
bool is_full_page_plugin_;
DISALLOW_COPY_AND_ASSIGN(WebContents);
};

View file

@ -15,7 +15,6 @@ app.on('ready', function() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
resizable: false,
'auto-hide-menu-bar': true,
'use-content-size': true,
});

View file

@ -41,7 +41,6 @@ createGuest = (embedder, params) ->
guest = webContents.create
isGuest: true
guestInstanceId: id
storagePartitionId: params.storagePartitionId
guestInstances[id] = {guest, embedder}
# Destroy guest when the embedder is gone or navigated.
@ -58,9 +57,14 @@ createGuest = (embedder, params) ->
delete @attachParams
@viewInstanceId = params.instanceId
min = width: params.minwidth, height: params.minheight
max = width: params.maxwidth, height: params.maxheight
@setAutoSize params.autosize, min, max
@setSize
normal:
width: params.elementWidth, height: params.elementHeight
enableAutoSize: params.autosize
min:
width: params.minwidth, height: params.minheight
max:
width: params.maxwidth, height: params.maxheight
if params.src
opts = {}
@ -123,7 +127,7 @@ destroyGuest = (embedder, id) ->
delete reverseEmbedderElementsMap[id]
delete embedderElementsMap[key]
ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', (event, type, params, requestId) ->
ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', (event, params, requestId) ->
event.sender.send "ATOM_SHELL_RESPONSE_#{requestId}", createGuest(event.sender, params)
ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', (event, elementInstanceId, guestInstanceId, params) ->
@ -132,8 +136,8 @@ ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', (event, elementInstanceId,
ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', (event, id) ->
destroyGuest event.sender, id
ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_AUTO_SIZE', (event, id, params) ->
guestInstances[id]?.guest.setAutoSize params.enableAutoSize, params.min, params.max
ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', (event, id, params) ->
guestInstances[id]?.guest.setSize params
ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', (event, id, allowtransparency) ->
guestInstances[id]?.guest.setAllowTransparency allowtransparency

View file

@ -10,9 +10,11 @@
#define USE(WTF_FEATURE) (defined WTF_USE_##WTF_FEATURE && WTF_USE_##WTF_FEATURE) // NOLINT
#define ENABLE(WTF_FEATURE) (defined ENABLE_##WTF_FEATURE && ENABLE_##WTF_FEATURE) // NOLINT
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/renderer/api/atom_api_spell_check_client.h"
#include "content/public/renderer/render_frame.h"
#include "native_mate/callback.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "third_party/WebKit/public/web/WebDocument.h"
@ -78,6 +80,14 @@ v8::Local<v8::Value> WebFrame::RegisterEmbedderCustomElement(
return web_frame_->document().registerEmbedderCustomElement(name, options, c);
}
void WebFrame::RegisterElementResizeCallback(
int element_instance_id,
const GuestViewContainer::ResizeCallback& callback) {
auto guest_view_container = GuestViewContainer::FromID(element_instance_id);
if (guest_view_container)
guest_view_container->RegisterElementResizeCallback(callback);
}
void WebFrame::AttachGuest(int id) {
content::RenderFrame::FromWebFrame(web_frame_)->AttachGuest(id);
}
@ -106,6 +116,8 @@ mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder(
.SetMethod("getZoomFactor", &WebFrame::GetZoomFactor)
.SetMethod("registerEmbedderCustomElement",
&WebFrame::RegisterEmbedderCustomElement)
.SetMethod("registerElementResizeCallback",
&WebFrame::RegisterElementResizeCallback)
.SetMethod("attachGuest", &WebFrame::AttachGuest)
.SetMethod("setSpellCheckProvider", &WebFrame::SetSpellCheckProvider)
.SetMethod("registerUrlSchemeAsSecure",

View file

@ -7,6 +7,7 @@
#include <string>
#include "atom/renderer/guest_view_container.h"
#include "base/memory/scoped_ptr.h"
#include "native_mate/handle.h"
#include "native_mate/wrappable.h"
@ -42,6 +43,9 @@ class WebFrame : public mate::Wrappable {
v8::Local<v8::Value> RegisterEmbedderCustomElement(
const base::string16& name, v8::Local<v8::Object> options);
void RegisterElementResizeCallback(
int element_instance_id,
const GuestViewContainer::ResizeCallback& callback);
void AttachGuest(int element_instance_id);
// Set the provider that will be used by SpellCheckClient for spell check.

View file

@ -4,12 +4,54 @@
#include "atom/renderer/guest_view_container.h"
#include <map>
#include "base/lazy_instance.h"
namespace atom {
GuestViewContainer::GuestViewContainer(content::RenderFrame* render_frame) {
namespace {
using GuestViewContainerMap = std::map<int, GuestViewContainer*>;
static base::LazyInstance<GuestViewContainerMap> g_guest_view_container_map =
LAZY_INSTANCE_INITIALIZER;
} // namespace
GuestViewContainer::GuestViewContainer(content::RenderFrame* render_frame)
: render_frame_(render_frame) {
}
GuestViewContainer::~GuestViewContainer() {
if (element_instance_id_ > 0)
g_guest_view_container_map.Get().erase(element_instance_id_);
}
// static
GuestViewContainer* GuestViewContainer::FromID(int element_instance_id) {
GuestViewContainerMap* guest_view_containers =
g_guest_view_container_map.Pointer();
auto it = guest_view_containers->find(element_instance_id);
return it == guest_view_containers->end() ? nullptr : it->second;
}
void GuestViewContainer::RegisterElementResizeCallback(
const ResizeCallback& callback) {
element_resize_callback_ = callback;
}
void GuestViewContainer::SetElementInstanceID(int element_instance_id) {
element_instance_id_ = element_instance_id;
g_guest_view_container_map.Get().insert(
std::make_pair(element_instance_id, this));
}
void GuestViewContainer::DidResizeElement(const gfx::Size& old_size,
const gfx::Size& new_size) {
if (element_resize_callback_.is_null())
return;
element_resize_callback_.Run(old_size, new_size);
}
} // namespace atom

View file

@ -5,17 +5,38 @@
#ifndef ATOM_RENDERER_GUEST_VIEW_CONTAINER_H_
#define ATOM_RENDERER_GUEST_VIEW_CONTAINER_H_
#include "base/callback.h"
#include "content/public/renderer/browser_plugin_delegate.h"
#include "v8/include/v8.h"
namespace gfx {
class Size;
}
namespace atom {
class GuestViewContainer : public content::BrowserPluginDelegate {
public:
typedef base::Callback<void(const gfx::Size&, const gfx::Size&)>
ResizeCallback;
explicit GuestViewContainer(content::RenderFrame* render_frame);
~GuestViewContainer() override;
static GuestViewContainer* FromID(int element_instance_id);
void RegisterElementResizeCallback(const ResizeCallback& callback);
// content::BrowserPluginDelegate:
void SetElementInstanceID(int element_instance_id) final;
void DidResizeElement(const gfx::Size& old_size,
const gfx::Size& new_size) final;
private:
int element_instance_id_;
content::RenderFrame* render_frame_;
ResizeCallback element_resize_callback_;
DISALLOW_COPY_AND_ASSIGN(GuestViewContainer);
};

View file

@ -55,9 +55,9 @@ module.exports =
ipc.removeAllListeners "ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-#{viewInstanceId}"
ipc.removeAllListeners "ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-#{viewInstanceId}"
createGuest: (type, params, callback) ->
createGuest: (params, callback) ->
requestId++
ipc.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', type, params, requestId
ipc.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', params, requestId
ipc.once "ATOM_SHELL_RESPONSE_#{requestId}", callback
attachGuest: (elementInstanceId, guestInstanceId, params) ->
@ -67,8 +67,8 @@ module.exports =
destroyGuest: (guestInstanceId) ->
ipc.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId
setAutoSize: (guestInstanceId, params) ->
ipc.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_AUTO_SIZE', guestInstanceId, params
setSize: (guestInstanceId, params) ->
ipc.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', guestInstanceId, params
setAllowTransparency: (guestInstanceId, allowtransparency) ->
ipc.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', guestInstanceId, allowtransparency

View file

@ -72,7 +72,7 @@ class AutosizeDimensionAttribute extends WebViewAttribute
handleMutation: (oldValue, newValue) ->
return unless @webViewImpl.guestInstanceId
guestViewInternal.setAutoSize @webViewImpl.guestInstanceId,
guestViewInternal.setSize @webViewImpl.guestInstanceId,
enableAutoSize: @webViewImpl.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue()
min:
width: parseInt @webViewImpl.attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() || 0

View file

@ -13,7 +13,6 @@ class WebViewImpl
constructor: (@webviewNode) ->
v8Util.setHiddenValue @webviewNode, 'internal', this
@attached = false
@pendingGuestCreation = false
@elementAttached = false
@beforeFirstNavigation = true
@ -87,9 +86,12 @@ class WebViewImpl
@browserPluginNode.removeAttribute webViewConstants.ATTRIBUTE_INTERNALINSTANCEID
@internalInstanceId = parseInt newValue
# Track when the element resizes using the element resize callback.
webFrame.registerElementResizeCallback @internalInstanceId, @onElementResize.bind(this)
return unless @guestInstanceId
guestViewInternal.attachGuest @internalInstanceId, @guestInstanceId, @buildAttachParams()
guestViewInternal.attachGuest @internalInstanceId, @guestInstanceId, @buildParams()
onSizeChanged: (webViewEvent) ->
newWidth = webViewEvent.newWidth
@ -107,6 +109,9 @@ class WebViewImpl
minWidth = @attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() | width
minHeight = @attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() | width
minWidth = Math.min minWidth, maxWidth
minHeight = Math.min minHeight, maxHeight
if not @attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue() or
(newWidth >= minWidth and
newWidth <= maxWidth and
@ -118,17 +123,21 @@ class WebViewImpl
# changed.
@dispatchEvent webViewEvent
onElementResize: (oldSize, newSize) ->
# Dispatch the 'resize' event.
resizeEvent = new Event('resize', bubbles: true)
resizeEvent.oldWidth = oldSize.width
resizeEvent.oldHeight = oldSize.height
resizeEvent.newWidth = newSize.width
resizeEvent.newHeight = newSize.height
@dispatchEvent resizeEvent
if @guestInstanceId
guestViewInternal.setSize @guestInstanceId, normal: newSize
createGuest: ->
return if @pendingGuestCreation
params =
storagePartitionId: @attributes[webViewConstants.ATTRIBUTE_PARTITION].getValue()
guestViewInternal.createGuest 'webview', params, (guestInstanceId) =>
@pendingGuestCreation = false
unless @elementAttached
guestViewInternal.destroyGuest guestInstanceId
return
guestViewInternal.createGuest @buildParams(), (guestInstanceId) =>
@attachWindow guestInstanceId
@pendingGuestCreation = true
dispatchEvent: (webViewEvent) ->
@webviewNode.dispatchEvent webViewEvent
@ -160,21 +169,30 @@ class WebViewImpl
onAttach: (storagePartitionId) ->
@attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue storagePartitionId
buildAttachParams: ->
buildParams: ->
params =
instanceId: @viewInstanceId
userAgentOverride: @userAgentOverride
for attributeName, attribute of @attributes
params[attributeName] = attribute.getValue()
# When the WebView is not participating in layout (display:none)
# then getBoundingClientRect() would report a width and height of 0.
# However, in the case where the WebView has a fixed size we can
# use that value to initially size the guest so as to avoid a relayout of
# the on display:block.
css = window.getComputedStyle @webviewNode, null
elementRect = @webviewNode.getBoundingClientRect()
params.elementWidth = parseInt(elementRect.width) ||
parseInt(css.getPropertyValue('width'))
params.elementHeight = parseInt(elementRect.height) ||
parseInt(css.getPropertyValue('height'))
params
attachWindow: (guestInstanceId) ->
@guestInstanceId = guestInstanceId
params = @buildAttachParams()
return true unless @internalInstanceId
guestViewInternal.attachGuest @internalInstanceId, @guestInstanceId, params
guestViewInternal.attachGuest @internalInstanceId, @guestInstanceId, @buildParams()
# Registers browser plugin <object> custom element.
registerBrowserPluginElement = ->

2
vendor/native_mate vendored

@ -1 +1 @@
Subproject commit 1696237a3f444f0e33a8947749b4d70f6feb511b
Subproject commit ad207eeabb0185f74c017e70ca3411d522627ff0