Web Inspector: Extract CommandLineAPI into its own InjectedScriptModule
https://bugs.webkit.org/show_bug.cgi?id=126038
Reviewed by Timothy Hatcher.
No tests, no observable change in behavior.
Move the CommandLineAPI source into its own module. Load the module
in InjectedScripts for WebCore::Pages. Not for workers.
Moving CommandLineAPI into it's own module moves it from being inside
the same anonymous function to being evaluated outside the anonymous
function. To connect the two InjectedScript passes itself to the
injected module, and the CommandLineAPI module places its class on the
injectedScript as injectedScript.CommandLineAPI.
This essentially makes the CommandLineAPI module an InjectedScript
extension. InjectedScriptSource checks for the existence of
this.CommandLineAPI to see if the fuller version is available. Otherwise
it falls back to a BasicCommandLineAPI which only exposes "$_",
which is the "last evaluated result". That will be useful for JS Contexts
and Workers.
At the same time, this patch makes InjectedScriptModule more generic,
to support being used in a pure JavaScript environment, meaning one
without "window" as the global object.
* CMakeLists.txt:
* DerivedSources.make:
* GNUmakefile.am:
* GNUmakefile.list.am:
* WebCore.vcxproj/WebCore.vcxproj:
* WebCore.vcxproj/WebCore.vcxproj.filters:
* WebCore.xcodeproj/project.pbxproj:
* inspector/InspectorAllInOne.cpp:
Add files. Minify the CommandLineAPIModuleSource in generation.
* inspector/CommandLineAPIModule.h: Added.
* inspector/CommandLineAPIModule.cpp: Added.
(WebCore::CommandLineAPIModule::CommandLineAPIModule):
(WebCore::CommandLineAPIModule::injectIfNeeded):
(WebCore::CommandLineAPIModule::source):
Inject the module that doesn't return an object, its just evaluated code
extending the original InjectedScript.
* inspector/InjectedScriptModule.h:
* inspector/InjectedScriptModule.cpp:
(WebCore::InjectedScriptModule::ensureInjected):
Only ASSERT the result was an object if the Module claims it returns an object.
* inspector/InjectedScriptCanvasModule.h:
(WebCore::InjectedScriptCanvasModule::returnsObject):
Return an object used later to call into the CanvasModule.
* inspector/PageRuntimeAgent.cpp:
(WebCore::PageRuntimeAgent::injectedScriptForEval):
Ensure the CommandLineAPIModule is loaded in the Page's InjectedScript.
* inspector/CommandLineAPIModuleSource.js: Added.
Create the CommandLineAPI class and place it on injectedScript.
* inspector/InjectedScriptSource.js:
(InjectedScript.prototype._evaluateOn):
Inject either the BasicCommandLineAPI or extended CommandLineAPI.
Derive the globalObject dynamically instead of assuming window.
Inject the commandLineAPI on window.console or the globalObject based on context.
Audit and rename uses of "window" to something like globalObject.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@160924 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/inspector/CommandLineAPIModuleSource.js b/Source/WebCore/inspector/CommandLineAPIModuleSource.js
new file mode 100644
index 0000000..7a2c5ae
--- /dev/null
+++ b/Source/WebCore/inspector/CommandLineAPIModuleSource.js
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @param {InjectedScriptHost} InjectedScriptHost
+ * @param {Window} inspectedWindow
+ * @param {number} injectedScriptId
+ */
+(function (InjectedScriptHost, inspectedWindow, injectedScriptId, injectedScript) {
+
+/**
+ * @param {Arguments} array
+ * @param {number=} index
+ * @return {Array.<*>}
+ */
+function slice(array, index)
+{
+ var result = [];
+ for (var i = index || 0; i < array.length; ++i)
+ result.push(array[i]);
+ return result;
+}
+
+/**
+ * Please use this bind, not the one from Function.prototype
+ * @param {function(...)} func
+ * @param {Object} thisObject
+ * @param {...number} var_args
+ */
+function bind(func, thisObject, var_args)
+{
+ var args = slice(arguments, 2);
+
+ /**
+ * @param {...number} var_args
+ */
+ function bound(var_args)
+ {
+ return func.apply(thisObject, args.concat(slice(arguments)));
+ }
+ bound.toString = function() {
+ return "bound: " + func;
+ };
+ return bound;
+}
+
+/**
+ * @constructor
+ * @param {CommandLineAPIImpl} commandLineAPIImpl
+ * @param {Object} callFrame
+ */
+function CommandLineAPI(commandLineAPIImpl, callFrame)
+{
+ /**
+ * @param {string} member
+ * @return {boolean}
+ */
+ function inScopeVariables(member)
+ {
+ if (!callFrame)
+ return false;
+
+ var scopeChain = callFrame.scopeChain;
+ for (var i = 0; i < scopeChain.length; ++i) {
+ if (member in scopeChain[i])
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param {string} name The name of the method for which a toString method should be generated.
+ * @return {function():string}
+ */
+ function customToStringMethod(name)
+ {
+ return function () { return "function " + name + "() { [Command Line API] }"; };
+ }
+
+ for (var i = 0; i < CommandLineAPI.members_.length; ++i) {
+ var member = CommandLineAPI.members_[i];
+ if (member in inspectedWindow || inScopeVariables(member))
+ continue;
+
+ this[member] = bind(commandLineAPIImpl[member], commandLineAPIImpl);
+ this[member].toString = customToStringMethod(member);
+ }
+
+ for (var i = 0; i < 5; ++i) {
+ var member = "$" + i;
+ if (member in inspectedWindow || inScopeVariables(member))
+ continue;
+
+ this.__defineGetter__("$" + i, bind(commandLineAPIImpl._inspectedObject, commandLineAPIImpl, i));
+ }
+
+ this.$_ = injectedScript._lastResult;
+}
+
+/**
+ * @type {Array.<string>}
+ * @const
+ */
+CommandLineAPI.members_ = [
+ "$", "$$", "$x", "dir", "dirxml", "keys", "values", "profile", "profileEnd",
+ "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear", "getEventListeners"
+];
+
+/**
+ * @constructor
+ */
+function CommandLineAPIImpl()
+{
+}
+
+CommandLineAPIImpl.prototype = {
+ /**
+ * @param {string} selector
+ * @param {Node=} start
+ */
+ $: function (selector, start)
+ {
+ if (this._canQuerySelectorOnNode(start))
+ return start.querySelector(selector);
+
+ var result = inspectedWindow.document.querySelector(selector);
+ if (result)
+ return result;
+ if (selector && selector[0] !== "#") {
+ result = inspectedWindow.document.getElementById(selector);
+ if (result) {
+ inspectedWindow.console.warn("The console function $() has changed from $=getElementById(id) to $=querySelector(selector). You might try $(\"#%s\")", selector );
+ return null;
+ }
+ }
+ return result;
+ },
+
+ /**
+ * @param {string} selector
+ * @param {Node=} start
+ */
+ $$: function (selector, start)
+ {
+ if (this._canQuerySelectorOnNode(start))
+ return start.querySelectorAll(selector);
+ return inspectedWindow.document.querySelectorAll(selector);
+ },
+
+ /**
+ * @param {Node=} node
+ * @return {boolean}
+ */
+ _canQuerySelectorOnNode: function(node)
+ {
+ return !!node && InjectedScriptHost.type(node) === "node" && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
+ },
+
+ /**
+ * @param {string} xpath
+ * @param {Node=} context
+ */
+ $x: function(xpath, context)
+ {
+ var doc = (context && context.ownerDocument) || inspectedWindow.document;
+ var result = doc.evaluate(xpath, context || doc, null, XPathResult.ANY_TYPE, null);
+ switch (result.resultType) {
+ case XPathResult.NUMBER_TYPE:
+ return result.numberValue;
+ case XPathResult.STRING_TYPE:
+ return result.stringValue;
+ case XPathResult.BOOLEAN_TYPE:
+ return result.booleanValue;
+ default:
+ var nodes = [];
+ var node;
+ while (node = result.iterateNext())
+ nodes.push(node);
+ return nodes;
+ }
+ },
+
+ dir: function()
+ {
+ return inspectedWindow.console.dir.apply(inspectedWindow.console, arguments)
+ },
+
+ dirxml: function()
+ {
+ return inspectedWindow.console.dirxml.apply(inspectedWindow.console, arguments)
+ },
+
+ keys: function(object)
+ {
+ return Object.keys(object);
+ },
+
+ values: function(object)
+ {
+ var result = [];
+ for (var key in object)
+ result.push(object[key]);
+ return result;
+ },
+
+ profile: function()
+ {
+ return inspectedWindow.console.profile.apply(inspectedWindow.console, arguments)
+ },
+
+ profileEnd: function()
+ {
+ return inspectedWindow.console.profileEnd.apply(inspectedWindow.console, arguments)
+ },
+
+ /**
+ * @param {Object} object
+ * @param {Array.<string>|string=} types
+ */
+ monitorEvents: function(object, types)
+ {
+ if (!object || !object.addEventListener || !object.removeEventListener)
+ return;
+ types = this._normalizeEventTypes(types);
+ for (var i = 0; i < types.length; ++i) {
+ object.removeEventListener(types[i], this._logEvent, false);
+ object.addEventListener(types[i], this._logEvent, false);
+ }
+ },
+
+ /**
+ * @param {Object} object
+ * @param {Array.<string>|string=} types
+ */
+ unmonitorEvents: function(object, types)
+ {
+ if (!object || !object.addEventListener || !object.removeEventListener)
+ return;
+ types = this._normalizeEventTypes(types);
+ for (var i = 0; i < types.length; ++i)
+ object.removeEventListener(types[i], this._logEvent, false);
+ },
+
+ /**
+ * @param {*} object
+ * @return {*}
+ */
+ inspect: function(object)
+ {
+ return injectedScript._inspect(object);
+ },
+
+ copy: function(object)
+ {
+ if (injectedScript._subtype(object) === "node")
+ object = object.outerHTML;
+ InjectedScriptHost.copyText(object);
+ },
+
+ clear: function()
+ {
+ InjectedScriptHost.clearConsoleMessages();
+ },
+
+ /**
+ * @param {Node} node
+ */
+ getEventListeners: function(node)
+ {
+ return InjectedScriptHost.getEventListeners(node);
+ },
+
+ /**
+ * @param {number} num
+ */
+ _inspectedObject: function(num)
+ {
+ return InjectedScriptHost.inspectedObject(num);
+ },
+
+ /**
+ * @param {Array.<string>|string=} types
+ * @return {Array.<string>}
+ */
+ _normalizeEventTypes: function(types)
+ {
+ if (typeof types === "undefined")
+ types = [ "mouse", "key", "touch", "control", "load", "unload", "abort", "error", "select", "change", "submit", "reset", "focus", "blur", "resize", "scroll", "search", "devicemotion", "deviceorientation" ];
+ else if (typeof types === "string")
+ types = [ types ];
+
+ var result = [];
+ for (var i = 0; i < types.length; i++) {
+ if (types[i] === "mouse")
+ result.splice(0, 0, "mousedown", "mouseup", "click", "dblclick", "mousemove", "mouseover", "mouseout", "mousewheel");
+ else if (types[i] === "key")
+ result.splice(0, 0, "keydown", "keyup", "keypress", "textInput");
+ else if (types[i] === "touch")
+ result.splice(0, 0, "touchstart", "touchmove", "touchend", "touchcancel");
+ else if (types[i] === "control")
+ result.splice(0, 0, "resize", "scroll", "zoom", "focus", "blur", "select", "change", "submit", "reset");
+ else
+ result.push(types[i]);
+ }
+ return result;
+ },
+
+ /**
+ * @param {Event} event
+ */
+ _logEvent: function(event)
+ {
+ inspectedWindow.console.log(event.type, event);
+ }
+}
+
+injectedScript.CommandLineAPI = CommandLineAPI;
+injectedScript._commandLineAPIImpl = new CommandLineAPIImpl();
+
+// This Module doesn't expose an object, it just adds an extension that InjectedScript uses.
+// However, we return an empty object, so that InjectedScript knows this module has been loaded.
+return {};
+
+})