Add code to register <webview> tag
This commit is contained in:
parent
e7c7e086b1
commit
380de24f2e
10 changed files with 649 additions and 1 deletions
5
atom.gyp
5
atom.gyp
|
@ -44,6 +44,7 @@
|
||||||
'atom/renderer/lib/init.coffee',
|
'atom/renderer/lib/init.coffee',
|
||||||
'atom/renderer/lib/inspector.coffee',
|
'atom/renderer/lib/inspector.coffee',
|
||||||
'atom/renderer/lib/override.coffee',
|
'atom/renderer/lib/override.coffee',
|
||||||
|
'atom/renderer/lib/webview.coffee',
|
||||||
'atom/renderer/api/lib/ipc.coffee',
|
'atom/renderer/api/lib/ipc.coffee',
|
||||||
'atom/renderer/api/lib/remote.coffee',
|
'atom/renderer/api/lib/remote.coffee',
|
||||||
'atom/renderer/api/lib/web-view.coffee',
|
'atom/renderer/api/lib/web-view.coffee',
|
||||||
|
@ -178,6 +179,10 @@
|
||||||
'atom/browser/ui/win/notify_icon.h',
|
'atom/browser/ui/win/notify_icon.h',
|
||||||
'atom/browser/ui/x/x_window_utils.cc',
|
'atom/browser/ui/x/x_window_utils.cc',
|
||||||
'atom/browser/ui/x/x_window_utils.h',
|
'atom/browser/ui/x/x_window_utils.h',
|
||||||
|
'atom/browser/web_view/web_view_guest.cc',
|
||||||
|
'atom/browser/web_view/web_view_guest.h',
|
||||||
|
'atom/browser/web_view/web_view_manager.cc',
|
||||||
|
'atom/browser/web_view/web_view_manager.h',
|
||||||
'atom/browser/window_list.cc',
|
'atom/browser/window_list.cc',
|
||||||
'atom/browser/window_list.h',
|
'atom/browser/window_list.h',
|
||||||
'atom/browser/window_list_observer.h',
|
'atom/browser/window_list_observer.h',
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "atom/browser/atom_browser_main_parts.h"
|
#include "atom/browser/atom_browser_main_parts.h"
|
||||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||||
#include "atom/browser/net/asar/asar_protocol_handler.h"
|
#include "atom/browser/net/asar/asar_protocol_handler.h"
|
||||||
|
#include "atom/browser/web_view/web_view_manager.h"
|
||||||
#include "base/threading/sequenced_worker_pool.h"
|
#include "base/threading/sequenced_worker_pool.h"
|
||||||
#include "base/threading/worker_pool.h"
|
#include "base/threading/worker_pool.h"
|
||||||
#include "chrome/browser/browser_process.h"
|
#include "chrome/browser/browser_process.h"
|
||||||
|
@ -68,6 +69,12 @@ net::URLRequestJobFactory* AtomBrowserContext::CreateURLRequestJobFactory(
|
||||||
return top_job_factory.release();
|
return top_job_factory.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
content::BrowserPluginGuestManager* AtomBrowserContext::GetGuestManager() {
|
||||||
|
if (!guest_manager_)
|
||||||
|
guest_manager_.reset(new WebViewManager(this));
|
||||||
|
return guest_manager_.get();
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
AtomBrowserContext* AtomBrowserContext::Get() {
|
AtomBrowserContext* AtomBrowserContext::Get() {
|
||||||
return static_cast<AtomBrowserContext*>(
|
return static_cast<AtomBrowserContext*>(
|
||||||
|
|
|
@ -12,6 +12,7 @@ class BrowserProcess;
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
class AtomURLRequestJobFactory;
|
class AtomURLRequestJobFactory;
|
||||||
|
class WebViewManager;
|
||||||
|
|
||||||
class AtomBrowserContext : public brightray::BrowserContext {
|
class AtomBrowserContext : public brightray::BrowserContext {
|
||||||
public:
|
public:
|
||||||
|
@ -27,11 +28,15 @@ class AtomBrowserContext : public brightray::BrowserContext {
|
||||||
// brightray::URLRequestContextGetter::Delegate:
|
// brightray::URLRequestContextGetter::Delegate:
|
||||||
virtual net::URLRequestJobFactory* CreateURLRequestJobFactory(
|
virtual net::URLRequestJobFactory* CreateURLRequestJobFactory(
|
||||||
content::ProtocolHandlerMap* handlers,
|
content::ProtocolHandlerMap* handlers,
|
||||||
content::URLRequestInterceptorScopedVector* interceptors) OVERRIDE;
|
content::URLRequestInterceptorScopedVector* interceptors) override;
|
||||||
|
|
||||||
|
// content::BrowserContext:
|
||||||
|
virtual content::BrowserPluginGuestManager* GetGuestManager() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// A fake BrowserProcess object that used to feed the source code from chrome.
|
// A fake BrowserProcess object that used to feed the source code from chrome.
|
||||||
scoped_ptr<BrowserProcess> fake_browser_process_;
|
scoped_ptr<BrowserProcess> fake_browser_process_;
|
||||||
|
scoped_ptr<WebViewManager> guest_manager_;
|
||||||
|
|
||||||
AtomURLRequestJobFactory* job_factory_; // Weak reference.
|
AtomURLRequestJobFactory* job_factory_; // Weak reference.
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,8 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<browser-plugin type="application/browser-plugin"></browser-plugin>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var execPath = require('remote').process.execPath;
|
var execPath = require('remote').process.execPath;
|
||||||
var command = execPath + ' path-to-your-app';
|
var command = execPath + ' path-to-your-app';
|
||||||
|
|
11
atom/browser/web_view/web_view_guest.cc
Normal file
11
atom/browser/web_view/web_view_guest.cc
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "atom/browser/web_view/web_view_guest.h"
|
||||||
|
|
||||||
|
namespace atom {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace atom
|
19
atom/browser/web_view/web_view_guest.h
Normal file
19
atom/browser/web_view/web_view_guest.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef ATOM_BROWSER_WEB_VIEW_WEB_VIEW_GUEST_H_
|
||||||
|
#define ATOM_BROWSER_WEB_VIEW_WEB_VIEW_GUEST_H_
|
||||||
|
|
||||||
|
namespace atom {
|
||||||
|
|
||||||
|
// A WebViewGuest provides the browser-side implementation of the <webview> API
|
||||||
|
// and manages the dispatch of <webview> extension events. WebViewGuest is
|
||||||
|
// created on attachment. That is, when a guest WebContents is associated with
|
||||||
|
// a particular embedder WebContents. This happens on either initial navigation
|
||||||
|
// or through the use of the New Window API, when a new window is attached to
|
||||||
|
// a particular <webview>.
|
||||||
|
|
||||||
|
} // namespace atom
|
||||||
|
|
||||||
|
#endif // ATOM_BROWSER_WEB_VIEW_WEB_VIEW_GUEST_H_
|
31
atom/browser/web_view/web_view_manager.cc
Normal file
31
atom/browser/web_view/web_view_manager.cc
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "atom/browser/web_view/web_view_manager.h"
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
|
||||||
|
namespace atom {
|
||||||
|
|
||||||
|
WebViewManager::WebViewManager(content::BrowserContext* context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
WebViewManager::~WebViewManager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebViewManager::MaybeGetGuestByInstanceIDOrKill(
|
||||||
|
int guest_instance_id,
|
||||||
|
int embedder_render_process_id,
|
||||||
|
const GuestByInstanceIDCallback& callback) {
|
||||||
|
LOG(ERROR) << "MaybeGetGuestByInstanceIDOrKill";
|
||||||
|
callback.Run(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebViewManager::ForEachGuest(content::WebContents* embedder_web_contents,
|
||||||
|
const GuestCallback& callback) {
|
||||||
|
LOG(ERROR) << "ForEachGuest";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace atom
|
35
atom/browser/web_view/web_view_manager.h
Normal file
35
atom/browser/web_view/web_view_manager.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef ATOM_BROWSER_WEB_VIEW_WEB_VIEW_MANAGER_H_
|
||||||
|
#define ATOM_BROWSER_WEB_VIEW_WEB_VIEW_MANAGER_H_
|
||||||
|
|
||||||
|
#include "content/public/browser/browser_plugin_guest_manager.h"
|
||||||
|
|
||||||
|
namespace content {
|
||||||
|
class BrowserContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace atom {
|
||||||
|
|
||||||
|
class WebViewManager : public content::BrowserPluginGuestManager {
|
||||||
|
public:
|
||||||
|
explicit WebViewManager(content::BrowserContext* context);
|
||||||
|
virtual ~WebViewManager();
|
||||||
|
|
||||||
|
// content::BrowserPluginGuestManager:
|
||||||
|
virtual void MaybeGetGuestByInstanceIDOrKill(
|
||||||
|
int guest_instance_id,
|
||||||
|
int embedder_render_process_id,
|
||||||
|
const GuestByInstanceIDCallback& callback) override;
|
||||||
|
virtual bool ForEachGuest(content::WebContents* embedder_web_contents,
|
||||||
|
const GuestCallback& callback) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(WebViewManager);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace atom
|
||||||
|
|
||||||
|
#endif // ATOM_BROWSER_WEB_VIEW_WEB_VIEW_MANAGER_H_
|
|
@ -56,3 +56,5 @@ else if location.protocol is 'chrome-extension:'
|
||||||
else
|
else
|
||||||
# Override default web functions.
|
# Override default web functions.
|
||||||
require path.join(__dirname, 'override')
|
require path.join(__dirname, 'override')
|
||||||
|
# Load webview tag implementation.
|
||||||
|
require path.join(__dirname, 'webview')
|
||||||
|
|
531
atom/renderer/lib/webview.coffee
Normal file
531
atom/renderer/lib/webview.coffee
Normal file
|
@ -0,0 +1,531 @@
|
||||||
|
v8Util = process.atomBinding 'v8_util'
|
||||||
|
webView = require 'web-view'
|
||||||
|
|
||||||
|
# ID generator.
|
||||||
|
nextId = 1
|
||||||
|
getNextId = -> nextId++
|
||||||
|
|
||||||
|
# Attributes.
|
||||||
|
WEB_VIEW_ATTRIBUTE_ALLOWTRANSPARENCY = 'allowtransparency'
|
||||||
|
WEB_VIEW_ATTRIBUTE_AUTOSIZE = 'autosize'
|
||||||
|
WEB_VIEW_ATTRIBUTE_MAXHEIGHT = 'maxheight'
|
||||||
|
WEB_VIEW_ATTRIBUTE_MAXWIDTH = 'maxwidth'
|
||||||
|
WEB_VIEW_ATTRIBUTE_MINHEIGHT = 'minheight'
|
||||||
|
WEB_VIEW_ATTRIBUTE_MINWIDTH = 'minwidth'
|
||||||
|
WEB_VIEW_ATTRIBUTE_PARTITION = 'partition'
|
||||||
|
AUTO_SIZE_ATTRIBUTES = [
|
||||||
|
WEB_VIEW_ATTRIBUTE_AUTOSIZE,
|
||||||
|
WEB_VIEW_ATTRIBUTE_MAXHEIGHT,
|
||||||
|
WEB_VIEW_ATTRIBUTE_MAXWIDTH,
|
||||||
|
WEB_VIEW_ATTRIBUTE_MINHEIGHT,
|
||||||
|
WEB_VIEW_ATTRIBUTE_MINWIDTH,
|
||||||
|
]
|
||||||
|
|
||||||
|
# Error messages.
|
||||||
|
ERROR_MSG_ALREADY_NAVIGATED =
|
||||||
|
'The object has already navigated, so its partition cannot be changed.'
|
||||||
|
ERROR_MSG_CANNOT_INJECT_SCRIPT = '<webview>: ' +
|
||||||
|
'Script cannot be injected into content until the page has loaded.'
|
||||||
|
ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE = '<webview>: ' +
|
||||||
|
'contentWindow is not available at this time. It will become available ' +
|
||||||
|
'when the page has finished loading.'
|
||||||
|
ERROR_MSG_INVALID_PARTITION_ATTRIBUTE = 'Invalid partition attribute.'
|
||||||
|
|
||||||
|
# Represents the state of the storage partition.
|
||||||
|
class Partition
|
||||||
|
constructor: ->
|
||||||
|
@validPartitionId = true
|
||||||
|
@persistStorage = false
|
||||||
|
@storagePartitionId = ''
|
||||||
|
|
||||||
|
toAttribute: ->
|
||||||
|
return '' unless @validPartitionId
|
||||||
|
(@persistStorage ? 'persist:' : '') + @storagePartitionId
|
||||||
|
|
||||||
|
fromAttribute: (value, hasNavigated) ->
|
||||||
|
result = {}
|
||||||
|
if hasNavigated
|
||||||
|
result.error = ERROR_MSG_ALREADY_NAVIGATED
|
||||||
|
return result
|
||||||
|
value ?= ''
|
||||||
|
|
||||||
|
LEN = 'persist:'.length
|
||||||
|
if value.substr(0, LEN) == 'persist:'
|
||||||
|
value = value.substr LEN
|
||||||
|
unless value
|
||||||
|
@validPartitionId = false
|
||||||
|
result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE
|
||||||
|
return result
|
||||||
|
@persistStorage = true
|
||||||
|
else
|
||||||
|
@persistStorage = false
|
||||||
|
|
||||||
|
@storagePartitionId = value
|
||||||
|
result
|
||||||
|
|
||||||
|
# Represents the internal state of the WebView node.
|
||||||
|
class WebView
|
||||||
|
constructor: (@webviewNode) ->
|
||||||
|
v8Util.setHiddenValue webviewNode, 'internal', this
|
||||||
|
@attached = false
|
||||||
|
@pendingGuestCreation = false
|
||||||
|
@elementAttached = false
|
||||||
|
|
||||||
|
@beforeFirstNavigation = true
|
||||||
|
@contentWindow = null
|
||||||
|
@validPartitionId = true
|
||||||
|
# Used to save some state upon deferred attachment.
|
||||||
|
# If <object> bindings is not available, we defer attachment.
|
||||||
|
# This state contains whether or not the attachment request was for
|
||||||
|
# newwindow.
|
||||||
|
@deferredAttachState = null
|
||||||
|
|
||||||
|
# on* Event handlers.
|
||||||
|
@on = {}
|
||||||
|
|
||||||
|
@browserPluginNode = @createBrowserPluginNode()
|
||||||
|
shadowRoot = @webviewNode.createShadowRoot()
|
||||||
|
@partition = new Partition()
|
||||||
|
|
||||||
|
@setupWebViewSrcAttributeMutationObserver()
|
||||||
|
@setupFocusPropagation()
|
||||||
|
@setupWebviewNodeProperties()
|
||||||
|
|
||||||
|
@viewInstanceId = getNextId()
|
||||||
|
|
||||||
|
shadowRoot.appendChild @browserPluginNode
|
||||||
|
|
||||||
|
createBrowserPluginNode: ->
|
||||||
|
# We create BrowserPlugin as a custom element in order to observe changes
|
||||||
|
# to attributes synchronously.
|
||||||
|
browserPluginNode = new WebView.BrowserPlugin()
|
||||||
|
v8Util.setHiddenValue browserPluginNode, 'internal', this
|
||||||
|
browserPluginNode
|
||||||
|
|
||||||
|
getGuestInstanceId: ->
|
||||||
|
@guestInstanceId
|
||||||
|
|
||||||
|
# Resets some state upon reattaching <webview> element to the DOM.
|
||||||
|
reset: ->
|
||||||
|
# If guestInstanceId is defined then the <webview> has navigated and has
|
||||||
|
# already picked up a partition ID. Thus, we need to reset the initialization
|
||||||
|
# state. However, it may be the case that beforeFirstNavigation is false BUT
|
||||||
|
# guestInstanceId has yet to be initialized. This means that we have not
|
||||||
|
# heard back from createGuest yet. We will not reset the flag in this case so
|
||||||
|
# that we don't end up allocating a second guest.
|
||||||
|
if @guestInstanceId
|
||||||
|
# FIXME
|
||||||
|
# GuestViewInternal.destroyGuest @guestInstanceId
|
||||||
|
@guestInstanceId = undefined
|
||||||
|
@beforeFirstNavigation = true
|
||||||
|
@validPartitionId = true
|
||||||
|
@partition.validPartitionId = true
|
||||||
|
@contentWindow = null
|
||||||
|
@internalInstanceId = 0
|
||||||
|
|
||||||
|
# Sets the <webview>.request property.
|
||||||
|
setRequestPropertyOnWebViewNode: (request) ->
|
||||||
|
Object.defineProperty @webviewNode, 'request', value: request, enumerable: true
|
||||||
|
|
||||||
|
setupFocusPropagation: ->
|
||||||
|
unless @webviewNode.hasAttribute 'tabIndex'
|
||||||
|
# <webview> needs a tabIndex in order to be focusable.
|
||||||
|
# TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute
|
||||||
|
# to allow <webview> to be focusable.
|
||||||
|
# See http://crbug.com/231664.
|
||||||
|
@webviewNode.setAttribute 'tabIndex', -1
|
||||||
|
@webviewNode.addEventListener 'focus', (e) =>
|
||||||
|
# Focus the BrowserPlugin when the <webview> takes focus.
|
||||||
|
@browserPluginNode.focus()
|
||||||
|
@webviewNode.addEventListener 'blur', (e) =>
|
||||||
|
# Blur the BrowserPlugin when the <webview> loses focus.
|
||||||
|
@browserPluginNode.blur()
|
||||||
|
|
||||||
|
# Validation helper function for executeScript() and insertCSS().
|
||||||
|
validateExecuteCodeCall: ->
|
||||||
|
throw new Error(ERROR_MSG_CANNOT_INJECT_SCRIPT) unless @guestInstanceId
|
||||||
|
|
||||||
|
setupAutoSizeProperties: ->
|
||||||
|
for attributeName in AUTO_SIZE_ATTRIBUTES
|
||||||
|
this[attributeName] = @webviewNode.getAttribute attributeName
|
||||||
|
Object.defineProperty @webviewNode, attributeName,
|
||||||
|
get: => this[attributeName]
|
||||||
|
set: (value) => @webviewNode.setAttribute attributeName, value
|
||||||
|
enumerable: true
|
||||||
|
|
||||||
|
setupWebviewNodeProperties: ->
|
||||||
|
@setupAutoSizeProperties()
|
||||||
|
|
||||||
|
Object.defineProperty @webviewNode, WEB_VIEW_ATTRIBUTE_ALLOWTRANSPARENCY,
|
||||||
|
get: => @allowtransparency
|
||||||
|
set: (value) =>
|
||||||
|
@webviewNode.setAttribute WEB_VIEW_ATTRIBUTE_ALLOWTRANSPARENCY, value
|
||||||
|
enumerable: true
|
||||||
|
|
||||||
|
# We cannot use {writable: true} property descriptor because we want a
|
||||||
|
# dynamic getter value.
|
||||||
|
Object.defineProperty @webviewNode, 'contentWindow',
|
||||||
|
get: =>
|
||||||
|
return @contentWindow if @contentWindow?
|
||||||
|
window.console.error ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE
|
||||||
|
# No setter.
|
||||||
|
enumerable: true
|
||||||
|
|
||||||
|
Object.defineProperty @webviewNode, 'name',
|
||||||
|
get: => @name
|
||||||
|
set: (value) => @webviewNode.setAttribute 'name', value
|
||||||
|
enumerable: true
|
||||||
|
|
||||||
|
Object.defineProperty @webviewNode, 'partition',
|
||||||
|
get: => @partition.toAttribute()
|
||||||
|
set: (value) =>
|
||||||
|
result = @partition.fromAttribute value, @hasNavigated()
|
||||||
|
throw result.error if result.error?
|
||||||
|
@webviewNode.setAttribute 'partition', value
|
||||||
|
enumerable: true
|
||||||
|
|
||||||
|
@src = @webviewNode.getAttribute 'src'
|
||||||
|
Object.defineProperty @webviewNode, 'src',
|
||||||
|
get: => @src
|
||||||
|
set: (value) => @webviewNode.setAttribute 'src', value
|
||||||
|
# No setter.
|
||||||
|
enumerable: true
|
||||||
|
|
||||||
|
# The purpose of this mutation observer is to catch assignment to the src
|
||||||
|
# attribute without any changes to its value. This is useful in the case
|
||||||
|
# where the webview guest has crashed and navigating to the same address
|
||||||
|
# spawns off a new process.
|
||||||
|
setupWebViewSrcAttributeMutationObserver: ->
|
||||||
|
@srcAndPartitionObserver = new MutationObserver (mutations) =>
|
||||||
|
for mutation in mutations
|
||||||
|
oldValue = mutation.oldValue
|
||||||
|
newValue = @webviewNode.getAttribute mutation.attributeName
|
||||||
|
return if oldValue isnt newValue
|
||||||
|
@handleWebviewAttributeMutation mutation.attributeName, oldValue, newValue
|
||||||
|
params =
|
||||||
|
attributes: true,
|
||||||
|
attributeOldValue: true,
|
||||||
|
attributeFilter: ['src', 'partition']
|
||||||
|
@srcAndPartitionObserver.observe @webviewNode, params
|
||||||
|
|
||||||
|
# This observer monitors mutations to attributes of the <webview> and
|
||||||
|
# updates the BrowserPlugin properties accordingly. In turn, updating
|
||||||
|
# a BrowserPlugin property will update the corresponding BrowserPlugin
|
||||||
|
# attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
|
||||||
|
# details.
|
||||||
|
handleWebviewAttributeMutation: (name, oldValue, newValue) ->
|
||||||
|
if name in AUTO_SIZE_ATTRIBUTES
|
||||||
|
this[name] = newValue
|
||||||
|
return unless @guestInstanceId
|
||||||
|
# Convert autosize attribute to boolean.
|
||||||
|
autosize = @webviewNode.hasAttribute WEB_VIEW_ATTRIBUTE_AUTOSIZE
|
||||||
|
# FIXME
|
||||||
|
# GuestViewInternal.setAutoSize @guestInstanceId,
|
||||||
|
# enableAutoSize: autosize,
|
||||||
|
# min:
|
||||||
|
# width: parseInt @minwidth ? 0
|
||||||
|
# height: parseInt @minheight ? 0
|
||||||
|
# max:
|
||||||
|
# width: parseInt @maxwidth ? 0
|
||||||
|
# height: parseInt @maxheight ? 0
|
||||||
|
else if name is WEB_VIEW_ATTRIBUTE_ALLOWTRANSPARENCY
|
||||||
|
# We treat null attribute (attribute removed) and the empty string as
|
||||||
|
# one case.
|
||||||
|
oldValue ?= ''
|
||||||
|
newValue ?= ''
|
||||||
|
|
||||||
|
return if oldValue is newValue
|
||||||
|
@allowtransparency = newValue != ''
|
||||||
|
|
||||||
|
return unless @guestInstanceId
|
||||||
|
|
||||||
|
# FIXME
|
||||||
|
# WebViewInternal.setAllowTransparency @guestInstanceId, @allowtransparency
|
||||||
|
else if name is 'name'
|
||||||
|
# We treat null attribute (attribute removed) and the empty string as
|
||||||
|
# one case.
|
||||||
|
oldValue ?= ''
|
||||||
|
newValue ?= ''
|
||||||
|
|
||||||
|
return if oldValue is newValue
|
||||||
|
@name = newValue
|
||||||
|
|
||||||
|
return unless @guestInstanceId
|
||||||
|
|
||||||
|
# FIXME
|
||||||
|
# WebViewInternal.setName @guestInstanceId, newValue
|
||||||
|
else if name is 'src'
|
||||||
|
# We treat null attribute (attribute removed) and the empty string as
|
||||||
|
# one case.
|
||||||
|
oldValue ?= ''
|
||||||
|
newValue ?= ''
|
||||||
|
# Once we have navigated, we don't allow clearing the src attribute.
|
||||||
|
# Once <webview> enters a navigated state, it cannot be return back to a
|
||||||
|
# placeholder state.
|
||||||
|
if newValue == '' and oldValue != ''
|
||||||
|
# src attribute changes normally initiate a navigation. We suppress
|
||||||
|
# the next src attribute handler call to avoid reloading the page
|
||||||
|
# on every guest-initiated navigation.
|
||||||
|
@ignoreNextSrcAttributeChange = true
|
||||||
|
@webviewNode.setAttribute 'src', oldValue
|
||||||
|
@src = newValue
|
||||||
|
if @ignoreNextSrcAttributeChange
|
||||||
|
# Don't allow the src mutation observer to see this change.
|
||||||
|
@srcAndPartitionObserver.takeRecords()
|
||||||
|
@ignoreNextSrcAttributeChange = false
|
||||||
|
return
|
||||||
|
result = {}
|
||||||
|
@parseSrcAttribute result
|
||||||
|
|
||||||
|
throw result.error if result.error?
|
||||||
|
else if name is 'partition'
|
||||||
|
# Note that throwing error here won't synchronously propagate.
|
||||||
|
@partition.fromAttribute newValue, @hasNavigated()
|
||||||
|
|
||||||
|
handleBrowserPluginAttributeMutation: (name, oldValue, newValue) ->
|
||||||
|
if name is 'internalinstanceid' and !oldValue and !!newValue
|
||||||
|
@browserPluginNode.removeAttribute 'internalinstanceid'
|
||||||
|
@internalInstanceId = parseInt newValue
|
||||||
|
|
||||||
|
if !!@guestInstanceId and @guestInstanceId != 0
|
||||||
|
isNewWindow = if @deferredAttachState then @deferredAttachState.isNewWindow else false
|
||||||
|
params = @buildAttachParams isNewWindow
|
||||||
|
# FIXME
|
||||||
|
# guestViewInternalNatives.AttachGuest
|
||||||
|
# @internalInstanceId,
|
||||||
|
# @guestInstanceId,
|
||||||
|
# params,
|
||||||
|
# (w) => @contentWindow = w
|
||||||
|
|
||||||
|
onSizeChanged: (webViewEvent) ->
|
||||||
|
newWidth = webViewEvent.newWidth
|
||||||
|
newHeight = webViewEvent.newHeight
|
||||||
|
|
||||||
|
node = @webviewNode
|
||||||
|
|
||||||
|
width = node.offsetWidth
|
||||||
|
height = node.offsetHeight
|
||||||
|
|
||||||
|
# Check the current bounds to make sure we do not resize <webview>
|
||||||
|
# outside of current constraints.
|
||||||
|
if node.hasAttribute(WEB_VIEW_ATTRIBUTE_MAXWIDTH) and
|
||||||
|
node[WEB_VIEW_ATTRIBUTE_MAXWIDTH]
|
||||||
|
maxWidth = node[WEB_VIEW_ATTRIBUTE_MAXWIDTH]
|
||||||
|
else
|
||||||
|
maxWidth = width
|
||||||
|
|
||||||
|
if node.hasAttribute(WEB_VIEW_ATTRIBUTE_MINWIDTH) and
|
||||||
|
node[WEB_VIEW_ATTRIBUTE_MINWIDTH]
|
||||||
|
minWidth = node[WEB_VIEW_ATTRIBUTE_MINWIDTH]
|
||||||
|
else
|
||||||
|
minWidth = width
|
||||||
|
minWidth = maxWidth if minWidth > maxWidth
|
||||||
|
|
||||||
|
if node.hasAttribute(WEB_VIEW_ATTRIBUTE_MAXHEIGHT) and
|
||||||
|
node[WEB_VIEW_ATTRIBUTE_MAXHEIGHT]
|
||||||
|
maxHeight = node[WEB_VIEW_ATTRIBUTE_MAXHEIGHT]
|
||||||
|
else
|
||||||
|
maxHeight = height
|
||||||
|
|
||||||
|
if node.hasAttribute(WEB_VIEW_ATTRIBUTE_MINHEIGHT) and
|
||||||
|
node[WEB_VIEW_ATTRIBUTE_MINHEIGHT]
|
||||||
|
minHeight = node[WEB_VIEW_ATTRIBUTE_MINHEIGHT]
|
||||||
|
else
|
||||||
|
minHeight = height
|
||||||
|
minHeight = maxHeight if minHeight > maxHeight
|
||||||
|
|
||||||
|
if not @webviewNode.hasAttribute WEB_VIEW_ATTRIBUTE_AUTOSIZE or
|
||||||
|
(newWidth >= minWidth and
|
||||||
|
newWidth <= maxWidth and
|
||||||
|
newHeight >= minHeight and
|
||||||
|
newHeight <= maxHeight)
|
||||||
|
node.style.width = newWidth + 'px'
|
||||||
|
node.style.height = newHeight + 'px'
|
||||||
|
# Only fire the DOM event if the size of the <webview> has actually
|
||||||
|
# changed.
|
||||||
|
@dispatchEvent webViewEvent
|
||||||
|
|
||||||
|
# Returns if <object> is in the render tree.
|
||||||
|
isPluginInRenderTree: ->
|
||||||
|
!!@internalInstanceId && @internalInstanceId != 0
|
||||||
|
|
||||||
|
hasNavigated: ->
|
||||||
|
not @beforeFirstNavigation
|
||||||
|
|
||||||
|
parseSrcAttribute: (result) ->
|
||||||
|
unless @partition.validPartitionId
|
||||||
|
result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE
|
||||||
|
return
|
||||||
|
@src = @webviewNode.getAttribute 'src'
|
||||||
|
|
||||||
|
return unless @src
|
||||||
|
|
||||||
|
unless @guestInstanceId?
|
||||||
|
if @beforeFirstNavigation
|
||||||
|
@beforeFirstNavigation = false
|
||||||
|
@createGuest()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Navigate to |this.src|.
|
||||||
|
# FIXME
|
||||||
|
# WebViewInternal.navigate @guestInstanceId, @src
|
||||||
|
|
||||||
|
parseAttributes: ->
|
||||||
|
return unless @elementAttached
|
||||||
|
hasNavigated = @hasNavigated()
|
||||||
|
attributeValue = @webviewNode.getAttribute 'partition'
|
||||||
|
result = @partition.fromAttribute attributeValue, hasNavigated
|
||||||
|
@parseSrcAttribute result
|
||||||
|
|
||||||
|
createGuest: ->
|
||||||
|
return if @pendingGuestCreation?
|
||||||
|
storagePartitionId =
|
||||||
|
@webviewNode.getAttribute(WEB_VIEW_ATTRIBUTE_PARTITION) or
|
||||||
|
@webviewNode[WEB_VIEW_ATTRIBUTE_PARTITION]
|
||||||
|
params = storagePartitionId: storagePartitionId
|
||||||
|
# FIXME
|
||||||
|
# GuestViewInternal.createGuest 'webview', params, (guestInstanceId) =>
|
||||||
|
# @pendingGuestCreation = false
|
||||||
|
# unless @elementAttached
|
||||||
|
# GuestViewInternal.destroyGuest guestInstanceId
|
||||||
|
# return
|
||||||
|
# @attachWindow guestInstanceId, false
|
||||||
|
@pendingGuestCreation = true
|
||||||
|
|
||||||
|
onFrameNameChanged: (name) ->
|
||||||
|
@name ?= ''
|
||||||
|
if @name is ''
|
||||||
|
@webviewNode.removeAttribute 'name'
|
||||||
|
else
|
||||||
|
@webviewNode.setAttribute 'name', @name
|
||||||
|
|
||||||
|
@dispatchEvent = (webViewEvent) ->
|
||||||
|
@webviewNode.dispatchEvent webViewEvent
|
||||||
|
|
||||||
|
# Adds an 'on<event>' property on the webview, which can be used to set/unset
|
||||||
|
# an event handler.
|
||||||
|
setupEventProperty: (eventName) ->
|
||||||
|
propertyName = 'on' + eventName.toLowerCase()
|
||||||
|
Object.defineProperty @webviewNode, propertyName,
|
||||||
|
get: => @on[propertyName]
|
||||||
|
set: (value) =>
|
||||||
|
if @on[propertyName]
|
||||||
|
@webviewNode.removeEventListener eventName, @on[propertyName]
|
||||||
|
@on[propertyName] = value
|
||||||
|
if value
|
||||||
|
@webviewNode.addEventListener eventName, value
|
||||||
|
enumerable: true
|
||||||
|
|
||||||
|
# Updates state upon loadcommit.
|
||||||
|
onLoadCommit: (@baseUrlForDataUrl, @currentEntryIndex, @entryCount, @processId, url, isTopLevel) ->
|
||||||
|
oldValue = @webviewNode.getAttribute 'src'
|
||||||
|
newValue = url
|
||||||
|
if isTopLevel and (oldValue != newValue)
|
||||||
|
# Touching the src attribute triggers a navigation. To avoid
|
||||||
|
# triggering a page reload on every guest-initiated navigation,
|
||||||
|
# we use the flag ignoreNextSrcAttributeChange here.
|
||||||
|
this.ignoreNextSrcAttributeChange = true
|
||||||
|
this.webviewNode.setAttribute 'src', newValue
|
||||||
|
|
||||||
|
onAttach: (storagePartitionId) ->
|
||||||
|
@webviewNode.setAttribute 'partition', storagePartitionId
|
||||||
|
@partition.fromAttribute storagePartitionId, this.hasNavigated()
|
||||||
|
|
||||||
|
buildAttachParams: (isNewWindow) ->
|
||||||
|
allowtransparency: @allowtransparency || false
|
||||||
|
autosize: @webviewNode.hasAttribute WEB_VIEW_ATTRIBUTE_AUTOSIZE
|
||||||
|
instanceId: @viewInstanceId
|
||||||
|
maxheight: parseInt @maxheight || 0
|
||||||
|
maxwidth: parseInt @maxwidth || 0
|
||||||
|
minheight: parseInt @minheight || 0
|
||||||
|
minwidth: parseInt @minwidth || 0
|
||||||
|
name: @name
|
||||||
|
# We don't need to navigate new window from here.
|
||||||
|
src: isNewWindow ? undefined : @src
|
||||||
|
# If we have a partition from the opener, that will also be already
|
||||||
|
# set via this.onAttach().
|
||||||
|
storagePartitionId: @partition.toAttribute()
|
||||||
|
userAgentOverride: @userAgentOverride
|
||||||
|
|
||||||
|
attachWindow: (guestInstanceId, isNewWindow) ->
|
||||||
|
@guestInstanceId = guestInstanceId
|
||||||
|
params = @buildAttachParams isNewWindow
|
||||||
|
|
||||||
|
unless @isPluginInRenderTree()
|
||||||
|
@deferredAttachState = isNewWindow: isNewWindow
|
||||||
|
return true
|
||||||
|
|
||||||
|
@deferredAttachState = null
|
||||||
|
# FIXME
|
||||||
|
# guestViewInternalNatives.AttachGuest @internalInstanceId, @guestInstanceId, params, (w) => @contentWindow = w
|
||||||
|
|
||||||
|
# Registers browser plugin <object> custom element.
|
||||||
|
registerBrowserPluginElement = ->
|
||||||
|
proto = Object.create HTMLObjectElement.prototype
|
||||||
|
|
||||||
|
proto.createdCallback = ->
|
||||||
|
@setAttribute 'type', 'application/browser-plugin'
|
||||||
|
@setAttribute 'id', 'browser-plugin-' + getNextId()
|
||||||
|
# The <object> node fills in the <webview> container.
|
||||||
|
@style.width = '100%'
|
||||||
|
@style.height = '100%'
|
||||||
|
|
||||||
|
proto.attributeChangedCallback = (name, oldValue, newValue) ->
|
||||||
|
internal = v8Util.getHiddenValue 'internal'
|
||||||
|
return unless internal
|
||||||
|
internal.handleBrowserPluginAttributeMutation name, oldValue, newValue
|
||||||
|
|
||||||
|
proto.attachedCallback = ->
|
||||||
|
# Load the plugin immediately.
|
||||||
|
unused = this.nonExistentAttribute
|
||||||
|
|
||||||
|
WebView.BrowserPlugin = webView.registerEmbedderCustomElement 'browserplugin',
|
||||||
|
extends: 'object', prototype: proto
|
||||||
|
|
||||||
|
delete proto.createdCallback
|
||||||
|
delete proto.attachedCallback
|
||||||
|
delete proto.detachedCallback
|
||||||
|
delete proto.attributeChangedCallback
|
||||||
|
|
||||||
|
# Registers <webview> custom element.
|
||||||
|
registerWebViewElement = ->
|
||||||
|
proto = Object.create HTMLObjectElement.prototype
|
||||||
|
|
||||||
|
proto.createdCallback = ->
|
||||||
|
new WebView(this)
|
||||||
|
|
||||||
|
proto.attributeChangedCallback = (name, oldValue, newValue) ->
|
||||||
|
internal = v8Util.getHiddenValue 'internal'
|
||||||
|
return unless internal
|
||||||
|
internal.handleWebviewAttributeMutation name, oldValue, newValue
|
||||||
|
|
||||||
|
proto.detachedCallback = ->
|
||||||
|
internal = v8Util.getHiddenValue 'internal'
|
||||||
|
return unless internal
|
||||||
|
internal.elementAttached = false
|
||||||
|
internal.reset()
|
||||||
|
|
||||||
|
proto.attachedCallback = ->
|
||||||
|
internal = v8Util.getHiddenValue 'internal'
|
||||||
|
return unless internal
|
||||||
|
unless internal.elementAttached
|
||||||
|
internal.elementAttached = true
|
||||||
|
internal.parseAttributes()
|
||||||
|
|
||||||
|
window.WebView = webView.registerEmbedderCustomElement 'webview',
|
||||||
|
prototype: proto
|
||||||
|
|
||||||
|
# Delete the callbacks so developers cannot call them and produce unexpected
|
||||||
|
# behavior.
|
||||||
|
delete proto.createdCallback
|
||||||
|
delete proto.attachedCallback
|
||||||
|
delete proto.detachedCallback
|
||||||
|
delete proto.attributeChangedCallback
|
||||||
|
|
||||||
|
useCapture = true
|
||||||
|
listener = (event) ->
|
||||||
|
return if document.readyState == 'loading'
|
||||||
|
registerBrowserPluginElement()
|
||||||
|
registerWebViewElement()
|
||||||
|
window.removeEventListener event.type, listener, useCapture
|
||||||
|
window.addEventListener 'readystatechange', listener, true
|
Loading…
Reference in a new issue