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 watchForTag = require('tagWatcher').watchForTag; | 10 var watchForTag = require('tagWatcher').watchForTag; |
11 var eventBindings = require('event_bindings'); | 11 var eventBindings = require('event_bindings'); |
12 | 12 |
13 /** @type {Array.<string>} */ | 13 /** @type {Array.<string>} */ |
14 var WEB_VIEW_ATTRIBUTES = ['name', 'src', 'partition', 'autosize', 'minheight', | 14 var WEB_VIEW_ATTRIBUTES = ['name', 'src', 'partition', 'autosize', 'minheight', |
15 'minwidth', 'maxheight', 'maxwidth']; | 15 'minwidth', 'maxheight', 'maxwidth']; |
16 | 16 |
17 | 17 |
18 // All exposed api methods for <webview>, these are forwarded to the browser | 18 // All exposed api methods for <webview>, these are forwarded to the browser |
19 // plugin. | 19 // plugin. |
20 var WEB_VIEW_API_METHODS = [ | 20 var WEB_VIEW_API_METHODS = [ |
21 'getProcessId', | 21 'getProcessId', |
22 'reload', | 22 'reload', |
23 'stop', | 23 'stop', |
24 'terminate' | 24 'terminate' |
25 ]; | 25 ]; |
26 | 26 |
27 var WEB_VIEW_EVENTS = { | 27 var WEB_VIEW_EVENTS = { |
28 'close': [], | 28 'close': [], |
29 'consolemessage': ['level', 'message', 'line', 'sourceId'], | 29 'consolemessage': ['level', 'message', 'line', 'sourceId'], |
30 'contentload' : [], | |
31 'exit' : ['processId', 'reason'], | 30 'exit' : ['processId', 'reason'], |
32 'loadabort' : ['url', 'isTopLevel', 'reason'], | 31 'loadabort' : ['url', 'isTopLevel', 'reason'], |
33 'loadcommit' : ['url', 'isTopLevel'], | |
34 'loadredirect' : ['oldUrl', 'newUrl', 'isTopLevel'], | 32 'loadredirect' : ['oldUrl', 'newUrl', 'isTopLevel'], |
35 'loadstart' : ['url', 'isTopLevel'], | 33 'loadstart' : ['url', 'isTopLevel'], |
36 'loadstop' : [], | |
37 'responsive' : ['processId'], | 34 'responsive' : ['processId'], |
38 'sizechanged': ['oldHeight', 'oldWidth', 'newHeight', 'newWidth'], | 35 'sizechanged': ['oldHeight', 'oldWidth', 'newHeight', 'newWidth'], |
39 'unresponsive' : ['processId'] | 36 'unresponsive' : ['processId'] |
40 }; | 37 }; |
41 | 38 |
42 var createEvent = function(name) { | 39 var createEvent = function(name) { |
43 var eventOpts = {supportsListeners: true, supportsFilters: true}; | 40 var eventOpts = {supportsListeners: true, supportsFilters: true}; |
44 return new eventBindings.Event(name, undefined, eventOpts); | 41 return new eventBindings.Event(name, undefined, eventOpts); |
45 }; | 42 }; |
46 | 43 |
| 44 var contentLoadEvent = createEvent('webview.onContentLoad'); |
47 var loadCommitEvent = createEvent('webview.onLoadCommit'); | 45 var loadCommitEvent = createEvent('webview.onLoadCommit'); |
48 var loadStopEvent = createEvent('webview.onLoadStop'); | 46 var loadStopEvent = createEvent('webview.onLoadStop'); |
49 | 47 |
| 48 var WEB_VIEW_EXT_EVENTS = { |
| 49 'contentload': { |
| 50 evt: contentLoadEvent, |
| 51 fields: [] |
| 52 }, |
| 53 'loadcommit': { |
| 54 customHandler: function(WebView, event) { |
| 55 WebView.currentEntryIndex_ = event.currentEntryIndex; |
| 56 WebView.entryCount_ = event.entryCount; |
| 57 }, |
| 58 evt: loadCommitEvent, |
| 59 fields: ['url', 'isTopLevel'] |
| 60 } |
| 61 , |
| 62 'loadstop': { |
| 63 evt: loadStopEvent, |
| 64 fields: [] |
| 65 } |
| 66 }; |
| 67 |
| 68 |
50 // The <webview> tags we wish to watch for (watchForTag) does not belong to the | 69 // The <webview> tags we wish to watch for (watchForTag) does not belong to the |
51 // current scope's "document" reference. We need to wait until the document | 70 // current scope's "document" reference. We need to wait until the document |
52 // begins loading, since only then will the "document" reference | 71 // begins loading, since only then will the "document" reference |
53 // point to the page's document (it will be reset between now and then). | 72 // point to the page's document (it will be reset between now and then). |
54 // We can't listen for the "readystatechange" event on the document (because | 73 // We can't listen for the "readystatechange" event on the document (because |
55 // the object that it's dispatched on doesn't exist yet), but we can instead | 74 // the object that it's dispatched on doesn't exist yet), but we can instead |
56 // do it at the window level in the capturing phase. | 75 // do it at the window level in the capturing phase. |
57 window.addEventListener('readystatechange', function(e) { | 76 window.addEventListener('readystatechange', function(e) { |
58 if (document.readyState != 'loading') { | 77 if (document.readyState != 'loading') { |
59 return; | 78 return; |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
299 } | 318 } |
300 } | 319 } |
301 }; | 320 }; |
302 | 321 |
303 /** | 322 /** |
304 * @private | 323 * @private |
305 */ | 324 */ |
306 WebView.prototype.setupWebviewNodeEvents_ = function() { | 325 WebView.prototype.setupWebviewNodeEvents_ = function() { |
307 var self = this; | 326 var self = this; |
308 var webviewNode = this.webviewNode_; | 327 var webviewNode = this.webviewNode_; |
309 // TODO(fsamuel): Generalize this further as we add more events. | 328 this.browserPluginNode_.addEventListener('-internal-attached', function(e) { |
310 var onAttached = function(e) { | |
311 var detail = e.detail ? JSON.parse(e.detail) : {}; | 329 var detail = e.detail ? JSON.parse(e.detail) : {}; |
312 loadCommitEvent.addListener(function(event) { | 330 self.instanceId_ = detail.windowId; |
313 var webviewEvent = new Event('loadcommit', {bubbles: true}); | 331 for (var eventName in WEB_VIEW_EXT_EVENTS) { |
314 var attribs = WEB_VIEW_EVENTS['loadcommit']; | 332 self.setupExtEvent_(eventName, WEB_VIEW_EXT_EVENTS[eventName]); |
315 $Array.forEach(attribs, function(attribName) { | 333 } |
316 webviewEvent[attribName] = event[attribName]; | 334 }); |
317 }); | |
318 self.currentEntryIndex_ = event.currentEntryIndex; | |
319 self.entryCount_ = event.entryCount; | |
320 webviewNode.dispatchEvent(webviewEvent); | |
321 }, {instanceId: detail.windowId}); | |
322 | |
323 loadStopEvent.addListener(function(event) { | |
324 var webviewEvent = new Event('loadstop', {bubbles: true}); | |
325 var attribs = WEB_VIEW_EVENTS['loadstop']; | |
326 $Array.forEach(attribs, function(attribName) { | |
327 webviewEvent[attribName] = event[attribName]; | |
328 }); | |
329 webviewNode.dispatchEvent(webviewEvent); | |
330 }, {instanceId: detail.windowId}); | |
331 }; | |
332 this.browserPluginNode_.addEventListener('-internal-attached', onAttached); | |
333 | 335 |
334 for (var eventName in WEB_VIEW_EVENTS) { | 336 for (var eventName in WEB_VIEW_EVENTS) { |
335 this.setupEvent_(eventName, WEB_VIEW_EVENTS[eventName]); | 337 this.setupEvent_(eventName, WEB_VIEW_EVENTS[eventName]); |
336 } | 338 } |
337 this.setupNewWindowEvent_(); | 339 this.setupNewWindowEvent_(); |
338 this.setupPermissionEvent_(); | 340 this.setupPermissionEvent_(); |
339 }; | 341 }; |
340 | 342 |
341 /** | 343 /** |
342 * @private | 344 * @private |
343 */ | 345 */ |
344 WebView.prototype.setupEvent_ = function(eventname, attribs) { | 346 WebView.prototype.setupExtEvent_ = function(eventName, eventInfo) { |
| 347 var self = this; |
345 var webviewNode = this.webviewNode_; | 348 var webviewNode = this.webviewNode_; |
346 var internalname = '-internal-' + eventname; | 349 eventInfo.evt.addListener(function(event) { |
| 350 var webviewEvent = new Event(eventName, {bubbles: true}); |
| 351 $Array.forEach(eventInfo.fields, function(field) { |
| 352 webviewEvent[field] = event[field]; |
| 353 }); |
| 354 if (eventInfo.customHandler) { |
| 355 eventInfo.customHandler(self, event); |
| 356 } |
| 357 webviewNode.dispatchEvent(webviewEvent); |
| 358 }, {instanceId: self.instanceId_}); |
| 359 }; |
| 360 |
| 361 /** |
| 362 * @private |
| 363 */ |
| 364 WebView.prototype.setupEvent_ = function(eventName, attribs) { |
| 365 var webviewNode = this.webviewNode_; |
| 366 var internalname = '-internal-' + eventName; |
347 this.browserPluginNode_.addEventListener(internalname, function(e) { | 367 this.browserPluginNode_.addEventListener(internalname, function(e) { |
348 var evt = new Event(eventname, { bubbles: true }); | 368 var evt = new Event(eventName, { bubbles: true }); |
349 var detail = e.detail ? JSON.parse(e.detail) : {}; | 369 var detail = e.detail ? JSON.parse(e.detail) : {}; |
350 $Array.forEach(attribs, function(attribName) { | 370 $Array.forEach(attribs, function(attribName) { |
351 evt[attribName] = detail[attribName]; | 371 evt[attribName] = detail[attribName]; |
352 }); | 372 }); |
353 webviewNode.dispatchEvent(evt); | 373 webviewNode.dispatchEvent(evt); |
354 }); | 374 }); |
355 }; | 375 }; |
356 | 376 |
357 /** | 377 /** |
358 * @private | 378 * @private |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
551 return []; | 571 return []; |
552 }; | 572 }; |
553 | 573 |
554 /** | 574 /** |
555 * Implemented when the experimental API is available. | 575 * Implemented when the experimental API is available. |
556 * @private | 576 * @private |
557 */ | 577 */ |
558 WebView.prototype.maybeSetupExperimentalAPI_ = function() {}; | 578 WebView.prototype.maybeSetupExperimentalAPI_ = function() {}; |
559 | 579 |
560 exports.WebView = WebView; | 580 exports.WebView = WebView; |
OLD | NEW |