forked from googlearchive/polymer-devtools-extension
-
Notifications
You must be signed in to change notification settings - Fork 0
/
evalHelper.js
155 lines (154 loc) · 5.71 KB
/
evalHelper.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
* A helper object to help `eval` code in host page
*/
function createEvalHelper(callback) {
// The extension's ID serves as the namespace.
var extensionNamespace = chrome.runtime.id;
/**
* Converts any object to a string
*/
function serialize(object) {
return JSON.stringify(object);
}
var srcURLID = 0;
/**
* gets a unique src URL.
*/
function getSrcURL(string) {
srcURLID++;
return '\n//@ sourceURL=src' + srcURLID + '.js';
}
/**
* Wraps a function into a self executing function that gets called with the
* unique namespace (extension ID) so that the function to be defined in the
* host page gets to know of the namespace and also gets defined in the same
* namespace.
* @param {String} fnName name of the function
* @param {String} fnString body of the function
* @return {String} the wrapped function string
*/
function wrapFunction(fnName, fnString) {
return '(function (NAMESPACE) {' +
'window["' + extensionNamespace + '"].' + fnName + ' = ' +
fnString + ';' +
'})("' + extensionNamespace + '");';
}
var helper = {
/**
* Define a function
* @param {String} name Name of the function
* @param {String} string Body of the function
* @param {Function} callback Function to be called after definion of function
*/
defineFunction: function(name, string, callback) {
chrome.devtools.inspectedWindow.eval(wrapFunction(name, string) + getSrcURL(),
function(result, error) {
callback && callback(result, error);
});
},
/**
* Define functions in a batch
* @param {Array} functionObjects Objects that have `name` and `string` keys
* to mean the name and body of the function respectively
* @param {Function} callback Function called when definitions are done
*/
defineFunctions: function(functionObjects, callback) {
var toEval = '';
for (var i = 0; i < functionObjects.length; i++) {
toEval += wrapFunction(functionObjects[i].name, functionObjects[i].string) + ';\n\n';
}
toEval += getSrcURL();
chrome.devtools.inspectedWindow.eval(toEval, function(result, error) {
callback && callback(result, error);
});
},
/**
* Execute a function with args and optionally assign the result to something
* @param {String} name Name of the function
* @param {Array} args An array of arguments
* @param {Function} callback Called when the execution is done
* @param {String} lhs Name of the variable to assign result to
*/
executeFunction: function(name, args, callback, lhs) {
var params = '(';
for (var i = 0; i < args.length - 1; i++) {
params += serialize(args[i]) + ', ';
}
if (args.length > 0) {
params += serialize(args[i]);
}
params += ')';
var toEval = (lhs ? ('window["' + extensionNamespace + '"].' + lhs + ' = ') : '') +
'window["' + extensionNamespace + '"].' + name + params + ';';
toEval += getSrcURL();
chrome.devtools.inspectedWindow.eval(toEval, function(result, error) {
callback && callback(result, error);
});
}
};
/**
* Does the necessary clean-up to remove all traces of the extension in the page
*/
function cleanUp() {
window.removeEventListener('clean-up', window[NAMESPACE].cleanUp);
var keys;
var i, j, methodNames;
// Remove all object observers that were registered
keys = Object.keys(window[NAMESPACE].observerCache);
for (i = 0; i < keys.length; i++) {
window[NAMESPACE].removeObjectObserver(keys[i], [], false);
}
// Remove all model object observers that were registered
keys = Object.keys(window[NAMESPACE].modelObserverCache);
for (i = 0; i < keys.length; i++) {
window[NAMESPACE].removeObjectObserver(keys[i], [], true);
}
// Remove any breakpoints that were set
keys = Object.keys(window[NAMESPACE].breakPointIndices);
for (i = 0; i < keys.length; i++) {
methodNames = Object.keys(window[NAMESPACE].breakPointIndices[keys[i]]);
for (j = 0; j < methodNames.length; j++) {
if (methodNames[i] in window[NAMESPACE].DOMCache[keys[i]]) {
undebug(window[NAMESPACE].DOMCache[keys[i]][methodNames[i]]);
}
}
}
keys = Object.keys(window[NAMESPACE].DOMCache);
for (i = 0; i < keys.length; i++) {
// Remove DOM mutation observers
if (keys[i] in window[NAMESPACE].mutationObserverCache) {
window[NAMESPACE].mutationObserverCache[keys[i]].disconnect();
}
// Remove the key property that we had added to all DOM objects
delete window[NAMESPACE].DOMCache[keys[i]].__keyPolymer__;
}
// Unhighlight any selected element
if (window[NAMESPACE].lastSelectedKey) {
window[NAMESPACE].unhighlight(window[NAMESPACE].lastSelectedKey, false);
}
// TODO: Unhighlight hovered elements too
delete window[NAMESPACE];
}
// Wait till the namespace is created and clean-up handler is created.
chrome.devtools.inspectedWindow.eval('window["' + extensionNamespace + '"] = {};',
function(result, error) {
// Define cleanUp
helper.defineFunction('cleanUp', cleanUp.toString(), function(result, error) {
if (error) {
throw error;
}
// Add an event listener that removes itself
chrome.devtools.inspectedWindow.eval('window.addEventListener("clean-up", ' +
'window["' + extensionNamespace + '"].cleanUp);',
function(result, error) {
if (error) {
throw error;
}
// We are ready to let helper be used
callback(helper);
}
);
});
}
);
}