From ee422a1957467759bee154d2b54a383b9fb19984 Mon Sep 17 00:00:00 2001 From: Brian Ford Date: Tue, 5 Nov 2013 11:03:00 -0800 Subject: [PATCH] wip --- js/background.js | 37 ++++++++++-------- js/controllers/ModelCtrl.js | 68 ++++++++++++++++++++-------------- js/inject/debug.js | 15 +++++++- js/panelApp.js | 29 ++++++++++++++- js/services/appContext.js | 63 +++++-------------------------- js/services/appDeps.js | 6 ++- js/services/appInfo.js | 6 ++- js/services/appModel.js | 43 +++++++++++++++------ js/services/appPerf.js | 8 ++-- js/services/chromeExtension.js | 6 +-- panes/model.html | 9 ++--- 11 files changed, 164 insertions(+), 126 deletions(-) diff --git a/js/background.js b/js/background.js index c76256a..9e2fffc 100644 --- a/js/background.js +++ b/js/background.js @@ -12,13 +12,16 @@ var scopeCacheEmpty = function () { var toForward = [ 'modelChange', 'scopeChange', + 'scopeDeleted', 'watcherChange', 'watchPerfChange', 'applyPerfChange' ]; chrome.extension.onConnect.addListener(function (port) { - port.onMessage.addListener(function (msg) { + + + port.onMessage.addListener(function (msg) { // [devtools] --> [background] // notify of page refreshes if (msg.action === 'register') { chrome.tabs.onUpdated.addListener(respond); @@ -28,34 +31,36 @@ chrome.extension.onConnect.addListener(function (port) { function respond (tabId, changeInfo, tab) { if (tabId === msg.inspectedTabId) { - port.postMessage('refresh'); + port.postMessage('refresh'); // [background] --> [devtools] + delete scopeCache[msg.appId]; // clear cache } } + + // immediately populate the scopes tree from the cache + Object.keys(scopeCache[msg.appId]).forEach(function (scopeId) { + port.postMessage({ // [background] --> [devtools] + action: 'scopeChange', + scope: scopeCache[msg.appId][scopeId], + scopeId: scopeId + }); + }); + } }); + // [content script] --> [background] --> [devtools] chrome.extension.onMessage.addListener(function (msg) { if (toForward.indexOf(msg.action) !== -1) { port.postMessage(msg); } }); - // immediately populate the scopes tree from the cache - - // TODO: how do we know that the cache refers to the - // tab that we're connected to? - Object.keys(scopeCache).forEach(function (scopeId) { - port.postMessage({ - action: 'scopeChange', - scope: scopeCache[scopeId], - scopeId: scopeId - }); - }); }); -chrome.extension.onMessage.addListener(function (msg) { +chrome.extension.onMessage.addListener(function (msg, sender) { if (msg.action === 'scopeChange') { - scopeCache[msg.id] = msg.scope; + scopeCache[msg.appId] = scopeCache[msg.appId] || {}; + scopeCache[msg.appId][msg.id] = msg.scope; } -}); \ No newline at end of file +}); diff --git a/js/controllers/ModelCtrl.js b/js/controllers/ModelCtrl.js index f4ae9e9..0b46168 100644 --- a/js/controllers/ModelCtrl.js +++ b/js/controllers/ModelCtrl.js @@ -1,50 +1,64 @@ -angular.module('panelApp').controller('ModelCtrl', function ModelCtrl($scope, appContext, appModel) { +angular.module('panelApp'). - $scope.modelsExpanded = true; - $scope.watchExpanded = true; +controller('ModelCtrl', function ModelCtrl($scope, appContext, appModel) { - $scope.roots = appModel.getRootScopes; - $scope.selectedRoot = null; - $scope.$watch('roots()', function (newVal, oldVal) { - if (newVal.length > 0 && newVal.indexOf($scope.selectedRoot) === -1) { - $scope.selectedRoot = newVal[0]; - } + $scope.rootScopeIds = appModel.getRootScopeIds(); + + $scope.$on('rootScopeChange', function () { + $scope.rootScopeIds = appModel.getRootScopeIds(); }); - - $scope.model = null; - - $scope.scopeTree = function () { - var select = $scope.selectedRoot; - if (!select) { - var rs = $scope.roots(); - if (rs.length === 0) { - return; + + $scope.$on('refresh', reset); + + function reset () { + $scope.modelsExpanded = true; + $scope.watchExpanded = true; + $scope.selectedRootScopeId = null; + } + + reset(); + + $scope.$watch('rootScopeIds', function (newVal, oldVal) { + if ($scope.selectedRootScopeId == null) { + if ($scope.rootScopeIds.some(function (id) { + return $scope.selectedRootScopeId = id; + })) { + while ($scope.rootScopeIds.indexOf(undefined) !== -1) { + $scope.rootScopeIds.splice($scope.rootScopeIds.indexOf(undefined), 1); + } } - select = rs[0]; } - return appModel.getScopeTree(select); - }; + }); + + $scope.$watch('selectedRootScopeId', function (newVal) { + $scope.scopeTree = newVal ? appModel.getScopeTree(newVal) : undefined; + }); + $scope.selectedScopeId = null; $scope.watching = {}; - appContext.watchModelChange(function (msg) { + $scope.$on('modelChange', function (ev, msg) { $scope.watching[msg.id] = $scope.watching[msg.id] || {}; - Object.keys(msg.changes).forEach(function (key) { - $scope.watching[msg.id][key] = msg.changes[key]; - }); + + Object.keys(msg.changes). + forEach(function (prop) { + $scope.watching[msg.id][prop] = msg.changes[prop]; + }); }); - appContext.watchWatcherChange(function (msg) { + $scope.$on('watcherChange', function (ev, msg) { $scope.watchers = msg.watchers; }); $scope.select = function () { - if ($scope.selectedScopeId === this.val.id) { + if (this.val && $scope.selectedScopeId === this.val.id) { return; } + appModel.unwatchModel($scope.selectedScopeId); delete $scope.watching[$scope.selectedScopeId]; + $scope.selectedScopeId = this.val.id; appModel.watchModel($scope.selectedScopeId); }; diff --git a/js/inject/debug.js b/js/inject/debug.js index 19a1366..716e54d 100644 --- a/js/inject/debug.js +++ b/js/inject/debug.js @@ -111,6 +111,7 @@ var instument = function instument (window) { customEvent.initEvent('myCustomEvent', true, true); var fireCustomEvent = function (data) { + data.appId = instrumentedAppId; eventProxyElement.innerText = JSON.stringify(data); eventProxyElement.dispatchEvent(customEvent); }; @@ -160,7 +161,7 @@ var instument = function instument (window) { }; var popover = null; - + var instrumentedAppId = window.location.host + '~' + Math.random(); // Utils @@ -250,6 +251,13 @@ var instument = function instument (window) { }); }, 50), + scopeDeleted: function (id) { + fireCustomEvent({ + action: 'scopeDeleted', + id: id + }); + }, + watcherChange: throttle(function (id) { if (debug.modelWatchers[id]) { fireCustomEvent({ @@ -348,6 +356,10 @@ var instument = function instument (window) { return Object.keys(debug.rootScopes); }, + getAppId: function () { + return instrumentedAppId; + }, + fireCustomEvent: fireCustomEvent, niceNames: function () { @@ -994,6 +1006,7 @@ var instument = function instument (window) { delete debug[prop][this.$id]; } }, this); + emit.scopeDeleted(this.$id); return _destroy.apply(this, arguments); }; diff --git a/js/panelApp.js b/js/panelApp.js index 990bd70..d4ffc6b 100644 --- a/js/panelApp.js +++ b/js/panelApp.js @@ -1,6 +1,33 @@ // Broadcast poll events -angular.module('panelApp', []).run(function ($rootScope) { +angular.module('panelApp', []). + +run(function ($rootScope, appContext) { + + // todo: kill this setInterval(function () { $rootScope.$broadcast('poll'); }, 500); + + var port = chrome.extension.connect(); + + port.onMessage.addListener(function (msg) { + if (msg === 'refresh') { + $rootScope.$apply(function () { + $rootScope.$broadcast('refresh'); + }); + } else if (msg.action) { + $rootScope.$apply(function () { + $rootScope.$broadcast(msg.action, msg); + }); + } + }); + + appContext.getAppId(function (id) { + port.postMessage({ + action: 'register', + appId: id, + inspectedTabId: chrome.devtools.inspectedWindow.tabId + }); + }); + }); diff --git a/js/services/appContext.js b/js/services/appContext.js index fac443a..76b86b2 100644 --- a/js/services/appContext.js +++ b/js/services/appContext.js @@ -1,8 +1,6 @@ // Service for running code in the context of the application being debugged angular.module('panelApp').factory('appContext', function (chromeExtension, $rootScope) { - var port = chrome.extension.connect(); - // Public API // ========== return { @@ -58,6 +56,8 @@ angular.module('panelApp').factory('appContext', function (chromeExtension, $roo } }, + getAppId: getAppId, + getDebug: function (cb) { chromeExtension.eval(function (window) { return document.cookie.indexOf('__ngDebug=true') !== -1; @@ -70,58 +70,13 @@ angular.module('panelApp').factory('appContext', function (chromeExtension, $roo chromeExtension.eval('function (window) {' + 'window.__ngDebug.log = ' + setting.toString() + ';' + '}'); - }, - - // Registering events - // ------------------ - - // TODO: depreciate this; only poll from now on? - // There are some cases where you need to gather data on a once-per-bootstrap basis, for - // instance getting the version of AngularJS - - // TODO: move to chromeExtension? - watchRefresh: function (cb) { - port.postMessage({ - action: 'register', - inspectedTabId: chrome.devtools.inspectedWindow.tabId - }); - port.onMessage.addListener(function (msg) { - if (msg === 'refresh') { - cb(); - } - }); - }, - - // TODO: move to chromeExtension? - watchModelChange: function (cb) { - port.onMessage.addListener(function (msg) { - if (msg.action === 'modelChange') { - $rootScope.$apply(function () { - cb(msg); - }); - } - }); - }, - - watchScopeChange: function (cb) { - port.onMessage.addListener(function (msg) { - if (msg.action === 'scopeChange') { - $rootScope.$apply(function () { - cb(msg); - }); - } - }); - }, - - watchWatcherChange: function (cb) { - port.onMessage.addListener(function (msg) { - if (msg.action === 'watcherChange') { - $rootScope.$apply(function () { - cb(msg); - }); - } - }); } - }; + + function getAppId (cb) { + chromeExtension.eval(function (window) { + return window.__ngDebug.getAppId(); + }, cb); + } + }); diff --git a/js/services/appDeps.js b/js/services/appDeps.js index bb4c028..7d77e5c 100644 --- a/js/services/appDeps.js +++ b/js/services/appDeps.js @@ -1,10 +1,12 @@ // Service for retrieving and caching application dependencies -angular.module('panelApp').factory('appDeps', function (chromeExtension, appContext) { +angular.module('panelApp'). + +factory('appDeps', function (chromeExtension, $rootScope) { var _depsCache = []; // clear cache on page refresh - appContext.watchRefresh(function () { + $rootScope.$on('refresh', function () { _depsCache = []; }); diff --git a/js/services/appInfo.js b/js/services/appInfo.js index 45d1223..014f521 100644 --- a/js/services/appInfo.js +++ b/js/services/appInfo.js @@ -1,11 +1,13 @@ // Service for running code in the context of the application being debugged -angular.module('panelApp').factory('appInfo', function (chromeExtension, appContext) { +angular.module('panelApp'). + +factory('appInfo', function (chromeExtension, $rootScope) { var _versionCache = null, _srcCache = null; // clear cache on page refresh - appContext.watchRefresh(function () { + $rootScope.$on('refresh', function () { _versionCache = null; _srcCache = null; }); diff --git a/js/services/appModel.js b/js/services/appModel.js index 488de14..6872138 100644 --- a/js/services/appModel.js +++ b/js/services/appModel.js @@ -1,26 +1,47 @@ // Service for running code in the context of the application being debugged -angular.module('panelApp').factory('appModel', function (chromeExtension, appContext) { +angular.module('panelApp'). + +factory('appModel', function ($rootScope, chromeExtension) { var _scopeTreeCache = {}, _scopeCache = {}, - _rootScopeCache = []; + _rootScopeIdCache = []; - appContext.watchRefresh(function clearCaches () { - _scopeCache = {}; - _scopeTreeCache = {}; - _rootScopeCache = []; + $rootScope.$on('referesh', function clearCaches () { + emptyObject(_scopeCache); + emptyObject(_scopeTreeCache); + emptyArray(_rootScopeIdCache); }); - appContext.watchScopeChange(function (data) { - if (_rootScopeCache.indexOf(data.id) === -1) { - _rootScopeCache.push(data.id); + function emptyObject (obj) { + for (prop in obj) { + if (obj.hasOwnProperty(obj)) { + delete obj[prop]; + } + } + } + + function emptyArray (arr) { + arr.splice(0, arr.length); + } + + $rootScope.$on('scopeChange', function (ev, data) { + if (_rootScopeIdCache.indexOf(data.id) === -1) { + _rootScopeIdCache.push(data.id); + $rootScope.$broadcast('rootScopeChange', _rootScopeIdCache); } _scopeTreeCache[data.id] = data.scope; }); + $rootScope.$on('scopeDeleted', function (ev, data) { + _rootScopeIdCache.splice(_rootScopeIdCache.indexOf(data.id), 1); + $rootScope.$broadcast('rootScopeChange', _rootScopeIdCache); + delete _scopeTreeCache[data.id]; + }); + return { - getRootScopes: function () { - return _rootScopeCache; + getRootScopeIds: function () { + return _rootScopeIdCache.slice(); }, getModel: function (id, path, callback) { diff --git a/js/services/appPerf.js b/js/services/appPerf.js index fe5c9a7..7b8db4a 100644 --- a/js/services/appPerf.js +++ b/js/services/appPerf.js @@ -1,5 +1,7 @@ // Service for retrieving and caching performance data -angular.module('panelApp').factory('appPerf', function (chromeExtension, appContext) { +angular.module('panelApp'). + +factory('appPerf', function (chromeExtension, $rootScope) { var _histogramCache = [], _watchNameToPerf = {}, @@ -12,9 +14,7 @@ angular.module('panelApp').factory('appPerf', function (chromeExtension, appCont }; // clear cache on page refresh - appContext.watchRefresh(function () { - clear(); - }); + $rootScope.$on('refresh', clear); var getHistogramData = function (callback) { chromeExtension.eval(function (window) { diff --git a/js/services/chromeExtension.js b/js/services/chromeExtension.js index 1a6ae01..08f782b 100644 --- a/js/services/chromeExtension.js +++ b/js/services/chromeExtension.js @@ -1,5 +1,7 @@ // abstraction layer for Chrome Extension APIs -angular.module('panelApp').value('chromeExtension', { +angular.module('panelApp'). + +value('chromeExtension', { sendRequest: function (requestName, cb) { chrome.extension.sendRequest({ script: requestName, @@ -8,8 +10,6 @@ angular.module('panelApp').value('chromeExtension', { }, // evaluates in the context of a window - //written because I don't like the API for chrome.devtools.inspectedWindow.eval; - // passing strings instead of functions are gross. eval: function (fn, args, cb) { // with two args if (!cb && typeof args === 'function') { diff --git a/panes/model.html b/panes/model.html index 509c8d8..034dacc 100644 --- a/panes/model.html +++ b/panes/model.html @@ -1,16 +1,15 @@
-
+