Skip to content

Commit

Permalink
Merge branch 'develop' for release 0.0.2
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicolas Joseph committed May 14, 2015
2 parents 247ea4c + e7a1dad commit 52cb692
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 55 deletions.
12 changes: 12 additions & 0 deletions .scss/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,16 @@

#digest-time-distribution-range-slider{
margin-top: 20px;
}

.rickshaw_annotation_timeline .annotation.active .content {
display: inline-block;
}

.rickshaw_annotation_timeline .annotation:hover .content{
display: inline-block;
}

.rickshaw_annotation_timeline .annotation .content {
width: auto;
}
15 changes: 15 additions & 0 deletions extension/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,18 @@
#digest-time-distribution-range-slider {
margin-top: 20px;
}

/* line 17, ../../.scss/app.scss */
.rickshaw_annotation_timeline .annotation.active .content {
display: inline-block;
}

/* line 21, ../../.scss/app.scss */
.rickshaw_annotation_timeline .annotation:hover .content {
display: inline-block;
}

/* line 25, ../../.scss/app.scss */
.rickshaw_annotation_timeline .annotation .content {
width: auto;
}
29 changes: 26 additions & 3 deletions extension/src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ chrome.runtime.onConnect.addListener(function(port){
}

if (sender.tab) {

var tabId = sender.tab.id;

if (!contentScriptConnections[tabId]){
Expand All @@ -73,7 +74,7 @@ chrome.runtime.onConnect.addListener(function(port){
} else if (tabId in panelConnections) {
panelConnections[tabId].postMessage(message);
} else {
console.log('background.js - Tab not found in connection list.');
console.log('background.js - Tab not found in connection list.', sender.tab.id, panelConnections);
}
} else {
console.log('background.js - sender.tab not defined.');
Expand All @@ -93,7 +94,6 @@ chrome.runtime.onConnect.addListener(function(port){
panelConnections[message.tabId] = port

} else if (message.task === 'sendTaskToInspector') {

contentScriptConnections[message.tabId].postMessage(message.data)

} else if (message.task === 'log'){
Expand Down Expand Up @@ -136,6 +136,14 @@ chrome.runtime.onConnect.addListener(function(port){

port.onDisconnect.addListener(function(){
port.onMessage.removeListener(contentScriptListener);

var tabs = Object.keys(contentScriptConnections);
for (var i=0, len=tabs.length; i < len; i++) {
if (contentScriptConnections[tabs[i]] == port) {
delete contentScriptConnections[tabs[i]];
break;
}
}
});


Expand All @@ -148,12 +156,27 @@ chrome.runtime.onConnect.addListener(function(port){
port.onMessage.removeListener(panelListener);

var tabs = Object.keys(panelConnections);
for (var i=0, len=tabs.length; i < len; i++) {
for (var i = 0, len = tabs.length; i < len ; i++) {
if (panelConnections[tabs[i]] == port) {
delete panelConnections[tabs[i]];
// On panel closing, clean up the tab from all wrapped functions and removes the injector.js
contentScriptConnections[tabs[i]].postMessage({
task: 'cleanUpInspectedApp',
source: 'angular-performance'
});
break;
}
}
});
}
});

// We want to inject the content-script again if the page is refreshed
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo){
// The page has to be completely loaded before we inject the content script inside.
if (changeInfo.status === 'complete' && panelConnections[tabId]) {
chrome.tabs.executeScript(tabId, {
file: 'src/injected/content-script.js'
});
}
});
8 changes: 8 additions & 0 deletions extension/src/injected/content-script.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ window.addEventListener('message', function(event) {
return;
}

if (message.task === 'removeInspector'){
// removes the inspector from the DOM
var inspector = document.getElementById("angular-performance-inspector");
inspector.parentNode.removeChild(inspector);
return;
}

backgroundPageConnection.postMessage(message);
}, false);

Expand All @@ -129,6 +136,7 @@ USER_EVENTS.forEach(function(eventType){
// Add injected script to the page
var script = document.createElement('script');
script.type = 'text/javascript';
script.id = 'angular-performance-inspector';
script.src = chrome.extension.getURL('src/injected/inspector.js');
document.head.appendChild(script);

Expand Down
162 changes: 122 additions & 40 deletions extension/src/injected/inspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
'use strict';

var
_angularInjector;
_angularInjector,
_isMonitoringActive = true,
_backUp = {
digest: null,
modules: {}
};

console.log('angular-performance - Inspector loaded into webpage');

Expand All @@ -36,6 +41,40 @@
source: 'angular-performance-inspector'
}, '*');

// We listen for async instrumentation instructions
window.addEventListener('message', function(event){
// We only accept messages from ourselves
if (event.source != window || event.data.source !== 'angular-performance') {
return;
}

var message = event.data;

switch (message.task){

case 'checkModuleName':
var moduleServices = getNgModuleServices(message.moduleName);
// If there is no services the method will return an empty object, if the module name is
// invalid, it will return undefined.
sendTask('reportModuleExistence', {
moduleName: message.moduleName,
services: (moduleServices) ? Object.keys(moduleServices) : undefined
});
break;

case 'instrumentModuleServices':
instrumentModuleServices(message.moduleName);
break;

case 'cleanUpInspectedApp':
_isMonitoringActive = false;
cleanUpInspectedApp();
// Once everything is cleaned up, we can remove this script from the DOM
sendTask('removeInspector');
break;
}
});

bootstrapInspector();
}
}
Expand All @@ -49,48 +88,53 @@

_angularInjector = angular.element(document.querySelector('[ng-app]')).injector().get;

var $rootScope = getRootScope();
var scopePrototype = Object.getPrototypeOf($rootScope);
var oldDigest = scopePrototype.$digest;
instrumentDigest();
initWatcherCount();
}

/**
* This should clean up all that has been instrumented by the inspector and get them back
* to their normal behaviour. (UnWrap everything)
*/
function cleanUpInspectedApp(){
restoreDigest();
restoreModuleServices();
}

// ------------------------------------------------------------------------------------------
// Digest Monitoring
// ------------------------------------------------------------------------------------------

/**
* Wraps the angular digest so that we can measure how long it take for the digest to happen.
*/
function instrumentDigest(){

var scopePrototype = Object.getPrototypeOf(getRootScope());
_backUp.digest = scopePrototype.$digest;

scopePrototype.$digest = function $digest() {
var start = performance.now();
oldDigest.apply(this, arguments);
_backUp.digest.apply(this, arguments);
var time = (performance.now() - start);
register('DigestTiming', {
timestamp: Date.now(),
time: time
});
};
}

// We listen for async instrumentation instructions
window.addEventListener('message', function(event){
// We only accept messages from ourselves
if (event.source != window || event.data.source !== 'angular-performance') {
return;
}

var message = event.data;

if (message.task === 'checkModuleName'){

var moduleServices = getNgModuleServices(message.moduleName);

// If there is no services the method will return an empty object, if the module name is
// invalid, it will return undefined.

sendTask('reportModuleExistence', {
moduleName: message.moduleName,
services: (moduleServices) ? Object.keys(moduleServices) : undefined
});
} else if (message.task === 'instrumentModuleServices'){
instrumentModuleServices(message.moduleName);
}
});

initWatcherCount();
/**
* Restores the classic angular digest.
*/
function restoreDigest(){
Object.getPrototypeOf(getRootScope()).$digest = _backUp.digest;
}

// ------------------------------------------------------------------------------------------
// Scope & Watcher Exploration
// ------------------------------------------------------------------------------------------

/**
* Function to be called once to init the watcher retrieval.
*/
Expand All @@ -102,14 +146,11 @@
location: window.location.href
}
});
setTimeout(initWatcherCount, 300);
if (_isMonitoringActive) {
setTimeout(initWatcherCount, 300);
}
}

// ------------------------------------------------------------------------------------------
// Scope & Watcher Exploration
// ------------------------------------------------------------------------------------------


/**
* Retrieves the watcher count for a particular scope
*
Expand Down Expand Up @@ -279,8 +320,10 @@
services = {};
}

var module;

try {
var module = angular.module(moduleName);
module = angular.module(moduleName);
} catch(e){
return;
}
Expand All @@ -305,10 +348,12 @@
*/
function instrumentModuleServices(moduleName){

_backUp.modules[moduleName] = {};
var services = getNgModuleServices(moduleName);

angular.forEach(Object.keys(services), function(serviceName){

_backUp.modules[moduleName][serviceName] = {};
var service = services[serviceName];

angular.forEach(Object.getOwnPropertyNames(service), function(propertyName){
Expand All @@ -323,7 +368,7 @@
return;
}

var functionToWrap = service[propertyName];
var functionToWrap = _backUp.modules[moduleName][serviceName][propertyName] = service[propertyName];

// We Wrap all the service functions to measure execution time.
service[propertyName] = function(){
Expand Down Expand Up @@ -363,6 +408,43 @@
console.log('Module: ' + moduleName + ' successfully instrumented');
}

/**
* Restores the services functions as they were before being wrapped
*
* @param {String} [moduleName] - name of the module to be unwrapped. If not mentioned, everything
* should be restored.
*/
function restoreModuleServices(moduleName){

var modules;

if (moduleName){

if (_backUp.modules[moduleName]) {
modules = [moduleName];
} else {
throw new Error('angular performance - We tried to restore the module '+ moduleName + 'but ' +
'we could not find any back up :(');
}

} else {
modules = Object.keys(_backUp.modules);
}

angular.forEach(modules, function(module){
var services = getNgModuleServices(module);

angular.forEach(Object.keys(_backUp.modules[module]), function(service){
angular.forEach(Object.keys(_backUp.modules[module][service]), function(fnName){
services[service][fnName] = _backUp.modules[module][service][fnName]
});
});

// Clean up back up
delete _backUp.modules[module];
});
}

// ------------------------------------------------------------------------------------------
// Utils
// ------------------------------------------------------------------------------------------
Expand All @@ -371,7 +453,7 @@
* Reports a metric
*
* @param {String} task - task to do
* @param {Object} value - data that can be sent along with the task
* @param {Object} [value] - data that can be sent along with the task
*/
function sendTask(task, value){
window.postMessage({
Expand Down
Loading

0 comments on commit 52cb692

Please sign in to comment.