1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// This module implements chrome-specific <webview> API.
6
7var ChromeWebView = require('chromeWebViewInternal').ChromeWebView;
8var CreateEvent = require('webViewEvents').CreateEvent;
9var DeclarativeWebRequestSchema =
10    requireNative('schema_registry').GetSchema('declarativeWebRequest');
11var EventBindings = require('event_bindings');
12var IdGenerator = requireNative('id_generator');
13var WebRequestEvent = require('webRequestInternal').WebRequestEvent;
14var WebRequestSchema =
15    requireNative('schema_registry').GetSchema('webRequest');
16var WebViewInternal = require('webView').WebViewInternal
17
18var WebRequestMessageEvent = CreateEvent('webViewInternal.onMessage');
19
20var CHROME_WEB_VIEW_EVENTS = {
21  'contextmenu': {
22    evt: CreateEvent('chromeWebViewInternal.contextmenu'),
23    cancelable: true,
24    customHandler: function(handler, event, webViewEvent) {
25      handler.webViewInternal.maybeHandleContextMenu(event, webViewEvent);
26    },
27    fields: ['items']
28  }
29};
30
31function DeclarativeWebRequestEvent(opt_eventName,
32                                    opt_argSchemas,
33                                    opt_eventOptions,
34                                    opt_webViewInstanceId) {
35  var subEventName = opt_eventName + '/' + IdGenerator.GetNextId();
36  EventBindings.Event.call(this, subEventName, opt_argSchemas, opt_eventOptions,
37      opt_webViewInstanceId);
38
39  // TODO(lazyboy): When do we dispose this listener?
40  WebRequestMessageEvent.addListener(function() {
41    // Re-dispatch to subEvent's listeners.
42    $Function.apply(this.dispatch, this, $Array.slice(arguments));
43  }.bind(this), {instanceId: opt_webViewInstanceId || 0});
44}
45
46DeclarativeWebRequestEvent.prototype = {
47  __proto__: EventBindings.Event.prototype
48};
49
50/**
51 * Implemented when the ChromeWebView API is available.
52 * @private
53 */
54WebViewInternal.prototype.maybeGetChromeWebViewEvents = function() {
55  return CHROME_WEB_VIEW_EVENTS;
56};
57
58/**
59 * Calls to show contextmenu right away instead of dispatching a 'contextmenu'
60 * event.
61 * This will be overridden in chrome_web_view_experimental.js to implement
62 * contextmenu  API.
63 */
64WebViewInternal.prototype.maybeHandleContextMenu = function(e, webViewEvent) {
65  var requestId = e.requestId;
66  // Setting |params| = undefined will show the context menu unmodified, hence
67  // the 'contextmenu' API is disabled for stable channel.
68  var params = undefined;
69  ChromeWebView.showContextMenu(this.guestInstanceId, requestId, params);
70};
71
72WebViewInternal.prototype.maybeSetupChromeWebViewEvents = function() {
73  var request = {};
74  var createWebRequestEvent = function(webRequestEvent) {
75    return function() {
76      if (!this[webRequestEvent.name]) {
77        this[webRequestEvent.name] =
78            new WebRequestEvent(
79                'webViewInternal.' + webRequestEvent.name,
80                webRequestEvent.parameters,
81                webRequestEvent.extraParameters, webRequestEvent.options,
82                this.viewInstanceId);
83      }
84      return this[webRequestEvent.name];
85    }.bind(this);
86  }.bind(this);
87
88  var createDeclarativeWebRequestEvent = function(webRequestEvent) {
89    return function() {
90      if (!this[webRequestEvent.name]) {
91        // The onMessage event gets a special event type because we want
92        // the listener to fire only for messages targeted for this particular
93        // <webview>.
94        var EventClass = webRequestEvent.name === 'onMessage' ?
95            DeclarativeWebRequestEvent : EventBindings.Event;
96        this[webRequestEvent.name] =
97            new EventClass(
98                'webViewInternal.' + webRequestEvent.name,
99                webRequestEvent.parameters,
100                webRequestEvent.options,
101                this.viewInstanceId);
102      }
103      return this[webRequestEvent.name];
104    }.bind(this);
105  }.bind(this);
106
107  for (var i = 0; i < DeclarativeWebRequestSchema.events.length; ++i) {
108    var eventSchema = DeclarativeWebRequestSchema.events[i];
109    var webRequestEvent = createDeclarativeWebRequestEvent(eventSchema);
110    Object.defineProperty(
111        request,
112        eventSchema.name,
113        {
114          get: webRequestEvent,
115          enumerable: true
116        }
117    );
118  }
119
120  // Populate the WebRequest events from the API definition.
121  for (var i = 0; i < WebRequestSchema.events.length; ++i) {
122    var webRequestEvent = createWebRequestEvent(WebRequestSchema.events[i]);
123    Object.defineProperty(
124        request,
125        WebRequestSchema.events[i].name,
126        {
127          get: webRequestEvent,
128          enumerable: true
129        }
130    );
131  }
132
133  this.setRequestPropertyOnWebViewNode(request);
134};
135