|
13 | 13 | 'use strict';
|
14 | 14 |
|
15 | 15 | var
|
16 |
| - _angularInjector; |
| 16 | + _angularInjector, |
| 17 | + _isMonitoringActive = true, |
| 18 | + _backUp = { |
| 19 | + digest: null, |
| 20 | + modules: {} |
| 21 | + }; |
17 | 22 |
|
18 | 23 | console.log('angular-performance - Inspector loaded into webpage');
|
19 | 24 |
|
|
36 | 41 | source: 'angular-performance-inspector'
|
37 | 42 | }, '*');
|
38 | 43 |
|
| 44 | + // We listen for async instrumentation instructions |
| 45 | + window.addEventListener('message', function(event){ |
| 46 | + // We only accept messages from ourselves |
| 47 | + if (event.source != window || event.data.source !== 'angular-performance') { |
| 48 | + return; |
| 49 | + } |
| 50 | + |
| 51 | + var message = event.data; |
| 52 | + |
| 53 | + switch (message.task){ |
| 54 | + |
| 55 | + case 'checkModuleName': |
| 56 | + var moduleServices = getNgModuleServices(message.moduleName); |
| 57 | + // If there is no services the method will return an empty object, if the module name is |
| 58 | + // invalid, it will return undefined. |
| 59 | + sendTask('reportModuleExistence', { |
| 60 | + moduleName: message.moduleName, |
| 61 | + services: (moduleServices) ? Object.keys(moduleServices) : undefined |
| 62 | + }); |
| 63 | + break; |
| 64 | + |
| 65 | + case 'instrumentModuleServices': |
| 66 | + instrumentModuleServices(message.moduleName); |
| 67 | + break; |
| 68 | + |
| 69 | + case 'cleanUpInspectedApp': |
| 70 | + _isMonitoringActive = false; |
| 71 | + cleanUpInspectedApp(); |
| 72 | + // Once everything is cleaned up, we can remove this script from the DOM |
| 73 | + sendTask('removeInspector'); |
| 74 | + break; |
| 75 | + } |
| 76 | + }); |
| 77 | + |
39 | 78 | bootstrapInspector();
|
40 | 79 | }
|
41 | 80 | }
|
|
49 | 88 |
|
50 | 89 | _angularInjector = angular.element(document.querySelector('[ng-app]')).injector().get;
|
51 | 90 |
|
52 |
| - var $rootScope = getRootScope(); |
53 |
| - var scopePrototype = Object.getPrototypeOf($rootScope); |
54 |
| - var oldDigest = scopePrototype.$digest; |
| 91 | + instrumentDigest(); |
| 92 | + initWatcherCount(); |
| 93 | + } |
| 94 | + |
| 95 | + /** |
| 96 | + * This should clean up all that has been instrumented by the inspector and get them back |
| 97 | + * to their normal behaviour. (UnWrap everything) |
| 98 | + */ |
| 99 | + function cleanUpInspectedApp(){ |
| 100 | + restoreDigest(); |
| 101 | + restoreModuleServices(); |
| 102 | + } |
| 103 | + |
| 104 | + // ------------------------------------------------------------------------------------------ |
| 105 | + // Digest Monitoring |
| 106 | + // ------------------------------------------------------------------------------------------ |
| 107 | + |
| 108 | + /** |
| 109 | + * Wraps the angular digest so that we can measure how long it take for the digest to happen. |
| 110 | + */ |
| 111 | + function instrumentDigest(){ |
| 112 | + |
| 113 | + var scopePrototype = Object.getPrototypeOf(getRootScope()); |
| 114 | + _backUp.digest = scopePrototype.$digest; |
55 | 115 |
|
56 | 116 | scopePrototype.$digest = function $digest() {
|
57 | 117 | var start = performance.now();
|
58 |
| - oldDigest.apply(this, arguments); |
| 118 | + _backUp.digest.apply(this, arguments); |
59 | 119 | var time = (performance.now() - start);
|
60 | 120 | register('DigestTiming', {
|
61 | 121 | timestamp: Date.now(),
|
62 | 122 | time: time
|
63 | 123 | });
|
64 | 124 | };
|
| 125 | + } |
65 | 126 |
|
66 |
| - // We listen for async instrumentation instructions |
67 |
| - window.addEventListener('message', function(event){ |
68 |
| - // We only accept messages from ourselves |
69 |
| - if (event.source != window || event.data.source !== 'angular-performance') { |
70 |
| - return; |
71 |
| - } |
72 |
| - |
73 |
| - var message = event.data; |
74 |
| - |
75 |
| - if (message.task === 'checkModuleName'){ |
76 |
| - |
77 |
| - var moduleServices = getNgModuleServices(message.moduleName); |
78 |
| - |
79 |
| - // If there is no services the method will return an empty object, if the module name is |
80 |
| - // invalid, it will return undefined. |
81 |
| - |
82 |
| - sendTask('reportModuleExistence', { |
83 |
| - moduleName: message.moduleName, |
84 |
| - services: (moduleServices) ? Object.keys(moduleServices) : undefined |
85 |
| - }); |
86 |
| - } else if (message.task === 'instrumentModuleServices'){ |
87 |
| - instrumentModuleServices(message.moduleName); |
88 |
| - } |
89 |
| - }); |
90 |
| - |
91 |
| - initWatcherCount(); |
| 127 | + /** |
| 128 | + * Restores the classic angular digest. |
| 129 | + */ |
| 130 | + function restoreDigest(){ |
| 131 | + Object.getPrototypeOf(getRootScope()).$digest = _backUp.digest; |
92 | 132 | }
|
93 | 133 |
|
| 134 | + // ------------------------------------------------------------------------------------------ |
| 135 | + // Scope & Watcher Exploration |
| 136 | + // ------------------------------------------------------------------------------------------ |
| 137 | + |
94 | 138 | /**
|
95 | 139 | * Function to be called once to init the watcher retrieval.
|
96 | 140 | */
|
|
102 | 146 | location: window.location.href
|
103 | 147 | }
|
104 | 148 | });
|
105 |
| - setTimeout(initWatcherCount, 300); |
| 149 | + if (_isMonitoringActive) { |
| 150 | + setTimeout(initWatcherCount, 300); |
| 151 | + } |
106 | 152 | }
|
107 | 153 |
|
108 |
| - // ------------------------------------------------------------------------------------------ |
109 |
| - // Scope & Watcher Exploration |
110 |
| - // ------------------------------------------------------------------------------------------ |
111 |
| - |
112 |
| - |
113 | 154 | /**
|
114 | 155 | * Retrieves the watcher count for a particular scope
|
115 | 156 | *
|
|
279 | 320 | services = {};
|
280 | 321 | }
|
281 | 322 |
|
| 323 | + var module; |
| 324 | + |
282 | 325 | try {
|
283 |
| - var module = angular.module(moduleName); |
| 326 | + module = angular.module(moduleName); |
284 | 327 | } catch(e){
|
285 | 328 | return;
|
286 | 329 | }
|
|
305 | 348 | */
|
306 | 349 | function instrumentModuleServices(moduleName){
|
307 | 350 |
|
| 351 | + _backUp.modules[moduleName] = {}; |
308 | 352 | var services = getNgModuleServices(moduleName);
|
309 | 353 |
|
310 | 354 | angular.forEach(Object.keys(services), function(serviceName){
|
311 | 355 |
|
| 356 | + _backUp.modules[moduleName][serviceName] = {}; |
312 | 357 | var service = services[serviceName];
|
313 | 358 |
|
314 | 359 | angular.forEach(Object.getOwnPropertyNames(service), function(propertyName){
|
|
323 | 368 | return;
|
324 | 369 | }
|
325 | 370 |
|
326 |
| - var functionToWrap = service[propertyName]; |
| 371 | + var functionToWrap = _backUp.modules[moduleName][serviceName][propertyName] = service[propertyName]; |
327 | 372 |
|
328 | 373 | // We Wrap all the service functions to measure execution time.
|
329 | 374 | service[propertyName] = function(){
|
|
363 | 408 | console.log('Module: ' + moduleName + ' successfully instrumented');
|
364 | 409 | }
|
365 | 410 |
|
| 411 | + /** |
| 412 | + * Restores the services functions as they were before being wrapped |
| 413 | + * |
| 414 | + * @param {String} [moduleName] - name of the module to be unwrapped. If not mentioned, everything |
| 415 | + * should be restored. |
| 416 | + */ |
| 417 | + function restoreModuleServices(moduleName){ |
| 418 | + |
| 419 | + var modules; |
| 420 | + |
| 421 | + if (moduleName){ |
| 422 | + |
| 423 | + if (_backUp.modules[moduleName]) { |
| 424 | + modules = [moduleName]; |
| 425 | + } else { |
| 426 | + throw new Error('angular performance - We tried to restore the module '+ moduleName + 'but ' + |
| 427 | + 'we could not find any back up :('); |
| 428 | + } |
| 429 | + |
| 430 | + } else { |
| 431 | + modules = Object.keys(_backUp.modules); |
| 432 | + } |
| 433 | + |
| 434 | + angular.forEach(modules, function(module){ |
| 435 | + var services = getNgModuleServices(module); |
| 436 | + |
| 437 | + angular.forEach(Object.keys(_backUp.modules[module]), function(service){ |
| 438 | + angular.forEach(Object.keys(_backUp.modules[module][service]), function(fnName){ |
| 439 | + services[service][fnName] = _backUp.modules[module][service][fnName] |
| 440 | + }); |
| 441 | + }); |
| 442 | + |
| 443 | + // Clean up back up |
| 444 | + delete _backUp.modules[module]; |
| 445 | + }); |
| 446 | + } |
| 447 | + |
366 | 448 | // ------------------------------------------------------------------------------------------
|
367 | 449 | // Utils
|
368 | 450 | // ------------------------------------------------------------------------------------------
|
|
371 | 453 | * Reports a metric
|
372 | 454 | *
|
373 | 455 | * @param {String} task - task to do
|
374 |
| - * @param {Object} value - data that can be sent along with the task |
| 456 | + * @param {Object} [value] - data that can be sent along with the task |
375 | 457 | */
|
376 | 458 | function sendTask(task, value){
|
377 | 459 | window.postMessage({
|
|
0 commit comments