| 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 // Event management for WebViewInternal. | |
| 6 | |
| 7 var DeclarativeWebRequestSchema = | |
| 8 requireNative('schema_registry').GetSchema('declarativeWebRequest'); | |
| 9 var EventBindings = require('event_bindings'); | |
| 10 var IdGenerator = requireNative('id_generator'); | |
| 11 var MessagingNatives = requireNative('messaging_natives'); | |
| 12 var WebRequestEvent = require('webRequestInternal').WebRequestEvent; | |
| 13 var WebRequestSchema = | |
| 14 requireNative('schema_registry').GetSchema('webRequest'); | |
| 15 var WebView = require('webViewInternal').WebView; | |
| 16 | |
| 17 var CreateEvent = function(name) { | |
| 18 var eventOpts = {supportsListeners: true, supportsFilters: true}; | |
| 19 return new EventBindings.Event(name, undefined, eventOpts); | |
| 20 }; | |
| 21 | |
| 22 var FrameNameChangedEvent = CreateEvent('webViewInternal.onFrameNameChanged'); | |
| 23 var PluginDestroyedEvent = CreateEvent('webViewInternal.onPluginDestroyed'); | |
| 24 var WebRequestMessageEvent = CreateEvent('webViewInternal.onMessage'); | |
| 25 | |
| 26 // WEB_VIEW_EVENTS is a map of stable <webview> DOM event names to their | |
| 27 // associated extension event descriptor objects. | |
| 28 // An event listener will be attached to the extension event |evt| specified in | |
| 29 // the descriptor. | |
| 30 // |fields| specifies the public-facing fields in the DOM event that are | |
| 31 // accessible to <webview> developers. | |
| 32 // |customHandler| allows a handler function to be called each time an extension | |
| 33 // event is caught by its event listener. The DOM event should be dispatched | |
| 34 // within this handler function. With no handler function, the DOM event | |
| 35 // will be dispatched by default each time the extension event is caught. | |
| 36 // |cancelable| (default: false) specifies whether the event's default | |
| 37 // behavior can be canceled. If the default action associated with the event | |
| 38 // is prevented, then its dispatch function will return false in its event | |
| 39 // handler. The event must have a custom handler for this to be meaningful. | |
| 40 var WEB_VIEW_EVENTS = { | |
| 41 'close': { | |
| 42 evt: CreateEvent('webViewInternal.onClose'), | |
| 43 fields: [] | |
| 44 }, | |
| 45 'consolemessage': { | |
| 46 evt: CreateEvent('webViewInternal.onConsoleMessage'), | |
| 47 fields: ['level', 'message', 'line', 'sourceId'] | |
| 48 }, | |
| 49 'contentload': { | |
| 50 evt: CreateEvent('webViewInternal.onContentLoad'), | |
| 51 fields: [] | |
| 52 }, | |
| 53 'contextmenu': { | |
| 54 evt: CreateEvent('chromeWebViewInternal.contextmenu'), | |
| 55 cancelable: true, | |
| 56 customHandler: function(handler, event, webViewEvent) { | |
| 57 handler.handleContextMenu(event, webViewEvent); | |
| 58 }, | |
| 59 fields: ['items'] | |
| 60 }, | |
| 61 'dialog': { | |
| 62 cancelable: true, | |
| 63 customHandler: function(handler, event, webViewEvent) { | |
| 64 handler.handleDialogEvent(event, webViewEvent); | |
| 65 }, | |
| 66 evt: CreateEvent('webViewInternal.onDialog'), | |
| 67 fields: ['defaultPromptText', 'messageText', 'messageType', 'url'] | |
| 68 }, | |
| 69 'exit': { | |
| 70 evt: CreateEvent('webViewInternal.onExit'), | |
| 71 fields: ['processId', 'reason'] | |
| 72 }, | |
| 73 'findupdate': { | |
| 74 evt: CreateEvent('webViewInternal.onFindReply'), | |
| 75 fields: [ | |
| 76 'searchText', | |
| 77 'numberOfMatches', | |
| 78 'activeMatchOrdinal', | |
| 79 'selectionRect', | |
| 80 'canceled', | |
| 81 'finalUpdate' | |
| 82 ] | |
| 83 }, | |
| 84 'loadabort': { | |
| 85 cancelable: true, | |
| 86 customHandler: function(handler, event, webViewEvent) { | |
| 87 handler.handleLoadAbortEvent(event, webViewEvent); | |
| 88 }, | |
| 89 evt: CreateEvent('webViewInternal.onLoadAbort'), | |
| 90 fields: ['url', 'isTopLevel', 'reason'] | |
| 91 }, | |
| 92 'loadcommit': { | |
| 93 customHandler: function(handler, event, webViewEvent) { | |
| 94 handler.handleLoadCommitEvent(event, webViewEvent); | |
| 95 }, | |
| 96 evt: CreateEvent('webViewInternal.onLoadCommit'), | |
| 97 fields: ['url', 'isTopLevel'] | |
| 98 }, | |
| 99 'loadprogress': { | |
| 100 evt: CreateEvent('webViewInternal.onLoadProgress'), | |
| 101 fields: ['url', 'progress'] | |
| 102 }, | |
| 103 'loadredirect': { | |
| 104 evt: CreateEvent('webViewInternal.onLoadRedirect'), | |
| 105 fields: ['isTopLevel', 'oldUrl', 'newUrl'] | |
| 106 }, | |
| 107 'loadstart': { | |
| 108 evt: CreateEvent('webViewInternal.onLoadStart'), | |
| 109 fields: ['url', 'isTopLevel'] | |
| 110 }, | |
| 111 'loadstop': { | |
| 112 evt: CreateEvent('webViewInternal.onLoadStop'), | |
| 113 fields: [] | |
| 114 }, | |
| 115 'newwindow': { | |
| 116 cancelable: true, | |
| 117 customHandler: function(handler, event, webViewEvent) { | |
| 118 handler.handleNewWindowEvent(event, webViewEvent); | |
| 119 }, | |
| 120 evt: CreateEvent('webViewInternal.onNewWindow'), | |
| 121 fields: [ | |
| 122 'initialHeight', | |
| 123 'initialWidth', | |
| 124 'targetUrl', | |
| 125 'windowOpenDisposition', | |
| 126 'name' | |
| 127 ] | |
| 128 }, | |
| 129 'permissionrequest': { | |
| 130 cancelable: true, | |
| 131 customHandler: function(handler, event, webViewEvent) { | |
| 132 handler.handlePermissionEvent(event, webViewEvent); | |
| 133 }, | |
| 134 evt: CreateEvent('webViewInternal.onPermissionRequest'), | |
| 135 fields: [ | |
| 136 'identifier', | |
| 137 'lastUnlockedBySelf', | |
| 138 'name', | |
| 139 'permission', | |
| 140 'requestMethod', | |
| 141 'url', | |
| 142 'userGesture' | |
| 143 ] | |
| 144 }, | |
| 145 'responsive': { | |
| 146 evt: CreateEvent('webViewInternal.onResponsive'), | |
| 147 fields: ['processId'] | |
| 148 }, | |
| 149 'sizechanged': { | |
| 150 evt: CreateEvent('webViewInternal.onSizeChanged'), | |
| 151 customHandler: function(handler, event, webViewEvent) { | |
| 152 handler.handleSizeChangedEvent(event, webViewEvent); | |
| 153 }, | |
| 154 fields: ['oldHeight', 'oldWidth', 'newHeight', 'newWidth'] | |
| 155 }, | |
| 156 'unresponsive': { | |
| 157 evt: CreateEvent('webViewInternal.onUnresponsive'), | |
| 158 fields: ['processId'] | |
| 159 }, | |
| 160 'zoomchange': { | |
| 161 evt: CreateEvent('webViewInternal.onZoomChange'), | |
| 162 fields: ['oldZoomFactor', 'newZoomFactor'] | |
| 163 } | |
| 164 }; | |
| 165 | |
| 166 function DeclarativeWebRequestEvent(opt_eventName, | |
| 167 opt_argSchemas, | |
| 168 opt_eventOptions, | |
| 169 opt_webViewInstanceId) { | |
| 170 var subEventName = opt_eventName + '/' + IdGenerator.GetNextId(); | |
| 171 EventBindings.Event.call(this, subEventName, opt_argSchemas, opt_eventOptions, | |
| 172 opt_webViewInstanceId); | |
| 173 | |
| 174 // TODO(lazyboy): When do we dispose this listener? | |
| 175 WebRequestMessageEvent.addListener(function() { | |
| 176 // Re-dispatch to subEvent's listeners. | |
| 177 $Function.apply(this.dispatch, this, $Array.slice(arguments)); | |
| 178 }.bind(this), {instanceId: opt_webViewInstanceId || 0}); | |
| 179 } | |
| 180 | |
| 181 DeclarativeWebRequestEvent.prototype = { | |
| 182 __proto__: EventBindings.Event.prototype | |
| 183 }; | |
| 184 | |
| 185 // Constructor. | |
| 186 function WebViewEvents(webViewInternal, viewInstanceId) { | |
| 187 this.webViewInternal = webViewInternal; | |
| 188 this.viewInstanceId = viewInstanceId; | |
| 189 this.setup(); | |
| 190 } | |
| 191 | |
| 192 // Sets up events. | |
| 193 WebViewEvents.prototype.setup = function() { | |
| 194 this.setupFrameNameChangedEvent(); | |
| 195 this.setupPluginDestroyedEvent(); | |
| 196 this.setupWebRequestEvents(); | |
| 197 this.webViewInternal.setupExperimentalContextMenus(); | |
| 198 | |
| 199 var events = this.getEvents(); | |
| 200 for (var eventName in events) { | |
| 201 this.setupEvent(eventName, events[eventName]); | |
| 202 } | |
| 203 }; | |
| 204 | |
| 205 WebViewEvents.prototype.setupFrameNameChangedEvent = function() { | |
| 206 FrameNameChangedEvent.addListener(function(e) { | |
| 207 this.webViewInternal.onFrameNameChanged(e.name); | |
| 208 }.bind(this), {instanceId: this.viewInstanceId}); | |
| 209 }; | |
| 210 | |
| 211 WebViewEvents.prototype.setupPluginDestroyedEvent = function() { | |
| 212 PluginDestroyedEvent.addListener(function(e) { | |
| 213 this.webViewInternal.onPluginDestroyed(); | |
| 214 }.bind(this), {instanceId: this.viewInstanceId}); | |
| 215 }; | |
| 216 | |
| 217 WebViewEvents.prototype.setupWebRequestEvents = function() { | |
| 218 var request = {}; | |
| 219 var createWebRequestEvent = function(webRequestEvent) { | |
| 220 return function() { | |
| 221 if (!this[webRequestEvent.name]) { | |
| 222 this[webRequestEvent.name] = | |
| 223 new WebRequestEvent( | |
| 224 'webViewInternal.' + webRequestEvent.name, | |
| 225 webRequestEvent.parameters, | |
| 226 webRequestEvent.extraParameters, webRequestEvent.options, | |
| 227 this.viewInstanceId); | |
| 228 } | |
| 229 return this[webRequestEvent.name]; | |
| 230 }.bind(this); | |
| 231 }.bind(this); | |
| 232 | |
| 233 var createDeclarativeWebRequestEvent = function(webRequestEvent) { | |
| 234 return function() { | |
| 235 if (!this[webRequestEvent.name]) { | |
| 236 // The onMessage event gets a special event type because we want | |
| 237 // the listener to fire only for messages targeted for this particular | |
| 238 // <webview>. | |
| 239 var EventClass = webRequestEvent.name === 'onMessage' ? | |
| 240 DeclarativeWebRequestEvent : EventBindings.Event; | |
| 241 this[webRequestEvent.name] = | |
| 242 new EventClass( | |
| 243 'webViewInternal.' + webRequestEvent.name, | |
| 244 webRequestEvent.parameters, | |
| 245 webRequestEvent.options, | |
| 246 this.viewInstanceId); | |
| 247 } | |
| 248 return this[webRequestEvent.name]; | |
| 249 }.bind(this); | |
| 250 }.bind(this); | |
| 251 | |
| 252 for (var i = 0; i < DeclarativeWebRequestSchema.events.length; ++i) { | |
| 253 var eventSchema = DeclarativeWebRequestSchema.events[i]; | |
| 254 var webRequestEvent = createDeclarativeWebRequestEvent(eventSchema); | |
| 255 Object.defineProperty( | |
| 256 request, | |
| 257 eventSchema.name, | |
| 258 { | |
| 259 get: webRequestEvent, | |
| 260 enumerable: true | |
| 261 } | |
| 262 ); | |
| 263 } | |
| 264 | |
| 265 // Populate the WebRequest events from the API definition. | |
| 266 for (var i = 0; i < WebRequestSchema.events.length; ++i) { | |
| 267 var webRequestEvent = createWebRequestEvent(WebRequestSchema.events[i]); | |
| 268 Object.defineProperty( | |
| 269 request, | |
| 270 WebRequestSchema.events[i].name, | |
| 271 { | |
| 272 get: webRequestEvent, | |
| 273 enumerable: true | |
| 274 } | |
| 275 ); | |
| 276 } | |
| 277 | |
| 278 this.webViewInternal.setRequestPropertyOnWebViewNode(request); | |
| 279 }; | |
| 280 | |
| 281 WebViewEvents.prototype.getEvents = function() { | |
| 282 var experimentalEvents = this.webViewInternal.maybeGetExperimentalEvents(); | |
| 283 for (var eventName in experimentalEvents) { | |
| 284 WEB_VIEW_EVENTS[eventName] = experimentalEvents[eventName]; | |
| 285 } | |
| 286 return WEB_VIEW_EVENTS; | |
| 287 }; | |
| 288 | |
| 289 WebViewEvents.prototype.setupEvent = function(name, info) { | |
| 290 info.evt.addListener(function(e) { | |
| 291 var details = {bubbles:true}; | |
| 292 if (info.cancelable) { | |
| 293 details.cancelable = true; | |
| 294 } | |
| 295 var webViewEvent = new Event(name, details); | |
| 296 $Array.forEach(info.fields, function(field) { | |
| 297 if (e[field] !== undefined) { | |
| 298 webViewEvent[field] = e[field]; | |
| 299 } | |
| 300 }.bind(this)); | |
| 301 if (info.customHandler) { | |
| 302 info.customHandler(this, e, webViewEvent); | |
| 303 return; | |
| 304 } | |
| 305 this.webViewInternal.dispatchEvent(webViewEvent); | |
| 306 }.bind(this), {instanceId: this.viewInstanceId}); | |
| 307 | |
| 308 this.webViewInternal.setupEventProperty(name); | |
| 309 }; | |
| 310 | |
| 311 | |
| 312 // Event handlers. | |
| 313 WebViewEvents.prototype.handleContextMenu = function(e, webViewEvent) { | |
| 314 this.webViewInternal.maybeHandleContextMenu(e, webViewEvent); | |
| 315 }; | |
| 316 | |
| 317 WebViewEvents.prototype.handleDialogEvent = function(event, webViewEvent) { | |
| 318 var showWarningMessage = function(dialogType) { | |
| 319 var VOWELS = ['a', 'e', 'i', 'o', 'u']; | |
| 320 var WARNING_MSG_DIALOG_BLOCKED = '<webview>: %1 %2 dialog was blocked.'; | |
| 321 var article = (VOWELS.indexOf(dialogType.charAt(0)) >= 0) ? 'An' : 'A'; | |
| 322 var output = WARNING_MSG_DIALOG_BLOCKED.replace('%1', article); | |
| 323 output = output.replace('%2', dialogType); | |
| 324 window.console.warn(output); | |
| 325 }; | |
| 326 | |
| 327 var requestId = event.requestId; | |
| 328 var actionTaken = false; | |
| 329 | |
| 330 var validateCall = function() { | |
| 331 var ERROR_MSG_DIALOG_ACTION_ALREADY_TAKEN = '<webview>: ' + | |
| 332 'An action has already been taken for this "dialog" event.'; | |
| 333 | |
| 334 if (actionTaken) { | |
| 335 throw new Error(ERROR_MSG_DIALOG_ACTION_ALREADY_TAKEN); | |
| 336 } | |
| 337 actionTaken = true; | |
| 338 }; | |
| 339 | |
| 340 var getGuestInstanceId = function() { | |
| 341 return this.webViewInternal.getGuestInstanceId(); | |
| 342 }.bind(this); | |
| 343 | |
| 344 var dialog = { | |
| 345 ok: function(user_input) { | |
| 346 validateCall(); | |
| 347 user_input = user_input || ''; | |
| 348 WebView.setPermission(getGuestInstanceId(), requestId, 'allow', | |
| 349 user_input); | |
| 350 }, | |
| 351 cancel: function() { | |
| 352 validateCall(); | |
| 353 WebView.setPermission(getGuestInstanceId(), requestId, 'deny'); | |
| 354 } | |
| 355 }; | |
| 356 webViewEvent.dialog = dialog; | |
| 357 | |
| 358 var defaultPrevented = !this.webViewInternal.dispatchEvent(webViewEvent); | |
| 359 if (actionTaken) { | |
| 360 return; | |
| 361 } | |
| 362 | |
| 363 if (defaultPrevented) { | |
| 364 // Tell the JavaScript garbage collector to track lifetime of |dialog| and | |
| 365 // call back when the dialog object has been collected. | |
| 366 MessagingNatives.BindToGC(dialog, function() { | |
| 367 // Avoid showing a warning message if the decision has already been made. | |
| 368 if (actionTaken) { | |
| 369 return; | |
| 370 } | |
| 371 WebView.setPermission( | |
| 372 getGuestInstanceId(), requestId, 'default', '', function(allowed) { | |
| 373 if (allowed) { | |
| 374 return; | |
| 375 } | |
| 376 showWarningMessage(event.messageType); | |
| 377 }); | |
| 378 }); | |
| 379 } else { | |
| 380 actionTaken = true; | |
| 381 // The default action is equivalent to canceling the dialog. | |
| 382 WebView.setPermission( | |
| 383 getGuestInstanceId(), requestId, 'default', '', function(allowed) { | |
| 384 if (allowed) { | |
| 385 return; | |
| 386 } | |
| 387 showWarningMessage(event.messageType); | |
| 388 }); | |
| 389 } | |
| 390 }; | |
| 391 | |
| 392 WebViewEvents.prototype.handleLoadAbortEvent = function(event, webViewEvent) { | |
| 393 var showWarningMessage = function(reason) { | |
| 394 var WARNING_MSG_LOAD_ABORTED = '<webview>: ' + | |
| 395 'The load has aborted with reason "%1".'; | |
| 396 window.console.warn(WARNING_MSG_LOAD_ABORTED.replace('%1', reason)); | |
| 397 }; | |
| 398 if (this.webViewInternal.dispatchEvent(webViewEvent)) { | |
| 399 showWarningMessage(event.reason); | |
| 400 } | |
| 401 }; | |
| 402 | |
| 403 WebViewEvents.prototype.handleLoadCommitEvent = function(event, webViewEvent) { | |
| 404 this.webViewInternal.onLoadCommit(event.currentEntryIndex, event.entryCount, | |
| 405 event.processId, event.url, | |
| 406 event.isTopLevel); | |
| 407 this.webViewInternal.dispatchEvent(webViewEvent); | |
| 408 }; | |
| 409 | |
| 410 WebViewEvents.prototype.handleNewWindowEvent = function(event, webViewEvent) { | |
| 411 var ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN = '<webview>: ' + | |
| 412 'An action has already been taken for this "newwindow" event.'; | |
| 413 | |
| 414 var ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH = '<webview>: ' + | |
| 415 'Unable to attach the new window to the provided webViewInternal.'; | |
| 416 | |
| 417 var ERROR_MSG_WEBVIEW_EXPECTED = '<webview> element expected.'; | |
| 418 | |
| 419 var showWarningMessage = function() { | |
| 420 var WARNING_MSG_NEWWINDOW_BLOCKED = '<webview>: A new window was blocked.'; | |
| 421 window.console.warn(WARNING_MSG_NEWWINDOW_BLOCKED); | |
| 422 }; | |
| 423 | |
| 424 var requestId = event.requestId; | |
| 425 var actionTaken = false; | |
| 426 var getGuestInstanceId = function() { | |
| 427 return this.webViewInternal.getGuestInstanceId(); | |
| 428 }.bind(this); | |
| 429 | |
| 430 var validateCall = function () { | |
| 431 if (actionTaken) { | |
| 432 throw new Error(ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN); | |
| 433 } | |
| 434 actionTaken = true; | |
| 435 }; | |
| 436 | |
| 437 var windowObj = { | |
| 438 attach: function(webview) { | |
| 439 validateCall(); | |
| 440 if (!webview || !webview.tagName || webview.tagName != 'WEBVIEW') | |
| 441 throw new Error(ERROR_MSG_WEBVIEW_EXPECTED); | |
| 442 // Attach happens asynchronously to give the tagWatcher an opportunity | |
| 443 // to pick up the new webview before attach operates on it, if it hasn't | |
| 444 // been attached to the DOM already. | |
| 445 // Note: Any subsequent errors cannot be exceptions because they happen | |
| 446 // asynchronously. | |
| 447 setTimeout(function() { | |
| 448 var webViewInternal = privates(webview).internal; | |
| 449 // Update the partition. | |
| 450 if (event.storagePartitionId) { | |
| 451 webViewInternal.onAttach(event.storagePartitionId); | |
| 452 } | |
| 453 | |
| 454 var attached = webViewInternal.attachWindow(event.windowId, true); | |
| 455 | |
| 456 if (!attached) { | |
| 457 window.console.error(ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH); | |
| 458 } | |
| 459 | |
| 460 var guestInstanceId = getGuestInstanceId(); | |
| 461 if (!guestInstanceId) { | |
| 462 // If the opener is already gone, then we won't have its | |
| 463 // guestInstanceId. | |
| 464 return; | |
| 465 } | |
| 466 | |
| 467 // If the object being passed into attach is not a valid <webview> | |
| 468 // then we will fail and it will be treated as if the new window | |
| 469 // was rejected. The permission API plumbing is used here to clean | |
| 470 // up the state created for the new window if attaching fails. | |
| 471 WebView.setPermission( | |
| 472 guestInstanceId, requestId, attached ? 'allow' : 'deny'); | |
| 473 }, 0); | |
| 474 }, | |
| 475 discard: function() { | |
| 476 validateCall(); | |
| 477 var guestInstanceId = getGuestInstanceId(); | |
| 478 if (!guestInstanceId) { | |
| 479 // If the opener is already gone, then we won't have its | |
| 480 // guestInstanceId. | |
| 481 return; | |
| 482 } | |
| 483 WebView.setPermission(guestInstanceId, requestId, 'deny'); | |
| 484 } | |
| 485 }; | |
| 486 webViewEvent.window = windowObj; | |
| 487 | |
| 488 var defaultPrevented = !this.webViewInternal.dispatchEvent(webViewEvent); | |
| 489 if (actionTaken) { | |
| 490 return; | |
| 491 } | |
| 492 | |
| 493 if (defaultPrevented) { | |
| 494 // Make browser plugin track lifetime of |windowObj|. | |
| 495 MessagingNatives.BindToGC(windowObj, function() { | |
| 496 // Avoid showing a warning message if the decision has already been made. | |
| 497 if (actionTaken) { | |
| 498 return; | |
| 499 } | |
| 500 | |
| 501 var guestInstanceId = getGuestInstanceId(); | |
| 502 if (!guestInstanceId) { | |
| 503 // If the opener is already gone, then we won't have its | |
| 504 // guestInstanceId. | |
| 505 return; | |
| 506 } | |
| 507 | |
| 508 WebView.setPermission( | |
| 509 guestInstanceId, requestId, 'default', '', function(allowed) { | |
| 510 if (allowed) { | |
| 511 return; | |
| 512 } | |
| 513 showWarningMessage(); | |
| 514 }); | |
| 515 }); | |
| 516 } else { | |
| 517 actionTaken = true; | |
| 518 // The default action is to discard the window. | |
| 519 WebView.setPermission( | |
| 520 getGuestInstanceId(), requestId, 'default', '', function(allowed) { | |
| 521 if (allowed) { | |
| 522 return; | |
| 523 } | |
| 524 showWarningMessage(); | |
| 525 }); | |
| 526 } | |
| 527 }; | |
| 528 | |
| 529 WebViewEvents.prototype.getPermissionTypes = function() { | |
| 530 var permissions = | |
| 531 ['media', | |
| 532 'geolocation', | |
| 533 'pointerLock', | |
| 534 'download', | |
| 535 'loadplugin', | |
| 536 'filesystem']; | |
| 537 return permissions.concat( | |
| 538 this.webViewInternal.maybeGetExperimentalPermissions()); | |
| 539 }; | |
| 540 | |
| 541 WebViewEvents.prototype.handlePermissionEvent = | |
| 542 function(event, webViewEvent) { | |
| 543 var ERROR_MSG_PERMISSION_ALREADY_DECIDED = '<webview>: ' + | |
| 544 'Permission has already been decided for this "permissionrequest" event.'; | |
| 545 | |
| 546 var showWarningMessage = function(permission) { | |
| 547 var WARNING_MSG_PERMISSION_DENIED = '<webview>: ' + | |
| 548 'The permission request for "%1" has been denied.'; | |
| 549 window.console.warn( | |
| 550 WARNING_MSG_PERMISSION_DENIED.replace('%1', permission)); | |
| 551 }; | |
| 552 | |
| 553 var requestId = event.requestId; | |
| 554 var getGuestInstanceId = function() { | |
| 555 return this.webViewInternal.getGuestInstanceId(); | |
| 556 }.bind(this); | |
| 557 | |
| 558 if (this.getPermissionTypes().indexOf(event.permission) < 0) { | |
| 559 // The permission type is not allowed. Trigger the default response. | |
| 560 WebView.setPermission( | |
| 561 getGuestInstanceId(), requestId, 'default', '', function(allowed) { | |
| 562 if (allowed) { | |
| 563 return; | |
| 564 } | |
| 565 showWarningMessage(event.permission); | |
| 566 }); | |
| 567 return; | |
| 568 } | |
| 569 | |
| 570 var decisionMade = false; | |
| 571 var validateCall = function() { | |
| 572 if (decisionMade) { | |
| 573 throw new Error(ERROR_MSG_PERMISSION_ALREADY_DECIDED); | |
| 574 } | |
| 575 decisionMade = true; | |
| 576 }; | |
| 577 | |
| 578 // Construct the event.request object. | |
| 579 var request = { | |
| 580 allow: function() { | |
| 581 validateCall(); | |
| 582 WebView.setPermission(getGuestInstanceId(), requestId, 'allow'); | |
| 583 }, | |
| 584 deny: function() { | |
| 585 validateCall(); | |
| 586 WebView.setPermission(getGuestInstanceId(), requestId, 'deny'); | |
| 587 } | |
| 588 }; | |
| 589 webViewEvent.request = request; | |
| 590 | |
| 591 var defaultPrevented = !this.webViewInternal.dispatchEvent(webViewEvent); | |
| 592 if (decisionMade) { | |
| 593 return; | |
| 594 } | |
| 595 | |
| 596 if (defaultPrevented) { | |
| 597 // Make browser plugin track lifetime of |request|. | |
| 598 MessagingNatives.BindToGC(request, function() { | |
| 599 // Avoid showing a warning message if the decision has already been made. | |
| 600 if (decisionMade) { | |
| 601 return; | |
| 602 } | |
| 603 WebView.setPermission( | |
| 604 getGuestInstanceId(), requestId, 'default', '', function(allowed) { | |
| 605 if (allowed) { | |
| 606 return; | |
| 607 } | |
| 608 showWarningMessage(event.permission); | |
| 609 }); | |
| 610 }); | |
| 611 } else { | |
| 612 decisionMade = true; | |
| 613 WebView.setPermission( | |
| 614 getGuestInstanceId(), requestId, 'default', '', | |
| 615 function(allowed) { | |
| 616 if (allowed) { | |
| 617 return; | |
| 618 } | |
| 619 showWarningMessage(event.permission); | |
| 620 }); | |
| 621 } | |
| 622 }; | |
| 623 | |
| 624 WebViewEvents.prototype.handleSizeChangedEvent = function( | |
| 625 event, webViewEvent) { | |
| 626 this.webViewInternal.onSizeChanged(webViewEvent); | |
| 627 }; | |
| 628 | |
| 629 exports.WebViewEvents = WebViewEvents; | |
| 630 exports.CreateEvent = CreateEvent; | |
| OLD | NEW |