| Index: chrome/renderer/resources/extensions/web_view.js
|
| diff --git a/chrome/renderer/resources/extensions/web_view.js b/chrome/renderer/resources/extensions/web_view.js
|
| index c0f4324b4438bcc8a61ba3641f14ae66ecfbf3e0..3cdf959fe4abdbde2a81b720c414875a8949b13e 100644
|
| --- a/chrome/renderer/resources/extensions/web_view.js
|
| +++ b/chrome/renderer/resources/extensions/web_view.js
|
| @@ -47,13 +47,14 @@ var WEB_VIEW_EXT_EVENTS = {
|
| fields: ['url', 'isTopLevel', 'reason']
|
| },
|
| 'loadcommit': {
|
| - customHandler: function(webview, event) {
|
| + customHandler: function(webview, event, webviewEvent) {
|
| webview.currentEntryIndex_ = event.currentEntryIndex;
|
| webview.entryCount_ = event.entryCount;
|
| webview.processId_ = event.processId;
|
| if (event.isTopLevel) {
|
| webview.browserPluginNode_.setAttribute('src', event.url);
|
| }
|
| + webview.webviewNode_.dispatchEvent(webviewEvent);
|
| },
|
| evt: createEvent('webview.onLoadCommit'),
|
| fields: ['url', 'isTopLevel']
|
| @@ -70,6 +71,34 @@ var WEB_VIEW_EXT_EVENTS = {
|
| evt: createEvent('webview.onLoadStop'),
|
| fields: []
|
| },
|
| + 'newwindow': {
|
| + cancelable: true,
|
| + customHandler: function(webview, event, webviewEvent) {
|
| + webview.setupExtNewWindowEvent_(event, webviewEvent);
|
| + },
|
| + evt: createEvent('webview.onNewWindow'),
|
| + fields: [
|
| + 'initialHeight',
|
| + 'initialWidth',
|
| + 'targetUrl',
|
| + 'windowOpenDisposition',
|
| + 'name'
|
| + ]
|
| + },
|
| + 'permissionrequest': {
|
| + cancelable: true,
|
| + customHandler: function(webview, event, webviewEvent) {
|
| + webview.setupExtPermissionEvent_(event, webviewEvent);
|
| + },
|
| + evt: createEvent('webview.onPermissionRequest'),
|
| + fields: [
|
| + 'lastUnlockedBySelf',
|
| + 'permission',
|
| + 'requestMethod',
|
| + 'url',
|
| + 'userGesture'
|
| + ]
|
| + },
|
| 'responsive': {
|
| evt: createEvent('webview.onResponsive'),
|
| fields: ['processId']
|
| @@ -343,6 +372,17 @@ WebView.prototype.handleBrowserPluginAttributeMutation_ = function(mutation) {
|
| /**
|
| * @private
|
| */
|
| +WebView.prototype.getWebviewExtEvents_ = function() {
|
| + var experimentalExtEvents = this.maybeGetWebviewExperimentalExtEvents_();
|
| + for (var eventName in experimentalExtEvents) {
|
| + WEB_VIEW_EXT_EVENTS[eventName] = experimentalExtEvents[eventName];
|
| + }
|
| + return WEB_VIEW_EXT_EVENTS;
|
| +};
|
| +
|
| +/**
|
| + * @private
|
| + */
|
| WebView.prototype.setupWebviewNodeEvents_ = function() {
|
| var self = this;
|
| this.viewInstanceId_ = ++webViewInstanceIdCounter;
|
| @@ -355,8 +395,9 @@ WebView.prototype.setupWebviewNodeEvents_ = function() {
|
| };
|
| self.browserPluginNode_['-internal-attach'](params);
|
|
|
| - for (var eventName in WEB_VIEW_EXT_EVENTS) {
|
| - self.setupExtEvent_(eventName, WEB_VIEW_EXT_EVENTS[eventName]);
|
| + var extEvents = self.getWebviewExtEvents_();
|
| + for (var eventName in extEvents) {
|
| + self.setupExtEvent_(eventName, extEvents[eventName]);
|
| }
|
| };
|
| this.browserPluginNode_.addEventListener('-internal-instanceid-allocated',
|
| @@ -365,8 +406,6 @@ WebView.prototype.setupWebviewNodeEvents_ = function() {
|
| for (var eventName in WEB_VIEW_EVENTS) {
|
| this.setupEvent_(eventName, WEB_VIEW_EVENTS[eventName]);
|
| }
|
| - this.setupNewWindowEvent_();
|
| - this.setupPermissionEvent_();
|
| };
|
|
|
| /**
|
| @@ -376,12 +415,18 @@ WebView.prototype.setupExtEvent_ = function(eventName, eventInfo) {
|
| var self = this;
|
| var webviewNode = this.webviewNode_;
|
| eventInfo.evt.addListener(function(event) {
|
| - var webviewEvent = new Event(eventName, {bubbles: true});
|
| + var details = {bubbles:true};
|
| + if (eventInfo.cancelable)
|
| + details.cancelable = true;
|
| + var webviewEvent = new Event(eventName, details);
|
| $Array.forEach(eventInfo.fields, function(field) {
|
| - webviewEvent[field] = event[field];
|
| + if (event[field] !== undefined) {
|
| + webviewEvent[field] = event[field];
|
| + }
|
| });
|
| if (eventInfo.customHandler) {
|
| - eventInfo.customHandler(self, event);
|
| + eventInfo.customHandler(self, event, webviewEvent);
|
| + return;
|
| }
|
| webviewNode.dispatchEvent(webviewEvent);
|
| }, {instanceId: self.instanceId_});
|
| @@ -406,7 +451,7 @@ WebView.prototype.setupEvent_ = function(eventName, attribs) {
|
| /**
|
| * @private
|
| */
|
| -WebView.prototype.setupNewWindowEvent_ = function() {
|
| +WebView.prototype.setupExtNewWindowEvent_ = function(event, webviewEvent) {
|
| var ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN = '<webview>: ' +
|
| 'An action has already been taken for this "newwindow" event.';
|
|
|
| @@ -420,98 +465,85 @@ WebView.prototype.setupNewWindowEvent_ = function() {
|
| console.warn(WARNING_MSG_NEWWINDOW_BLOCKED);
|
| };
|
|
|
| - var NEW_WINDOW_EVENT_ATTRIBUTES = [
|
| - 'initialHeight',
|
| - 'initialWidth',
|
| - 'targetUrl',
|
| - 'windowOpenDisposition',
|
| - 'name'
|
| - ];
|
| -
|
| var self = this;
|
| - var node = this.webviewNode_;
|
| var browserPluginNode = this.browserPluginNode_;
|
| + var webviewNode = this.webviewNode_;
|
| +
|
| + var requestId = event.requestId;
|
| + var actionTaken = false;
|
|
|
| var onTrackedObjectGone = function(requestId, e) {
|
| var detail = e.detail ? JSON.parse(e.detail) : {};
|
| if (detail.id != requestId) {
|
| return;
|
| }
|
| - // If the request was pending then show a warning indiciating that a dialog
|
| - // was blocked.
|
| - if (browserPluginNode['-internal-setPermission'](requestId, false, '')) {
|
| - showWarningMessage();
|
| - }
|
| - };
|
|
|
| - browserPluginNode.addEventListener('-internal-newwindow', function(e) {
|
| - var evt = new Event('newwindow', { bubbles: true, cancelable: true });
|
| - var detail = e.detail ? JSON.parse(e.detail) : {};
|
| -
|
| - $Array.forEach(NEW_WINDOW_EVENT_ATTRIBUTES, function(attribName) {
|
| - evt[attribName] = detail[attribName];
|
| - });
|
| - var requestId = detail.requestId;
|
| - var actionTaken = false;
|
| + // Avoid showing a warning message if the decision has already been made.
|
| + if (actionTaken) {
|
| + return;
|
| + }
|
|
|
| - var validateCall = function () {
|
| - if (actionTaken) {
|
| - throw new Error(ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN);
|
| - }
|
| - actionTaken = true;
|
| - };
|
| + chrome.webview.setPermission(self.instanceId_, requestId, false, '');
|
| + showWarningMessage();
|
| + };
|
|
|
| - var window = {
|
| - attach: function(webview) {
|
| - validateCall();
|
| - if (!webview)
|
| - throw new Error(ERROR_MSG_WEBVIEW_EXPECTED);
|
| - // Attach happens asynchronously to give the tagWatcher an opportunity
|
| - // to pick up the new webview before attach operates on it, if it hasn't
|
| - // been attached to the DOM already.
|
| - // Note: Any subsequent errors cannot be exceptions because they happen
|
| - // asynchronously.
|
| - setTimeout(function() {
|
| - var attached =
|
| - browserPluginNode['-internal-attachWindowTo'](webview,
|
| - detail.windowId);
|
| - if (!attached) {
|
| - console.error(ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH);
|
| - }
|
| - // If the object being passed into attach is not a valid <webview>
|
| - // then we will fail and it will be treated as if the new window
|
| - // was rejected. The permission API plumbing is used here to clean
|
| - // up the state created for the new window if attaching fails.
|
| - browserPluginNode['-internal-setPermission'](requestId, attached, '');
|
| - }, 0);
|
| - },
|
| - discard: function() {
|
| - validateCall();
|
| - browserPluginNode['-internal-setPermission'](requestId, false, '');
|
| - }
|
| - };
|
| - evt.window = window;
|
|
|
| - var defaultPrevented = !node.dispatchEvent(evt);
|
| + var validateCall = function () {
|
| if (actionTaken) {
|
| - return;
|
| + throw new Error(ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN);
|
| }
|
| + actionTaken = true;
|
| + };
|
|
|
| - if (defaultPrevented) {
|
| - // Make browser plugin track lifetime of |window|.
|
| - var onTrackedObjectGoneWithRequestId =
|
| - $Function.bind(onTrackedObjectGone, self, requestId);
|
| - browserPluginNode.addEventListener('-internal-trackedobjectgone',
|
| - onTrackedObjectGoneWithRequestId);
|
| - browserPluginNode['-internal-trackObjectLifetime'](window, requestId);
|
| - } else {
|
| - actionTaken = true;
|
| - // The default action is to discard the window.
|
| - browserPluginNode['-internal-setPermission'](requestId, false, '');
|
| - showWarningMessage();
|
| + var window = {
|
| + attach: function(webview) {
|
| + validateCall();
|
| + if (!webview)
|
| + throw new Error(ERROR_MSG_WEBVIEW_EXPECTED);
|
| + // Attach happens asynchronously to give the tagWatcher an opportunity
|
| + // to pick up the new webview before attach operates on it, if it hasn't
|
| + // been attached to the DOM already.
|
| + // Note: Any subsequent errors cannot be exceptions because they happen
|
| + // asynchronously.
|
| + setTimeout(function() {
|
| + var attached =
|
| + browserPluginNode['-internal-attachWindowTo'](webview,
|
| + event.windowId);
|
| + if (!attached) {
|
| + console.error(ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH);
|
| + }
|
| + // If the object being passed into attach is not a valid <webview>
|
| + // then we will fail and it will be treated as if the new window
|
| + // was rejected. The permission API plumbing is used here to clean
|
| + // up the state created for the new window if attaching fails.
|
| + chrome.webview.setPermission(self.instanceId_, requestId, attached, '');
|
| + }, 0);
|
| + },
|
| + discard: function() {
|
| + validateCall();
|
| + chrome.webview.setPermission(self.instanceId_, requestId, false, '');
|
| }
|
| + };
|
| + webviewEvent.window = window;
|
|
|
| - });
|
| + var defaultPrevented = !webviewNode.dispatchEvent(webviewEvent);
|
| + if (actionTaken) {
|
| + return;
|
| + }
|
| +
|
| + if (defaultPrevented) {
|
| + // Make browser plugin track lifetime of |window|.
|
| + var onTrackedObjectGoneWithRequestId =
|
| + $Function.bind(onTrackedObjectGone, self, requestId);
|
| + browserPluginNode.addEventListener('-internal-trackedobjectgone',
|
| + onTrackedObjectGoneWithRequestId);
|
| + browserPluginNode['-internal-trackObjectLifetime'](window, requestId);
|
| + } else {
|
| + actionTaken = true;
|
| + // The default action is to discard the window.
|
| + chrome.webview.setPermission(self.instanceId_, requestId, false, '');
|
| + showWarningMessage();
|
| + }
|
| };
|
|
|
| /**
|
| @@ -543,10 +575,13 @@ WebView.prototype.setupExecuteCodeAPI_ = function() {
|
| };
|
|
|
| /**
|
| - * @param {!Object} detail The event details, originated from <object>.
|
| * @private
|
| */
|
| -WebView.prototype.setupPermissionEvent_ = function() {
|
| +WebView.prototype.getPermissionTypes_ = function() {
|
| + return ['media', 'geolocation', 'pointerLock', 'download'];
|
| +};
|
| +
|
| +WebView.prototype.setupExtPermissionEvent_ = function(event, webviewEvent) {
|
| var ERROR_MSG_PERMISSION_ALREADY_DECIDED = '<webview>: ' +
|
| 'Permission has already been decided for this "permissionrequest" event.';
|
|
|
| @@ -556,89 +591,68 @@ WebView.prototype.setupPermissionEvent_ = function() {
|
| console.warn(WARNING_MSG_PERMISSION_DENIED.replace('%1', permission));
|
| };
|
|
|
| - var PERMISSION_TYPES = ['media', 'geolocation', 'pointerLock', 'download'];
|
| -
|
| - var EXPOSED_PERMISSION_EVENT_ATTRIBS = [
|
| - 'lastUnlockedBySelf',
|
| - 'permission',
|
| - 'requestMethod',
|
| - 'url',
|
| - 'userGesture'
|
| - ];
|
| + var PERMISSION_TYPES = this.getPermissionTypes_();
|
|
|
| var self = this;
|
| - var node = this.webviewNode_;
|
| var browserPluginNode = this.browserPluginNode_;
|
| - var internalevent = '-internal-permissionrequest';
|
| + var webviewNode = this.webviewNode_;
|
| +
|
| + var requestId = event.requestId;
|
| + var decisionMade = false;
|
|
|
| var onTrackedObjectGone = function(requestId, permission, e) {
|
| var detail = e.detail ? JSON.parse(e.detail) : {};
|
| - if (detail.id != requestId)
|
| + if (detail.id != requestId) {
|
| return;
|
| - // If the request was pending then show a warning indiciating that the
|
| - // permission was denied.
|
| - if (browserPluginNode['-internal-setPermission'](requestId, false, '')) {
|
| - showWarningMessage(permission);
|
| }
|
| - };
|
|
|
| - browserPluginNode.addEventListener(internalevent, function(e) {
|
| - var evt = new Event('permissionrequest', {bubbles: true, cancelable: true});
|
| - var detail = e.detail ? JSON.parse(e.detail) : {};
|
| - $Array.forEach(EXPOSED_PERMISSION_EVENT_ATTRIBS, function(attribName) {
|
| - if (detail[attribName] !== undefined)
|
| - evt[attribName] = detail[attribName];
|
| - });
|
| - var requestId = detail.requestId;
|
| -
|
| - if (detail.requestId == undefined ||
|
| - PERMISSION_TYPES.indexOf(detail.permission) < 0) {
|
| + // Avoid showing a warning message if the decision has already been made.
|
| + if (decisionMade) {
|
| return;
|
| }
|
|
|
| - // TODO(lazyboy): Also fill in evt.details (see webview specs).
|
| - // http://crbug.com/141197.
|
| - var decisionMade = false;
|
| -
|
| - var validateCall = function() {
|
| - if (decisionMade) {
|
| - throw new Error(ERROR_MSG_PERMISSION_ALREADY_DECIDED);
|
| - }
|
| - decisionMade = true;
|
| - };
|
| -
|
| - // Construct the event.request object.
|
| - var request = {
|
| - allow: function() {
|
| - validateCall();
|
| - browserPluginNode['-internal-setPermission'](requestId, true, '');
|
| - },
|
| - deny: function() {
|
| - validateCall();
|
| - browserPluginNode['-internal-setPermission'](requestId, false, '');
|
| - }
|
| - };
|
| - evt.request = request;
|
| + chrome.webview.setPermission(self.instanceId_, requestId, false, '');
|
| + showWarningMessage(permission);
|
| + };
|
|
|
| - var defaultPrevented = !node.dispatchEvent(evt);
|
| + var validateCall = function() {
|
| if (decisionMade) {
|
| - return;
|
| + throw new Error(ERROR_MSG_PERMISSION_ALREADY_DECIDED);
|
| }
|
| + decisionMade = true;
|
| + };
|
|
|
| - if (defaultPrevented) {
|
| - // Make browser plugin track lifetime of |request|.
|
| - var onTrackedObjectGoneWithRequestId =
|
| - $Function.bind(
|
| - onTrackedObjectGone, self, requestId, detail.permission);
|
| - browserPluginNode.addEventListener('-internal-trackedobjectgone',
|
| - onTrackedObjectGoneWithRequestId);
|
| - browserPluginNode['-internal-trackObjectLifetime'](request, requestId);
|
| - } else {
|
| - decisionMade = true;
|
| - browserPluginNode['-internal-setPermission'](requestId, false, '');
|
| - showWarningMessage(detail.permission);
|
| + // Construct the event.request object.
|
| + var request = {
|
| + allow: function() {
|
| + validateCall();
|
| + chrome.webview.setPermission(self.instanceId_, requestId, true, '');
|
| + },
|
| + deny: function() {
|
| + validateCall();
|
| + chrome.webview.setPermission(self.instanceId_, requestId, false, '');
|
| }
|
| - });
|
| + };
|
| + webviewEvent.request = request;
|
| +
|
| + var defaultPrevented = !webviewNode.dispatchEvent(webviewEvent);
|
| + if (decisionMade) {
|
| + return;
|
| + }
|
| +
|
| + if (defaultPrevented) {
|
| + // Make browser plugin track lifetime of |request|.
|
| + var onTrackedObjectGoneWithRequestId =
|
| + $Function.bind(
|
| + onTrackedObjectGone, self, requestId, event.permission);
|
| + browserPluginNode.addEventListener('-internal-trackedobjectgone',
|
| + onTrackedObjectGoneWithRequestId);
|
| + browserPluginNode['-internal-trackObjectLifetime'](request, requestId);
|
| + } else {
|
| + decisionMade = true;
|
| + chrome.webview.setPermission(self.instanceId_, requestId, false, '');
|
| + showWarningMessage(event.permission);
|
| + }
|
| };
|
|
|
| /**
|
| @@ -647,4 +661,17 @@ WebView.prototype.setupPermissionEvent_ = function() {
|
| */
|
| WebView.prototype.maybeSetupExperimentalAPI_ = function() {};
|
|
|
| +/**
|
| + * Implemented when the experimental API is available.
|
| + * @private
|
| + */
|
| +WebView.prototype.maybeSetupExtDialogEvent_ = function() {};
|
| +
|
| +/**
|
| + * Implemented when the experimental API is available.
|
| + * @private
|
| + */
|
| +WebView.prototype.maybeGetWebviewExperimentalExtEvents_ = function() {};
|
| +
|
| exports.WebView = WebView;
|
| +exports.CreateEvent = createEvent;
|
|
|