| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // Shim that simulates a <webview> tag via Mutation Observers. | 5 // Shim that simulates a <webview> tag via Mutation Observers. |
| 6 // | 6 // |
| 7 // The actual tag is implemented via the browser plugin. The internals of this | 7 // The actual tag is implemented via the browser plugin. The internals of this |
| 8 // are hidden via Shadow DOM. | 8 // are hidden via Shadow DOM. |
| 9 | 9 |
| 10 var addTagWatcher = require('tagWatcher').addTagWatcher; | 10 var addTagWatcher = require('tagWatcher').addTagWatcher; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 }, | 40 }, |
| 41 'exit': { | 41 'exit': { |
| 42 evt: createEvent('webview.onExit'), | 42 evt: createEvent('webview.onExit'), |
| 43 fields: ['processId', 'reason'] | 43 fields: ['processId', 'reason'] |
| 44 }, | 44 }, |
| 45 'loadabort': { | 45 'loadabort': { |
| 46 evt: createEvent('webview.onLoadAbort'), | 46 evt: createEvent('webview.onLoadAbort'), |
| 47 fields: ['url', 'isTopLevel', 'reason'] | 47 fields: ['url', 'isTopLevel', 'reason'] |
| 48 }, | 48 }, |
| 49 'loadcommit': { | 49 'loadcommit': { |
| 50 customHandler: function(webview, event) { | 50 customHandler: function(webview, event, webviewEvent) { |
| 51 webview.currentEntryIndex_ = event.currentEntryIndex; | 51 webview.currentEntryIndex_ = event.currentEntryIndex; |
| 52 webview.entryCount_ = event.entryCount; | 52 webview.entryCount_ = event.entryCount; |
| 53 webview.processId_ = event.processId; | 53 webview.processId_ = event.processId; |
| 54 if (event.isTopLevel) { | 54 if (event.isTopLevel) { |
| 55 webview.browserPluginNode_.setAttribute('src', event.url); | 55 webview.browserPluginNode_.setAttribute('src', event.url); |
| 56 } | 56 } |
| 57 webview.webviewNode_.dispatchEvent(webviewEvent); |
| 57 }, | 58 }, |
| 58 evt: createEvent('webview.onLoadCommit'), | 59 evt: createEvent('webview.onLoadCommit'), |
| 59 fields: ['url', 'isTopLevel'] | 60 fields: ['url', 'isTopLevel'] |
| 60 }, | 61 }, |
| 61 'loadredirect': { | 62 'loadredirect': { |
| 62 evt: createEvent('webview.onLoadRedirect'), | 63 evt: createEvent('webview.onLoadRedirect'), |
| 63 fields: ['isTopLevel', 'oldUrl', 'newUrl'] | 64 fields: ['isTopLevel', 'oldUrl', 'newUrl'] |
| 64 }, | 65 }, |
| 65 'loadstart': { | 66 'loadstart': { |
| 66 evt: createEvent('webview.onLoadStart'), | 67 evt: createEvent('webview.onLoadStart'), |
| 67 fields: ['url', 'isTopLevel'] | 68 fields: ['url', 'isTopLevel'] |
| 68 }, | 69 }, |
| 69 'loadstop': { | 70 'loadstop': { |
| 70 evt: createEvent('webview.onLoadStop'), | 71 evt: createEvent('webview.onLoadStop'), |
| 71 fields: [] | 72 fields: [] |
| 72 }, | 73 }, |
| 74 'newwindow': { |
| 75 cancelable: true, |
| 76 customHandler: function(webview, event, webviewEvent) { |
| 77 webview.setupExtNewWindowEvent_(event, webviewEvent); |
| 78 }, |
| 79 evt: createEvent('webview.onNewWindow'), |
| 80 fields: [ |
| 81 'initialHeight', |
| 82 'initialWidth', |
| 83 'targetUrl', |
| 84 'windowOpenDisposition', |
| 85 'name' |
| 86 ] |
| 87 }, |
| 88 'permissionrequest': { |
| 89 cancelable: true, |
| 90 customHandler: function(webview, event, webviewEvent) { |
| 91 webview.setupExtPermissionEvent_(event, webviewEvent); |
| 92 }, |
| 93 evt: createEvent('webview.onPermissionRequest'), |
| 94 fields: [ |
| 95 'lastUnlockedBySelf', |
| 96 'permission', |
| 97 'requestMethod', |
| 98 'url', |
| 99 'userGesture' |
| 100 ] |
| 101 }, |
| 73 'responsive': { | 102 'responsive': { |
| 74 evt: createEvent('webview.onResponsive'), | 103 evt: createEvent('webview.onResponsive'), |
| 75 fields: ['processId'] | 104 fields: ['processId'] |
| 76 }, | 105 }, |
| 77 'unresponsive': { | 106 'unresponsive': { |
| 78 evt: createEvent('webview.onUnresponsive'), | 107 evt: createEvent('webview.onUnresponsive'), |
| 79 fields: ['processId'] | 108 fields: ['processId'] |
| 80 } | 109 } |
| 81 }; | 110 }; |
| 82 | 111 |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 336 var newValue = this.browserPluginNode_.getAttribute(mutation.attributeName); | 365 var newValue = this.browserPluginNode_.getAttribute(mutation.attributeName); |
| 337 if (newValue != oldValue) { | 366 if (newValue != oldValue) { |
| 338 this.webviewNode_.setAttribute(mutation.attributeName, newValue); | 367 this.webviewNode_.setAttribute(mutation.attributeName, newValue); |
| 339 } | 368 } |
| 340 } | 369 } |
| 341 }; | 370 }; |
| 342 | 371 |
| 343 /** | 372 /** |
| 344 * @private | 373 * @private |
| 345 */ | 374 */ |
| 375 WebView.prototype.getWebviewExtEvents_ = function() { |
| 376 var experimentalExtEvents = this.maybeGetWebviewExperimentalExtEvents_(); |
| 377 for (var eventName in experimentalExtEvents) { |
| 378 WEB_VIEW_EXT_EVENTS[eventName] = experimentalExtEvents[eventName]; |
| 379 } |
| 380 return WEB_VIEW_EXT_EVENTS; |
| 381 }; |
| 382 |
| 383 /** |
| 384 * @private |
| 385 */ |
| 346 WebView.prototype.setupWebviewNodeEvents_ = function() { | 386 WebView.prototype.setupWebviewNodeEvents_ = function() { |
| 347 var self = this; | 387 var self = this; |
| 348 this.viewInstanceId_ = ++webViewInstanceIdCounter; | 388 this.viewInstanceId_ = ++webViewInstanceIdCounter; |
| 349 var onInstanceIdAllocated = function(e) { | 389 var onInstanceIdAllocated = function(e) { |
| 350 var detail = e.detail ? JSON.parse(e.detail) : {}; | 390 var detail = e.detail ? JSON.parse(e.detail) : {}; |
| 351 self.instanceId_ = detail.windowId; | 391 self.instanceId_ = detail.windowId; |
| 352 var params = { | 392 var params = { |
| 353 'api': 'webview', | 393 'api': 'webview', |
| 354 'instanceId': self.viewInstanceId_ | 394 'instanceId': self.viewInstanceId_ |
| 355 }; | 395 }; |
| 356 self.browserPluginNode_['-internal-attach'](params); | 396 self.browserPluginNode_['-internal-attach'](params); |
| 357 | 397 |
| 358 for (var eventName in WEB_VIEW_EXT_EVENTS) { | 398 var extEvents = self.getWebviewExtEvents_(); |
| 359 self.setupExtEvent_(eventName, WEB_VIEW_EXT_EVENTS[eventName]); | 399 for (var eventName in extEvents) { |
| 400 self.setupExtEvent_(eventName, extEvents[eventName]); |
| 360 } | 401 } |
| 361 }; | 402 }; |
| 362 this.browserPluginNode_.addEventListener('-internal-instanceid-allocated', | 403 this.browserPluginNode_.addEventListener('-internal-instanceid-allocated', |
| 363 onInstanceIdAllocated); | 404 onInstanceIdAllocated); |
| 364 | 405 |
| 365 for (var eventName in WEB_VIEW_EVENTS) { | 406 for (var eventName in WEB_VIEW_EVENTS) { |
| 366 this.setupEvent_(eventName, WEB_VIEW_EVENTS[eventName]); | 407 this.setupEvent_(eventName, WEB_VIEW_EVENTS[eventName]); |
| 367 } | 408 } |
| 368 this.setupNewWindowEvent_(); | |
| 369 this.setupPermissionEvent_(); | |
| 370 }; | 409 }; |
| 371 | 410 |
| 372 /** | 411 /** |
| 373 * @private | 412 * @private |
| 374 */ | 413 */ |
| 375 WebView.prototype.setupExtEvent_ = function(eventName, eventInfo) { | 414 WebView.prototype.setupExtEvent_ = function(eventName, eventInfo) { |
| 376 var self = this; | 415 var self = this; |
| 377 var webviewNode = this.webviewNode_; | 416 var webviewNode = this.webviewNode_; |
| 378 eventInfo.evt.addListener(function(event) { | 417 eventInfo.evt.addListener(function(event) { |
| 379 var webviewEvent = new Event(eventName, {bubbles: true}); | 418 var details = {bubbles:true}; |
| 419 if (eventInfo.cancelable) |
| 420 details.cancelable = true; |
| 421 var webviewEvent = new Event(eventName, details); |
| 380 $Array.forEach(eventInfo.fields, function(field) { | 422 $Array.forEach(eventInfo.fields, function(field) { |
| 381 webviewEvent[field] = event[field]; | 423 if (event[field] !== undefined) { |
| 424 webviewEvent[field] = event[field]; |
| 425 } |
| 382 }); | 426 }); |
| 383 if (eventInfo.customHandler) { | 427 if (eventInfo.customHandler) { |
| 384 eventInfo.customHandler(self, event); | 428 eventInfo.customHandler(self, event, webviewEvent); |
| 429 return; |
| 385 } | 430 } |
| 386 webviewNode.dispatchEvent(webviewEvent); | 431 webviewNode.dispatchEvent(webviewEvent); |
| 387 }, {instanceId: self.instanceId_}); | 432 }, {instanceId: self.instanceId_}); |
| 388 }; | 433 }; |
| 389 | 434 |
| 390 /** | 435 /** |
| 391 * @private | 436 * @private |
| 392 */ | 437 */ |
| 393 WebView.prototype.setupEvent_ = function(eventName, attribs) { | 438 WebView.prototype.setupEvent_ = function(eventName, attribs) { |
| 394 var webviewNode = this.webviewNode_; | 439 var webviewNode = this.webviewNode_; |
| 395 var internalname = '-internal-' + eventName; | 440 var internalname = '-internal-' + eventName; |
| 396 this.browserPluginNode_.addEventListener(internalname, function(e) { | 441 this.browserPluginNode_.addEventListener(internalname, function(e) { |
| 397 var evt = new Event(eventName, { bubbles: true }); | 442 var evt = new Event(eventName, { bubbles: true }); |
| 398 var detail = e.detail ? JSON.parse(e.detail) : {}; | 443 var detail = e.detail ? JSON.parse(e.detail) : {}; |
| 399 $Array.forEach(attribs, function(attribName) { | 444 $Array.forEach(attribs, function(attribName) { |
| 400 evt[attribName] = detail[attribName]; | 445 evt[attribName] = detail[attribName]; |
| 401 }); | 446 }); |
| 402 webviewNode.dispatchEvent(evt); | 447 webviewNode.dispatchEvent(evt); |
| 403 }); | 448 }); |
| 404 }; | 449 }; |
| 405 | 450 |
| 406 /** | 451 /** |
| 407 * @private | 452 * @private |
| 408 */ | 453 */ |
| 409 WebView.prototype.setupNewWindowEvent_ = function() { | 454 WebView.prototype.setupExtNewWindowEvent_ = function(event, webviewEvent) { |
| 410 var ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN = '<webview>: ' + | 455 var ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN = '<webview>: ' + |
| 411 'An action has already been taken for this "newwindow" event.'; | 456 'An action has already been taken for this "newwindow" event.'; |
| 412 | 457 |
| 413 var ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH = '<webview>: ' + | 458 var ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH = '<webview>: ' + |
| 414 'Unable to attach the new window to the provided webview.'; | 459 'Unable to attach the new window to the provided webview.'; |
| 415 | 460 |
| 416 var ERROR_MSG_WEBVIEW_EXPECTED = '<webview> element expected.'; | 461 var ERROR_MSG_WEBVIEW_EXPECTED = '<webview> element expected.'; |
| 417 | 462 |
| 418 var showWarningMessage = function() { | 463 var showWarningMessage = function() { |
| 419 var WARNING_MSG_NEWWINDOW_BLOCKED = '<webview>: A new window was blocked.'; | 464 var WARNING_MSG_NEWWINDOW_BLOCKED = '<webview>: A new window was blocked.'; |
| 420 console.warn(WARNING_MSG_NEWWINDOW_BLOCKED); | 465 console.warn(WARNING_MSG_NEWWINDOW_BLOCKED); |
| 421 }; | 466 }; |
| 422 | 467 |
| 423 var NEW_WINDOW_EVENT_ATTRIBUTES = [ | 468 var self = this; |
| 424 'initialHeight', | 469 var browserPluginNode = this.browserPluginNode_; |
| 425 'initialWidth', | 470 var webviewNode = this.webviewNode_; |
| 426 'targetUrl', | |
| 427 'windowOpenDisposition', | |
| 428 'name' | |
| 429 ]; | |
| 430 | 471 |
| 431 var self = this; | 472 var requestId = event.requestId; |
| 432 var node = this.webviewNode_; | 473 var actionTaken = false; |
| 433 var browserPluginNode = this.browserPluginNode_; | |
| 434 | 474 |
| 435 var onTrackedObjectGone = function(requestId, e) { | 475 var onTrackedObjectGone = function(requestId, e) { |
| 436 var detail = e.detail ? JSON.parse(e.detail) : {}; | 476 var detail = e.detail ? JSON.parse(e.detail) : {}; |
| 437 if (detail.id != requestId) { | 477 if (detail.id != requestId) { |
| 438 return; | 478 return; |
| 439 } | 479 } |
| 440 // If the request was pending then show a warning indiciating that a dialog | |
| 441 // was blocked. | |
| 442 if (browserPluginNode['-internal-setPermission'](requestId, false, '')) { | |
| 443 showWarningMessage(); | |
| 444 } | |
| 445 }; | |
| 446 | 480 |
| 447 browserPluginNode.addEventListener('-internal-newwindow', function(e) { | 481 // Avoid showing a warning message if the decision has already been made. |
| 448 var evt = new Event('newwindow', { bubbles: true, cancelable: true }); | |
| 449 var detail = e.detail ? JSON.parse(e.detail) : {}; | |
| 450 | |
| 451 $Array.forEach(NEW_WINDOW_EVENT_ATTRIBUTES, function(attribName) { | |
| 452 evt[attribName] = detail[attribName]; | |
| 453 }); | |
| 454 var requestId = detail.requestId; | |
| 455 var actionTaken = false; | |
| 456 | |
| 457 var validateCall = function () { | |
| 458 if (actionTaken) { | |
| 459 throw new Error(ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN); | |
| 460 } | |
| 461 actionTaken = true; | |
| 462 }; | |
| 463 | |
| 464 var window = { | |
| 465 attach: function(webview) { | |
| 466 validateCall(); | |
| 467 if (!webview) | |
| 468 throw new Error(ERROR_MSG_WEBVIEW_EXPECTED); | |
| 469 // Attach happens asynchronously to give the tagWatcher an opportunity | |
| 470 // to pick up the new webview before attach operates on it, if it hasn't | |
| 471 // been attached to the DOM already. | |
| 472 // Note: Any subsequent errors cannot be exceptions because they happen | |
| 473 // asynchronously. | |
| 474 setTimeout(function() { | |
| 475 var attached = | |
| 476 browserPluginNode['-internal-attachWindowTo'](webview, | |
| 477 detail.windowId); | |
| 478 if (!attached) { | |
| 479 console.error(ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH); | |
| 480 } | |
| 481 // If the object being passed into attach is not a valid <webview> | |
| 482 // then we will fail and it will be treated as if the new window | |
| 483 // was rejected. The permission API plumbing is used here to clean | |
| 484 // up the state created for the new window if attaching fails. | |
| 485 browserPluginNode['-internal-setPermission'](requestId, attached, ''); | |
| 486 }, 0); | |
| 487 }, | |
| 488 discard: function() { | |
| 489 validateCall(); | |
| 490 browserPluginNode['-internal-setPermission'](requestId, false, ''); | |
| 491 } | |
| 492 }; | |
| 493 evt.window = window; | |
| 494 | |
| 495 var defaultPrevented = !node.dispatchEvent(evt); | |
| 496 if (actionTaken) { | 482 if (actionTaken) { |
| 497 return; | 483 return; |
| 498 } | 484 } |
| 499 | 485 |
| 500 if (defaultPrevented) { | 486 chrome.webview.setPermission(self.instanceId_, requestId, false, ''); |
| 501 // Make browser plugin track lifetime of |window|. | 487 showWarningMessage(); |
| 502 var onTrackedObjectGoneWithRequestId = | 488 }; |
| 503 $Function.bind(onTrackedObjectGone, self, requestId); | 489 |
| 504 browserPluginNode.addEventListener('-internal-trackedobjectgone', | 490 |
| 505 onTrackedObjectGoneWithRequestId); | 491 var validateCall = function () { |
| 506 browserPluginNode['-internal-trackObjectLifetime'](window, requestId); | 492 if (actionTaken) { |
| 507 } else { | 493 throw new Error(ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN); |
| 508 actionTaken = true; | |
| 509 // The default action is to discard the window. | |
| 510 browserPluginNode['-internal-setPermission'](requestId, false, ''); | |
| 511 showWarningMessage(); | |
| 512 } | 494 } |
| 495 actionTaken = true; |
| 496 }; |
| 513 | 497 |
| 514 }); | 498 var window = { |
| 499 attach: function(webview) { |
| 500 validateCall(); |
| 501 if (!webview) |
| 502 throw new Error(ERROR_MSG_WEBVIEW_EXPECTED); |
| 503 // Attach happens asynchronously to give the tagWatcher an opportunity |
| 504 // to pick up the new webview before attach operates on it, if it hasn't |
| 505 // been attached to the DOM already. |
| 506 // Note: Any subsequent errors cannot be exceptions because they happen |
| 507 // asynchronously. |
| 508 setTimeout(function() { |
| 509 var attached = |
| 510 browserPluginNode['-internal-attachWindowTo'](webview, |
| 511 event.windowId); |
| 512 if (!attached) { |
| 513 console.error(ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH); |
| 514 } |
| 515 // If the object being passed into attach is not a valid <webview> |
| 516 // then we will fail and it will be treated as if the new window |
| 517 // was rejected. The permission API plumbing is used here to clean |
| 518 // up the state created for the new window if attaching fails. |
| 519 chrome.webview.setPermission(self.instanceId_, requestId, attached, ''); |
| 520 }, 0); |
| 521 }, |
| 522 discard: function() { |
| 523 validateCall(); |
| 524 chrome.webview.setPermission(self.instanceId_, requestId, false, ''); |
| 525 } |
| 526 }; |
| 527 webviewEvent.window = window; |
| 528 |
| 529 var defaultPrevented = !webviewNode.dispatchEvent(webviewEvent); |
| 530 if (actionTaken) { |
| 531 return; |
| 532 } |
| 533 |
| 534 if (defaultPrevented) { |
| 535 // Make browser plugin track lifetime of |window|. |
| 536 var onTrackedObjectGoneWithRequestId = |
| 537 $Function.bind(onTrackedObjectGone, self, requestId); |
| 538 browserPluginNode.addEventListener('-internal-trackedobjectgone', |
| 539 onTrackedObjectGoneWithRequestId); |
| 540 browserPluginNode['-internal-trackObjectLifetime'](window, requestId); |
| 541 } else { |
| 542 actionTaken = true; |
| 543 // The default action is to discard the window. |
| 544 chrome.webview.setPermission(self.instanceId_, requestId, false, ''); |
| 545 showWarningMessage(); |
| 546 } |
| 515 }; | 547 }; |
| 516 | 548 |
| 517 /** | 549 /** |
| 518 * @private | 550 * @private |
| 519 */ | 551 */ |
| 520 WebView.prototype.setupExecuteCodeAPI_ = function() { | 552 WebView.prototype.setupExecuteCodeAPI_ = function() { |
| 521 var ERROR_MSG_CANNOT_INJECT_SCRIPT = '<webview>: ' + | 553 var ERROR_MSG_CANNOT_INJECT_SCRIPT = '<webview>: ' + |
| 522 'Script cannot be injected into content until the page has loaded.'; | 554 'Script cannot be injected into content until the page has loaded.'; |
| 523 | 555 |
| 524 var self = this; | 556 var self = this; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 536 } | 568 } |
| 537 this.webviewNode_['insertCSS'] = function(var_args) { | 569 this.webviewNode_['insertCSS'] = function(var_args) { |
| 538 validateCall(); | 570 validateCall(); |
| 539 var args = $Array.concat([self.browserPluginNode_.getGuestInstanceId()], | 571 var args = $Array.concat([self.browserPluginNode_.getGuestInstanceId()], |
| 540 $Array.slice(arguments)); | 572 $Array.slice(arguments)); |
| 541 $Function.apply(chrome.webview.insertCSS, null, args); | 573 $Function.apply(chrome.webview.insertCSS, null, args); |
| 542 } | 574 } |
| 543 }; | 575 }; |
| 544 | 576 |
| 545 /** | 577 /** |
| 546 * @param {!Object} detail The event details, originated from <object>. | |
| 547 * @private | 578 * @private |
| 548 */ | 579 */ |
| 549 WebView.prototype.setupPermissionEvent_ = function() { | 580 WebView.prototype.getPermissionTypes_ = function() { |
| 581 return ['media', 'geolocation', 'pointerLock', 'download']; |
| 582 }; |
| 583 |
| 584 WebView.prototype.setupExtPermissionEvent_ = function(event, webviewEvent) { |
| 550 var ERROR_MSG_PERMISSION_ALREADY_DECIDED = '<webview>: ' + | 585 var ERROR_MSG_PERMISSION_ALREADY_DECIDED = '<webview>: ' + |
| 551 'Permission has already been decided for this "permissionrequest" event.'; | 586 'Permission has already been decided for this "permissionrequest" event.'; |
| 552 | 587 |
| 553 var showWarningMessage = function(permission) { | 588 var showWarningMessage = function(permission) { |
| 554 var WARNING_MSG_PERMISSION_DENIED = '<webview>: ' + | 589 var WARNING_MSG_PERMISSION_DENIED = '<webview>: ' + |
| 555 'The permission request for "%1" has been denied.'; | 590 'The permission request for "%1" has been denied.'; |
| 556 console.warn(WARNING_MSG_PERMISSION_DENIED.replace('%1', permission)); | 591 console.warn(WARNING_MSG_PERMISSION_DENIED.replace('%1', permission)); |
| 557 }; | 592 }; |
| 558 | 593 |
| 559 var PERMISSION_TYPES = ['media', 'geolocation', 'pointerLock', 'download']; | 594 var PERMISSION_TYPES = this.getPermissionTypes_(); |
| 560 | |
| 561 var EXPOSED_PERMISSION_EVENT_ATTRIBS = [ | |
| 562 'lastUnlockedBySelf', | |
| 563 'permission', | |
| 564 'requestMethod', | |
| 565 'url', | |
| 566 'userGesture' | |
| 567 ]; | |
| 568 | 595 |
| 569 var self = this; | 596 var self = this; |
| 570 var node = this.webviewNode_; | |
| 571 var browserPluginNode = this.browserPluginNode_; | 597 var browserPluginNode = this.browserPluginNode_; |
| 572 var internalevent = '-internal-permissionrequest'; | 598 var webviewNode = this.webviewNode_; |
| 599 |
| 600 var requestId = event.requestId; |
| 601 var decisionMade = false; |
| 573 | 602 |
| 574 var onTrackedObjectGone = function(requestId, permission, e) { | 603 var onTrackedObjectGone = function(requestId, permission, e) { |
| 575 var detail = e.detail ? JSON.parse(e.detail) : {}; | 604 var detail = e.detail ? JSON.parse(e.detail) : {}; |
| 576 if (detail.id != requestId) | 605 if (detail.id != requestId) { |
| 577 return; | |
| 578 // If the request was pending then show a warning indiciating that the | |
| 579 // permission was denied. | |
| 580 if (browserPluginNode['-internal-setPermission'](requestId, false, '')) { | |
| 581 showWarningMessage(permission); | |
| 582 } | |
| 583 }; | |
| 584 | |
| 585 browserPluginNode.addEventListener(internalevent, function(e) { | |
| 586 var evt = new Event('permissionrequest', {bubbles: true, cancelable: true}); | |
| 587 var detail = e.detail ? JSON.parse(e.detail) : {}; | |
| 588 $Array.forEach(EXPOSED_PERMISSION_EVENT_ATTRIBS, function(attribName) { | |
| 589 if (detail[attribName] !== undefined) | |
| 590 evt[attribName] = detail[attribName]; | |
| 591 }); | |
| 592 var requestId = detail.requestId; | |
| 593 | |
| 594 if (detail.requestId == undefined || | |
| 595 PERMISSION_TYPES.indexOf(detail.permission) < 0) { | |
| 596 return; | 606 return; |
| 597 } | 607 } |
| 598 | 608 |
| 599 // TODO(lazyboy): Also fill in evt.details (see webview specs). | 609 // Avoid showing a warning message if the decision has already been made. |
| 600 // http://crbug.com/141197. | |
| 601 var decisionMade = false; | |
| 602 | |
| 603 var validateCall = function() { | |
| 604 if (decisionMade) { | |
| 605 throw new Error(ERROR_MSG_PERMISSION_ALREADY_DECIDED); | |
| 606 } | |
| 607 decisionMade = true; | |
| 608 }; | |
| 609 | |
| 610 // Construct the event.request object. | |
| 611 var request = { | |
| 612 allow: function() { | |
| 613 validateCall(); | |
| 614 browserPluginNode['-internal-setPermission'](requestId, true, ''); | |
| 615 }, | |
| 616 deny: function() { | |
| 617 validateCall(); | |
| 618 browserPluginNode['-internal-setPermission'](requestId, false, ''); | |
| 619 } | |
| 620 }; | |
| 621 evt.request = request; | |
| 622 | |
| 623 var defaultPrevented = !node.dispatchEvent(evt); | |
| 624 if (decisionMade) { | 610 if (decisionMade) { |
| 625 return; | 611 return; |
| 626 } | 612 } |
| 627 | 613 |
| 628 if (defaultPrevented) { | 614 chrome.webview.setPermission(self.instanceId_, requestId, false, ''); |
| 629 // Make browser plugin track lifetime of |request|. | 615 showWarningMessage(permission); |
| 630 var onTrackedObjectGoneWithRequestId = | 616 }; |
| 631 $Function.bind( | 617 |
| 632 onTrackedObjectGone, self, requestId, detail.permission); | 618 var validateCall = function() { |
| 633 browserPluginNode.addEventListener('-internal-trackedobjectgone', | 619 if (decisionMade) { |
| 634 onTrackedObjectGoneWithRequestId); | 620 throw new Error(ERROR_MSG_PERMISSION_ALREADY_DECIDED); |
| 635 browserPluginNode['-internal-trackObjectLifetime'](request, requestId); | |
| 636 } else { | |
| 637 decisionMade = true; | |
| 638 browserPluginNode['-internal-setPermission'](requestId, false, ''); | |
| 639 showWarningMessage(detail.permission); | |
| 640 } | 621 } |
| 641 }); | 622 decisionMade = true; |
| 623 }; |
| 624 |
| 625 // Construct the event.request object. |
| 626 var request = { |
| 627 allow: function() { |
| 628 validateCall(); |
| 629 chrome.webview.setPermission(self.instanceId_, requestId, true, ''); |
| 630 }, |
| 631 deny: function() { |
| 632 validateCall(); |
| 633 chrome.webview.setPermission(self.instanceId_, requestId, false, ''); |
| 634 } |
| 635 }; |
| 636 webviewEvent.request = request; |
| 637 |
| 638 var defaultPrevented = !webviewNode.dispatchEvent(webviewEvent); |
| 639 if (decisionMade) { |
| 640 return; |
| 641 } |
| 642 |
| 643 if (defaultPrevented) { |
| 644 // Make browser plugin track lifetime of |request|. |
| 645 var onTrackedObjectGoneWithRequestId = |
| 646 $Function.bind( |
| 647 onTrackedObjectGone, self, requestId, event.permission); |
| 648 browserPluginNode.addEventListener('-internal-trackedobjectgone', |
| 649 onTrackedObjectGoneWithRequestId); |
| 650 browserPluginNode['-internal-trackObjectLifetime'](request, requestId); |
| 651 } else { |
| 652 decisionMade = true; |
| 653 chrome.webview.setPermission(self.instanceId_, requestId, false, ''); |
| 654 showWarningMessage(event.permission); |
| 655 } |
| 642 }; | 656 }; |
| 643 | 657 |
| 644 /** | 658 /** |
| 645 * Implemented when the experimental API is available. | 659 * Implemented when the experimental API is available. |
| 646 * @private | 660 * @private |
| 647 */ | 661 */ |
| 648 WebView.prototype.maybeSetupExperimentalAPI_ = function() {}; | 662 WebView.prototype.maybeSetupExperimentalAPI_ = function() {}; |
| 649 | 663 |
| 664 /** |
| 665 * Implemented when the experimental API is available. |
| 666 * @private |
| 667 */ |
| 668 WebView.prototype.maybeSetupExtDialogEvent_ = function() {}; |
| 669 |
| 670 /** |
| 671 * Implemented when the experimental API is available. |
| 672 * @private |
| 673 */ |
| 674 WebView.prototype.maybeGetWebviewExperimentalExtEvents_ = function() {}; |
| 675 |
| 650 exports.WebView = WebView; | 676 exports.WebView = WebView; |
| 677 exports.CreateEvent = createEvent; |
| OLD | NEW |