blob: 88083227b9f0606c1f46792cf24f50f73bc6dc2f [file] [log] [blame]
// Copyright (c) 2014 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.
// This module implements the attributes of the <webview> tag.
var GuestViewInternal =
require('binding').Binding.create('guestViewInternal').generate();
var WebView = require('webView').WebView;
var WebViewConstants = require('webViewConstants').WebViewConstants;
var WebViewInternal = require('webViewInternal').WebViewInternal;
// -----------------------------------------------------------------------------
// Attribute objects.
// Default implementation of a WebView attribute.
function WebViewAttribute(name, webViewImpl) {
this.name = name;
this.webViewImpl = webViewImpl;
this.ignoreMutation = false;
this.defineProperty();
}
// Retrieves and returns the attribute's value.
WebViewAttribute.prototype.getValue = function() {
return this.webViewImpl.webviewNode.getAttribute(this.name) || '';
};
// Sets the attribute's value.
WebViewAttribute.prototype.setValue = function(value) {
this.webViewImpl.webviewNode.setAttribute(this.name, value || '');
};
// Changes the attribute's value without triggering its mutation handler.
WebViewAttribute.prototype.setValueIgnoreMutation = function(value) {
this.ignoreMutation = true;
this.webViewImpl.webviewNode.setAttribute(this.name, value || '');
this.ignoreMutation = false;
}
// Defines this attribute as a property on the webview node.
WebViewAttribute.prototype.defineProperty = function() {
Object.defineProperty(this.webViewImpl.webviewNode, this.name, {
get: function() {
return this.getValue();
}.bind(this),
set: function(value) {
this.setValue(value);
}.bind(this),
enumerable: true
});
};
// Called when the attribute's value changes.
WebViewAttribute.prototype.handleMutation = function(oldValue, newValue) {};
// An attribute that is treated as a Boolean.
function BooleanAttribute(name, webViewImpl) {
WebViewAttribute.call(this, name, webViewImpl);
}
BooleanAttribute.prototype.__proto__ = WebViewAttribute.prototype;
BooleanAttribute.prototype.getValue = function() {
return this.webViewImpl.webviewNode.hasAttribute(this.name);
};
BooleanAttribute.prototype.setValue = function(value) {
if (!value) {
this.webViewImpl.webviewNode.removeAttribute(this.name);
} else {
this.webViewImpl.webviewNode.setAttribute(this.name, '');
}
};
// Attribute that specifies whether transparency is allowed in the webview.
function AllowTransparencyAttribute(webViewImpl) {
BooleanAttribute.call(
this, WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, webViewImpl);
}
AllowTransparencyAttribute.prototype.__proto__ = BooleanAttribute.prototype;
AllowTransparencyAttribute.prototype.handleMutation = function(oldValue,
newValue) {
if (!this.webViewImpl.guestInstanceId) {
return;
}
WebViewInternal.setAllowTransparency(
this.webViewImpl.guestInstanceId,
this.webViewImpl.attributes[
WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].getValue());
};
// Attribute used to define the demension limits of autosizing.
function AutosizeDimensionAttribute(name, webViewImpl) {
WebViewAttribute.call(this, name, webViewImpl);
}
AutosizeDimensionAttribute.prototype.__proto__ = WebViewAttribute.prototype;
AutosizeDimensionAttribute.prototype.getValue = function() {
return parseInt(this.webViewImpl.webviewNode.getAttribute(this.name)) || 0;
};
AutosizeDimensionAttribute.prototype.handleMutation = function(
oldValue, newValue) {
if (!this.webViewImpl.guestInstanceId) {
return;
}
GuestViewInternal.setAutoSize(this.webViewImpl.guestInstanceId, {
'enableAutoSize': this.webViewImpl.attributes[
WebViewConstants.ATTRIBUTE_AUTOSIZE].getValue(),
'min': {
'width': this.webViewImpl.attributes[
WebViewConstants.ATTRIBUTE_MINWIDTH].getValue(),
'height': this.webViewImpl.attributes[
WebViewConstants.ATTRIBUTE_MINHEIGHT].getValue()
},
'max': {
'width': this.webViewImpl.attributes[
WebViewConstants.ATTRIBUTE_MAXWIDTH].getValue(),
'height': this.webViewImpl.attributes[
WebViewConstants.ATTRIBUTE_MAXHEIGHT].getValue()
}
});
return;
};
// Attribute that specifies whether the webview should be autosized.
function AutosizeAttribute(webViewImpl) {
BooleanAttribute.call(this, WebViewConstants.ATTRIBUTE_AUTOSIZE, webViewImpl);
}
AutosizeAttribute.prototype.__proto__ = BooleanAttribute.prototype;
AutosizeAttribute.prototype.handleMutation =
AutosizeDimensionAttribute.prototype.handleMutation;
// Attribute that sets the guest content's window.name object.
function NameAttribute(webViewImpl) {
WebViewAttribute.call(this, WebViewConstants.ATTRIBUTE_NAME, webViewImpl);
}
NameAttribute.prototype.__proto__ = WebViewAttribute.prototype
NameAttribute.prototype.handleMutation = function(oldValue, newValue) {
oldValue = oldValue || '';
newValue = newValue || '';
if (oldValue === newValue || !this.webViewImpl.guestInstanceId) {
return;
}
WebViewInternal.setName(this.webViewImpl.guestInstanceId, newValue);
};
// Attribute representing the state of the storage partition.
function PartitionAttribute(webViewImpl) {
WebViewAttribute.call(
this, WebViewConstants.ATTRIBUTE_PARTITION, webViewImpl);
this.validPartitionId = true;
}
PartitionAttribute.prototype.__proto__ = WebViewAttribute.prototype;
PartitionAttribute.prototype.handleMutation = function(oldValue, newValue) {
newValue = newValue || '';
// The partition cannot change if the webview has already navigated.
if (!this.webViewImpl.beforeFirstNavigation) {
window.console.error(WebViewConstants.ERROR_MSG_ALREADY_NAVIGATED);
this.setValueIgnoreMutation(oldValue);
return;
}
if (newValue == 'persist:') {
this.validPartitionId = false;
window.console.error(
WebViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE);
}
};
// Attribute that handles the location and navigation of the webview.
function SrcAttribute(webViewImpl) {
WebViewAttribute.call(this, WebViewConstants.ATTRIBUTE_SRC, webViewImpl);
this.setupMutationObserver();
}
SrcAttribute.prototype.__proto__ = WebViewAttribute.prototype;
SrcAttribute.prototype.handleMutation = function(oldValue, newValue) {
// Once we have navigated, we don't allow clearing the src attribute.
// Once <webview> enters a navigated state, it cannot return to a
// placeholder state.
if (!newValue && 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.
this.setValueIgnoreMutation(oldValue);
return;
}
this.webViewImpl.parseSrcAttribute();
};
// 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.
SrcAttribute.prototype.setupMutationObserver =
function() {
this.observer = new MutationObserver(function(mutations) {
$Array.forEach(mutations, function(mutation) {
var oldValue = mutation.oldValue;
var newValue = this.getValue();
if (oldValue != newValue) {
return;
}
this.handleMutation(oldValue, newValue);
}.bind(this));
}.bind(this));
var params = {
attributes: true,
attributeOldValue: true,
attributeFilter: [this.name]
};
this.observer.observe(this.webViewImpl.webviewNode, params);
};
// -----------------------------------------------------------------------------
// Sets up all of the webview attributes.
WebView.prototype.setupWebViewAttributes = function() {
this.attributes = {};
this.attributes[WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY] =
new AllowTransparencyAttribute(this);
this.attributes[WebViewConstants.ATTRIBUTE_AUTOSIZE] =
new AutosizeAttribute(this);
this.attributes[WebViewConstants.ATTRIBUTE_NAME] =
new NameAttribute(this);
this.attributes[WebViewConstants.ATTRIBUTE_PARTITION] =
new PartitionAttribute(this);
this.attributes[WebViewConstants.ATTRIBUTE_SRC] =
new SrcAttribute(this);
var autosizeAttributes = [WebViewConstants.ATTRIBUTE_MAXHEIGHT,
WebViewConstants.ATTRIBUTE_MAXWIDTH,
WebViewConstants.ATTRIBUTE_MINHEIGHT,
WebViewConstants.ATTRIBUTE_MINWIDTH];
for (var i = 0; autosizeAttributes[i]; ++i) {
this.attributes[autosizeAttributes[i]] =
new AutosizeDimensionAttribute(autosizeAttributes[i], this);
}
};