diff --git a/js/background.js b/js/background.js index c64b09a..c76256a 100644 --- a/js/background.js +++ b/js/background.js @@ -1,29 +1,61 @@ -// notify of page refreshes +// scopes need to be cached here so that if the devtools connects, +// the list of scopes can be immediately populated +// TODO: clear this on refresh ? +var scopeCache = {}; + +var scopeCacheEmpty = function () { + return Object.keys(scopeCache).length; +}; + +// messages to be forwarded from content script to the devtools +var toForward = [ + 'modelChange', + 'scopeChange', + 'watcherChange', + 'watchPerfChange', + 'applyPerfChange' +]; + chrome.extension.onConnect.addListener(function (port) { port.onMessage.addListener(function (msg) { + // notify of page refreshes if (msg.action === 'register') { - var respond = function (tabId, changeInfo, tab) { - if (tabId !== msg.inspectedTabId) { - return; - } - port.postMessage('refresh'); - }; - chrome.tabs.onUpdated.addListener(respond); port.onDisconnect.addListener(function () { chrome.tabs.onUpdated.removeListener(respond); }); + + function respond (tabId, changeInfo, tab) { + if (tabId === msg.inspectedTabId) { + port.postMessage('refresh'); + } + } } }); chrome.extension.onMessage.addListener(function (msg) { - if (msg.action === 'modelChange' || - msg.action === 'scopeChange' || - msg.action === 'watcherChange') { - + 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) { + if (msg.action === 'scopeChange') { + scopeCache[msg.id] = msg.scope; + } +}); \ No newline at end of file diff --git a/js/inject/debug.js b/js/inject/debug.js index 972e081..19a1366 100644 --- a/js/inject/debug.js +++ b/js/inject/debug.js @@ -145,6 +145,9 @@ var instument = function instument (window) { // map of scope.$ids --> $scope objects scopes: {}, + // whether or not to emit profile data + profiling: false, + // map of $ids --> [] array of things being watched modelWatchers: {}, // map of $id + watcher --> value @@ -257,6 +260,26 @@ var instument = function instument (window) { } }, 50), + watchPerfChange: throttle(function (str) { + if (debug.profiling) { + fireCustomEvent({ + action: 'watchPerfChange', + watcher: str, + value: debug.watchPerf[str] + }); + } + }, 50), + + applyPerfChange: throttle(function (str) { + if (debug.profiling) { + fireCustomEvent({ + action: 'applyPerfChange', + watcher: str, + value: debug.applyPerf[str] + }); + } + }, 50), + // might be worth limiting watchPerf: function () { throw new Error('Implement me :c'); @@ -313,6 +336,10 @@ var instument = function instument (window) { var api = window.__ngDebug = { + profiling: function (setting) { + debug.profiling = setting; + }, + getDeps: function () { return debug.deps; }, @@ -340,18 +367,32 @@ var instument = function instument (window) { ]. forEach(function (attr) { var val = $elt.attr(attr), - className = $elt[0].className; + className = $elt[0].className, + match, + lhs, + valueIdentifier, + keyIdentifier; + if (val) { name[attr] = val; if (attr === 'ng-repeat') { - var lhs = /(.+) in/.exec(val); - lhs = lhs[1]; - name.lhs = lhs + summarizeObject(scope[lhs]); + match = /(.+) in/.exec(val); + lhs = match[1]; + + match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); + valueIdentifier = match[3] || match[1]; + keyIdentifier = match[2]; + + if (keyIdentifier) { + name.lhs = valueIdentifier + '["' + scope[keyIdentifier] + '"]' + summarizeObject(scope[valueIdentifier]); + } else { + name.lhs = valueIdentifier + summarizeObject(scope[valueIdentifier]); + } + } } else if (className.indexOf(attr) !== -1) { - val = (new RegExp(attr + ': ([a-zA-Z0-9]+);')).exec(className); - val = val[1]; - name[attr] = val; + match = (new RegExp(attr + ': ([a-zA-Z0-9]+);')).exec(className); + name[attr] = match[1]; } }); @@ -430,6 +471,9 @@ var instument = function instument (window) { watchModel: function (id, path) { debug.modelWatchers[id] = debug.modelWatchers[id] || {}; debug.modelWatchers[id][path || ''] = true; + if (!path || path === '') { + debug.modelWatchersState = {}; + } emit.modelChange(id); emit.watcherChange(id); }, @@ -895,6 +939,7 @@ var instument = function instument (window) { var end = performance.now(); debug.watchPerf[watchStr].time += (end - start); debug.watchPerf[watchStr].calls += 1; + emit.watchPerfChange(watchStr); return ret; }; } else { @@ -904,6 +949,7 @@ var instument = function instument (window) { var end = performance.now(); debug.watchPerf[watchStr].time += (end - start); debug.watchPerf[watchStr].calls += 1; + emit.watchPerfChange(watchStr); return ret; }; } @@ -927,6 +973,7 @@ var instument = function instument (window) { } debug.applyPerf[applyStr].time += (end - start); debug.applyPerf[applyStr].calls += 1; + emit.applyPerfChange(applyStr); return ret; }; } diff --git a/js/services/appContext.js b/js/services/appContext.js index a34e7ea..fac443a 100644 --- a/js/services/appContext.js +++ b/js/services/appContext.js @@ -31,12 +31,6 @@ angular.module('panelApp').factory('appContext', function (chromeExtension, $roo "}", args, cb); }, - refresh: function (cb) { - chromeExtension.eval(function (window) { - window.document.location.reload(); - }, cb); - }, - inspect: function (scopeId) { this.executeOnScope(scopeId, function (scope, elt) { inspect(elt); @@ -49,9 +43,12 @@ angular.module('panelApp').factory('appContext', function (chromeExtension, $roo // takes a bool setDebug: function (setting) { if (setting) { + // prevent a refresh if debugging is already enabled chromeExtension.eval(function (window) { - window.document.cookie = '__ngDebug=true;'; - window.document.location.reload(); + if (document.cookie.indexOf('__ngDebug=true') === -1) { + window.document.cookie = '__ngDebug=true;'; + window.document.location.reload(); + } }); } else { chromeExtension.eval(function (window) { diff --git a/js/services/appModel.js b/js/services/appModel.js index e5ebc24..488de14 100644 --- a/js/services/appModel.js +++ b/js/services/appModel.js @@ -5,9 +5,7 @@ angular.module('panelApp').factory('appModel', function (chromeExtension, appCon _scopeCache = {}, _rootScopeCache = []; - - // clear cache on page refresh - appContext.watchRefresh(function () { + appContext.watchRefresh(function clearCaches () { _scopeCache = {}; _scopeTreeCache = {}; _rootScopeCache = [];