OLD | NEW |
| (Empty) |
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 helper objects for the dialog, newwindow, and | |
6 // permissionrequest <webview> events. | |
7 | |
8 var MessagingNatives = requireNative('messaging_natives'); | |
9 var WebViewConstants = require('webViewConstants').WebViewConstants; | |
10 var WebViewInternal = require('webViewInternal').WebViewInternal; | |
11 | |
12 var PERMISSION_TYPES = ['media', | |
13 'geolocation', | |
14 'pointerLock', | |
15 'download', | |
16 'loadplugin', | |
17 'filesystem']; | |
18 | |
19 // ----------------------------------------------------------------------------- | |
20 // WebViewActionRequest object. | |
21 | |
22 // Default partial implementation of a webview action request. | |
23 function WebViewActionRequest(webViewImpl, event, webViewEvent, interfaceName) { | |
24 this.webViewImpl = webViewImpl; | |
25 this.event = event; | |
26 this.webViewEvent = webViewEvent; | |
27 this.interfaceName = interfaceName; | |
28 this.guestInstanceId = this.webViewImpl.guest.getId(); | |
29 this.requestId = event.requestId; | |
30 this.actionTaken = false; | |
31 } | |
32 | |
33 // Performs the default action for the request. | |
34 WebViewActionRequest.prototype.defaultAction = function() { | |
35 // Do nothing if the action has already been taken or the requester is | |
36 // already gone (in which case its guestInstanceId will be stale). | |
37 if (this.actionTaken || | |
38 this.guestInstanceId != this.webViewImpl.guest.getId()) { | |
39 return; | |
40 } | |
41 | |
42 this.actionTaken = true; | |
43 WebViewInternal.setPermission(this.guestInstanceId, this.requestId, | |
44 'default', '', function(allowed) { | |
45 if (allowed) { | |
46 return; | |
47 } | |
48 this.showWarningMessage(); | |
49 }.bind(this)); | |
50 }; | |
51 | |
52 // Called to handle the action request's event. | |
53 WebViewActionRequest.prototype.handleActionRequestEvent = function() { | |
54 // Construct the interface object and attach it to |webViewEvent|. | |
55 var request = this.getInterfaceObject(); | |
56 this.webViewEvent[this.interfaceName] = request; | |
57 | |
58 var defaultPrevented = !this.webViewImpl.dispatchEvent(this.webViewEvent); | |
59 // Set |webViewEvent| to null to break the circular reference to |request| so | |
60 // that the garbage collector can eventually collect it. | |
61 this.webViewEvent = null; | |
62 if (this.actionTaken) { | |
63 return; | |
64 } | |
65 | |
66 if (defaultPrevented) { | |
67 // Track the lifetime of |request| with the garbage collector. | |
68 MessagingNatives.BindToGC(request, this.defaultAction.bind(this)); | |
69 } else { | |
70 this.defaultAction(); | |
71 } | |
72 }; | |
73 | |
74 // Displays a warning message when an action request is blocked by default. | |
75 WebViewActionRequest.prototype.showWarningMessage = function() { | |
76 window.console.warn(this.WARNING_MSG_REQUEST_BLOCKED); | |
77 }; | |
78 | |
79 // This function ensures that each action is taken at most once. | |
80 WebViewActionRequest.prototype.validateCall = function() { | |
81 if (this.actionTaken) { | |
82 throw new Error(this.ERROR_MSG_ACTION_ALREADY_TAKEN); | |
83 } | |
84 this.actionTaken = true; | |
85 }; | |
86 | |
87 // The following are implemented by the specific action request. | |
88 | |
89 // Returns the interface object for this action request. | |
90 WebViewActionRequest.prototype.getInterfaceObject = undefined; | |
91 | |
92 // Error/warning messages. | |
93 WebViewActionRequest.prototype.ERROR_MSG_ACTION_ALREADY_TAKEN = undefined; | |
94 WebViewActionRequest.prototype.WARNING_MSG_REQUEST_BLOCKED = undefined; | |
95 | |
96 // ----------------------------------------------------------------------------- | |
97 // Dialog object. | |
98 | |
99 // Represents a dialog box request (e.g. alert()). | |
100 function Dialog(webViewImpl, event, webViewEvent) { | |
101 WebViewActionRequest.call(this, webViewImpl, event, webViewEvent, 'dialog'); | |
102 | |
103 this.handleActionRequestEvent(); | |
104 } | |
105 | |
106 Dialog.prototype.__proto__ = WebViewActionRequest.prototype; | |
107 | |
108 Dialog.prototype.getInterfaceObject = function() { | |
109 return { | |
110 ok: function(user_input) { | |
111 this.validateCall(); | |
112 user_input = user_input || ''; | |
113 WebViewInternal.setPermission( | |
114 this.guestInstanceId, this.requestId, 'allow', user_input); | |
115 }.bind(this), | |
116 cancel: function() { | |
117 this.validateCall(); | |
118 WebViewInternal.setPermission( | |
119 this.guestInstanceId, this.requestId, 'deny'); | |
120 }.bind(this) | |
121 }; | |
122 }; | |
123 | |
124 Dialog.prototype.showWarningMessage = function() { | |
125 var VOWELS = ['a', 'e', 'i', 'o', 'u']; | |
126 var dialogType = this.event.messageType; | |
127 var article = (VOWELS.indexOf(dialogType.charAt(0)) >= 0) ? 'An' : 'A'; | |
128 this.WARNING_MSG_REQUEST_BLOCKED = this.WARNING_MSG_REQUEST_BLOCKED. | |
129 replace('%1', article).replace('%2', dialogType); | |
130 window.console.warn(this.WARNING_MSG_REQUEST_BLOCKED); | |
131 }; | |
132 | |
133 Dialog.prototype.ERROR_MSG_ACTION_ALREADY_TAKEN = | |
134 WebViewConstants.ERROR_MSG_DIALOG_ACTION_ALREADY_TAKEN; | |
135 Dialog.prototype.WARNING_MSG_REQUEST_BLOCKED = | |
136 WebViewConstants.WARNING_MSG_DIALOG_REQUEST_BLOCKED; | |
137 | |
138 // ----------------------------------------------------------------------------- | |
139 // NewWindow object. | |
140 | |
141 // Represents a new window request. | |
142 function NewWindow(webViewImpl, event, webViewEvent) { | |
143 WebViewActionRequest.call(this, webViewImpl, event, webViewEvent, 'window'); | |
144 | |
145 this.handleActionRequestEvent(); | |
146 } | |
147 | |
148 NewWindow.prototype.__proto__ = WebViewActionRequest.prototype; | |
149 | |
150 NewWindow.prototype.getInterfaceObject = function() { | |
151 return { | |
152 attach: function(webview) { | |
153 this.validateCall(); | |
154 if (!webview || !webview.tagName || webview.tagName != 'WEBVIEW') { | |
155 throw new Error(ERROR_MSG_WEBVIEW_EXPECTED); | |
156 } | |
157 | |
158 var webViewImpl = privates(webview).internal; | |
159 // Update the partition. | |
160 if (this.event.partition) { | |
161 webViewImpl.onAttach(this.event.partition); | |
162 } | |
163 | |
164 var attached = webViewImpl.attachWindow(this.event.windowId); | |
165 if (!attached) { | |
166 window.console.error(ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH); | |
167 } | |
168 | |
169 if (this.guestInstanceId != this.webViewImpl.guest.getId()) { | |
170 // If the opener is already gone, then its guestInstanceId will be | |
171 // stale. | |
172 return; | |
173 } | |
174 | |
175 // If the object being passed into attach is not a valid <webview> | |
176 // then we will fail and it will be treated as if the new window | |
177 // was rejected. The permission API plumbing is used here to clean | |
178 // up the state created for the new window if attaching fails. | |
179 WebViewInternal.setPermission(this.guestInstanceId, this.requestId, | |
180 attached ? 'allow' : 'deny'); | |
181 }.bind(this), | |
182 discard: function() { | |
183 this.validateCall(); | |
184 if (!this.guestInstanceId) { | |
185 // If the opener is already gone, then we won't have its | |
186 // guestInstanceId. | |
187 return; | |
188 } | |
189 WebViewInternal.setPermission( | |
190 this.guestInstanceId, this.requestId, 'deny'); | |
191 }.bind(this) | |
192 }; | |
193 }; | |
194 | |
195 NewWindow.prototype.ERROR_MSG_ACTION_ALREADY_TAKEN = | |
196 WebViewConstants.ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN; | |
197 NewWindow.prototype.WARNING_MSG_REQUEST_BLOCKED = | |
198 WebViewConstants.WARNING_MSG_NEWWINDOW_REQUEST_BLOCKED; | |
199 | |
200 // ----------------------------------------------------------------------------- | |
201 // PermissionRequest object. | |
202 | |
203 // Represents a permission request (e.g. to access the filesystem). | |
204 function PermissionRequest(webViewImpl, event, webViewEvent) { | |
205 WebViewActionRequest.call(this, webViewImpl, event, webViewEvent, 'request'); | |
206 | |
207 if (!this.validPermissionCheck()) { | |
208 return; | |
209 } | |
210 | |
211 this.handleActionRequestEvent(); | |
212 } | |
213 | |
214 PermissionRequest.prototype.__proto__ = WebViewActionRequest.prototype; | |
215 | |
216 PermissionRequest.prototype.getInterfaceObject = function() { | |
217 return { | |
218 allow: function() { | |
219 this.validateCall(); | |
220 WebViewInternal.setPermission( | |
221 this.guestInstanceId, this.requestId, 'allow'); | |
222 }.bind(this), | |
223 deny: function() { | |
224 this.validateCall(); | |
225 WebViewInternal.setPermission( | |
226 this.guestInstanceId, this.requestId, 'deny'); | |
227 }.bind(this) | |
228 }; | |
229 }; | |
230 | |
231 PermissionRequest.prototype.showWarningMessage = function() { | |
232 window.console.warn( | |
233 this.WARNING_MSG_REQUEST_BLOCKED.replace('%1', this.event.permission)); | |
234 }; | |
235 | |
236 // Checks that the requested permission is valid. Returns true if valid. | |
237 PermissionRequest.prototype.validPermissionCheck = function() { | |
238 if (PERMISSION_TYPES.indexOf(this.event.permission) < 0) { | |
239 // The permission type is not allowed. Trigger the default response. | |
240 this.defaultAction(); | |
241 return false; | |
242 } | |
243 return true; | |
244 }; | |
245 | |
246 PermissionRequest.prototype.ERROR_MSG_ACTION_ALREADY_TAKEN = | |
247 WebViewConstants.ERROR_MSG_PERMISSION_ACTION_ALREADY_TAKEN; | |
248 PermissionRequest.prototype.WARNING_MSG_REQUEST_BLOCKED = | |
249 WebViewConstants.WARNING_MSG_PERMISSION_REQUEST_BLOCKED; | |
250 | |
251 // ----------------------------------------------------------------------------- | |
252 | |
253 var WebViewActionRequests = { | |
254 WebViewActionRequest: WebViewActionRequest, | |
255 Dialog: Dialog, | |
256 NewWindow: NewWindow, | |
257 PermissionRequest: PermissionRequest | |
258 }; | |
259 | |
260 exports.WebViewActionRequests = WebViewActionRequests; | |
OLD | NEW |