| // Copyright 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. |
| |
| // Event management for WebView. |
| |
| var CreateEvent = require('guestViewEvents').CreateEvent; |
| var DCHECK = requireNative('logging').DCHECK; |
| var DeclarativeWebRequestSchema = |
| requireNative('schema_registry').GetSchema('declarativeWebRequest'); |
| var GuestViewEvents = require('guestViewEvents').GuestViewEvents; |
| var GuestViewInternalNatives = requireNative('guest_view_internal'); |
| var IdGenerator = requireNative('id_generator'); |
| var WebRequestEvent = require('webRequestEvent').WebRequestEvent; |
| var WebRequestSchema = |
| requireNative('schema_registry').GetSchema('webRequest'); |
| var WebViewActionRequests = |
| require('webViewActionRequests').WebViewActionRequests; |
| |
| var WebRequestMessageEvent = CreateEvent('webViewInternal.onMessage'); |
| |
| function WebViewEvents(webViewImpl) { |
| $Function.call(GuestViewEvents, this, webViewImpl); |
| |
| this.setupWebRequestEvents(); |
| this.view.maybeSetupContextMenus(); |
| } |
| |
| var jsEvent; |
| function createCustomDeclarativeEvent(name, schema, options, webviewId) { |
| if (bindingUtil) { |
| return bindingUtil.createCustomDeclarativeEvent( |
| name, options.actions, options.conditions, webviewId || 0); |
| } |
| if (!jsEvent) |
| jsEvent = require('event_bindings').Event; |
| return new jsEvent(name, schema, options, webviewId); |
| } |
| |
| function createCustomEvent(name, schema, options) { |
| var supportsLazyListeners = false; |
| if (bindingUtil) { |
| return bindingUtil.createCustomEvent(name, undefined, false, |
| supportsLazyListeners); |
| } |
| if (!jsEvent) |
| jsEvent = require('event_bindings').Event; |
| |
| if (!options) |
| options = {__proto__: null, supportsLazyListeners: false}; |
| DCHECK(!options.supportsLazyListeners); |
| return new jsEvent(name, schema, options); |
| } |
| |
| function createOnMessageEvent(name, schema, options, webviewId) { |
| var subEventName = name + '/' + IdGenerator.GetNextId(); |
| var newEvent = createCustomEvent(subEventName, schema, options); |
| |
| var view = GuestViewInternalNatives.GetViewFromID(webviewId || 0); |
| if (view) { |
| view.events.addScopedListener( |
| WebRequestMessageEvent, |
| $Function.bind(function() { |
| // Re-dispatch to subEvent's listeners. |
| $Function.apply(newEvent.dispatch, newEvent, $Array.slice(arguments)); |
| }, newEvent), |
| {instanceId: webviewId || 0}); |
| } |
| |
| return newEvent; |
| } |
| |
| WebViewEvents.prototype.__proto__ = GuestViewEvents.prototype; |
| |
| // A dictionary of <webview> extension events to be listened for. This |
| // dictionary augments |GuestViewEvents.EVENTS| in guest_view_events.js. See the |
| // documentation there for details. |
| WebViewEvents.EVENTS = { |
| 'audiostatechanged': { |
| evt: CreateEvent('webViewInternal.onAudioStateChanged'), |
| fields: ['audible'] |
| }, |
| 'close': { |
| evt: CreateEvent('webViewInternal.onClose') |
| }, |
| 'consolemessage': { |
| evt: CreateEvent('webViewInternal.onConsoleMessage'), |
| fields: ['level', 'message', 'line', 'sourceId'] |
| }, |
| 'contentload': { |
| evt: CreateEvent('webViewInternal.onContentLoad') |
| }, |
| 'dialog': { |
| cancelable: true, |
| evt: CreateEvent('webViewInternal.onDialog'), |
| fields: ['defaultPromptText', 'messageText', 'messageType', 'url'], |
| handler: 'handleDialogEvent' |
| }, |
| 'droplink': { |
| evt: CreateEvent('webViewInternal.onDropLink'), |
| fields: ['url'] |
| }, |
| 'exit': { |
| evt: CreateEvent('webViewInternal.onExit'), |
| fields: ['processId', 'reason'] |
| }, |
| 'exitfullscreen': { |
| evt: CreateEvent('webViewInternal.onExitFullscreen'), |
| fields: ['url'], |
| handler: 'handleFullscreenExitEvent', |
| internal: true |
| }, |
| 'findupdate': { |
| evt: CreateEvent('webViewInternal.onFindReply'), |
| fields: [ |
| 'searchText', |
| 'numberOfMatches', |
| 'activeMatchOrdinal', |
| 'selectionRect', |
| 'canceled', |
| 'finalUpdate' |
| ] |
| }, |
| 'framenamechanged': { |
| evt: CreateEvent('webViewInternal.onFrameNameChanged'), |
| handler: 'handleFrameNameChangedEvent', |
| internal: true |
| }, |
| 'loadabort': { |
| cancelable: true, |
| evt: CreateEvent('webViewInternal.onLoadAbort'), |
| fields: ['url', 'isTopLevel', 'code', 'reason'], |
| handler: 'handleLoadAbortEvent' |
| }, |
| 'loadcommit': { |
| evt: CreateEvent('webViewInternal.onLoadCommit'), |
| fields: ['url', 'isTopLevel'], |
| handler: 'handleLoadCommitEvent' |
| }, |
| 'loadprogress': { |
| evt: CreateEvent('webViewInternal.onLoadProgress'), |
| fields: ['url', 'progress'] |
| }, |
| 'loadredirect': { |
| evt: CreateEvent('webViewInternal.onLoadRedirect'), |
| fields: ['isTopLevel', 'oldUrl', 'newUrl'] |
| }, |
| 'loadstart': { |
| evt: CreateEvent('webViewInternal.onLoadStart'), |
| fields: ['url', 'isTopLevel'] |
| }, |
| 'loadstop': { |
| evt: CreateEvent('webViewInternal.onLoadStop') |
| }, |
| 'newwindow': { |
| cancelable: true, |
| evt: CreateEvent('webViewInternal.onNewWindow'), |
| fields: [ |
| 'initialHeight', |
| 'initialWidth', |
| 'targetUrl', |
| 'windowOpenDisposition', |
| 'name' |
| ], |
| handler: 'handleNewWindowEvent' |
| }, |
| 'permissionrequest': { |
| cancelable: true, |
| evt: CreateEvent('webViewInternal.onPermissionRequest'), |
| fields: [ |
| 'identifier', |
| 'lastUnlockedBySelf', |
| 'name', |
| 'permission', |
| 'requestMethod', |
| 'url', |
| 'userGesture' |
| ], |
| handler: 'handlePermissionEvent' |
| }, |
| 'responsive': { |
| evt: CreateEvent('webViewInternal.onResponsive'), |
| fields: ['processId'] |
| }, |
| 'sizechanged': { |
| evt: CreateEvent('webViewInternal.onSizeChanged'), |
| fields: ['oldHeight', 'oldWidth', 'newHeight', 'newWidth'], |
| handler: 'handleSizeChangedEvent' |
| }, |
| 'unresponsive': { |
| evt: CreateEvent('webViewInternal.onUnresponsive'), |
| fields: ['processId'] |
| }, |
| 'zoomchange': { |
| evt: CreateEvent('webViewInternal.onZoomChange'), |
| fields: ['oldZoomFactor', 'newZoomFactor'] |
| } |
| }; |
| |
| WebViewEvents.prototype.setupWebRequestEvents = function() { |
| var request = {}; |
| var createWebRequestEvent = $Function.bind(function(webRequestEvent) { |
| return this.weakWrapper(function() { |
| if (!this[webRequestEvent.name]) { |
| this[webRequestEvent.name] = |
| new WebRequestEvent( |
| 'webViewInternal.' + webRequestEvent.name, |
| webRequestEvent.parameters, |
| webRequestEvent.extraParameters, webRequestEvent.options, |
| this.view.viewInstanceId); |
| } |
| return this[webRequestEvent.name]; |
| }); |
| }, this); |
| |
| var createDeclarativeWebRequestEvent = |
| $Function.bind(function(webRequestEvent) { |
| return this.weakWrapper(function() { |
| if (!this[webRequestEvent.name]) { |
| var newEvent; |
| var eventName = |
| 'webViewInternal.declarativeWebRequest.' + webRequestEvent.name; |
| if (webRequestEvent.name === 'onMessage') { |
| // The onMessage event gets a special event type because we want the |
| // listener to fire only for messages targeted for this particular |
| // <webview>. |
| newEvent = createOnMessageEvent(eventName, |
| webRequestEvent.parameters, |
| webRequestEvent.options, |
| this.view.viewInstanceId); |
| } else { |
| newEvent = |
| createCustomDeclarativeEvent(eventName, |
| webRequestEvent.parameters, |
| webRequestEvent.options, |
| this.view.viewInstanceId); |
| } |
| this[webRequestEvent.name] = newEvent; |
| } |
| return this[webRequestEvent.name]; |
| }); |
| }, this); |
| |
| for (var i = 0; i < DeclarativeWebRequestSchema.events.length; ++i) { |
| var eventSchema = DeclarativeWebRequestSchema.events[i]; |
| var webRequestEvent = createDeclarativeWebRequestEvent(eventSchema); |
| $Object.defineProperty( |
| request, eventSchema.name, {get: webRequestEvent, enumerable: true}); |
| } |
| |
| // Populate the WebRequest events from the API definition. |
| for (var i = 0; i < WebRequestSchema.events.length; ++i) { |
| var eventSchema = WebRequestSchema.events[i]; |
| |
| // Skip "onActionIgnored" which is not relevant for webviews. |
| if (eventSchema.name === 'onActionIgnored') |
| continue; |
| |
| var webRequestEvent = createWebRequestEvent(eventSchema); |
| $Object.defineProperty( |
| request, eventSchema.name, {get: webRequestEvent, enumerable: true}); |
| } |
| |
| this.view.setRequestPropertyOnWebViewElement(request); |
| }; |
| |
| WebViewEvents.prototype.getEvents = function() { |
| return WebViewEvents.EVENTS; |
| }; |
| |
| WebViewEvents.prototype.handleDialogEvent = function(event, eventName) { |
| var webViewEvent = this.makeDomEvent(event, eventName); |
| new WebViewActionRequests.Dialog(this.view, event, webViewEvent); |
| }; |
| |
| WebViewEvents.prototype.handleFrameNameChangedEvent = function(event) { |
| this.view.onFrameNameChanged(event.name); |
| }; |
| |
| WebViewEvents.prototype.handleFullscreenExitEvent = function(event, eventName) { |
| document.webkitCancelFullScreen(); |
| }; |
| |
| WebViewEvents.prototype.handleLoadAbortEvent = function(event, eventName) { |
| var showWarningMessage = function(code, reason) { |
| var WARNING_MSG_LOAD_ABORTED = '<webview>: ' + |
| 'The load has aborted with error %1: %2.'; |
| window.console.warn($String.replace( |
| $String.replace(WARNING_MSG_LOAD_ABORTED, '%1', code), '%2', reason)); |
| }; |
| var webViewEvent = this.makeDomEvent(event, eventName); |
| if (this.view.dispatchEvent(webViewEvent)) { |
| showWarningMessage(event.code, event.reason); |
| } |
| }; |
| |
| WebViewEvents.prototype.handleLoadCommitEvent = function(event, eventName) { |
| this.view.onLoadCommit(event.baseUrlForDataUrl, |
| event.currentEntryIndex, |
| event.entryCount, |
| event.processId, |
| event.url, |
| event.isTopLevel); |
| var webViewEvent = this.makeDomEvent(event, eventName); |
| this.view.dispatchEvent(webViewEvent); |
| }; |
| |
| WebViewEvents.prototype.handleNewWindowEvent = function(event, eventName) { |
| var webViewEvent = this.makeDomEvent(event, eventName); |
| new WebViewActionRequests.NewWindow(this.view, event, webViewEvent); |
| }; |
| |
| WebViewEvents.prototype.handlePermissionEvent = function(event, eventName) { |
| var webViewEvent = this.makeDomEvent(event, eventName); |
| if (event.permission === 'fullscreen') { |
| new WebViewActionRequests.FullscreenPermissionRequest( |
| this.view, event, webViewEvent); |
| } else { |
| new WebViewActionRequests.PermissionRequest(this.view, event, webViewEvent); |
| } |
| }; |
| |
| WebViewEvents.prototype.handleSizeChangedEvent = function(event, eventName) { |
| var webViewEvent = this.makeDomEvent(event, eventName); |
| this.view.onSizeChanged(webViewEvent); |
| }; |
| |
| // Exports. |
| exports.$set('WebViewEvents', WebViewEvents); |