diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2012-11-07 11:22:47 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2012-11-07 11:22:47 +0100 |
commit | cfd86b747d32ac22246a1aa908eaa720c63a88c1 (patch) | |
tree | 24d68c6f61c464ecba1e05670b80390ea3b0e50c /Source/WebCore/inspector | |
parent | 69d7c744c9de19d152dbe2d8e46eb7dfd4511d1a (diff) | |
download | qtwebkit-cfd86b747d32ac22246a1aa908eaa720c63a88c1.tar.gz |
Imported WebKit commit 20271caf2e2c016d5cef40184cddeefeac4f1876 (http://svn.webkit.org/repository/webkit/trunk@133733)
New snapshot that contains all previous fixes as well as build fix for latest QtMultimedia API changes.
Diffstat (limited to 'Source/WebCore/inspector')
157 files changed, 6722 insertions, 3884 deletions
diff --git a/Source/WebCore/inspector/BindingVisitors.h b/Source/WebCore/inspector/BindingVisitors.h index 7530c9247..c885703ac 100644 --- a/Source/WebCore/inspector/BindingVisitors.h +++ b/Source/WebCore/inspector/BindingVisitors.h @@ -38,11 +38,11 @@ namespace WebCore { class Node; -class NodeWrapperVisitor { +class WrappedNodeVisitor { public: virtual void visitNode(Node*) = 0; protected: - virtual ~NodeWrapperVisitor() { } + virtual ~WrappedNodeVisitor() { } }; class ExternalStringVisitor { diff --git a/Source/WebCore/inspector/ConsoleMessage.cpp b/Source/WebCore/inspector/ConsoleMessage.cpp index 801755390..5a873622b 100644 --- a/Source/WebCore/inspector/ConsoleMessage.cpp +++ b/Source/WebCore/inspector/ConsoleMessage.cpp @@ -35,6 +35,7 @@ #include "ConsoleMessage.h" #include "Console.h" +#include "IdentifiersFactory.h" #include "InjectedScript.h" #include "InjectedScriptManager.h" #include "InspectorFrontend.h" @@ -46,7 +47,7 @@ namespace WebCore { -ConsoleMessage::ConsoleMessage(MessageSource s, MessageType t, MessageLevel l, const String& m, const String& u, unsigned li, const String& requestId) +ConsoleMessage::ConsoleMessage(MessageSource s, MessageType t, MessageLevel l, const String& m, const String& u, unsigned li, unsigned long requestIdentifier) : m_source(s) , m_type(t) , m_level(l) @@ -54,11 +55,11 @@ ConsoleMessage::ConsoleMessage(MessageSource s, MessageType t, MessageLevel l, c , m_url(u) , m_line(li) , m_repeatCount(1) - , m_requestId(requestId) + , m_requestId(IdentifiersFactory::requestId(requestIdentifier)) { } -ConsoleMessage::ConsoleMessage(MessageSource s, MessageType t, MessageLevel l, const String& m, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) +ConsoleMessage::ConsoleMessage(MessageSource s, MessageType t, MessageLevel l, const String& m, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack, unsigned long requestIdentifier) : m_source(s) , m_type(t) , m_level(l) @@ -67,6 +68,7 @@ ConsoleMessage::ConsoleMessage(MessageSource s, MessageType t, MessageLevel l, c , m_url() , m_line(0) , m_repeatCount(1) + , m_requestId(IdentifiersFactory::requestId(requestIdentifier)) { if (callStack && callStack->size()) { const ScriptCallFrame& frame = callStack->at(0); @@ -76,7 +78,7 @@ ConsoleMessage::ConsoleMessage(MessageSource s, MessageType t, MessageLevel l, c m_callStack = callStack; } -ConsoleMessage::ConsoleMessage(MessageSource s, MessageType t, MessageLevel l, const String& m, const String& responseUrl, const String& requestId) +ConsoleMessage::ConsoleMessage(MessageSource s, MessageType t, MessageLevel l, const String& m, const String& responseUrl, unsigned long requestIdentifier) : m_source(s) , m_type(t) , m_level(l) @@ -84,7 +86,7 @@ ConsoleMessage::ConsoleMessage(MessageSource s, MessageType t, MessageLevel l, c , m_url(responseUrl) , m_line(0) , m_repeatCount(1) - , m_requestId(requestId) + , m_requestId(IdentifiersFactory::requestId(requestIdentifier)) { } @@ -110,6 +112,7 @@ static TypeBuilder::Console::ConsoleMessage::Type::Enum messageTypeValue(Message { switch (type) { case LogMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Log; + case ClearMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Clear; case DirMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Dir; case DirXMLMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Dirxml; case TraceMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Trace; diff --git a/Source/WebCore/inspector/ConsoleMessage.h b/Source/WebCore/inspector/ConsoleMessage.h index 6461db720..07efa1762 100644 --- a/Source/WebCore/inspector/ConsoleMessage.h +++ b/Source/WebCore/inspector/ConsoleMessage.h @@ -51,9 +51,9 @@ class ScriptValue; class ConsoleMessage { WTF_MAKE_NONCOPYABLE(ConsoleMessage); WTF_MAKE_FAST_ALLOCATED; public: - ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& m, const String& u, unsigned li, const String& requestId = String()); - ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& m, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack>); - ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& m, const String& responseUrl, const String& requestId); + ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& m, const String& u, unsigned li, unsigned long requestIdentifier = 0); + ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& m, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack>, unsigned long requestIdentifier = 0); + ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& m, const String& responseUrl, unsigned long requestIdentifier); ~ConsoleMessage(); void addToFrontend(InspectorFrontend::Console*, InjectedScriptManager*, bool generatePreview); diff --git a/Source/WebCore/inspector/IdentifiersFactory.cpp b/Source/WebCore/inspector/IdentifiersFactory.cpp index c2af2a566..dce2a2850 100644 --- a/Source/WebCore/inspector/IdentifiersFactory.cpp +++ b/Source/WebCore/inspector/IdentifiersFactory.cpp @@ -49,7 +49,9 @@ String IdentifiersFactory::createIdentifier() // static String IdentifiersFactory::requestId(unsigned long identifier) { - return addProcessIdPrefixTo(String::number(identifier)); + if (identifier) + return addProcessIdPrefixTo(String::number(identifier)); + return String(); } // static diff --git a/Source/WebCore/inspector/InjectedScript.cpp b/Source/WebCore/inspector/InjectedScript.cpp index 67dd39e90..53f13e033 100644 --- a/Source/WebCore/inspector/InjectedScript.cpp +++ b/Source/WebCore/inspector/InjectedScript.cpp @@ -61,27 +61,29 @@ InjectedScript::InjectedScript(ScriptObject injectedScriptObject, InspectedState { } -void InjectedScript::evaluate(ErrorString* errorString, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, RefPtr<TypeBuilder::Runtime::RemoteObject>* result, TypeBuilder::OptOutput<bool>* wasThrown) +void InjectedScript::evaluate(ErrorString* errorString, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, RefPtr<TypeBuilder::Runtime::RemoteObject>* result, TypeBuilder::OptOutput<bool>* wasThrown) { ScriptFunctionCall function(injectedScriptObject(), "evaluate"); function.appendArgument(expression); function.appendArgument(objectGroup); function.appendArgument(includeCommandLineAPI); function.appendArgument(returnByValue); + function.appendArgument(generatePreview); makeEvalCall(errorString, function, result, wasThrown); } -void InjectedScript::callFunctionOn(ErrorString* errorString, const String& objectId, const String& expression, const String& arguments, bool returnByValue, RefPtr<TypeBuilder::Runtime::RemoteObject>* result, TypeBuilder::OptOutput<bool>* wasThrown) +void InjectedScript::callFunctionOn(ErrorString* errorString, const String& objectId, const String& expression, const String& arguments, bool returnByValue, bool generatePreview, RefPtr<TypeBuilder::Runtime::RemoteObject>* result, TypeBuilder::OptOutput<bool>* wasThrown) { ScriptFunctionCall function(injectedScriptObject(), "callFunctionOn"); function.appendArgument(objectId); function.appendArgument(expression); function.appendArgument(arguments); function.appendArgument(returnByValue); + function.appendArgument(generatePreview); makeEvalCall(errorString, function, result, wasThrown); } -void InjectedScript::evaluateOnCallFrame(ErrorString* errorString, const ScriptValue& callFrames, const String& callFrameId, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, RefPtr<RemoteObject>* result, TypeBuilder::OptOutput<bool>* wasThrown) +void InjectedScript::evaluateOnCallFrame(ErrorString* errorString, const ScriptValue& callFrames, const String& callFrameId, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, RefPtr<RemoteObject>* result, TypeBuilder::OptOutput<bool>* wasThrown) { ScriptFunctionCall function(injectedScriptObject(), "evaluateOnCallFrame"); function.appendArgument(callFrames); @@ -90,6 +92,7 @@ void InjectedScript::evaluateOnCallFrame(ErrorString* errorString, const ScriptV function.appendArgument(objectGroup); function.appendArgument(includeCommandLineAPI); function.appendArgument(returnByValue); + function.appendArgument(generatePreview); makeEvalCall(errorString, function, result, wasThrown); } diff --git a/Source/WebCore/inspector/InjectedScript.h b/Source/WebCore/inspector/InjectedScript.h index 8a6f33b16..dd9c29920 100644 --- a/Source/WebCore/inspector/InjectedScript.h +++ b/Source/WebCore/inspector/InjectedScript.h @@ -58,6 +58,7 @@ public: const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, + bool generatePreview, RefPtr<TypeBuilder::Runtime::RemoteObject>* result, TypeBuilder::OptOutput<bool>* wasThrown); void callFunctionOn(ErrorString*, @@ -65,6 +66,7 @@ public: const String& expression, const String& arguments, bool returnByValue, + bool generatePreview, RefPtr<TypeBuilder::Runtime::RemoteObject>* result, TypeBuilder::OptOutput<bool>* wasThrown); void evaluateOnCallFrame(ErrorString*, @@ -74,6 +76,7 @@ public: const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, + bool generatePreview, RefPtr<TypeBuilder::Runtime::RemoteObject>* result, TypeBuilder::OptOutput<bool>* wasThrown); void restartFrame(ErrorString*, const ScriptValue& callFrames, const String& callFrameId, RefPtr<InspectorObject>* result); diff --git a/Source/WebCore/inspector/InjectedScriptCanvasModuleSource.js b/Source/WebCore/inspector/InjectedScriptCanvasModuleSource.js index 3295fc4c9..eeb3ee396 100644 --- a/Source/WebCore/inspector/InjectedScriptCanvasModuleSource.js +++ b/Source/WebCore/inspector/InjectedScriptCanvasModuleSource.js @@ -38,7 +38,7 @@ var TypeUtils = { /** * http://www.khronos.org/registry/typedarray/specs/latest/#7 - * @type {Array.<Function>} + * @type {!Array.<Function>} */ _typedArrayClasses: (function(typeNames) { var result = []; @@ -117,6 +117,20 @@ var TypeUtils = { }, /** + * @param {Object=} obj + * @return {Object} + */ + cloneObject: function(obj) + { + if (!obj) + return null; + var result = {}; + for (var key in obj) + result[key] = obj[key]; + return result; + }, + + /** * @return {CanvasRenderingContext2D} */ _dummyCanvas2dContext: function() @@ -124,7 +138,7 @@ var TypeUtils = { var context = TypeUtils._dummyCanvas2dContextInstance; if (!context) { var canvas = inspectedWindow.document.createElement("canvas"); - context = /** @type {CanvasRenderingContext2D} */ Resource.wrappedObject(canvas.getContext("2d")); + context = /** @type {CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d"))); TypeUtils._dummyCanvas2dContextInstance = context; } return context; @@ -175,7 +189,7 @@ function StackTraceV8(stackTraceLimit, topMostFunctionToIgnore) if (typeof stackTraceLimit === "number") Error.stackTraceLimit = stackTraceLimit; - this._error = /** @type {{stack: Array}} */ {}; + this._error = /** @type {{stack: Array}} */ ({}); Error.captureStackTrace(this._error, topMostFunctionToIgnore || arguments.callee); Error.stackTraceLimit = oldStackTraceLimit; @@ -358,6 +372,28 @@ Call.prototype = { this._result = result; }, + /** + * @param {string} name + * @param {Object} attachment + */ + setAttachment: function(name, attachment) + { + if (attachment) { + this._attachments = this._attachments || {}; + this._attachments[name] = attachment; + } else if (this._attachments) + delete this._attachments[name]; + }, + + /** + * @param {string} name + * @return {Object} + */ + attachment: function(name) + { + return this._attachments && this._attachments[name]; + }, + freeze: function() { if (this._freezed) @@ -377,12 +413,13 @@ Call.prototype = { toReplayable: function(cache) { this.freeze(); - var thisObject = /** @type {ReplayableResource} */ Resource.toReplayable(this._thisObject, cache); + var thisObject = /** @type {ReplayableResource} */ (Resource.toReplayable(this._thisObject, cache)); var result = Resource.toReplayable(this._result, cache); var args = this._args.map(function(obj) { return Resource.toReplayable(obj, cache); }); - return new ReplayableCall(thisObject, this._functionName, args, result, this._stackTrace); + var attachments = TypeUtils.cloneObject(this._attachments); + return new ReplayableCall(thisObject, this._functionName, args, result, this._stackTrace, attachments); }, /** @@ -417,6 +454,9 @@ Call.prototype = { this._result = replayResult; this._stackTrace = replayableCall.stackTrace(); this._freezed = true; + var attachments = replayableCall.attachments(); + if (attachments) + this._attachments = TypeUtils.cloneObject(attachments); return this; } } @@ -428,14 +468,17 @@ Call.prototype = { * @param {Array.<ReplayableResource|*>} args * @param {ReplayableResource|*} result * @param {StackTrace} stackTrace + * @param {Object.<string, Object>} attachments */ -function ReplayableCall(thisObject, functionName, args, result, stackTrace) +function ReplayableCall(thisObject, functionName, args, result, stackTrace, attachments) { this._thisObject = thisObject; this._functionName = functionName; this._args = args; this._result = result; this._stackTrace = stackTrace; + if (attachments) + this._attachments = attachments; } ReplayableCall.prototype = { @@ -488,6 +531,23 @@ ReplayableCall.prototype = { }, /** + * @return {Object.<string, Object>} + */ + attachments: function() + { + return this._attachments; + }, + + /** + * @param {string} name + * @return {Object} + */ + attachment: function(name) + { + return this._attachments && this._attachments[name]; + }, + + /** * @param {Cache} cache * @return {Call} */ @@ -619,7 +679,7 @@ Resource.prototype = { */ toReplayable: function(cache) { - var result = /** @type {ReplayableResource} */ cache.get(this._id); + var result = /** @type {ReplayableResource} */ (cache.get(this._id)); if (result) return result; var data = { @@ -650,7 +710,7 @@ Resource.prototype = { */ replay: function(data, cache) { - var resource = /** @type {ReplayableResource} */ cache.get(data.id); + var resource = /** @type {ReplayableResource} */ (cache.get(data.id)); if (resource) return resource; this._id = data.id; @@ -722,7 +782,9 @@ Resource.prototype = { Object.defineProperty(proxy, property, { get: function() { - return wrappedObject[property]; + var obj = wrappedObject[property]; + var resource = Resource.forObject(obj); + return resource ? resource : obj; }, set: self._wrapPropertySetter(self, wrappedObject, property), enumerable: true @@ -804,12 +866,12 @@ Resource.prototype = { { var manager = resource.manager(); if (!manager || !manager.capturing()) { - originalObject[propertyName] = value; + originalObject[propertyName] = Resource.wrappedObject(value); return; } var args = [propertyName, value]; manager.captureArguments(resource, args); - originalObject[propertyName] = value; + originalObject[propertyName] = Resource.wrappedObject(value); var stackTrace = StackTrace.create(1, arguments.callee); var call = new Call(resource, "", args, undefined, stackTrace); manager.captureCall(call); @@ -1350,7 +1412,7 @@ function WebGLRenderingContextResource(glContext, replayContextCallback) /** * @const - * @type {Array.<string>} + * @type {!Array.<string>} */ WebGLRenderingContextResource.GLCapabilities = [ "BLEND", @@ -1366,7 +1428,7 @@ WebGLRenderingContextResource.GLCapabilities = [ /** * @const - * @type {Array.<string>} + * @type {!Array.<string>} */ WebGLRenderingContextResource.PixelStoreParameters = [ "PACK_ALIGNMENT", @@ -1378,7 +1440,7 @@ WebGLRenderingContextResource.PixelStoreParameters = [ /** * @const - * @type {Array.<string>} + * @type {!Array.<string>} */ WebGLRenderingContextResource.StateParameters = [ "ACTIVE_TEXTURE", @@ -1819,12 +1881,11 @@ function CanvasRenderingContext2DResource(context, replayContextCallback) { Resource.call(this, context); this._replayContextCallback = replayContextCallback; - this._attributesStack = []; } /** * @const - * @type {Array.<string>} + * @type {!Array.<string>} */ CanvasRenderingContext2DResource.AttributeProperties = [ "strokeStyle", @@ -1841,12 +1902,16 @@ CanvasRenderingContext2DResource.AttributeProperties = [ "globalCompositeOperation", "font", "textAlign", - "textBaseline" + "textBaseline", + "lineDashOffset", + // FIXME: Temporary properties implemented in JSC, but not in V8. + "webkitLineDash", + "webkitLineDashOffset" ]; /** * @const - * @type {Array.<string>} + * @type {!Array.<string>} */ CanvasRenderingContext2DResource.PathMethods = [ "beginPath", @@ -1862,7 +1927,7 @@ CanvasRenderingContext2DResource.PathMethods = [ /** * @const - * @type {Array.<string>} + * @type {!Array.<string>} */ CanvasRenderingContext2DResource.TransformationMatrixMethods = [ "scale", @@ -1881,8 +1946,13 @@ CanvasRenderingContext2DResource.prototype = { _populateReplayableData: function(data, cache) { data.replayContextCallback = this._replayContextCallback; - data.attributesStack = this._attributesStack.slice(0); data.currentAttributes = this._currentAttributesState(); + var ctx = this.wrappedObject(); + try { + data.originalImageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); + } catch (e) { + console.error("ASSERT_NOT_REACHED: getImageData failed.", e); + } }, /** @@ -1893,21 +1963,24 @@ CanvasRenderingContext2DResource.prototype = { _doReplayCalls: function(data, cache) { this._replayContextCallback = data.replayContextCallback; - this._attributesStack = data.attributesStack.slice(0); var ctx = Resource.wrappedObject(this._replayContextCallback()); this.setWrappedObject(ctx); - var saveCalls = 0; + if (data.originalImageData) { + try { + ctx.putImageData(data.originalImageData, 0, 0); + } catch (e) { + console.error("ASSERT_NOT_REACHED: putImageData failed.", e); + } + } + for (var i = 0, n = data.calls.length; i < n; ++i) { var replayableCall = data.calls[i]; - if (replayableCall.functionName() === "save") { - console.assert(saveCalls < this._attributesStack.length, "Size of attributes stack is less than 'save' calls"); - this._applyAttributesState(this._attributesStack[saveCalls++]); - } + if (replayableCall.functionName() === "save") + this._applyAttributesState(replayableCall.attachment("canvas2dAttributesState")); this._calls.push(replayableCall.replay(cache)); } - console.assert(saveCalls === this._attributesStack.length, "Size of attributes stack should be equal to the number of 'save' calls"); this._applyAttributesState(data.currentAttributes); }, @@ -1916,7 +1989,11 @@ CanvasRenderingContext2DResource.prototype = { */ pushCall_setTransform: function(call) { - // FIXME: Remove obsolete transform matrix methods. + var saveCallIndex = this._lastIndexOfMatchingSaveCall(); + var index = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods); + index = Math.max(index, saveCallIndex); + if (this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1)) + this._removeAllObsoleteCallsFromLog(); this.pushCall(call); }, @@ -1925,7 +2002,9 @@ CanvasRenderingContext2DResource.prototype = { */ pushCall_beginPath: function(call) { - // FIXME: Remove obsolete path methods. + var index = this._lastIndexOfAnyCall(["clip"]); + if (this._removeCallsFromLog(CanvasRenderingContext2DResource.PathMethods, index + 1)) + this._removeAllObsoleteCallsFromLog(); this.pushCall(call); }, @@ -1934,7 +2013,7 @@ CanvasRenderingContext2DResource.prototype = { */ pushCall_save: function(call) { - this._attributesStack.push(this._currentAttributesState()); + call.setAttachment("canvas2dAttributesState", this._currentAttributesState()); this.pushCall(call); }, @@ -1943,9 +2022,138 @@ CanvasRenderingContext2DResource.prototype = { */ pushCall_restore: function(call) { - this._attributesStack.pop(); - // FIXME: Remove obsolete clip,save methods. - this.pushCall(call); + var lastIndexOfSave = this._lastIndexOfMatchingSaveCall(); + if (lastIndexOfSave === -1) + return; + this._calls[lastIndexOfSave].setAttachment("canvas2dAttributesState", null); // No longer needed, free memory. + + var modified = false; + if (this._removeCallsFromLog(["clip"], lastIndexOfSave + 1)) + modified = true; + + var lastIndexOfAnyPathMethod = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods); + var index = Math.max(lastIndexOfSave, lastIndexOfAnyPathMethod); + if (this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1)) + modified = true; + + if (modified) + this._removeAllObsoleteCallsFromLog(); + + var lastCall = this._calls[this._calls.length - 1]; + if (lastCall && lastCall.functionName() === "save") + this._calls.pop(); + else + this.pushCall(call); + }, + + /** + * @param {number=} fromIndex + * @return {number} + */ + _lastIndexOfMatchingSaveCall: function(fromIndex) + { + if (typeof fromIndex !== "number") + fromIndex = this._calls.length - 1; + else + fromIndex = Math.min(fromIndex, this._calls.length - 1); + var stackDepth = 1; + for (var i = fromIndex; i >= 0; --i) { + var functionName = this._calls[i].functionName(); + if (functionName === "restore") + ++stackDepth; + else if (functionName === "save") { + --stackDepth; + if (!stackDepth) + return i; + } + } + return -1; + }, + + /** + * @param {!Array.<string>} functionNames + * @param {number=} fromIndex + * @return {number} + */ + _lastIndexOfAnyCall: function(functionNames, fromIndex) + { + if (typeof fromIndex !== "number") + fromIndex = this._calls.length - 1; + else + fromIndex = Math.min(fromIndex, this._calls.length - 1); + for (var i = fromIndex; i >= 0; --i) { + if (functionNames.indexOf(this._calls[i].functionName()) !== -1) + return i; + } + return -1; + }, + + _removeAllObsoleteCallsFromLog: function() + { + // Remove all PATH methods between clip() and beginPath() calls. + var lastIndexOfBeginPath = this._lastIndexOfAnyCall(["beginPath"]); + while (lastIndexOfBeginPath !== -1) { + var index = this._lastIndexOfAnyCall(["clip"], lastIndexOfBeginPath - 1); + this._removeCallsFromLog(CanvasRenderingContext2DResource.PathMethods, index + 1, lastIndexOfBeginPath); + lastIndexOfBeginPath = this._lastIndexOfAnyCall(["beginPath"], index - 1); + } + + // Remove all TRASFORMATION MATRIX methods before restore() or setTransform() but after any PATH or corresponding save() method. + var lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform"]); + while (lastRestore !== -1) { + var saveCallIndex = this._lastIndexOfMatchingSaveCall(lastRestore - 1); + var index = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods, lastRestore - 1); + index = Math.max(index, saveCallIndex); + this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1, lastRestore); + lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform"], index - 1); + } + + // Remove all save-restore consecutive pairs. + var restoreCalls = 0; + for (var i = this._calls.length - 1; i >= 0; --i) { + var functionName = this._calls[i].functionName(); + if (functionName === "restore") { + ++restoreCalls; + continue; + } + if (functionName === "save" && restoreCalls > 0) { + var saveCallIndex = i; + for (var j = i - 1; j >= 0 && i - j < restoreCalls; --j) { + if (this._calls[j].functionName() === "save") + saveCallIndex = j; + else + break; + } + this._calls.splice(saveCallIndex, (i - saveCallIndex + 1) * 2); + i = saveCallIndex; + } + restoreCalls = 0; + } + }, + + /** + * @param {!Array.<string>} functionNames + * @param {number} fromIndex + * @param {number=} toIndex + * @return {boolean} + */ + _removeCallsFromLog: function(functionNames, fromIndex, toIndex) + { + var oldLength = this._calls.length; + if (typeof toIndex !== "number") + toIndex = oldLength; + else + toIndex = Math.min(toIndex, oldLength); + var newIndex = Math.min(fromIndex, oldLength); + for (var i = newIndex; i < toIndex; ++i) { + var call = this._calls[i]; + if (functionNames.indexOf(call.functionName()) === -1) + this._calls[newIndex++] = call; + } + if (newIndex >= toIndex) + return false; + this._calls.splice(newIndex, toIndex - newIndex); + return true; }, /** @@ -1955,21 +2163,30 @@ CanvasRenderingContext2DResource.prototype = { { var ctx = this.wrappedObject(); var state = {}; + state.attributes = {}; CanvasRenderingContext2DResource.AttributeProperties.forEach(function(attribute) { - state[attribute] = ctx[attribute]; + state.attributes[attribute] = ctx[attribute]; }); + if (ctx.getLineDash) + state.lineDash = ctx.getLineDash(); return state; }, /** - * @param {!Object.<string, string>} state + * @param {Object.<string, string>=} state */ _applyAttributesState: function(state) { + if (!state) + return; var ctx = this.wrappedObject(); - Object.keys(state).forEach(function(attribute) { - ctx[attribute] = state[attribute]; - }); + if (state.attributes) { + Object.keys(state.attributes).forEach(function(attribute) { + ctx[attribute] = state.attributes[attribute]; + }); + } + if (ctx.setLineDash) + ctx.setLineDash(state.lineDash); }, /** @@ -1982,6 +2199,7 @@ CanvasRenderingContext2DResource.prototype = { if (!wrapFunctions) { wrapFunctions = Object.create(null); + // FIXME: Save state of the CanvasGradient objects. wrapFunctions["createLinearGradient"] = Resource.WrapFunction.resourceFactoryMethod(Resource); wrapFunctions["createRadialGradient"] = Resource.WrapFunction.resourceFactoryMethod(Resource); wrapFunctions["createPattern"] = Resource.WrapFunction.resourceFactoryMethod(Resource); @@ -2006,14 +2224,12 @@ CanvasRenderingContext2DResource.prototype = { } } } - CanvasRenderingContext2DResource.TransformationMatrixMethods.forEach(function(methodName) { - var func = methodName === "setTransform" ? this.pushCall_setTransform : null; - stateModifyingWrapFunction(methodName, func); - }); - CanvasRenderingContext2DResource.PathMethods.forEach(function(methodName) { - var func = methodName === "beginPath" ? this.pushCall_beginPath : null; - stateModifyingWrapFunction(methodName, func); - }); + + for (var i = 0, methodName; methodName = CanvasRenderingContext2DResource.TransformationMatrixMethods[i]; ++i) + stateModifyingWrapFunction(methodName, methodName === "setTransform" ? this.pushCall_setTransform : null); + for (var i = 0, methodName; methodName = CanvasRenderingContext2DResource.PathMethods[i]; ++i) + stateModifyingWrapFunction(methodName, methodName === "beginPath" ? this.pushCall_beginPath : null); + stateModifyingWrapFunction("save", this.pushCall_save); stateModifyingWrapFunction("restore", this.pushCall_restore); stateModifyingWrapFunction("clip"); @@ -2378,7 +2594,7 @@ InjectedScript.prototype = { for (var i = 0, contextId; contextId = contextIds[i]; ++i) { replayContext = canvas.getContext(contextId, attributes); if (replayContext) { - replayContext = /** @type {WebGLRenderingContext} */ Resource.wrappedObject(replayContext); + replayContext = /** @type {WebGLRenderingContext} */ (Resource.wrappedObject(replayContext)); break; } } @@ -2401,20 +2617,10 @@ InjectedScript.prototype = { */ _constructCanvas2DReplayContext: function(originalContext) { - var replayContext = originalContext["__replayContext"]; - if (!replayContext) { - var canvas = originalContext.canvas.cloneNode(true); - replayContext = /** @type {CanvasRenderingContext2D} */ Resource.wrappedObject(canvas.getContext("2d")); - Object.defineProperty(originalContext, "__replayContext", { - value: replayContext, - writable: false, - enumerable: false, - configurable: true - }); - this._replayContext = replayContext; - } else { - // FIXME: Clear the canvas. - } + // Create a new 2D context each time to start with an empty context drawing state stack (managed by save() and restore() methods). + var canvas = originalContext.canvas.cloneNode(true); + var replayContext = /** @type {CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d"))); + this._replayContext = replayContext; return replayContext; } } diff --git a/Source/WebCore/inspector/InjectedScriptSource.js b/Source/WebCore/inspector/InjectedScriptSource.js index ecf66af08..83455e3f9 100644 --- a/Source/WebCore/inspector/InjectedScriptSource.js +++ b/Source/WebCore/inspector/InjectedScriptSource.js @@ -33,6 +33,9 @@ */ (function (InjectedScriptHost, inspectedWindow, injectedScriptId) { +// Protect against Object overwritten by the user code. +var Object = {}.constructor; + /** * @param {Arguments} array * @param {number=} index @@ -48,7 +51,7 @@ function slice(array, index) /** * Please use this bind, not the one from Function.prototype - * @param {function()} func + * @param {function(...)} func * @param {Object} thisObject * @param {...number} var_args */ @@ -364,7 +367,10 @@ InjectedScript.prototype = { if (!descriptor) { // Not all bindings provide proper descriptors. Fall back to the writable, configurable property. try { - descriptors.push({ name: name, value: object[name], writable: false, configurable: false, enumerable: false}); + descriptor = { name: name, value: object[name], writable: false, configurable: false, enumerable: false}; + if (o === object) + descriptor.isOwn = true; + descriptors.push(descriptor); } catch (e) { // Silent catch. } @@ -377,11 +383,13 @@ InjectedScript.prototype = { } descriptor.name = name; - descriptors.push(descriptor); + if (o === object) + descriptor.isOwn = true; + descriptors.push(descriptor); } if (ownProperties) { if (object.__proto__) - descriptors.push({ name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false}); + descriptors.push({ name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true}); break; } } @@ -393,11 +401,12 @@ InjectedScript.prototype = { * @param {string} objectGroup * @param {boolean} injectCommandLineAPI * @param {boolean} returnByValue + * @param {boolean} generatePreview * @return {*} */ - evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByValue) + evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview) { - return this._evaluateAndWrap(InjectedScriptHost.evaluate, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue); + return this._evaluateAndWrap(InjectedScriptHost.evaluate, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue, generatePreview); }, /** @@ -455,13 +464,14 @@ InjectedScript.prototype = { * @param {boolean} isEvalOnCallFrame * @param {boolean} injectCommandLineAPI * @param {boolean} returnByValue + * @param {boolean} generatePreview * @return {*} */ - _evaluateAndWrap: function(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, injectCommandLineAPI, returnByValue) + _evaluateAndWrap: function(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, injectCommandLineAPI, returnByValue, generatePreview) { try { return { wasThrown: false, - result: this._wrapObject(this._evaluateOn(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI), objectGroup, returnByValue, false) }; + result: this._wrapObject(this._evaluateOn(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI), objectGroup, returnByValue, generatePreview) }; } catch (e) { return this._createThrownValue(e, objectGroup); } @@ -537,14 +547,15 @@ InjectedScript.prototype = { * @param {string} objectGroup * @param {boolean} injectCommandLineAPI * @param {boolean} returnByValue + * @param {boolean} generatePreview * @return {*} */ - evaluateOnCallFrame: function(topCallFrame, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue) + evaluateOnCallFrame: function(topCallFrame, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview) { var callFrame = this._callFrameForId(topCallFrame, callFrameId); if (!callFrame) return "Could not find call frame with given id"; - return this._evaluateAndWrap(callFrame.evaluate, callFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue); + return this._evaluateAndWrap(callFrame.evaluate, callFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue, generatePreview); }, /** @@ -606,7 +617,7 @@ InjectedScript.prototype = { var object = this.findObjectById(objectId); if (!object || this._subtype(object) !== "node") return null; - return /** @type {Node} */ object; + return /** @type {Node} */ (object); }, /** @@ -697,7 +708,7 @@ InjectedScript.prototype = { if (this.isPrimitiveValue(obj)) return null; - obj = /** @type {Object} */ obj; + obj = /** @type {Object} */ (obj); // Type is object, get subtype. var subtype = this._subtype(obj); @@ -784,7 +795,7 @@ InjectedScript.RemoteObject = function(object, objectGroupName, forceValueType, return; } - object = /** @type {Object} */ object; + object = /** @type {Object} */ (object); this.objectId = injectedScript._bind(object, objectGroupName); var subtype = injectedScript._subtype(object); diff --git a/Source/WebCore/inspector/Inspector.json b/Source/WebCore/inspector/Inspector.json index 051aba495..57bd79a72 100644 --- a/Source/WebCore/inspector/Inspector.json +++ b/Source/WebCore/inspector/Inspector.json @@ -28,6 +28,13 @@ { "name": "object", "$ref": "Runtime.RemoteObject" }, { "name": "hints", "type": "object" } ] + }, + { + "name": "detached", + "description": "Fired when remote debugging connection is about to be terminated. Contains detach reason.", + "parameters": [ + { "name": "reason", "type": "string", "description": "The reason why connection has been terminated." } + ] } ] }, @@ -510,7 +517,9 @@ { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." }, { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." }, { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." }, - { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." } + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, + { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object." } + ] }, { @@ -560,7 +569,8 @@ { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation.", "hidden": true }, { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, { "name": "contextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Specifies in which isolated context to perform evaluation. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page.", "hidden": true }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." } + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." } ], "returns": [ { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." }, @@ -575,7 +585,8 @@ { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." }, { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." }, { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether function call should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." } + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." } ], "returns": [ { "name": "result", "$ref": "RemoteObject", "description": "Call result." }, @@ -648,7 +659,7 @@ { "name": "source", "type": "string", "enum": ["html", "wml", "xml", "javascript", "network", "console-api", "other"], "description": "Message source." }, { "name": "level", "type": "string", "enum": ["tip", "log", "warning", "error", "debug"], "description": "Message severity." }, { "name": "text", "type": "string", "description": "Message text." }, - { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "trace", "startGroup", "startGroupCollapsed", "endGroup", "assert"], "description": "Console message type." }, + { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert"], "description": "Console message type." }, { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." }, { "name": "repeatCount", "type": "integer", "optional": true, "description": "Repeat count for repeated messages." }, @@ -2478,6 +2489,14 @@ ], "hidden": true, "description": "Tells whether timeline agent supports frame instrumentation." + }, + { + "name": "canMonitorMainThread", + "returns": [ + { "name": "result", "type": "boolean", "description": "True if timeline supports main thread CPU utilization instrumentation." } + ], + "hidden": true, + "description": "Tells whether timeline agent supports main thread CPU utilization instrumentation." } ], "events": [ @@ -2728,7 +2747,8 @@ { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." }, { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false.", "hidden": true }, { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." } + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." } ], "returns": [ { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." }, diff --git a/Source/WebCore/inspector/InspectorAgent.cpp b/Source/WebCore/inspector/InspectorAgent.cpp index 5c28ca778..01baeb800 100644 --- a/Source/WebCore/inspector/InspectorAgent.cpp +++ b/Source/WebCore/inspector/InspectorAgent.cpp @@ -68,7 +68,6 @@ InspectorAgent::InspectorAgent(Page* page, InjectedScriptManager* injectedScript , m_inspectedPage(page) , m_frontend(0) , m_injectedScriptManager(injectedScriptManager) - , m_didCommitLoadFired(false) { ASSERT_ARG(page, page); m_instrumentingAgents->setInspectorAgent(this); @@ -102,7 +101,6 @@ void InspectorAgent::clearFrontend() { m_pendingEvaluateTestCommands.clear(); m_frontend = 0; - m_didCommitLoadFired = false; m_injectedScriptManager->discardInjectedScripts(); ErrorString error; disable(&error); @@ -110,7 +108,6 @@ void InspectorAgent::clearFrontend() void InspectorAgent::didCommitLoad() { - m_didCommitLoadFired = true; m_injectedScriptManager->discardInjectedScripts(); } diff --git a/Source/WebCore/inspector/InspectorAgent.h b/Source/WebCore/inspector/InspectorAgent.h index 3e6fd93f2..62310f246 100644 --- a/Source/WebCore/inspector/InspectorAgent.h +++ b/Source/WebCore/inspector/InspectorAgent.h @@ -79,7 +79,6 @@ public: void didCommitLoad(); void domContentLoadedEventFired(); - bool didCommitLoadFired() { return m_didCommitLoadFired; } bool hasFrontend() const { return m_frontend; } @@ -109,7 +108,6 @@ private: pair<RefPtr<TypeBuilder::Runtime::RemoteObject>, RefPtr<InspectorObject> > m_pendingInspectData; typedef HashMap<String, String> InjectedScriptForOriginMap; InjectedScriptForOriginMap m_injectedScriptForOrigin; - bool m_didCommitLoadFired; }; } // namespace WebCore diff --git a/Source/WebCore/inspector/InspectorClient.h b/Source/WebCore/inspector/InspectorClient.h index 2a4745a81..a42c2aabc 100644 --- a/Source/WebCore/inspector/InspectorClient.h +++ b/Source/WebCore/inspector/InspectorClient.h @@ -57,6 +57,7 @@ public: virtual void clearBrowserCache() { } virtual bool canClearBrowserCookies() { return false; } virtual void clearBrowserCookies() { } + virtual bool canMonitorMainThread() { return false; } virtual void startMainThreadMonitoring() { } virtual void stopMainThreadMonitoring() { } diff --git a/Source/WebCore/inspector/InspectorConsoleAgent.cpp b/Source/WebCore/inspector/InspectorConsoleAgent.cpp index 9e622c04c..c37da7be6 100644 --- a/Source/WebCore/inspector/InspectorConsoleAgent.cpp +++ b/Source/WebCore/inspector/InspectorConsoleAgent.cpp @@ -32,7 +32,6 @@ #include "Console.h" #include "ConsoleMessage.h" #include "DOMWindow.h" -#include "IdentifiersFactory.h" #include "InjectedScriptHost.h" #include "InjectedScriptManager.h" #include "InspectorFrontend.h" @@ -94,7 +93,7 @@ void InspectorConsoleAgent::enable(ErrorString*) m_state->setBoolean(ConsoleAgentState::consoleMessagesEnabled, true); if (m_expiredConsoleMessageCount) { - ConsoleMessage expiredMessage(OtherMessageSource, LogMessageType, WarningMessageLevel, String::format("%d console messages are not shown.", m_expiredConsoleMessageCount), "", 0, ""); + ConsoleMessage expiredMessage(OtherMessageSource, LogMessageType, WarningMessageLevel, String::format("%d console messages are not shown.", m_expiredConsoleMessageCount), "", 0, 0); expiredMessage.addToFrontend(m_frontend, m_injectedScriptManager, false); } @@ -152,18 +151,24 @@ void InspectorConsoleAgent::clearFrontend() disable(&errorString); } -void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) +void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack, unsigned long requestIdentifier) { if (!developerExtrasEnabled()) return; - addConsoleMessage(adoptPtr(new ConsoleMessage(source, type, level, message, arguments, callStack))); + + if (type == ClearMessageType) { + ErrorString error; + clearMessages(&error); + } + + addConsoleMessage(adoptPtr(new ConsoleMessage(source, type, level, message, arguments, callStack, requestIdentifier))); } -void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, const String& scriptId, unsigned lineNumber) +void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, const String& scriptId, unsigned lineNumber, unsigned long requestIdentifier) { if (!developerExtrasEnabled()) return; - addConsoleMessage(adoptPtr(new ConsoleMessage(source, type, level, message, scriptId, lineNumber))); + addConsoleMessage(adoptPtr(new ConsoleMessage(source, type, level, message, scriptId, lineNumber, requestIdentifier))); } Vector<unsigned> InspectorConsoleAgent::consoleMessageArgumentCounts() @@ -236,32 +241,30 @@ void InspectorConsoleAgent::frameWindowDiscarded(DOMWindow* window) m_injectedScriptManager->discardInjectedScriptsFor(window); } -void InspectorConsoleAgent::didFinishXHRLoading(unsigned long identifier, const String& url, const String& sendURL, unsigned sendLineNumber) +void InspectorConsoleAgent::didFinishXHRLoading(unsigned long requestIdentifier, const String& url, const String& sendURL, unsigned sendLineNumber) { if (!developerExtrasEnabled()) return; if (m_frontend && m_state->getBoolean(ConsoleAgentState::monitoringXHR)) { String message = "XHR finished loading: \"" + url + "\"."; - String requestId = IdentifiersFactory::requestId(identifier); - addConsoleMessage(adoptPtr(new ConsoleMessage(NetworkMessageSource, LogMessageType, LogMessageLevel, message, sendURL, sendLineNumber, requestId))); + addMessageToConsole(NetworkMessageSource, LogMessageType, LogMessageLevel, message, sendURL, sendLineNumber, requestIdentifier); } } -void InspectorConsoleAgent::didReceiveResponse(unsigned long identifier, const ResourceResponse& response) +void InspectorConsoleAgent::didReceiveResponse(unsigned long requestIdentifier, const ResourceResponse& response) { if (!developerExtrasEnabled()) return; if (response.httpStatusCode() >= 400) { String message = "Failed to load resource: the server responded with a status of " + String::number(response.httpStatusCode()) + " (" + response.httpStatusText() + ')'; - String requestId = IdentifiersFactory::requestId(identifier); - addConsoleMessage(adoptPtr(new ConsoleMessage(NetworkMessageSource, LogMessageType, ErrorMessageLevel, message, response.url().string(), requestId))); + addMessageToConsole(NetworkMessageSource, LogMessageType, ErrorMessageLevel, message, response.url().string(), 0, requestIdentifier); } } -void InspectorConsoleAgent::didFailLoading(unsigned long identifier, const ResourceError& error) +void InspectorConsoleAgent::didFailLoading(unsigned long requestIdentifier, const ResourceError& error) { if (!developerExtrasEnabled()) return; @@ -273,8 +276,7 @@ void InspectorConsoleAgent::didFailLoading(unsigned long identifier, const Resou message.appendLiteral(": "); message.append(error.localizedDescription()); } - String requestId = IdentifiersFactory::requestId(identifier); - addConsoleMessage(adoptPtr(new ConsoleMessage(NetworkMessageSource, LogMessageType, ErrorMessageLevel, message.toString(), error.failingURL(), requestId))); + addMessageToConsole(NetworkMessageSource, LogMessageType, ErrorMessageLevel, message.toString(), error.failingURL(), 0, requestIdentifier); } void InspectorConsoleAgent::setMonitoringXHREnabled(ErrorString*, bool enabled) diff --git a/Source/WebCore/inspector/InspectorConsoleAgent.h b/Source/WebCore/inspector/InspectorConsoleAgent.h index 7f4339313..1fe714e49 100644 --- a/Source/WebCore/inspector/InspectorConsoleAgent.h +++ b/Source/WebCore/inspector/InspectorConsoleAgent.h @@ -68,8 +68,8 @@ public: virtual void clearFrontend(); virtual void restore(); - void addMessageToConsole(MessageSource, MessageType, MessageLevel, const String& message, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack>); - void addMessageToConsole(MessageSource, MessageType, MessageLevel, const String& message, const String& scriptId, unsigned lineNumber); + void addMessageToConsole(MessageSource, MessageType, MessageLevel, const String& message, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack>, unsigned long requestIdentifier = 0); + void addMessageToConsole(MessageSource, MessageType, MessageLevel, const String& message, const String& scriptId, unsigned lineNumber, unsigned long requestIdentifier = 0); Vector<unsigned> consoleMessageArgumentCounts(); void startTiming(const String& title); @@ -78,9 +78,9 @@ public: void frameWindowDiscarded(DOMWindow*); - void didFinishXHRLoading(unsigned long identifier, const String& url, const String& sendURL, unsigned sendLineNumber); - void didReceiveResponse(unsigned long identifier, const ResourceResponse&); - void didFailLoading(unsigned long identifier, const ResourceError&); + void didFinishXHRLoading(unsigned long requestIdentifier, const String& url, const String& sendURL, unsigned sendLineNumber); + void didReceiveResponse(unsigned long requestIdentifier, const ResourceResponse&); + void didFailLoading(unsigned long requestIdentifier, const ResourceError&); #if ENABLE(JAVASCRIPT_DEBUGGER) void addProfileFinishedMessageToConsole(PassRefPtr<ScriptProfile>, unsigned lineNumber, const String& sourceURL); void addStartProfilingMessageToConsole(const String& title, unsigned lineNumber, const String& sourceURL); diff --git a/Source/WebCore/inspector/InspectorConsoleInstrumentation.h b/Source/WebCore/inspector/InspectorConsoleInstrumentation.h index f04326b69..01b60c8d3 100644 --- a/Source/WebCore/inspector/InspectorConsoleInstrumentation.h +++ b/Source/WebCore/inspector/InspectorConsoleInstrumentation.h @@ -39,36 +39,36 @@ namespace WebCore { -inline void InspectorInstrumentation::addMessageToConsole(Page* page, MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) +inline void InspectorInstrumentation::addMessageToConsole(Page* page, MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack, unsigned long requestIdentifier) { #if ENABLE(INSPECTOR) if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) - addMessageToConsoleImpl(instrumentingAgents, source, type, level, message, arguments, callStack); + addMessageToConsoleImpl(instrumentingAgents, source, type, level, message, arguments, callStack, requestIdentifier); #endif } -inline void InspectorInstrumentation::addMessageToConsole(Page* page, MessageSource source, MessageType type, MessageLevel level, const String& message, const String& scriptId, unsigned lineNumber) +inline void InspectorInstrumentation::addMessageToConsole(Page* page, MessageSource source, MessageType type, MessageLevel level, const String& message, const String& scriptId, unsigned lineNumber, unsigned long requestIdentifier) { #if ENABLE(INSPECTOR) if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForPage(page)) - addMessageToConsoleImpl(instrumentingAgents, source, type, level, message, scriptId, lineNumber); + addMessageToConsoleImpl(instrumentingAgents, source, type, level, message, scriptId, lineNumber, requestIdentifier); #endif } #if ENABLE(WORKERS) -inline void InspectorInstrumentation::addMessageToConsole(WorkerContext* workerContext, MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) +inline void InspectorInstrumentation::addMessageToConsole(WorkerContext* workerContext, MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack, unsigned long requestIdentifier) { #if ENABLE(INSPECTOR) if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForWorkerContext(workerContext)) - addMessageToConsoleImpl(instrumentingAgents, source, type, level, message, arguments, callStack); + addMessageToConsoleImpl(instrumentingAgents, source, type, level, message, arguments, callStack, requestIdentifier); #endif } -inline void InspectorInstrumentation::addMessageToConsole(WorkerContext* workerContext, MessageSource source, MessageType type, MessageLevel level, const String& message, const String& scriptId, unsigned lineNumber) +inline void InspectorInstrumentation::addMessageToConsole(WorkerContext* workerContext, MessageSource source, MessageType type, MessageLevel level, const String& message, const String& scriptId, unsigned lineNumber, unsigned long requestIdentifier) { #if ENABLE(INSPECTOR) if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForWorkerContext(workerContext)) - addMessageToConsoleImpl(instrumentingAgents, source, type, level, message, scriptId, lineNumber); + addMessageToConsoleImpl(instrumentingAgents, source, type, level, message, scriptId, lineNumber, requestIdentifier); #endif } #endif diff --git a/Source/WebCore/inspector/InspectorController.cpp b/Source/WebCore/inspector/InspectorController.cpp index 8bad7cc0c..2ff0d39c5 100644 --- a/Source/WebCore/inspector/InspectorController.cpp +++ b/Source/WebCore/inspector/InspectorController.cpp @@ -125,7 +125,7 @@ InspectorController::InspectorController(Page* page, InspectorClient* inspectorC m_resourceAgent = resourceAgentPtr.get(); m_agents.append(resourceAgentPtr.release()); - OwnPtr<InspectorRuntimeAgent> runtimeAgentPtr(PageRuntimeAgent::create(m_instrumentingAgents.get(), m_state.get(), m_injectedScriptManager.get(), page, pageAgent, m_inspectorAgent)); + OwnPtr<InspectorRuntimeAgent> runtimeAgentPtr(PageRuntimeAgent::create(m_instrumentingAgents.get(), m_state.get(), m_injectedScriptManager.get(), page, pageAgent)); InspectorRuntimeAgent* runtimeAgent = runtimeAgentPtr.get(); m_agents.append(runtimeAgentPtr.release()); @@ -404,7 +404,7 @@ void InspectorController::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) info.addMember(m_inspectorFrontendClient); info.addMember(m_inspectorFrontend); info.addMember(m_page); - info.addMember(m_inspectorClient); + info.addWeakPointer(m_inspectorClient); info.addMember(m_agents); } diff --git a/Source/WebCore/inspector/InspectorDOMStorageAgent.cpp b/Source/WebCore/inspector/InspectorDOMStorageAgent.cpp index 5d305890c..410adeccb 100644 --- a/Source/WebCore/inspector/InspectorDOMStorageAgent.cpp +++ b/Source/WebCore/inspector/InspectorDOMStorageAgent.cpp @@ -228,7 +228,7 @@ void InspectorDOMStorageAgent::reportMemoryUsage(MemoryObjectInfo* memoryObjectI MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::InspectorDOMStorageAgent); InspectorBaseAgent<InspectorDOMStorageAgent>::reportMemoryUsage(memoryObjectInfo); info.addMember(m_resources); - info.addMember(m_frontend); + info.addWeakPointer(m_frontend); } } // namespace WebCore diff --git a/Source/WebCore/inspector/InspectorDOMStorageResource.cpp b/Source/WebCore/inspector/InspectorDOMStorageResource.cpp index 029e39228..5e7c9577d 100644 --- a/Source/WebCore/inspector/InspectorDOMStorageResource.cpp +++ b/Source/WebCore/inspector/InspectorDOMStorageResource.cpp @@ -90,7 +90,7 @@ void InspectorDOMStorageResource::reportMemoryUsage(MemoryObjectInfo* memoryObje MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::InspectorDOMStorageResources); info.addMember(m_storageArea); info.addMember(m_frame); - info.addMember(m_frontend); + info.addWeakPointer(m_frontend); info.addMember(m_id); info.addPrivateBuffer(m_storageArea->memoryBytesUsedByCache()); } diff --git a/Source/WebCore/inspector/InspectorDebuggerAgent.cpp b/Source/WebCore/inspector/InspectorDebuggerAgent.cpp index a77c06fb5..e65315000 100644 --- a/Source/WebCore/inspector/InspectorDebuggerAgent.cpp +++ b/Source/WebCore/inspector/InspectorDebuggerAgent.cpp @@ -43,6 +43,8 @@ #include "RegularExpression.h" #include "ScriptDebugServer.h" #include "ScriptObject.h" +#include <wtf/MemoryInstrumentationHashMap.h> +#include <wtf/MemoryInstrumentationVector.h> #include <wtf/text/WTFString.h> using WebCore::TypeBuilder::Array; @@ -498,7 +500,7 @@ void InspectorDebuggerAgent::setPauseOnExceptionsImpl(ErrorString* errorString, m_state->setLong(DebuggerAgentState::pauseOnExceptionsState, pauseState); } -void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString* errorString, const String& callFrameId, const String& expression, const String* const objectGroup, const bool* const includeCommandLineAPI, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* const returnByValue, RefPtr<TypeBuilder::Runtime::RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown) +void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString* errorString, const String& callFrameId, const String& expression, const String* const objectGroup, const bool* const includeCommandLineAPI, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* const returnByValue, const bool* generatePreview, RefPtr<TypeBuilder::Runtime::RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown) { InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(callFrameId); if (injectedScript.hasNoValue()) { @@ -513,7 +515,7 @@ void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString* errorString, const muteConsole(); } - injectedScript.evaluateOnCallFrame(errorString, m_currentCallStack, callFrameId, expression, objectGroup ? *objectGroup : "", includeCommandLineAPI ? *includeCommandLineAPI : false, returnByValue ? *returnByValue : false, &result, wasThrown); + injectedScript.evaluateOnCallFrame(errorString, m_currentCallStack, callFrameId, expression, objectGroup ? *objectGroup : "", includeCommandLineAPI ? *includeCommandLineAPI : false, returnByValue ? *returnByValue : false, generatePreview ? *generatePreview : false, &result, wasThrown); if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) { unmuteConsole(); @@ -734,6 +736,29 @@ void InspectorDebuggerAgent::clearBreakDetails() m_breakAuxData = 0; } +void InspectorDebuggerAgent::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const +{ + MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::InspectorDebuggerAgent); + InspectorBaseAgent<InspectorDebuggerAgent>::reportMemoryUsage(memoryObjectInfo); + info.addMember(m_injectedScriptManager); + info.addWeakPointer(m_frontend); + info.addMember(m_pausedScriptState); + info.addMember(m_currentCallStack); + info.addMember(m_scripts); + info.addMember(m_breakpointIdToDebugServerBreakpointIds); + info.addMember(m_continueToLocationBreakpointId); + info.addMember(m_breakAuxData); + info.addWeakPointer(m_listener); +} + +void ScriptDebugListener::Script::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const +{ + MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::InspectorDebuggerAgent); + info.addMember(url); + info.addMember(source); + info.addMember(sourceMappingURL); +} + } // namespace WebCore #endif // ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR) diff --git a/Source/WebCore/inspector/InspectorDebuggerAgent.h b/Source/WebCore/inspector/InspectorDebuggerAgent.h index d1118dc49..81bceedec 100644 --- a/Source/WebCore/inspector/InspectorDebuggerAgent.h +++ b/Source/WebCore/inspector/InspectorDebuggerAgent.h @@ -107,6 +107,7 @@ public: const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, + const bool* generatePreview, RefPtr<TypeBuilder::Runtime::RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown); void compileScript(ErrorString*, const String& expression, const String& sourceURL, TypeBuilder::OptOutput<TypeBuilder::Debugger::ScriptId>*, TypeBuilder::OptOutput<String>* syntaxErrorMessage); @@ -128,6 +129,8 @@ public: virtual ScriptDebugServer& scriptDebugServer() = 0; + virtual void reportMemoryUsage(MemoryObjectInfo*) const; + protected: InspectorDebuggerAgent(InstrumentingAgents*, InspectorState*, InjectedScriptManager*); diff --git a/Source/WebCore/inspector/InspectorInstrumentation.cpp b/Source/WebCore/inspector/InspectorInstrumentation.cpp index 08f2b0d9e..ad3536a70 100644 --- a/Source/WebCore/inspector/InspectorInstrumentation.cpp +++ b/Source/WebCore/inspector/InspectorInstrumentation.cpp @@ -57,7 +57,6 @@ #include "InspectorPageAgent.h" #include "InspectorProfilerAgent.h" #include "InspectorResourceAgent.h" -#include "InspectorRuntimeAgent.h" #include "InspectorTimelineAgent.h" #include "InspectorWorkerAgent.h" #include "InstrumentingAgents.h" @@ -67,6 +66,7 @@ #include "ScriptProfile.h" #include "StyleRule.h" #include "WorkerContext.h" +#include "WorkerRuntimeAgent.h" #include "WorkerThread.h" #include "XMLHttpRequest.h" #include <wtf/StdLibExtras.h> @@ -127,7 +127,7 @@ void InspectorInstrumentation::didClearWindowObjectInWorldImpl(InstrumentingAgen #endif if (PageRuntimeAgent* pageRuntimeAgent = instrumentingAgents->pageRuntimeAgent()) { if (world == mainThreadNormalWorld()) - pageRuntimeAgent->didClearWindowObject(frame); + pageRuntimeAgent->didCreateMainWorldContext(frame); } } @@ -494,25 +494,22 @@ void InspectorInstrumentation::didDispatchXHRLoadEventImpl(const InspectorInstru timelineAgent->didDispatchXHRLoadEvent(); } -InspectorInstrumentationCookie InspectorInstrumentation::willPaintImpl(InstrumentingAgents* instrumentingAgents, GraphicsContext* context, const LayoutRect& rect, Frame* frame) +InspectorInstrumentationCookie InspectorInstrumentation::willPaintImpl(InstrumentingAgents* instrumentingAgents, Frame* frame) { - if (InspectorPageAgent* pageAgent = instrumentingAgents->inspectorPageAgent()) - pageAgent->willPaint(context, rect); - int timelineAgentId = 0; if (InspectorTimelineAgent* timelineAgent = instrumentingAgents->inspectorTimelineAgent()) { - timelineAgent->willPaint(rect, frame); + timelineAgent->willPaint(frame); timelineAgentId = timelineAgent->id(); } return InspectorInstrumentationCookie(instrumentingAgents, timelineAgentId); } -void InspectorInstrumentation::didPaintImpl(const InspectorInstrumentationCookie& cookie) +void InspectorInstrumentation::didPaintImpl(const InspectorInstrumentationCookie& cookie, GraphicsContext* context, const LayoutRect& rect) { if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie)) - timelineAgent->didPaint(); + timelineAgent->didPaint(rect); if (InspectorPageAgent* pageAgent = cookie.first ? cookie.first->inspectorPageAgent() : 0) - pageAgent->didPaint(); + pageAgent->didPaint(context, rect); } void InspectorInstrumentation::willScrollLayerImpl(InstrumentingAgents* instrumentingAgents, Frame* frame) @@ -954,20 +951,20 @@ void InspectorInstrumentation::didWriteHTMLImpl(const InspectorInstrumentationCo timelineAgent->didWriteHTML(endLine); } -void InspectorInstrumentation::addMessageToConsoleImpl(InstrumentingAgents* instrumentingAgents, MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) +void InspectorInstrumentation::addMessageToConsoleImpl(InstrumentingAgents* instrumentingAgents, MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack, unsigned long requestIdentifier) { if (InspectorConsoleAgent* consoleAgent = instrumentingAgents->inspectorConsoleAgent()) - consoleAgent->addMessageToConsole(source, type, level, message, arguments, callStack); + consoleAgent->addMessageToConsole(source, type, level, message, arguments, callStack, requestIdentifier); #if ENABLE(JAVASCRIPT_DEBUGGER) if (InspectorDebuggerAgent* debuggerAgent = instrumentingAgents->inspectorDebuggerAgent()) debuggerAgent->addMessageToConsole(source, type); #endif } -void InspectorInstrumentation::addMessageToConsoleImpl(InstrumentingAgents* instrumentingAgents, MessageSource source, MessageType type, MessageLevel level, const String& message, const String& scriptId, unsigned lineNumber) +void InspectorInstrumentation::addMessageToConsoleImpl(InstrumentingAgents* instrumentingAgents, MessageSource source, MessageType type, MessageLevel level, const String& message, const String& scriptId, unsigned lineNumber, unsigned long requestIdentifier) { if (InspectorConsoleAgent* consoleAgent = instrumentingAgents->inspectorConsoleAgent()) - consoleAgent->addMessageToConsole(source, type, level, message, scriptId, lineNumber); + consoleAgent->addMessageToConsole(source, type, level, message, scriptId, lineNumber, requestIdentifier); } void InspectorInstrumentation::consoleCountImpl(InstrumentingAgents* instrumentingAgents, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> stack) @@ -1079,7 +1076,7 @@ void InspectorInstrumentation::willEvaluateWorkerScript(WorkerContext* workerCon if (!instrumentingAgents) return; #if ENABLE(JAVASCRIPT_DEBUGGER) - if (InspectorRuntimeAgent* runtimeAgent = instrumentingAgents->inspectorRuntimeAgent()) + if (WorkerRuntimeAgent* runtimeAgent = instrumentingAgents->workerRuntimeAgent()) runtimeAgent->pauseWorkerContext(workerContext); #endif } @@ -1167,13 +1164,6 @@ bool InspectorInstrumentation::consoleAgentEnabled(ScriptExecutionContext* scrip return consoleAgent && consoleAgent->enabled(); } -bool InspectorInstrumentation::runtimeAgentEnabled(Frame* frame) -{ - InstrumentingAgents* instrumentingAgents = instrumentingAgentsForFrame(frame); - InspectorRuntimeAgent* runtimeAgent = instrumentingAgents ? instrumentingAgents->inspectorRuntimeAgent() : 0; - return runtimeAgent && runtimeAgent->enabled(); -} - bool InspectorInstrumentation::timelineAgentEnabled(ScriptExecutionContext* scriptExecutionContext) { InstrumentingAgents* instrumentingAgents = instrumentingAgentsForContext(scriptExecutionContext); diff --git a/Source/WebCore/inspector/InspectorInstrumentation.h b/Source/WebCore/inspector/InspectorInstrumentation.h index 469e68690..cca054a05 100644 --- a/Source/WebCore/inspector/InspectorInstrumentation.h +++ b/Source/WebCore/inspector/InspectorInstrumentation.h @@ -141,10 +141,10 @@ public: static void didScroll(Page*); static InspectorInstrumentationCookie willDispatchXHRLoadEvent(ScriptExecutionContext*, XMLHttpRequest*); static void didDispatchXHRLoadEvent(const InspectorInstrumentationCookie&); - static InspectorInstrumentationCookie willPaint(Frame*, GraphicsContext*, const LayoutRect&); - static void didPaint(const InspectorInstrumentationCookie&); static void willScrollLayer(Frame*); static void didScrollLayer(Frame*); + static InspectorInstrumentationCookie willPaint(Frame*); + static void didPaint(const InspectorInstrumentationCookie&, GraphicsContext*, const LayoutRect&); static void willComposite(Page*); static void didComposite(Page*); static InspectorInstrumentationCookie willRecalculateStyle(Document*); @@ -196,11 +196,11 @@ public: static InspectorInstrumentationCookie willWriteHTML(Document*, unsigned int length, unsigned int startLine); static void didWriteHTML(const InspectorInstrumentationCookie&, unsigned int endLine); - static void addMessageToConsole(Page*, MessageSource, MessageType, MessageLevel, const String& message, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack>); - static void addMessageToConsole(Page*, MessageSource, MessageType, MessageLevel, const String& message, const String&, unsigned lineNumber); + static void addMessageToConsole(Page*, MessageSource, MessageType, MessageLevel, const String& message, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack>, unsigned long requestIdentifier = 0); + static void addMessageToConsole(Page*, MessageSource, MessageType, MessageLevel, const String& message, const String&, unsigned lineNumber, unsigned long requestIdentifier = 0); #if ENABLE(WORKERS) - static void addMessageToConsole(WorkerContext*, MessageSource, MessageType, MessageLevel, const String& message, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack>); - static void addMessageToConsole(WorkerContext*, MessageSource, MessageType, MessageLevel, const String& message, const String&, unsigned lineNumber); + static void addMessageToConsole(WorkerContext*, MessageSource, MessageType, MessageLevel, const String& message, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack>, unsigned long requestIdentifier = 0); + static void addMessageToConsole(WorkerContext*, MessageSource, MessageType, MessageLevel, const String& message, const String&, unsigned lineNumber, unsigned long requestIdentifier = 0); #endif static void consoleCount(Page*, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack>); static void startConsoleTiming(Frame*, const String& title); @@ -257,7 +257,6 @@ public: static bool hasFrontends() { return s_frontendCounter; } static bool canvasAgentEnabled(ScriptExecutionContext*); static bool consoleAgentEnabled(ScriptExecutionContext*); - static bool runtimeAgentEnabled(Frame*); static bool timelineAgentEnabled(ScriptExecutionContext*); static bool collectingHTMLParseErrors(Page*); #else @@ -333,10 +332,10 @@ private: static void didScrollImpl(InstrumentingAgents*); static InspectorInstrumentationCookie willDispatchXHRLoadEventImpl(InstrumentingAgents*, XMLHttpRequest*, ScriptExecutionContext*); static void didDispatchXHRLoadEventImpl(const InspectorInstrumentationCookie&); - static InspectorInstrumentationCookie willPaintImpl(InstrumentingAgents*, GraphicsContext*, const LayoutRect&, Frame*); - static void didPaintImpl(const InspectorInstrumentationCookie&); static void willScrollLayerImpl(InstrumentingAgents*, Frame*); static void didScrollLayerImpl(InstrumentingAgents*); + static InspectorInstrumentationCookie willPaintImpl(InstrumentingAgents*, Frame*); + static void didPaintImpl(const InspectorInstrumentationCookie&, GraphicsContext*, const LayoutRect&); static void willCompositeImpl(InstrumentingAgents*); static void didCompositeImpl(InstrumentingAgents*); static InspectorInstrumentationCookie willRecalculateStyleImpl(InstrumentingAgents*, Frame*); @@ -389,8 +388,8 @@ private: static InspectorInstrumentationCookie willWriteHTMLImpl(InstrumentingAgents*, unsigned int length, unsigned int startLine, Frame*); static void didWriteHTMLImpl(const InspectorInstrumentationCookie&, unsigned int endLine); - static void addMessageToConsoleImpl(InstrumentingAgents*, MessageSource, MessageType, MessageLevel, const String& message, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack>); - static void addMessageToConsoleImpl(InstrumentingAgents*, MessageSource, MessageType, MessageLevel, const String& message, const String& scriptId, unsigned lineNumber); + static void addMessageToConsoleImpl(InstrumentingAgents*, MessageSource, MessageType, MessageLevel, const String& message, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack>, unsigned long requestIdentifier); + static void addMessageToConsoleImpl(InstrumentingAgents*, MessageSource, MessageType, MessageLevel, const String& message, const String& scriptId, unsigned lineNumber, unsigned long requestIdentifier); static void consoleCountImpl(InstrumentingAgents*, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack>); static void startConsoleTimingImpl(InstrumentingAgents*, Frame*, const String& title); static void stopConsoleTimingImpl(InstrumentingAgents*, Frame*, const String& title, PassRefPtr<ScriptCallStack>); @@ -903,22 +902,22 @@ inline void InspectorInstrumentation::didDispatchXHRLoadEvent(const InspectorIns #endif } -inline InspectorInstrumentationCookie InspectorInstrumentation::willPaint(Frame* frame, GraphicsContext* context, const LayoutRect& rect) +inline InspectorInstrumentationCookie InspectorInstrumentation::willPaint(Frame* frame) { #if ENABLE(INSPECTOR) FAST_RETURN_IF_NO_FRONTENDS(InspectorInstrumentationCookie()); if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForFrame(frame)) - return willPaintImpl(instrumentingAgents, context, rect, frame); + return willPaintImpl(instrumentingAgents, frame); #endif return InspectorInstrumentationCookie(); } -inline void InspectorInstrumentation::didPaint(const InspectorInstrumentationCookie& cookie) +inline void InspectorInstrumentation::didPaint(const InspectorInstrumentationCookie& cookie, GraphicsContext* context, const LayoutRect& rect) { #if ENABLE(INSPECTOR) FAST_RETURN_IF_NO_FRONTENDS(void()); if (cookie.first) - didPaintImpl(cookie); + didPaintImpl(cookie, context, rect); #endif } diff --git a/Source/WebCore/inspector/InspectorMemoryAgent.cpp b/Source/WebCore/inspector/InspectorMemoryAgent.cpp index fdc72afeb..75c2913d5 100644 --- a/Source/WebCore/inspector/InspectorMemoryAgent.cpp +++ b/Source/WebCore/inspector/InspectorMemoryAgent.cpp @@ -276,7 +276,7 @@ private: CharacterDataStatistics& m_characterDataStatistics; }; -class CounterVisitor : public NodeWrapperVisitor, public ExternalStringVisitor { +class CounterVisitor : public WrappedNodeVisitor, public ExternalStringVisitor { public: CounterVisitor(Page* page) : m_page(page) @@ -450,7 +450,7 @@ static void reportRenderTreeInfo(WTF::MemoryInstrumentationClient& memoryInstrum namespace { -class DOMTreesIterator : public NodeWrapperVisitor { +class DOMTreesIterator : public WrappedNodeVisitor { public: DOMTreesIterator(MemoryInstrumentationImpl& memoryInstrumentation, Page* page) : m_page(page) @@ -559,7 +559,7 @@ void InspectorMemoryAgent::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) { MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::Inspector); InspectorBaseAgent<InspectorMemoryAgent>::reportMemoryUsage(memoryObjectInfo); - info.addMember(m_inspectorClient); + info.addWeakPointer(m_inspectorClient); info.addMember(m_page); } diff --git a/Source/WebCore/inspector/InspectorOverlay.cpp b/Source/WebCore/inspector/InspectorOverlay.cpp index b141a1604..3ceab7fd9 100644 --- a/Source/WebCore/inspector/InspectorOverlay.cpp +++ b/Source/WebCore/inspector/InspectorOverlay.cpp @@ -504,7 +504,7 @@ void InspectorOverlay::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) con { MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::InspectorOverlay); info.addMember(m_page); - info.addMember(m_client); + info.addWeakPointer(m_client); info.addMember(m_pausedInDebuggerMessage); info.addMember(m_highlightNode); info.addMember(m_nodeHighlightConfig); diff --git a/Source/WebCore/inspector/InspectorPageAgent.cpp b/Source/WebCore/inspector/InspectorPageAgent.cpp index 788685b1e..1ddaa7f56 100644 --- a/Source/WebCore/inspector/InspectorPageAgent.cpp +++ b/Source/WebCore/inspector/InspectorPageAgent.cpp @@ -139,7 +139,7 @@ static bool prepareCachedResourceBuffer(CachedResource* cachedResource, bool* ha static bool hasTextContent(CachedResource* cachedResource) { InspectorPageAgent::ResourceType type = InspectorPageAgent::cachedResourceType(*cachedResource); - return type == InspectorPageAgent::StylesheetResource || type == InspectorPageAgent::ScriptResource || type == InspectorPageAgent::XHRResource; + return type == InspectorPageAgent::DocumentResource || type == InspectorPageAgent::StylesheetResource || type == InspectorPageAgent::ScriptResource || type == InspectorPageAgent::XHRResource; } static PassRefPtr<TextResourceDecoder> createXHRTextDecoder(const String& mimeType, const String& textEncodingName) @@ -308,6 +308,8 @@ InspectorPageAgent::ResourceType InspectorPageAgent::cachedResourceType(const Ca return InspectorPageAgent::ScriptResource; case CachedResource::RawResource: return InspectorPageAgent::XHRResource; + case CachedResource::MainResource: + return InspectorPageAgent::DocumentResource; default: break; } @@ -328,7 +330,6 @@ InspectorPageAgent::InspectorPageAgent(InstrumentingAgents* instrumentingAgents, , m_frontend(0) , m_overlay(overlay) , m_lastScriptIdentifier(0) - , m_lastPaintContext(0) , m_didLoadEventFire(false) , m_geolocationOverridden(false) { @@ -354,9 +355,6 @@ void InspectorPageAgent::restore() if (m_state->getBoolean(PageAgentState::pageAgentEnabled)) { ErrorString error; enable(&error); - - if (m_inspectorAgent->didCommitLoadFired()) - frameNavigated(m_page->mainFrame()->loader()->documentLoader()); #if ENABLE(TOUCH_EVENTS) updateTouchEventEmulationInPage(m_state->getBoolean(PageAgentState::touchEventEmulationEnabled)); #endif @@ -876,18 +874,9 @@ void InspectorPageAgent::applyScreenHeightOverride(long* height) *height = heightOverride; } -void InspectorPageAgent::willPaint(GraphicsContext* context, const LayoutRect& rect) -{ - if (m_state->getBoolean(PageAgentState::showPaintRects)) { - m_lastPaintContext = context; - m_lastPaintRect = rect; - m_lastPaintRect.inflate(-1); - } -} - -void InspectorPageAgent::didPaint() +void InspectorPageAgent::didPaint(GraphicsContext* context, const LayoutRect& rect) { - if (!m_lastPaintContext || !m_state->getBoolean(PageAgentState::showPaintRects)) + if (!m_state->getBoolean(PageAgentState::showPaintRects)) return; static int colorSelector = 0; @@ -897,9 +886,9 @@ void InspectorPageAgent::didPaint() Color(0, 0, 0xFF, 0x3F), }; - m_overlay->drawOutline(m_lastPaintContext, m_lastPaintRect, colors[colorSelector++ % WTF_ARRAY_LENGTH(colors)]); - - m_lastPaintContext = 0; + LayoutRect inflatedRect(rect); + inflatedRect.inflate(-1); + m_overlay->drawOutline(context, inflatedRect, colors[colorSelector++ % WTF_ARRAY_LENGTH(colors)]); } void InspectorPageAgent::didLayout() @@ -958,10 +947,10 @@ PassRefPtr<TypeBuilder::Page::FrameResourceTree> InspectorPageAgent::buildObject .setUrl(cachedResource->url()) .setType(cachedResourceTypeJson(*cachedResource)) .setMimeType(cachedResource->response().mimeType()); - if (cachedResource->status() == CachedResource::LoadError) - resourceObject->setFailed(true); - if (cachedResource->status() == CachedResource::Canceled) + if (cachedResource->wasCanceled()) resourceObject->setCanceled(true); + else if (cachedResource->status() == CachedResource::LoadError) + resourceObject->setFailed(true); subresources->addItem(resourceObject); } diff --git a/Source/WebCore/inspector/InspectorPageAgent.h b/Source/WebCore/inspector/InspectorPageAgent.h index e54bb7257..2768bb17f 100644 --- a/Source/WebCore/inspector/InspectorPageAgent.h +++ b/Source/WebCore/inspector/InspectorPageAgent.h @@ -136,8 +136,7 @@ public: void loaderDetachedFromFrame(DocumentLoader*); void applyScreenWidthOverride(long*); void applyScreenHeightOverride(long*); - void willPaint(GraphicsContext*, const LayoutRect&); - void didPaint(); + void didPaint(GraphicsContext*, const LayoutRect&); void didLayout(); void didScroll(); @@ -179,8 +178,6 @@ private: HashMap<Frame*, String> m_frameToIdentifier; HashMap<String, Frame*> m_identifierToFrame; HashMap<DocumentLoader*, String> m_loaderToIdentifier; - GraphicsContext* m_lastPaintContext; - LayoutRect m_lastPaintRect; bool m_didLoadEventFire; bool m_geolocationOverridden; RefPtr<GeolocationPosition> m_geolocationPosition; diff --git a/Source/WebCore/inspector/InspectorProfilerAgent.cpp b/Source/WebCore/inspector/InspectorProfilerAgent.cpp index 6c28ed231..1466205b8 100644 --- a/Source/WebCore/inspector/InspectorProfilerAgent.cpp +++ b/Source/WebCore/inspector/InspectorProfilerAgent.cpp @@ -490,7 +490,7 @@ void InspectorProfilerAgent::reportMemoryUsage(MemoryObjectInfo* memoryObjectInf InspectorBaseAgent<InspectorProfilerAgent>::reportMemoryUsage(memoryObjectInfo); info.addMember(m_consoleAgent); info.addMember(m_injectedScriptManager); - info.addMember(m_frontend); + info.addWeakPointer(m_frontend); info.addMember(m_profiles); info.addMember(m_snapshots); diff --git a/Source/WebCore/inspector/InspectorResourceAgent.cpp b/Source/WebCore/inspector/InspectorResourceAgent.cpp index 8d15b8e50..333cfe14c 100644 --- a/Source/WebCore/inspector/InspectorResourceAgent.cpp +++ b/Source/WebCore/inspector/InspectorResourceAgent.cpp @@ -71,6 +71,7 @@ #include <wtf/CurrentTime.h> #include <wtf/HexNumber.h> #include <wtf/ListHashSet.h> +#include <wtf/MemoryInstrumentationHashMap.h> #include <wtf/RefPtr.h> #include <wtf/text/StringBuilder.h> @@ -570,6 +571,7 @@ void InspectorResourceAgent::enable() void InspectorResourceAgent::disable(ErrorString*) { m_state->setBoolean(ResourceAgentState::resourceAgentEnabled, false); + m_state->setString(ResourceAgentState::userAgentOverride, ""); m_instrumentingAgents->setInspectorResourceAgent(0); m_resourcesData->clear(); } @@ -668,6 +670,19 @@ void InspectorResourceAgent::mainFrameNavigated(DocumentLoader* loader) m_resourcesData->clear(m_pageAgent->loaderId(loader)); } +void InspectorResourceAgent::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const +{ + MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::InspectorResourceAgent); + InspectorBaseAgent<InspectorResourceAgent>::reportMemoryUsage(memoryObjectInfo); + info.addWeakPointer(m_pageAgent); + info.addWeakPointer(m_client); + info.addWeakPointer(m_frontend); + info.addMember(m_userAgentOverride); + info.addMember(m_resourcesData); + info.addMember(m_pendingXHRReplayData); + info.addMember(m_styleRecalculationInitiator); +} + InspectorResourceAgent::InspectorResourceAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorClient* client, InspectorState* state) : InspectorBaseAgent<InspectorResourceAgent>("Network", instrumentingAgents, state) , m_pageAgent(pageAgent) diff --git a/Source/WebCore/inspector/InspectorResourceAgent.h b/Source/WebCore/inspector/InspectorResourceAgent.h index 5949e5ab8..06c9eaba2 100644 --- a/Source/WebCore/inspector/InspectorResourceAgent.h +++ b/Source/WebCore/inspector/InspectorResourceAgent.h @@ -153,6 +153,8 @@ public: virtual void clearBrowserCookies(ErrorString*); virtual void setCacheDisabled(ErrorString*, bool cacheDisabled); + virtual void reportMemoryUsage(MemoryObjectInfo*) const OVERRIDE; + private: InspectorResourceAgent(InstrumentingAgents*, InspectorPageAgent*, InspectorClient*, InspectorState*); diff --git a/Source/WebCore/inspector/InspectorRuntimeAgent.cpp b/Source/WebCore/inspector/InspectorRuntimeAgent.cpp index 80be50c2a..9b0d2d72e 100644 --- a/Source/WebCore/inspector/InspectorRuntimeAgent.cpp +++ b/Source/WebCore/inspector/InspectorRuntimeAgent.cpp @@ -37,11 +37,6 @@ #include "InjectedScript.h" #include "InjectedScriptManager.h" #include "InspectorValues.h" -#include "InstrumentingAgents.h" -#include "WorkerContext.h" -#include "WorkerDebuggerAgent.h" -#include "WorkerRunLoop.h" -#include "WorkerThread.h" #include <wtf/PassRefPtr.h> @@ -63,14 +58,11 @@ InspectorRuntimeAgent::InspectorRuntimeAgent(InstrumentingAgents* instrumentingA #if ENABLE(JAVASCRIPT_DEBUGGER) , m_scriptDebugServer(0) #endif - , m_paused(false) { - m_instrumentingAgents->setInspectorRuntimeAgent(this); } InspectorRuntimeAgent::~InspectorRuntimeAgent() { - m_instrumentingAgents->setInspectorRuntimeAgent(0); } #if ENABLE(JAVASCRIPT_DEBUGGER) @@ -84,7 +76,7 @@ static ScriptDebugServer::PauseOnExceptionsState setPauseOnExceptionsState(Scrip } #endif -void InspectorRuntimeAgent::evaluate(ErrorString* errorString, const String& expression, const String* const objectGroup, const bool* const includeCommandLineAPI, const bool* const doNotPauseOnExceptionsAndMuteConsole, const int* executionContextId, const bool* const returnByValue, RefPtr<TypeBuilder::Runtime::RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown) +void InspectorRuntimeAgent::evaluate(ErrorString* errorString, const String& expression, const String* const objectGroup, const bool* const includeCommandLineAPI, const bool* const doNotPauseOnExceptionsAndMuteConsole, const int* executionContextId, const bool* const returnByValue, const bool* generatePreview, RefPtr<TypeBuilder::Runtime::RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown) { InjectedScript injectedScript = injectedScriptForEval(errorString, executionContextId); if (injectedScript.hasNoValue()) @@ -97,7 +89,7 @@ void InspectorRuntimeAgent::evaluate(ErrorString* errorString, const String& exp if (asBool(doNotPauseOnExceptionsAndMuteConsole)) muteConsole(); - injectedScript.evaluate(errorString, expression, objectGroup ? *objectGroup : "", asBool(includeCommandLineAPI), asBool(returnByValue), &result, wasThrown); + injectedScript.evaluate(errorString, expression, objectGroup ? *objectGroup : "", asBool(includeCommandLineAPI), asBool(returnByValue), asBool(generatePreview), &result, wasThrown); if (asBool(doNotPauseOnExceptionsAndMuteConsole)) { unmuteConsole(); @@ -107,7 +99,7 @@ void InspectorRuntimeAgent::evaluate(ErrorString* errorString, const String& exp } } -void InspectorRuntimeAgent::callFunctionOn(ErrorString* errorString, const String& objectId, const String& expression, const RefPtr<InspectorArray>* const optionalArguments, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* const returnByValue, RefPtr<TypeBuilder::Runtime::RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown) +void InspectorRuntimeAgent::callFunctionOn(ErrorString* errorString, const String& objectId, const String& expression, const RefPtr<InspectorArray>* const optionalArguments, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* const returnByValue, const bool* generatePreview, RefPtr<TypeBuilder::Runtime::RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown) { InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId); if (injectedScript.hasNoValue()) { @@ -126,7 +118,7 @@ void InspectorRuntimeAgent::callFunctionOn(ErrorString* errorString, const Strin if (asBool(doNotPauseOnExceptionsAndMuteConsole)) muteConsole(); - injectedScript.callFunctionOn(errorString, objectId, expression, arguments, asBool(returnByValue), &result, wasThrown); + injectedScript.callFunctionOn(errorString, objectId, expression, arguments, asBool(returnByValue), asBool(generatePreview), &result, wasThrown); if (asBool(doNotPauseOnExceptionsAndMuteConsole)) { unmuteConsole(); @@ -172,7 +164,6 @@ void InspectorRuntimeAgent::releaseObjectGroup(ErrorString*, const String& objec void InspectorRuntimeAgent::run(ErrorString*) { - m_paused = false; } #if ENABLE(JAVASCRIPT_DEBUGGER) @@ -180,18 +171,6 @@ void InspectorRuntimeAgent::setScriptDebugServer(ScriptDebugServer* scriptDebugS { m_scriptDebugServer = scriptDebugServer; } - -#if ENABLE(WORKERS) -void InspectorRuntimeAgent::pauseWorkerContext(WorkerContext* context) -{ - m_paused = true; - MessageQueueWaitResult result; - do { - result = context->thread()->runLoop().runInMode(context, WorkerDebuggerAgent::debuggerTaskMode); - // Keep waiting until execution is resumed. - } while (result == MessageQueueMessageReceived && m_paused); -} -#endif // ENABLE(WORKERS) #endif // ENABLE(JAVASCRIPT_DEBUGGER) } // namespace WebCore diff --git a/Source/WebCore/inspector/InspectorRuntimeAgent.h b/Source/WebCore/inspector/InspectorRuntimeAgent.h index 29e57d22f..362f2bd13 100644 --- a/Source/WebCore/inspector/InspectorRuntimeAgent.h +++ b/Source/WebCore/inspector/InspectorRuntimeAgent.h @@ -68,6 +68,7 @@ public: const bool* doNotPauseOnExceptionsAndMuteConsole, const int* executionContextId, const bool* returnByValue, + const bool* generatePreview, RefPtr<TypeBuilder::Runtime::RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown); virtual void callFunctionOn(ErrorString*, @@ -76,6 +77,7 @@ public: const RefPtr<InspectorArray>* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, + const bool* generatePreview, RefPtr<TypeBuilder::Runtime::RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown); virtual void releaseObject(ErrorString*, const String& objectId); @@ -85,9 +87,6 @@ public: #if ENABLE(JAVASCRIPT_DEBUGGER) void setScriptDebugServer(ScriptDebugServer*); -#if ENABLE(WORKERS) - void pauseWorkerContext(WorkerContext*); -#endif #endif protected: @@ -105,7 +104,6 @@ private: #if ENABLE(JAVASCRIPT_DEBUGGER) ScriptDebugServer* m_scriptDebugServer; #endif - bool m_paused; }; } // namespace WebCore diff --git a/Source/WebCore/inspector/InspectorStyleSheet.cpp b/Source/WebCore/inspector/InspectorStyleSheet.cpp index 95da1770e..32cbe7639 100644 --- a/Source/WebCore/inspector/InspectorStyleSheet.cpp +++ b/Source/WebCore/inspector/InspectorStyleSheet.cpp @@ -266,6 +266,11 @@ static void fillMediaListChain(CSSRule* rule, Array<TypeBuilder::CSS::CSSMedia>* } } +static PassOwnPtr<CSSParser> createCSSParser(Document* document) +{ + return adoptPtr(new CSSParser(document ? CSSParserContext(document) : strictCSSParserContext())); +} + PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet) { return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet)); @@ -339,8 +344,8 @@ bool InspectorStyle::setPropertyText(unsigned index, const String& propertyText, if (propertyText.stripWhiteSpace().length()) { RefPtr<StylePropertySet> tempMutableStyle = StylePropertySet::create(); RefPtr<CSSRuleSourceData> sourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE); - CSSParser p(CSSStrictMode); - p.parseDeclaration(tempMutableStyle.get(), propertyText + " " + bogusPropertyName + ": none", sourceData, m_style->parentStyleSheet()->contents()); + Document* ownerDocument = m_parentStyleSheet->pageStyleSheet() ? m_parentStyleSheet->pageStyleSheet()->ownerDocument() : 0; + createCSSParser(ownerDocument)->parseDeclaration(tempMutableStyle.get(), propertyText + " " + bogusPropertyName + ": none", sourceData, m_style->parentStyleSheet()->contents()); Vector<CSSPropertySourceData>& propertyData = sourceData->styleSourceData->propertyData; unsigned propertyCount = propertyData.size(); @@ -811,8 +816,20 @@ bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String return true; } +static bool checkStyleRuleSelector(Document* document, const String& selector) +{ + CSSSelectorList selectorList; + createCSSParser(document)->parseSelector(selector, selectorList); + return selectorList.first(); +} + CSSStyleRule* InspectorStyleSheet::addRule(const String& selector, ExceptionCode& ec) { + if (!checkStyleRuleSelector(m_pageStyleSheet->ownerDocument(), selector)) { + ec = SYNTAX_ERR; + return 0; + } + String text; bool success = getText(&text); if (!success) { @@ -826,9 +843,20 @@ CSSStyleRule* InspectorStyleSheet::addRule(const String& selector, ExceptionCode if (ec) return 0; ASSERT(m_pageStyleSheet->length()); - CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(m_pageStyleSheet->item(m_pageStyleSheet->length() - 1)); + unsigned lastRuleIndex = m_pageStyleSheet->length() - 1; + CSSRule* rule = m_pageStyleSheet->item(lastRuleIndex); ASSERT(rule); + CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule); + if (!styleRule) { + // What we just added has to be a CSSStyleRule - we cannot handle other types of rules yet. + // If it is not a style rule, pretend we never touched the stylesheet. + m_pageStyleSheet->deleteRule(lastRuleIndex, ec); + ASSERT(!ec); + ec = SYNTAX_ERR; + return 0; + } + if (!styleSheetText.isEmpty()) styleSheetText.append('\n'); @@ -839,7 +867,7 @@ CSSStyleRule* InspectorStyleSheet::addRule(const String& selector, ExceptionCode fireStyleSheetChanged(); - return rule; + return styleRule; } bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, ExceptionCode& ec) @@ -855,16 +883,18 @@ bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, ExceptionCode& ec return false; } - styleSheet->deleteRule(id.ordinal(), ec); - if (ec) - return false; - RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style()); if (!sourceData) { ec = NOT_FOUND_ERR; return false; } + styleSheet->deleteRule(id.ordinal(), ec); + // |rule| MAY NOT be addressed after this line! + + if (ec) + return false; + String sheetText = m_parsedStyleSheet->text(); sheetText.remove(sourceData->ruleHeaderRange.start, sourceData->ruleBodyRange.end - sourceData->ruleHeaderRange.start + 1); m_parsedStyleSheet->setText(sheetText); @@ -1142,10 +1172,8 @@ bool InspectorStyleSheet::ensureSourceData() return false; RefPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create(); - Document* ownerDocument = m_pageStyleSheet->ownerDocument(); - CSSParser p(ownerDocument ? CSSParserContext(ownerDocument) : strictCSSParserContext()); OwnPtr<RuleSourceDataList> ruleSourceDataResult = adoptPtr(new RuleSourceDataList()); - p.parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), 0, ruleSourceDataResult.get()); + createCSSParser(m_pageStyleSheet->ownerDocument())->parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), 0, ruleSourceDataResult.get()); m_parsedStyleSheet->setSourceData(ruleSourceDataResult.release()); return m_parsedStyleSheet->hasSourceData(); } @@ -1408,8 +1436,7 @@ bool InspectorStyleSheetForInlineStyle::getStyleAttributeRanges(CSSRuleSourceDat } RefPtr<StylePropertySet> tempDeclaration = StylePropertySet::create(); - CSSParser p(m_element->document()); - p.parseDeclaration(tempDeclaration.get(), m_styleText, result, m_element->document()->elementSheet()->contents()); + createCSSParser(m_element->document())->parseDeclaration(tempDeclaration.get(), m_styleText, result, m_element->document()->elementSheet()->contents()); return true; } diff --git a/Source/WebCore/inspector/InspectorTimelineAgent.cpp b/Source/WebCore/inspector/InspectorTimelineAgent.cpp index 25e5388ec..aad827ef7 100644 --- a/Source/WebCore/inspector/InspectorTimelineAgent.cpp +++ b/Source/WebCore/inspector/InspectorTimelineAgent.cpp @@ -198,6 +198,11 @@ void InspectorTimelineAgent::setIncludeMemoryDetails(ErrorString*, bool value) m_state->setBoolean(TimelineAgentState::includeMemoryDetails, value); } +void InspectorTimelineAgent::canMonitorMainThread(ErrorString*, bool* result) +{ + *result = m_client && m_client->canMonitorMainThread(); +} + void InspectorTimelineAgent::supportsFrameInstrumentation(ErrorString*, bool* result) { *result = m_client && m_client->supportsFrameInstrumentation(); @@ -269,13 +274,16 @@ void InspectorTimelineAgent::didRecalculateStyle() didCompleteCurrentRecord(TimelineRecordType::RecalculateStyles); } -void InspectorTimelineAgent::willPaint(const LayoutRect& rect, Frame* frame) +void InspectorTimelineAgent::willPaint(Frame* frame) { - pushCurrentRecord(TimelineRecordFactory::createPaintData(rect), TimelineRecordType::Paint, true, frame, true); + pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Paint, true, frame, true); } -void InspectorTimelineAgent::didPaint() +void InspectorTimelineAgent::didPaint(const LayoutRect& rect) { + TimelineRecordEntry entry = m_recordStack.last(); + ASSERT(entry.type == TimelineRecordType::Paint); + TimelineRecordFactory::addRectData(entry.data.get(), rect); didCompleteCurrentRecord(TimelineRecordType::Paint); } diff --git a/Source/WebCore/inspector/InspectorTimelineAgent.h b/Source/WebCore/inspector/InspectorTimelineAgent.h index 4b7bc7780..36e2b117b 100644 --- a/Source/WebCore/inspector/InspectorTimelineAgent.h +++ b/Source/WebCore/inspector/InspectorTimelineAgent.h @@ -81,6 +81,7 @@ public: virtual void start(ErrorString*, const int* maxCallStackDepth); virtual void stop(ErrorString*); virtual void setIncludeMemoryDetails(ErrorString*, bool); + virtual void canMonitorMainThread(ErrorString*, bool*); virtual void supportsFrameInstrumentation(ErrorString*, bool*); int id() const { return m_id; } @@ -105,8 +106,8 @@ public: void willRecalculateStyle(Frame*); void didRecalculateStyle(); - void willPaint(const LayoutRect&, Frame*); - void didPaint(); + void willPaint(Frame*); + void didPaint(const LayoutRect&); void willScroll(Frame*); void didScroll(); diff --git a/Source/WebCore/inspector/InspectorWorkerAgent.cpp b/Source/WebCore/inspector/InspectorWorkerAgent.cpp index e90f7cb4b..b2b9374cd 100644 --- a/Source/WebCore/inspector/InspectorWorkerAgent.cpp +++ b/Source/WebCore/inspector/InspectorWorkerAgent.cpp @@ -63,6 +63,7 @@ public: } virtual ~WorkerFrontendChannel() { + disconnectFromWorkerContext(); } int id() const { return m_id; } @@ -136,9 +137,9 @@ void InspectorWorkerAgent::restore() void InspectorWorkerAgent::clearFrontend() { - m_inspectorFrontend = 0; m_state->setBoolean(WorkerAgentState::autoconnectToWorkers, false); - destroyWorkerFrontendChannels(); + disable(0); + m_inspectorFrontend = 0; } void InspectorWorkerAgent::enable(ErrorString*) diff --git a/Source/WebCore/inspector/InstrumentingAgents.h b/Source/WebCore/inspector/InstrumentingAgents.h index 2306e7a1e..884d9bb11 100644 --- a/Source/WebCore/inspector/InstrumentingAgents.h +++ b/Source/WebCore/inspector/InstrumentingAgents.h @@ -50,12 +50,12 @@ class InspectorFileSystemAgent; class InspectorPageAgent; class InspectorProfilerAgent; class InspectorResourceAgent; -class InspectorRuntimeAgent; class InspectorTimelineAgent; class InspectorWorkerAgent; class Page; class PageRuntimeAgent; class WorkerContext; +class WorkerRuntimeAgent; class InstrumentingAgents { WTF_MAKE_NONCOPYABLE(InstrumentingAgents); @@ -68,8 +68,8 @@ public: , m_inspectorConsoleAgent(0) , m_inspectorDOMAgent(0) , m_inspectorResourceAgent(0) - , m_inspectorRuntimeAgent(0) , m_pageRuntimeAgent(0) + , m_workerRuntimeAgent(0) , m_inspectorTimelineAgent(0) , m_inspectorDOMStorageAgent(0) #if ENABLE(SQL_DATABASE) @@ -109,12 +109,12 @@ public: InspectorResourceAgent* inspectorResourceAgent() const { return m_inspectorResourceAgent; } void setInspectorResourceAgent(InspectorResourceAgent* agent) { m_inspectorResourceAgent = agent; } - InspectorRuntimeAgent* inspectorRuntimeAgent() const { return m_inspectorRuntimeAgent; } - void setInspectorRuntimeAgent(InspectorRuntimeAgent* agent) { m_inspectorRuntimeAgent = agent; } - PageRuntimeAgent* pageRuntimeAgent() const { return m_pageRuntimeAgent; } void setPageRuntimeAgent(PageRuntimeAgent* agent) { m_pageRuntimeAgent = agent; } + WorkerRuntimeAgent* workerRuntimeAgent() const { return m_workerRuntimeAgent; } + void setWorkerRuntimeAgent(WorkerRuntimeAgent* agent) { m_workerRuntimeAgent = agent; } + InspectorTimelineAgent* inspectorTimelineAgent() const { return m_inspectorTimelineAgent; } void setInspectorTimelineAgent(InspectorTimelineAgent* agent) { m_inspectorTimelineAgent = agent; } @@ -160,8 +160,8 @@ private: InspectorConsoleAgent* m_inspectorConsoleAgent; InspectorDOMAgent* m_inspectorDOMAgent; InspectorResourceAgent* m_inspectorResourceAgent; - InspectorRuntimeAgent* m_inspectorRuntimeAgent; PageRuntimeAgent* m_pageRuntimeAgent; + WorkerRuntimeAgent* m_workerRuntimeAgent; InspectorTimelineAgent* m_inspectorTimelineAgent; InspectorDOMStorageAgent* m_inspectorDOMStorageAgent; #if ENABLE(SQL_DATABASE) diff --git a/Source/WebCore/inspector/MemoryInstrumentationImpl.cpp b/Source/WebCore/inspector/MemoryInstrumentationImpl.cpp index 20d16cb1e..ae59b991c 100644 --- a/Source/WebCore/inspector/MemoryInstrumentationImpl.cpp +++ b/Source/WebCore/inspector/MemoryInstrumentationImpl.cpp @@ -70,10 +70,8 @@ void MemoryInstrumentationClientImpl::countObjectSize(const void* object, Memory if (!checkInstrumentedObjects()) return; - if (object) { + if (object) m_countedObjects.add(object, size); - checkCountedObject(object); - } } bool MemoryInstrumentationClientImpl::visited(const void* object) diff --git a/Source/WebCore/inspector/NetworkResourcesData.cpp b/Source/WebCore/inspector/NetworkResourcesData.cpp index 788ebea55..1a57cb0a9 100644 --- a/Source/WebCore/inspector/NetworkResourcesData.cpp +++ b/Source/WebCore/inspector/NetworkResourcesData.cpp @@ -35,6 +35,7 @@ #include "DOMImplementation.h" #include "SharedBuffer.h" #include "TextResourceDecoder.h" +#include <wtf/MemoryInstrumentationHashMap.h> namespace { // 100MB @@ -66,6 +67,15 @@ XHRReplayData::XHRReplayData(const String &method, const KURL& url, bool async, { } +void XHRReplayData::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const +{ + MemoryClassInfo info(memoryObjectInfo, this); + info.addMember(m_method); + info.addMember(m_url); + info.addMember(m_formData); + info.addMember(m_headers); +} + // ResourceData NetworkResourcesData::ResourceData::ResourceData(const String& requestId, const String& loaderId) : m_requestId(requestId) @@ -137,6 +147,22 @@ size_t NetworkResourcesData::ResourceData::decodeDataToContent() return contentSizeInBytes(m_content) - dataLength; } +void NetworkResourcesData::ResourceData::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const +{ + MemoryClassInfo info(memoryObjectInfo, this); + info.addMember(m_requestId); + info.addMember(m_loaderId); + info.addMember(m_frameId); + info.addMember(m_url); + info.addMember(m_content); + info.addMember(m_xhrReplayData); + info.addMember(m_dataBuffer); + info.addMember(m_textEncodingName); + info.addMember(m_decoder); + info.addMember(m_buffer); + info.addMember(m_cachedResource); +} + // NetworkResourcesData NetworkResourcesData::NetworkResourcesData() : m_contentSize(0) @@ -382,6 +408,14 @@ bool NetworkResourcesData::ensureFreeSpace(size_t size) return true; } +void NetworkResourcesData::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const +{ + MemoryClassInfo info(memoryObjectInfo, this); + info.addMember(m_requestIdsDeque); + info.addMember(m_reusedXHRReplayDataRequestIds); + info.addMember(m_requestIdToResourceDataMap); +} + } // namespace WebCore #endif // ENABLE(INSPECTOR) diff --git a/Source/WebCore/inspector/NetworkResourcesData.h b/Source/WebCore/inspector/NetworkResourcesData.h index 22093ebd3..41340a3d8 100644 --- a/Source/WebCore/inspector/NetworkResourcesData.h +++ b/Source/WebCore/inspector/NetworkResourcesData.h @@ -57,6 +57,7 @@ public: PassRefPtr<FormData> formData() const { return m_formData; } const HTTPHeaderMap& headers() const { return m_headers; } bool includeCredentials() const { return m_includeCredentials; } + void reportMemoryUsage(MemoryObjectInfo*) const; private: XHRReplayData(const String &method, const KURL&, bool async, PassRefPtr<FormData>, bool includeCredentials); @@ -116,6 +117,9 @@ public: XHRReplayData* xhrReplayData() const { return m_xhrReplayData.get(); } void setXHRReplayData(XHRReplayData* xhrReplayData) { m_xhrReplayData = xhrReplayData; } + + void reportMemoryUsage(MemoryObjectInfo*) const; + private: bool hasData() const { return m_dataBuffer; } size_t dataLength() const; @@ -163,6 +167,8 @@ public: void reuseXHRReplayData(const String& requestId, const String& reusedRequestId); XHRReplayData* xhrReplayData(const String& requestId); + void reportMemoryUsage(MemoryObjectInfo*) const; + private: void ensureNoDataForRequestId(const String& requestId); bool ensureFreeSpace(size_t); diff --git a/Source/WebCore/inspector/PageRuntimeAgent.cpp b/Source/WebCore/inspector/PageRuntimeAgent.cpp index 74c3b7ff9..5d1312a34 100644 --- a/Source/WebCore/inspector/PageRuntimeAgent.cpp +++ b/Source/WebCore/inspector/PageRuntimeAgent.cpp @@ -38,7 +38,6 @@ #include "Document.h" #include "InjectedScript.h" #include "InjectedScriptManager.h" -#include "InspectorAgent.h" #include "InspectorPageAgent.h" #include "InspectorState.h" #include "InstrumentingAgents.h" @@ -53,28 +52,28 @@ namespace PageRuntimeAgentState { static const char runtimeEnabled[] = "runtimeEnabled"; }; -PageRuntimeAgent::PageRuntimeAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, InjectedScriptManager* injectedScriptManager, Page* page, InspectorPageAgent* pageAgent, InspectorAgent* inspectorAgent) +PageRuntimeAgent::PageRuntimeAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, InjectedScriptManager* injectedScriptManager, Page* page, InspectorPageAgent* pageAgent) : InspectorRuntimeAgent(instrumentingAgents, state, injectedScriptManager) , m_inspectedPage(page) , m_pageAgent(pageAgent) - , m_inspectorAgent(inspectorAgent) , m_frontend(0) + , m_mainWorldContextCreated(false) { + m_instrumentingAgents->setPageRuntimeAgent(this); } PageRuntimeAgent::~PageRuntimeAgent() { + m_instrumentingAgents->setPageRuntimeAgent(0); } void PageRuntimeAgent::setFrontend(InspectorFrontend* frontend) { m_frontend = frontend->runtime(); - m_instrumentingAgents->setPageRuntimeAgent(this); } void PageRuntimeAgent::clearFrontend() { - m_instrumentingAgents->setPageRuntimeAgent(0); m_frontend = 0; String errorString; disable(&errorString); @@ -98,7 +97,7 @@ void PageRuntimeAgent::enable(ErrorString* errorString) // Only report existing contexts if the page did commit load, otherwise we may // unintentionally initialize contexts in the frames which may trigger some listeners // that are expected to be triggered only after the load is committed, see http://crbug.com/131623 - if (m_inspectorAgent->didCommitLoadFired()) + if (m_mainWorldContextCreated) reportExecutionContextCreation(); } @@ -111,8 +110,10 @@ void PageRuntimeAgent::disable(ErrorString* errorString) m_state->setBoolean(PageRuntimeAgentState::runtimeEnabled, false); } -void PageRuntimeAgent::didClearWindowObject(Frame* frame) +void PageRuntimeAgent::didCreateMainWorldContext(Frame* frame) { + m_mainWorldContextCreated = true; + if (!m_enabled) return; ASSERT(m_frontend); diff --git a/Source/WebCore/inspector/PageRuntimeAgent.h b/Source/WebCore/inspector/PageRuntimeAgent.h index 5278aa737..6d1e44ac1 100644 --- a/Source/WebCore/inspector/PageRuntimeAgent.h +++ b/Source/WebCore/inspector/PageRuntimeAgent.h @@ -40,16 +40,15 @@ namespace WebCore { -class InspectorAgent; class InspectorPageAgent; class Page; class SecurityOrigin; class PageRuntimeAgent : public InspectorRuntimeAgent { public: - static PassOwnPtr<PageRuntimeAgent> create(InstrumentingAgents* instrumentingAgents, InspectorState* state, InjectedScriptManager* injectedScriptManager, Page* page, InspectorPageAgent* pageAgent, InspectorAgent* inspectorAgent) + static PassOwnPtr<PageRuntimeAgent> create(InstrumentingAgents* instrumentingAgents, InspectorState* state, InjectedScriptManager* injectedScriptManager, Page* page, InspectorPageAgent* pageAgent) { - return adoptPtr(new PageRuntimeAgent(instrumentingAgents, state, injectedScriptManager, page, pageAgent, inspectorAgent)); + return adoptPtr(new PageRuntimeAgent(instrumentingAgents, state, injectedScriptManager, page, pageAgent)); } virtual ~PageRuntimeAgent(); virtual void setFrontend(InspectorFrontend*); @@ -58,11 +57,11 @@ public: virtual void enable(ErrorString*); virtual void disable(ErrorString*); - void didClearWindowObject(Frame*); + void didCreateMainWorldContext(Frame*); void didCreateIsolatedContext(Frame*, ScriptState*, SecurityOrigin*); private: - PageRuntimeAgent(InstrumentingAgents*, InspectorState*, InjectedScriptManager*, Page*, InspectorPageAgent*, InspectorAgent*); + PageRuntimeAgent(InstrumentingAgents*, InspectorState*, InjectedScriptManager*, Page*, InspectorPageAgent*); virtual InjectedScript injectedScriptForEval(ErrorString*, const int* executionContextId); virtual void muteConsole(); @@ -72,8 +71,8 @@ private: Page* m_inspectedPage; InspectorPageAgent* m_pageAgent; - InspectorAgent* m_inspectorAgent; InspectorFrontend::Runtime* m_frontend; + bool m_mainWorldContextCreated; }; } // namespace WebCore diff --git a/Source/WebCore/inspector/ScriptDebugListener.h b/Source/WebCore/inspector/ScriptDebugListener.h index 579440149..ebcb7ac62 100644 --- a/Source/WebCore/inspector/ScriptDebugListener.h +++ b/Source/WebCore/inspector/ScriptDebugListener.h @@ -60,6 +60,8 @@ public: int endLine; int endColumn; bool isContentScript; + + void reportMemoryUsage(MemoryObjectInfo*) const; }; virtual ~ScriptDebugListener() { } diff --git a/Source/WebCore/inspector/TimelineRecordFactory.cpp b/Source/WebCore/inspector/TimelineRecordFactory.cpp index 1af580a57..776b59f6f 100644 --- a/Source/WebCore/inspector/TimelineRecordFactory.cpp +++ b/Source/WebCore/inspector/TimelineRecordFactory.cpp @@ -170,13 +170,6 @@ PassRefPtr<InspectorObject> TimelineRecordFactory::createReceiveResourceData(con return data.release(); } -PassRefPtr<InspectorObject> TimelineRecordFactory::createPaintData(const LayoutRect& rect) -{ - RefPtr<InspectorObject> data = InspectorObject::create(); - addRectData(data.get(), rect); - return data.release(); -} - PassRefPtr<InspectorObject> TimelineRecordFactory::createDecodeImageData(const String& imageType) { RefPtr<InspectorObject> data = InspectorObject::create(); diff --git a/Source/WebCore/inspector/TimelineRecordFactory.h b/Source/WebCore/inspector/TimelineRecordFactory.h index f24436abc..c5dcec2b7 100644 --- a/Source/WebCore/inspector/TimelineRecordFactory.h +++ b/Source/WebCore/inspector/TimelineRecordFactory.h @@ -75,8 +75,6 @@ namespace WebCore { static PassRefPtr<InspectorObject> createResourceFinishData(const String& requestId, bool didFail, double finishTime); - static PassRefPtr<InspectorObject> createPaintData(const LayoutRect&); - static void addRectData(InspectorObject*, const LayoutRect&); static PassRefPtr<InspectorObject> createDecodeImageData(const String& imageType); diff --git a/Source/WebCore/inspector/WorkerInspectorController.cpp b/Source/WebCore/inspector/WorkerInspectorController.cpp index d383c8cdd..e04e7eeed 100644 --- a/Source/WebCore/inspector/WorkerInspectorController.cpp +++ b/Source/WebCore/inspector/WorkerInspectorController.cpp @@ -114,7 +114,9 @@ WorkerInspectorController::WorkerInspectorController(WorkerContext* workerContex #endif , 0 , 0 - , 0 +#if ENABLE(JAVASCRIPT_DEBUGGER) + , m_debuggerAgent.get() +#endif ); #if ENABLE(JAVASCRIPT_DEBUGGER) diff --git a/Source/WebCore/inspector/WorkerRuntimeAgent.cpp b/Source/WebCore/inspector/WorkerRuntimeAgent.cpp index d74e2acfb..7ff2ad127 100644 --- a/Source/WebCore/inspector/WorkerRuntimeAgent.cpp +++ b/Source/WebCore/inspector/WorkerRuntimeAgent.cpp @@ -35,18 +35,26 @@ #include "WorkerRuntimeAgent.h" #include "InjectedScript.h" +#include "InstrumentingAgents.h" #include "ScriptState.h" +#include "WorkerContext.h" +#include "WorkerDebuggerAgent.h" +#include "WorkerRunLoop.h" +#include "WorkerThread.h" namespace WebCore { WorkerRuntimeAgent::WorkerRuntimeAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, InjectedScriptManager* injectedScriptManager, WorkerContext* workerContext) : InspectorRuntimeAgent(instrumentingAgents, state, injectedScriptManager) , m_workerContext(workerContext) + , m_paused(false) { + m_instrumentingAgents->setWorkerRuntimeAgent(this); } WorkerRuntimeAgent::~WorkerRuntimeAgent() { + m_instrumentingAgents->setWorkerRuntimeAgent(0); } InjectedScript WorkerRuntimeAgent::injectedScriptForEval(ErrorString* error, const int* executionContextId) @@ -69,6 +77,23 @@ void WorkerRuntimeAgent::unmuteConsole() // We don't need to mute console for workers. } +void WorkerRuntimeAgent::run(ErrorString*) +{ + m_paused = false; +} + +#if ENABLE(JAVASCRIPT_DEBUGGER) +void WorkerRuntimeAgent::pauseWorkerContext(WorkerContext* context) +{ + m_paused = true; + MessageQueueWaitResult result; + do { + result = context->thread()->runLoop().runInMode(context, WorkerDebuggerAgent::debuggerTaskMode); + // Keep waiting until execution is resumed. + } while (result == MessageQueueMessageReceived && m_paused); +} +#endif // ENABLE(JAVASCRIPT_DEBUGGER) + } // namespace WebCore #endif // ENABLE(INSPECTOR) && ENABLE(WORKERS) diff --git a/Source/WebCore/inspector/WorkerRuntimeAgent.h b/Source/WebCore/inspector/WorkerRuntimeAgent.h index 9f1997b74..50ddef369 100644 --- a/Source/WebCore/inspector/WorkerRuntimeAgent.h +++ b/Source/WebCore/inspector/WorkerRuntimeAgent.h @@ -48,12 +48,20 @@ public: } virtual ~WorkerRuntimeAgent(); + // Protocol commands. + virtual void run(ErrorString*); + +#if ENABLE(JAVASCRIPT_DEBUGGER) + void pauseWorkerContext(WorkerContext*); +#endif // ENABLE(JAVASCRIPT_DEBUGGER) + private: WorkerRuntimeAgent(InstrumentingAgents*, InspectorState*, InjectedScriptManager*, WorkerContext*); virtual InjectedScript injectedScriptForEval(ErrorString*, const int* executionContextId); virtual void muteConsole(); virtual void unmuteConsole(); WorkerContext* m_workerContext; + bool m_paused; }; } // namespace WebCore diff --git a/Source/WebCore/inspector/compile-front-end.py b/Source/WebCore/inspector/compile-front-end.py index 1710c3c6a..9bf395cfa 100755 --- a/Source/WebCore/inspector/compile-front-end.py +++ b/Source/WebCore/inspector/compile-front-end.py @@ -84,6 +84,7 @@ modules = [ "Linkifier.js", "NetworkLog.js", "NetworkUISourceCodeProvider.js", + "NetworkWorkspaceProvider.js", "PresentationConsoleMessageHelper.js", "RuntimeModel.js", "SASSSourceMapping.js", diff --git a/Source/WebCore/inspector/front-end/ApplicationCacheModel.js b/Source/WebCore/inspector/front-end/ApplicationCacheModel.js index b5529a666..7f4083065 100644 --- a/Source/WebCore/inspector/front-end/ApplicationCacheModel.js +++ b/Source/WebCore/inspector/front-end/ApplicationCacheModel.js @@ -56,7 +56,7 @@ WebInspector.ApplicationCacheModel.EventTypes = { WebInspector.ApplicationCacheModel.prototype = { _frameNavigated: function(event) { - var frame = /** @type {WebInspector.ResourceTreeFrame} */ event.data; + var frame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data); if (frame.isMainFrame()) { this._mainFrameNavigated(); return; @@ -70,7 +70,7 @@ WebInspector.ApplicationCacheModel.prototype = { */ _frameDetached: function(event) { - var frame = /** @type {WebInspector.ResourceTreeFrame} */ event.data; + var frame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data); this._frameManifestRemoved(frame.id); }, diff --git a/Source/WebCore/inspector/front-end/AuditLauncherView.js b/Source/WebCore/inspector/front-end/AuditLauncherView.js index 2939f1b21..bf6db437d 100644 --- a/Source/WebCore/inspector/front-end/AuditLauncherView.js +++ b/Source/WebCore/inspector/front-end/AuditLauncherView.js @@ -71,7 +71,7 @@ WebInspector.AuditLauncherView.prototype = { _onRequestStarted: function(event) { - var request = /** @type {WebInspector.NetworkRequest} */ event.data; + var request = /** @type {WebInspector.NetworkRequest} */ (event.data); // Ignore long-living WebSockets for the sake of progress indicator, as we won't be waiting them anyway. if (request.type === WebInspector.resourceTypes.WebSocket) return; @@ -81,7 +81,7 @@ WebInspector.AuditLauncherView.prototype = { _onRequestFinished: function(event) { - var request = /** @type {WebInspector.NetworkRequest} */ event.data; + var request = /** @type {WebInspector.NetworkRequest} */ (event.data); // See resorceStarted for details. if (request.type === WebInspector.resourceTypes.WebSocket) return; @@ -89,6 +89,9 @@ WebInspector.AuditLauncherView.prototype = { this._updateResourceProgress(); }, + /** + * @param {!WebInspector.AuditCategory} category + */ addCategory: function(category) { if (!this._sortedCategories.length) @@ -113,6 +116,9 @@ WebInspector.AuditLauncherView.prototype = { this._updateButton(); }, + /** + * @param {boolean} auditRunning + */ _setAuditRunning: function(auditRunning) { if (this._auditRunning === auditRunning) @@ -158,6 +164,9 @@ WebInspector.AuditLauncherView.prototype = { this._setAuditRunning(!this._auditRunning); }, + /** + * @param {boolean} checkCategories + */ _selectAllClicked: function(checkCategories) { var childNodes = this._categoriesElement.childNodes; @@ -174,6 +183,10 @@ WebInspector.AuditLauncherView.prototype = { this._updateButton(); }, + /** + * @param {string} title + * @param {string} id + */ _createCategoryElement: function(title, id) { var labelElement = document.createElement("label"); diff --git a/Source/WebCore/inspector/front-end/AuditResultView.js b/Source/WebCore/inspector/front-end/AuditResultView.js index dbd7a89c7..0a90b4a66 100644 --- a/Source/WebCore/inspector/front-end/AuditResultView.js +++ b/Source/WebCore/inspector/front-end/AuditResultView.js @@ -31,6 +31,7 @@ /** * @constructor * @extends {WebInspector.View} + * @param {!Array.<!WebInspector.AuditCategoryResult>} categoryResults */ WebInspector.AuditResultView = function(categoryResults) { @@ -52,6 +53,7 @@ WebInspector.AuditResultView.prototype = { /** * @constructor * @extends {WebInspector.SidebarPane} + * @param {!WebInspector.AuditCategoryResult} categoryResult */ WebInspector.AuditCategoryResultPane = function(categoryResult) { @@ -88,6 +90,10 @@ WebInspector.AuditCategoryResultPane = function(categoryResult) } WebInspector.AuditCategoryResultPane.prototype = { + /** + * @param {(TreeOutline|TreeElement)} parentTreeElement + * @param {!WebInspector.AuditCategoryResult} result + */ _appendResult: function(parentTreeElement, result) { var title = ""; diff --git a/Source/WebCore/inspector/front-end/AuditRules.js b/Source/WebCore/inspector/front-end/AuditRules.js index 383482f5b..9b0fcba70 100644 --- a/Source/WebCore/inspector/front-end/AuditRules.js +++ b/Source/WebCore/inspector/front-end/AuditRules.js @@ -42,6 +42,12 @@ WebInspector.AuditRules.CacheableResponseCodes = 304: true // Underlying request is cacheable } +/** + * @param {!Array.<!WebInspector.NetworkRequest>} requests + * @param {Array.<!WebInspector.resourceTypes>} types + * @param {boolean} needFullResources + * @return {(Object.<string, !Array.<!WebInspector.NetworkRequest>>|Object.<string, !Array.<string>>)} + */ WebInspector.AuditRules.getDomainToResourcesMap = function(requests, types, needFullResources) { var domainToResourcesMap = {}; @@ -74,10 +80,10 @@ WebInspector.AuditRules.GzipRule = function() WebInspector.AuditRules.GzipRule.prototype = { /** - * @param {Array.<WebInspector.NetworkRequest>} requests - * @param {WebInspector.AuditRuleResult} result + * @param {!Array.<!WebInspector.NetworkRequest>} requests + * @param {!WebInspector.AuditRuleResult} result * @param {function(WebInspector.AuditRuleResult)} callback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ doRun: function(requests, result, callback, progress) { @@ -139,10 +145,10 @@ WebInspector.AuditRules.CombineExternalResourcesRule = function(id, name, type, WebInspector.AuditRules.CombineExternalResourcesRule.prototype = { /** - * @param {Array.<WebInspector.NetworkRequest>} requests - * @param {WebInspector.AuditRuleResult} result + * @param {!Array.<!WebInspector.NetworkRequest>} requests + * @param {!WebInspector.AuditRuleResult} result * @param {function(WebInspector.AuditRuleResult)} callback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ doRun: function(requests, result, callback, progress) { @@ -204,15 +210,15 @@ WebInspector.AuditRules.MinimizeDnsLookupsRule = function(hostCountThreshold) { WebInspector.AuditRules.MinimizeDnsLookupsRule.prototype = { /** - * @param {Array.<WebInspector.NetworkRequest>} requests - * @param {WebInspector.AuditRuleResult} result + * @param {!Array.<!WebInspector.NetworkRequest>} requests + * @param {!WebInspector.AuditRuleResult} result * @param {function(WebInspector.AuditRuleResult)} callback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ doRun: function(requests, result, callback, progress) { var summary = result.addChild(""); - var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(requests, undefined, false); + var domainToResourcesMap = WebInspector.AuditRules.getDomainToResourcesMap(requests, null, false); for (var domain in domainToResourcesMap) { if (domainToResourcesMap[domain].length > 1) continue; @@ -248,10 +254,10 @@ WebInspector.AuditRules.ParallelizeDownloadRule = function(optimalHostnameCount, WebInspector.AuditRules.ParallelizeDownloadRule.prototype = { /** - * @param {Array.<WebInspector.NetworkRequest>} requests - * @param {WebInspector.AuditRuleResult} result + * @param {!Array.<!WebInspector.NetworkRequest>} requests + * @param {!WebInspector.AuditRuleResult} result * @param {function(WebInspector.AuditRuleResult)} callback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ doRun: function(requests, result, callback, progress) { @@ -323,10 +329,10 @@ WebInspector.AuditRules.UnusedCssRule = function() WebInspector.AuditRules.UnusedCssRule.prototype = { /** - * @param {Array.<WebInspector.NetworkRequest>} requests - * @param {WebInspector.AuditRuleResult} result + * @param {!Array.<!WebInspector.NetworkRequest>} requests + * @param {!WebInspector.AuditRuleResult} result * @param {function(WebInspector.AuditRuleResult)} callback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ doRun: function(requests, result, callback, progress) { @@ -475,10 +481,10 @@ WebInspector.AuditRules.CacheControlRule.MillisPerMonth = 1000 * 60 * 60 * 24 * WebInspector.AuditRules.CacheControlRule.prototype = { /** - * @param {Array.<WebInspector.NetworkRequest>} requests - * @param {WebInspector.AuditRuleResult} result + * @param {!Array.<!WebInspector.NetworkRequest>} requests + * @param {!WebInspector.AuditRuleResult} result * @param {function(WebInspector.AuditRuleResult)} callback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ doRun: function(requests, result, callback, progress) { @@ -728,10 +734,10 @@ WebInspector.AuditRules.ImageDimensionsRule = function() WebInspector.AuditRules.ImageDimensionsRule.prototype = { /** - * @param {Array.<WebInspector.NetworkRequest>} requests - * @param {WebInspector.AuditRuleResult} result + * @param {!Array.<!WebInspector.NetworkRequest>} requests + * @param {!WebInspector.AuditRuleResult} result * @param {function(WebInspector.AuditRuleResult)} callback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ doRun: function(requests, result, callback, progress) { @@ -860,10 +866,10 @@ WebInspector.AuditRules.CssInHeadRule = function() WebInspector.AuditRules.CssInHeadRule.prototype = { /** - * @param {Array.<WebInspector.NetworkRequest>} requests - * @param {WebInspector.AuditRuleResult} result + * @param {!Array.<!WebInspector.NetworkRequest>} requests + * @param {!WebInspector.AuditRuleResult} result * @param {function(WebInspector.AuditRuleResult)} callback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ doRun: function(requests, result, callback, progress) { @@ -950,10 +956,10 @@ WebInspector.AuditRules.StylesScriptsOrderRule = function() WebInspector.AuditRules.StylesScriptsOrderRule.prototype = { /** - * @param {Array.<WebInspector.NetworkRequest>} requests - * @param {WebInspector.AuditRuleResult} result + * @param {!Array.<!WebInspector.NetworkRequest>} requests + * @param {!WebInspector.AuditRuleResult} result * @param {function(WebInspector.AuditRuleResult)} callback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ doRun: function(requests, result, callback, progress) { @@ -1038,10 +1044,10 @@ WebInspector.AuditRules.CSSRuleBase = function(id, name) WebInspector.AuditRules.CSSRuleBase.prototype = { /** - * @param {Array.<WebInspector.NetworkRequest>} requests - * @param {WebInspector.AuditRuleResult} result + * @param {!Array.<!WebInspector.NetworkRequest>} requests + * @param {!WebInspector.AuditRuleResult} result * @param {function(WebInspector.AuditRuleResult)} callback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ doRun: function(requests, result, callback, progress) { @@ -1205,10 +1211,10 @@ WebInspector.AuditRules.CookieRuleBase = function(id, name) WebInspector.AuditRules.CookieRuleBase.prototype = { /** - * @param {Array.<WebInspector.NetworkRequest>} requests - * @param {WebInspector.AuditRuleResult} result + * @param {!Array.<!WebInspector.NetworkRequest>} requests + * @param {!WebInspector.AuditRuleResult} result * @param {function(WebInspector.AuditRuleResult)} callback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ doRun: function(requests, result, callback, progress) { @@ -1228,7 +1234,7 @@ WebInspector.AuditRules.CookieRuleBase.prototype = { { for (var i = 0; i < allCookies.length; ++i) { for (var requestDomain in requestsByDomain) { - if (WebInspector.Cookies.cookieDomainMatchesResourceDomain(allCookies[i].domain, requestDomain)) + if (WebInspector.Cookies.cookieDomainMatchesResourceDomain(allCookies[i].domain(), requestDomain)) this._callbackForResourceCookiePairs(requestsByDomain[requestDomain], allCookies[i], callback); } } @@ -1263,7 +1269,7 @@ WebInspector.AuditRules.CookieSizeRule.prototype = { { var total = 0; for (var i = 0; i < cookieArray.length; ++i) - total += cookieArray[i].size; + total += cookieArray[i].size(); return cookieArray.length ? Math.round(total / cookieArray.length) : 0; }, @@ -1271,7 +1277,7 @@ WebInspector.AuditRules.CookieSizeRule.prototype = { { var result = 0; for (var i = 0; i < cookieArray.length; ++i) - result = Math.max(cookieArray[i].size, result); + result = Math.max(cookieArray[i].size(), result); return result; }, @@ -1397,7 +1403,7 @@ WebInspector.AuditRules.StaticCookielessRule.prototype = { _collectorCallback: function(matchingResourceData, request, cookie) { - matchingResourceData[request.url] = (matchingResourceData[request.url] || 0) + cookie.size; + matchingResourceData[request.url] = (matchingResourceData[request.url] || 0) + cookie.size(); }, __proto__: WebInspector.AuditRules.CookieRuleBase.prototype diff --git a/Source/WebCore/inspector/front-end/AuditsPanel.js b/Source/WebCore/inspector/front-end/AuditsPanel.js index 5d005a487..2624d4d15 100644 --- a/Source/WebCore/inspector/front-end/AuditsPanel.js +++ b/Source/WebCore/inspector/front-end/AuditsPanel.js @@ -70,17 +70,27 @@ WebInspector.AuditsPanel.prototype = { return [this.clearResultsButton.element]; }, + /** + * @return {!Object.<string, !WebInspector.AuditCategory>} + */ get categoriesById() { return this._auditCategoriesById; }, + /** + * @param {!WebInspector.AuditCategory} category + */ addCategory: function(category) { this.categoriesById[category.id] = category; this._launcherView.addCategory(category); }, + /** + * @param {string} id + * @return {WebInspector.AuditCategory} + */ getCategory: function(id) { return this.categoriesById[id]; @@ -96,6 +106,10 @@ WebInspector.AuditsPanel.prototype = { } }, + /** + * @param {!Array.<!WebInspector.AuditCategory>} categories + * @param {function(string, !Array.<!WebInspector.AuditCategoryResult>)} resultCallback + */ _executeAudit: function(categories, resultCallback) { this._progress.setTitle(WebInspector.UIString("Running audit")); @@ -133,6 +147,11 @@ WebInspector.AuditsPanel.prototype = { } }, + /** + * @param {function()} launcherCallback + * @param {string} mainResourceURL + * @param {!Array.<!WebInspector.AuditCategoryResult>} results + */ _auditFinishedCallback: function(launcherCallback, mainResourceURL, results) { var children = this.auditResultsTreeElement.children; @@ -196,6 +215,9 @@ WebInspector.AuditsPanel.prototype = { } }, + /** + * @param {!Array.<!WebInspector.AuditCategoryResult>} categoryResults + */ showResults: function(categoryResults) { if (!categoryResults._resultView) @@ -246,6 +268,7 @@ WebInspector.AuditsPanel.prototype = { /** * @constructor + * @param {string} displayName */ WebInspector.AuditCategory = function(displayName) { @@ -254,17 +277,27 @@ WebInspector.AuditCategory = function(displayName) } WebInspector.AuditCategory.prototype = { + /** + * @return {string} + */ get id() { // this._id value is injected at construction time. return this._id; }, + /** + * @return {string} + */ get displayName() { return this._displayName; }, + /** + * @param {!WebInspector.AuditRule} rule + * @param {!WebInspector.AuditRule.Severity} severity + */ addRule: function(rule, severity) { rule.severity = severity; @@ -272,19 +305,19 @@ WebInspector.AuditCategory.prototype = { }, /** - * @param {Array.<WebInspector.NetworkRequest>} requests + * @param {!Array.<!WebInspector.NetworkRequest>} requests * @param {function(WebInspector.AuditRuleResult)} ruleResultCallback * @param {function()} categoryDoneCallback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ run: function(requests, ruleResultCallback, categoryDoneCallback, progress) { this._ensureInitialized(); var remainingRulesCount = this._rules.length; progress.setTotalWork(remainingRulesCount); - function callbackWrapper() + function callbackWrapper(result) { - ruleResultCallback.apply(this, arguments); + ruleResultCallback(result); progress.worked(); if (!--remainingRulesCount) categoryDoneCallback(); @@ -305,6 +338,8 @@ WebInspector.AuditCategory.prototype = { /** * @constructor + * @param {string} id + * @param {string} displayName */ WebInspector.AuditRule = function(id, displayName) { @@ -312,12 +347,18 @@ WebInspector.AuditRule = function(id, displayName) this._displayName = displayName; } +/** + * @enum {string} + */ WebInspector.AuditRule.Severity = { Info: "info", Warning: "warning", Severe: "severe" } +/** + * @type {Object.<WebInspector.AuditRule.Severity, number>} + */ WebInspector.AuditRule.SeverityOrder = { "info": 3, "warning": 2, @@ -335,15 +376,18 @@ WebInspector.AuditRule.prototype = { return this._displayName; }, + /** + * @param {WebInspector.AuditRule.Severity} severity + */ set severity(severity) { this._severity = severity; }, /** - * @param {Array.<WebInspector.NetworkRequest>} requests + * @param {!Array.<!WebInspector.NetworkRequest>} requests * @param {function(WebInspector.AuditRuleResult)} callback - * @param {WebInspector.Progress} progress + * @param {!WebInspector.Progress} progress */ run: function(requests, callback, progress) { @@ -369,6 +413,7 @@ WebInspector.AuditRule.prototype = { /** * @constructor + * @param {!WebInspector.AuditCategory} category */ WebInspector.AuditCategoryResult = function(category) { @@ -377,6 +422,9 @@ WebInspector.AuditCategoryResult = function(category) } WebInspector.AuditCategoryResult.prototype = { + /** + * @param {!WebInspector.AuditCategoryResult} ruleResult + */ addRuleResult: function(ruleResult) { this.ruleResults.push(ruleResult); @@ -385,6 +433,7 @@ WebInspector.AuditCategoryResult.prototype = { /** * @constructor + * @param {(string|boolean|number|Object)} value * @param {boolean=} expanded * @param {string=} className */ @@ -402,6 +451,10 @@ WebInspector.AuditRuleResult = function(value, expanded, className) this._formatters[standardFormatters[i]] = String.standardFormatters[standardFormatters[i]]; } +/** + * @param {string} url + * @return {!Element} + */ WebInspector.AuditRuleResult.linkifyDisplayName = function(url) { return WebInspector.linkifyURLAsNode(url, WebInspector.displayNameForURL(url)); @@ -414,8 +467,10 @@ WebInspector.AuditRuleResult.resourceDomain = function(domain) WebInspector.AuditRuleResult.prototype = { /** + * @param {(string|boolean|number|Object)} value * @param {boolean=} expanded * @param {string=} className + * @return {!WebInspector.AuditRuleResult} */ addChild: function(value, expanded, className) { @@ -426,43 +481,53 @@ WebInspector.AuditRuleResult.prototype = { return entry; }, + /** + * @param {string} url + */ addURL: function(url) { - return this.addChild(WebInspector.AuditRuleResult.linkifyDisplayName(url)); + this.addChild(WebInspector.AuditRuleResult.linkifyDisplayName(url)); }, + /** + * @param {!Array.<string>} urls + */ addURLs: function(urls) { for (var i = 0; i < urls.length; ++i) this.addURL(urls[i]); }, + /** + * @param {string} snippet + */ addSnippet: function(snippet) { - return this.addChild(snippet, false, "source-code"); + this.addChild(snippet, false, "source-code"); }, /** * @param {string} format * @param {...*} vararg + * @return {!WebInspector.AuditRuleResult} */ addFormatted: function(format, vararg) { var substitutions = Array.prototype.slice.call(arguments, 1); var fragment = document.createDocumentFragment(); - var formattedResult = String.format(format, substitutions, this._formatters, fragment, this._append).formattedResult; + function append(a, b) + { + if (!(b instanceof Node)) + b = document.createTextNode(b); + a.appendChild(b); + return a; + } + + var formattedResult = String.format(format, substitutions, this._formatters, fragment, append).formattedResult; if (formattedResult instanceof Node) formattedResult.normalize(); return this.addChild(formattedResult); - }, - - _append: function(a, b) - { - if (!(b instanceof Node)) - b = document.createTextNode(b); - a.appendChild(b); - return a; } } @@ -505,7 +570,10 @@ WebInspector.AuditsSidebarTreeElement.prototype = { /** * @constructor * @extends {WebInspector.SidebarTreeElement} - * @param {WebInspector.AuditsPanel} panel + * @param {!WebInspector.AuditsPanel} panel + * @param {!Array.<!WebInspector.AuditCategoryResult>} results + * @param {string} mainResourceURL + * @param {number} ordinal */ WebInspector.AuditResultSidebarTreeElement = function(panel, results, mainResourceURL, ordinal) { @@ -532,7 +600,10 @@ WebInspector.AuditResultSidebarTreeElement.prototype = { // Contributed audit rules should go into this namespace. WebInspector.AuditRules = {}; -// Contributed audit categories should go into this namespace. +/** + * Contributed audit categories should go into this namespace. + * @type {Object.<string, function(new:WebInspector.AuditCategory)>} + */ WebInspector.AuditCategories = {}; importScript("AuditCategories.js"); diff --git a/Source/WebCore/inspector/front-end/BreakpointManager.js b/Source/WebCore/inspector/front-end/BreakpointManager.js index 709cf1193..982595015 100644 --- a/Source/WebCore/inspector/front-end/BreakpointManager.js +++ b/Source/WebCore/inspector/front-end/BreakpointManager.js @@ -58,10 +58,8 @@ WebInspector.BreakpointManager.Events = { BreakpointRemoved: "breakpoint-removed" } -WebInspector.BreakpointManager.breakpointStorageId = function(uiSourceCode) +WebInspector.BreakpointManager.sourceFileId = function(uiSourceCode) { - if (uiSourceCode.isTemporary) - return ""; return uiSourceCode.formatted() ? "deobfuscated:" + uiSourceCode.url : uiSourceCode.url; } @@ -69,9 +67,9 @@ WebInspector.BreakpointManager.prototype = { /** * @param {WebInspector.UISourceCode} uiSourceCode */ - restoreBreakpoints: function(uiSourceCode) + _restoreBreakpoints: function(uiSourceCode) { - var sourceFileId = WebInspector.BreakpointManager.breakpointStorageId(uiSourceCode); + var sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode); if (!sourceFileId || this._sourceFilesWithRestoredBreakpoints[sourceFileId]) return; this._sourceFilesWithRestoredBreakpoints[sourceFileId] = true; @@ -79,7 +77,7 @@ WebInspector.BreakpointManager.prototype = { // Erase provisional breakpoints prior to restoring them. for (var debuggerId in this._breakpointForDebuggerId) { var breakpoint = this._breakpointForDebuggerId[debuggerId]; - if (breakpoint._sourceCodeStorageId !== sourceFileId) + if (breakpoint._sourceFileId !== sourceFileId) continue; this._debuggerModel.removeBreakpoint(debuggerId); delete this._breakpointForDebuggerId[debuggerId]; @@ -93,9 +91,20 @@ WebInspector.BreakpointManager.prototype = { */ _uiSourceCodeAdded: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data; - if (uiSourceCode.contentType() === WebInspector.resourceTypes.Script || uiSourceCode.contentType() === WebInspector.resourceTypes.Document) - this.restoreBreakpoints(uiSourceCode); + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data); + if (uiSourceCode.contentType() === WebInspector.resourceTypes.Script || uiSourceCode.contentType() === WebInspector.resourceTypes.Document) { + this._restoreBreakpoints(uiSourceCode); + uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormatted, this); + } + }, + + /** + * @param {WebInspector.Event} event + */ + _uiSourceCodeFormatted: function(event) + { + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); + this._restoreBreakpoints(uiSourceCode); }, /** @@ -103,16 +112,16 @@ WebInspector.BreakpointManager.prototype = { */ _uiSourceCodeRemoved: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data); if (uiSourceCode.contentType() !== WebInspector.resourceTypes.Script && uiSourceCode.contentType() !== WebInspector.resourceTypes.Document) return; if (uiSourceCode.divergedVersion) return; - - var sourceFileId = WebInspector.BreakpointManager.breakpointStorageId(uiSourceCode); + + var sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode); if (!sourceFileId) return; - + var breakpoints = this._breakpoints.slice(); for (var i = 0; i < breakpoints.length; ++i) { var breakpoint = breakpoints[i]; @@ -122,14 +131,12 @@ WebInspector.BreakpointManager.prototype = { breakpoint.remove(true); } } - + delete this._sourceFilesWithRestoredBreakpoints[sourceFileId]; - + var uiSourceCodes = this._workspace.uiSourceCodes(); - for (var i = 0; i < uiSourceCodes.length; ++i) { - if (WebInspector.BreakpointManager.breakpointStorageId(uiSourceCodes[i]) === sourceFileId) - this.restoreBreakpoints(uiSourceCodes[i]); - } + for (var i = 0; i < uiSourceCodes.length; ++i) + this._restoreBreakpoints(uiSourceCodes[i]); }, /** @@ -202,9 +209,9 @@ WebInspector.BreakpointManager.prototype = { { function filter(breakpoint, uiLocation) { - return uiLocation.uiSourceCode === uiSourceCode; + return uiLocation.uiSourceCode === uiSourceCode; } - + return this._filteredBreakpointLocations(filter); }, @@ -263,8 +270,8 @@ WebInspector.BreakpointManager.prototype = { _breakpointResolved: function(event) { - var breakpointId = /** @type {DebuggerAgent.BreakpointId} */ event.data.breakpointId; - var location = /** @type {WebInspector.DebuggerModel.Location} */ event.data.location; + var breakpointId = /** @type {DebuggerAgent.BreakpointId} */ (event.data.breakpointId); + var location = /** @type {WebInspector.DebuggerModel.Location} */ (event.data.location); var breakpoint = this._breakpointForDebuggerId[breakpointId]; if (!breakpoint || breakpoint._isProvisional) return; @@ -340,10 +347,10 @@ WebInspector.BreakpointManager.Breakpoint = function(breakpointManager, uiSource { this._breakpointManager = breakpointManager; this._primaryUILocation = new WebInspector.UILocation(uiSourceCode, lineNumber, 0); - this._sourceCodeStorageId = WebInspector.BreakpointManager.breakpointStorageId(uiSourceCode); - /** @type {Array.<WebInspector.Script.Location>} */ + this._sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode); + /** @type {Array.<WebInspector.Script.Location>} */ this._liveLocations = []; - /** @type {Object.<string, WebInspector.UILocation>} */ + /** @type {Object.<string, WebInspector.UILocation>} */ this._uiLocations = {}; // Force breakpoint update. @@ -376,7 +383,7 @@ WebInspector.BreakpointManager.Breakpoint.prototype = { _locationUpdated: function(location, uiLocation) { var stringifiedLocation = location.scriptId + ":" + location.lineNumber + ":" + location.columnNumber; - var oldUILocation = /** @type {WebInspector.UILocation} */ this._uiLocations[stringifiedLocation]; + var oldUILocation = /** @type {WebInspector.UILocation} */ (this._uiLocations[stringifiedLocation]); if (oldUILocation) this._breakpointManager._uiLocationRemoved(this, oldUILocation); if (this._uiLocations[""]) { @@ -458,12 +465,12 @@ WebInspector.BreakpointManager.Breakpoint.prototype = { _setInDebugger: function() { var rawLocation = this._primaryUILocation.uiLocationToRawLocation(); - var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ rawLocation; + var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (rawLocation); if (debuggerModelLocation) this._breakpointManager._debuggerModel.setBreakpointByScriptLocation(debuggerModelLocation, this._condition, didSetBreakpoint.bind(this)); else this._breakpointManager._debuggerModel.setBreakpointByURL(this._primaryUILocation.uiSourceCode.url, this._primaryUILocation.lineNumber, 0, this._condition, didSetBreakpoint.bind(this)); - + /** * @this {WebInspector.BreakpointManager.Breakpoint} * @param {?DebuggerAgent.BreakpointId} breakpointId @@ -527,7 +534,7 @@ WebInspector.BreakpointManager.Breakpoint.prototype = { */ _breakpointStorageId: function() { - return WebInspector.BreakpointManager.breakpointStorageId(this._primaryUILocation.uiSourceCode) + ":" + this._primaryUILocation.lineNumber; + return this._sourceFileId + ":" + this._primaryUILocation.lineNumber; }, _fakeBreakpointAtPrimaryLocation: function() @@ -551,7 +558,7 @@ WebInspector.BreakpointManager.Storage = function(breakpointManager, setting) /** @type {Object.<string,WebInspector.BreakpointManager.Storage.Item>} */ this._breakpoints = {}; for (var i = 0; i < breakpoints.length; ++i) { - var breakpoint = /** @type {WebInspector.BreakpointManager.Storage.Item} */ breakpoints[i]; + var breakpoint = /** @type {WebInspector.BreakpointManager.Storage.Item} */ (breakpoints[i]); this._breakpoints[breakpoint.sourceFileId + ":" + breakpoint.lineNumber] = breakpoint; } } @@ -563,10 +570,10 @@ WebInspector.BreakpointManager.Storage.prototype = { _restoreBreakpoints: function(uiSourceCode) { this._muted = true; - var breakpointStorageId = WebInspector.BreakpointManager.breakpointStorageId(uiSourceCode); + var sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode); for (var id in this._breakpoints) { var breakpoint = this._breakpoints[id]; - if (breakpoint.sourceFileId === breakpointStorageId) + if (breakpoint.sourceFileId === sourceFileId) this._breakpointManager._innerSetBreakpoint(uiSourceCode, breakpoint.lineNumber, breakpoint.condition, breakpoint.enabled); } delete this._muted; @@ -610,7 +617,7 @@ WebInspector.BreakpointManager.Storage.prototype = { WebInspector.BreakpointManager.Storage.Item = function(breakpoint) { var primaryUILocation = breakpoint.primaryUILocation(); - this.sourceFileId = WebInspector.BreakpointManager.breakpointStorageId(primaryUILocation.uiSourceCode); + this.sourceFileId = breakpoint._sourceFileId; this.lineNumber = primaryUILocation.lineNumber; this.condition = breakpoint.condition(); this.enabled = breakpoint.enabled(); diff --git a/Source/WebCore/inspector/front-end/BreakpointsSidebarPane.js b/Source/WebCore/inspector/front-end/BreakpointsSidebarPane.js index bc4bb6b02..54c0a87c3 100644 --- a/Source/WebCore/inspector/front-end/BreakpointsSidebarPane.js +++ b/Source/WebCore/inspector/front-end/BreakpointsSidebarPane.js @@ -52,9 +52,20 @@ WebInspector.JavaScriptBreakpointsSidebarPane = function(breakpointManager, show this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this); this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this); + + this.emptyElement.addEventListener("contextmenu", this._emptyElementContextMenu.bind(this), true); } WebInspector.JavaScriptBreakpointsSidebarPane.prototype = { + _emptyElementContextMenu: function(event) + { + var contextMenu = new WebInspector.ContextMenu(event); + var breakpointActive = WebInspector.debuggerModel.breakpointsActive(); + var breakpointActiveTitle = WebInspector.UIString(breakpointActive ? "Deactivate Breakpoints" : "Activate Breakpoints"); + contextMenu.appendItem(breakpointActiveTitle, WebInspector.debuggerModel.setBreakpointsActive.bind(WebInspector.debuggerModel, !breakpointActive)); + contextMenu.show(); + }, + /** * @param {WebInspector.Event} event */ @@ -62,8 +73,8 @@ WebInspector.JavaScriptBreakpointsSidebarPane.prototype = { { this._breakpointRemoved(event); - var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ event.data.breakpoint; - var uiLocation = /** @type {WebInspector.UILocation} */ event.data.uiLocation; + var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint); + var uiLocation = /** @type {WebInspector.UILocation} */ (event.data.uiLocation); this._addBreakpoint(breakpoint, uiLocation); }, @@ -128,8 +139,8 @@ WebInspector.JavaScriptBreakpointsSidebarPane.prototype = { */ _breakpointRemoved: function(event) { - var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ event.data.breakpoint; - var uiLocation = /** @type {WebInspector.UILocation} */ event.data.uiLocation; + var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint); + var uiLocation = /** @type {WebInspector.UILocation} */ (event.data.uiLocation); var breakpointItem = this._items.get(breakpoint); if (!breakpointItem) return; @@ -177,13 +188,17 @@ WebInspector.JavaScriptBreakpointsSidebarPane.prototype = { */ _breakpointContextMenu: function(breakpoint, event) { - var contextMenu = new WebInspector.ContextMenu(); + var breakpoints = this._items.values(); + var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), breakpoint.remove.bind(breakpoint)); - var removeAllTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove all breakpoints" : "Remove All Breakpoints"); - contextMenu.appendItem(removeAllTitle, this._breakpointManager.removeAllBreakpoints.bind(this._breakpointManager)); - + if (breakpoints.length > 1) { + var removeAllTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove all breakpoints" : "Remove All Breakpoints"); + contextMenu.appendItem(removeAllTitle, this._breakpointManager.removeAllBreakpoints.bind(this._breakpointManager)); + } + + contextMenu.appendSeparator(); var breakpointActive = WebInspector.debuggerModel.breakpointsActive(); - var breakpointActiveTitle = WebInspector.UIString(breakpointActive ? "Deactivate All Breakpoints" : "Activate All Breakpoints"); + var breakpointActiveTitle = WebInspector.UIString(breakpointActive ? "Deactivate Breakpoints" : "Activate Breakpoints"); contextMenu.appendItem(breakpointActiveTitle, WebInspector.debuggerModel.setBreakpointsActive.bind(WebInspector.debuggerModel, !breakpointActive)); function enabledBreakpointCount(breakpoints) @@ -195,7 +210,6 @@ WebInspector.JavaScriptBreakpointsSidebarPane.prototype = { } return count; } - var breakpoints = this._items.values(); if (breakpoints.length > 1) { var enableBreakpointCount = enabledBreakpointCount(breakpoints); var enableTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Enable all breakpoints" : "Enable All Breakpoints"); @@ -207,7 +221,7 @@ WebInspector.JavaScriptBreakpointsSidebarPane.prototype = { contextMenu.appendItem(disableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._breakpointManager, false), !(enableBreakpointCount > 1)); } - contextMenu.show(event); + contextMenu.show(); }, _addListElement: function(element, beforeElement) @@ -270,15 +284,26 @@ WebInspector.XHRBreakpointsSidebarPane = function() var addButton = document.createElement("button"); addButton.className = "pane-title-button add"; addButton.addEventListener("click", this._addButtonClicked.bind(this), false); + addButton.title = WebInspector.UIString("Add XHR breakpoint"); this.titleElement.appendChild(addButton); + this.emptyElement.addEventListener("contextmenu", this._emptyElementContextMenu.bind(this), true); + this._restoreBreakpoints(); } WebInspector.XHRBreakpointsSidebarPane.prototype = { + _emptyElementContextMenu: function(event) + { + var contextMenu = new WebInspector.ContextMenu(event); + contextMenu.appendItem(WebInspector.UIString("Add Breakpoint"), this._addButtonClicked.bind(this)); + contextMenu.show(); + }, + _addButtonClicked: function(event) { - event.consume(); + if (event) + event.consume(); this.expanded = true; @@ -356,14 +381,24 @@ WebInspector.XHRBreakpointsSidebarPane.prototype = { _contextMenu: function(url, event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); function removeBreakpoint() { this._removeBreakpoint(url); this._saveBreakpoints(); } + function removeAllBreakpoints() + { + for (var url in this._breakpointElements) + this._removeBreakpoint(url); + this._saveBreakpoints(); + } + var removeAllTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove all breakpoints" : "Remove All Breakpoints"); + + contextMenu.appendItem(WebInspector.UIString("Add Breakpoint"), this._addButtonClicked.bind(this)); contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), removeBreakpoint.bind(this)); - contextMenu.show(event); + contextMenu.appendItem(removeAllTitle, removeAllBreakpoints.bind(this)); + contextMenu.show(); }, _checkboxClicked: function(url, event) diff --git a/Source/WebCore/inspector/front-end/CSSNamedFlowCollectionsView.js b/Source/WebCore/inspector/front-end/CSSNamedFlowCollectionsView.js index b2a302402..b92a066da 100644 --- a/Source/WebCore/inspector/front-end/CSSNamedFlowCollectionsView.js +++ b/Source/WebCore/inspector/front-end/CSSNamedFlowCollectionsView.js @@ -91,7 +91,7 @@ WebInspector.CSSNamedFlowCollectionsView.prototype = { */ _documentUpdated: function(event) { - var document = /** @type {WebInspector.DOMDocument} */ event.data; + var document = /** @type {WebInspector.DOMDocument} */ (event.data); this._setDocument(document); }, @@ -219,7 +219,7 @@ WebInspector.CSSNamedFlowCollectionsView.prototype = { if (event.data.documentNodeId !== this._document.id) return; - var flow = /** @type {WebInspector.NamedFlow} */ event.data; + var flow = /** @type {WebInspector.NamedFlow} */ (event.data); this._appendNamedFlow(flow); }, @@ -244,7 +244,7 @@ WebInspector.CSSNamedFlowCollectionsView.prototype = { if (event.data.documentNodeId !== this._document.id) return; - var flow = /** @type {WebInspector.NamedFlow} */ event.data; + var flow = /** @type {WebInspector.NamedFlow} */ (event.data); this._updateNamedFlow(flow); }, @@ -298,7 +298,7 @@ WebInspector.CSSNamedFlowCollectionsView.prototype = { */ _selectedNodeChanged: function(event) { - var node = /** @type {WebInspector.DOMNode} */ event.data; + var node = /** @type {WebInspector.DOMNode} */ (event.data); this._showNamedFlowForNode(node); }, diff --git a/Source/WebCore/inspector/front-end/CSSNamedFlowView.js b/Source/WebCore/inspector/front-end/CSSNamedFlowView.js index c6a09f208..ade2643b1 100644 --- a/Source/WebCore/inspector/front-end/CSSNamedFlowView.js +++ b/Source/WebCore/inspector/front-end/CSSNamedFlowView.js @@ -81,7 +81,7 @@ WebInspector.CSSNamedFlowView.prototype = { treeOutline.wireToDomAgent(); WebInspector.domAgent.removeEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, treeOutline._elementsTreeUpdater._documentUpdated, treeOutline._elementsTreeUpdater); - return treeOutline; + return treeOutline; }, /** diff --git a/Source/WebCore/inspector/front-end/CSSSelectorProfileView.js b/Source/WebCore/inspector/front-end/CSSSelectorProfileView.js index e10f8dc6e..e9d71ec85 100644 --- a/Source/WebCore/inspector/front-end/CSSSelectorProfileView.js +++ b/Source/WebCore/inspector/front-end/CSSSelectorProfileView.js @@ -399,7 +399,7 @@ WebInspector.CSSProfileHeader.prototype = { */ createView: function(profilesPanel) { - var profile = /** @type {CSSAgent.SelectorProfile} */this._protocolData; + var profile = /** @type {CSSAgent.SelectorProfile} */ (this._protocolData); return new WebInspector.CSSSelectorProfileView(profile); }, diff --git a/Source/WebCore/inspector/front-end/CSSStyleModel.js b/Source/WebCore/inspector/front-end/CSSStyleModel.js index 669e77e1d..6a6f1c555 100644 --- a/Source/WebCore/inspector/front-end/CSSStyleModel.js +++ b/Source/WebCore/inspector/front-end/CSSStyleModel.js @@ -35,6 +35,8 @@ WebInspector.CSSStyleModel = function() { this._pendingCommandsMajorState = []; + /** @type {Array.<WebInspector.CSSStyleModel.LiveLocation>} */ + this._locations = []; this._sourceMappings = {}; WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.UndoRedoRequested, this._undoRedoRequested, this); WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.UndoRedoCompleted, this._undoRedoCompleted, this); @@ -481,6 +483,7 @@ WebInspector.CSSStyleModel.prototype = { setSourceMapping: function(url, sourceMapping) { this._sourceMappings[url] = sourceMapping; + this._updateLocations(); }, resetSourceMappings: function() @@ -493,6 +496,28 @@ WebInspector.CSSStyleModel.prototype = { this._namedFlowCollections = {}; }, + _updateLocations: function() + { + for (var i = 0; i < this._locations.length; ++i) + this._locations[i].update(); + }, + + /** + * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate + * @return {?WebInspector.LiveLocation} + */ + createLiveLocation: function(cssRule, updateDelegate) + { + if (!cssRule._rawLocation) + return null; + var location = new WebInspector.CSSStyleModel.LiveLocation(cssRule._rawLocation, updateDelegate); + if (!location.uiLocation()) + return null; + this._locations.push(location); + location.update(); + return location; + }, + /** * @param {WebInspector.CSSLocation} rawLocation * @return {?WebInspector.UILocation} @@ -508,6 +533,38 @@ WebInspector.CSSStyleModel.prototype = { /** * @constructor + * @extends {WebInspector.LiveLocation} + * @param {WebInspector.CSSLocation} rawLocation + * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate + */ +WebInspector.CSSStyleModel.LiveLocation = function(rawLocation, updateDelegate) +{ + WebInspector.LiveLocation.call(this, rawLocation, updateDelegate); +} + +WebInspector.CSSStyleModel.LiveLocation.prototype = { + /** + * @return {WebInspector.UILocation} + */ + uiLocation: function() + { + var cssLocation = /** @type WebInspector.CSSLocation */ (this.rawLocation()); + return WebInspector.cssModel._rawLocationToUILocation(cssLocation); + }, + + dispose: function() + { + WebInspector.LiveLocation.prototype.dispose.call(this); + var locations = WebInspector.cssModel._locations; + if (locations) + locations.remove(this); + }, + + __proto__: WebInspector.LiveLocation.prototype +} + +/** + * @constructor * @implements {WebInspector.RawLocation} * @param {string} url * @param {number} lineNumber @@ -579,7 +636,7 @@ WebInspector.CSSStyleDeclaration.parsePayload = function(payload) */ WebInspector.CSSStyleDeclaration.parseComputedStylePayload = function(payload) { - var newPayload = /** @type {CSSAgent.CSSStyle} */ { cssProperties: [], shorthandEntries: [], width: "", height: "" }; + var newPayload = /** @type {CSSAgent.CSSStyle} */ ({ cssProperties: [], shorthandEntries: [], width: "", height: "" }); if (payload) newPayload.cssProperties = payload; @@ -785,16 +842,6 @@ WebInspector.CSSRule.prototype = { get isRegular() { return this.origin === "regular"; - }, - - /** - * @return {?WebInspector.UILocation} - */ - uiLocation: function() - { - if (!this._rawLocation) - return null; - return WebInspector.cssModel._rawLocationToUILocation(this._rawLocation); } } diff --git a/Source/WebCore/inspector/front-end/CallStackSidebarPane.js b/Source/WebCore/inspector/front-end/CallStackSidebarPane.js index 9c71edb69..c065c9067 100644 --- a/Source/WebCore/inspector/front-end/CallStackSidebarPane.js +++ b/Source/WebCore/inspector/front-end/CallStackSidebarPane.js @@ -183,7 +183,7 @@ WebInspector.CallStackSidebarPane.Placard.prototype = { _placardContextMenu: function(event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); if (WebInspector.debuggerModel.canSetScriptSource()) { contextMenu.appendItem(WebInspector.UIString("Restart Frame"), this._restartFrame.bind(this)); @@ -191,7 +191,7 @@ WebInspector.CallStackSidebarPane.Placard.prototype = { } contextMenu.appendItem(WebInspector.UIString("Copy Stack Trace"), this._pane._copyStackTrace.bind(this._pane)); - contextMenu.show(event); + contextMenu.show(); }, _restartFrame: function() diff --git a/Source/WebCore/inspector/front-end/CodeMirrorTextEditor.js b/Source/WebCore/inspector/front-end/CodeMirrorTextEditor.js index c45784be0..7af304679 100644 --- a/Source/WebCore/inspector/front-end/CodeMirrorTextEditor.js +++ b/Source/WebCore/inspector/front-end/CodeMirrorTextEditor.js @@ -52,15 +52,16 @@ WebInspector.CodeMirrorTextEditor = function(url, delegate) this._codeMirror = window.CodeMirror(this.element, { lineNumbers: true, - fixedGutter: true, - onChange: this._onChange.bind(this), - onGutterClick: this._onGutterClick.bind(this) + gutters: ["CodeMirror-linenumbers", "breakpoints"] }); + CodeMirror.on(this._codeMirror, "change", this._change.bind(this)); + CodeMirror.on(this._codeMirror, "gutterClick", this._gutterClick.bind(this)); this._lastRange = this.range(); this.element.firstChild.addStyleClass("source-code"); this.element.firstChild.addStyleClass("fill"); + this._elementToWidget = new Map(); } WebInspector.CodeMirrorTextEditor.prototype = { @@ -120,7 +121,7 @@ WebInspector.CodeMirrorTextEditor.prototype = { this._codeMirror.scrollTo(coords.x, coords.y); }, - _onGutterClick: function(instance, lineNumber, event) + _gutterClick: function(instance, lineNumber, gutter, event) { this.dispatchEventToListeners(WebInspector.TextEditor.Events.GutterClick, { lineNumber: lineNumber, event: event }); }, @@ -132,8 +133,10 @@ WebInspector.CodeMirrorTextEditor.prototype = { */ addBreakpoint: function(lineNumber, disabled, conditional) { - var className = "cm-breakpoint" + (disabled ? " cm-breakpoint-disabled" : "") + (conditional ? " cm-breakpoint-conditional" : ""); - this._codeMirror.setMarker(lineNumber, null, className); + var element = document.createElement("span"); + element.textContent = lineNumber + 1; + element.className = "cm-breakpoint" + (disabled ? " cm-breakpoint-disabled" : "") + (conditional ? " cm-breakpoint-conditional" : ""); + this._codeMirror.setGutterMarker(lineNumber, "breakpoints", element); }, /** @@ -141,7 +144,7 @@ WebInspector.CodeMirrorTextEditor.prototype = { */ removeBreakpoint: function(lineNumber) { - this._codeMirror.clearMarker(lineNumber); + this._codeMirror.setGutterMarker(lineNumber, "breakpoints", null); }, /** @@ -149,14 +152,14 @@ WebInspector.CodeMirrorTextEditor.prototype = { */ setExecutionLine: function(lineNumber) { - this._executionLine = this._codeMirror.getLineHandle(lineNumber) - this._codeMirror.setLineClass(this._executionLine, null, "cm-execution-line"); + this._executionLine = this._codeMirror.getLineHandle(lineNumber); + this._codeMirror.addLineClass(this._executionLine, null, "cm-execution-line"); }, clearExecutionLine: function() { if (this._executionLine) - this._codeMirror.setLineClass(this._executionLine, null, null); + this._codeMirror.removeLineClass(this._executionLine, null, "cm-execution-line"); delete this._executionLine; }, @@ -166,7 +169,8 @@ WebInspector.CodeMirrorTextEditor.prototype = { */ addDecoration: function(lineNumber, element) { - // TODO implement so that it doesn't hide context code + var widget = this._codeMirror.addLineWidget(lineNumber, element); + this._elementToWidget.put(element, widget); }, /** @@ -175,7 +179,9 @@ WebInspector.CodeMirrorTextEditor.prototype = { */ removeDecoration: function(lineNumber, element) { - // TODO implement so that it doesn't hide context code + var widget = this._elementToWidget.remove(element); + if (widget) + this._codeMirror.removeLineWidget(widget); }, /** @@ -194,7 +200,9 @@ WebInspector.CodeMirrorTextEditor.prototype = { { this.clearLineHighlight(); this._highlightedLine = this._codeMirror.getLineHandle(lineNumber); - this._codeMirror.setLineClass(this._highlightedLine, null, "cm-highlight"); + if (!this._highlightedLine) + return; + this._codeMirror.addLineClass(this._highlightedLine, null, "cm-highlight"); this._clearHighlightTimeout = setTimeout(this.clearLineHighlight.bind(this), 2000); }, @@ -204,8 +212,8 @@ WebInspector.CodeMirrorTextEditor.prototype = { clearTimeout(this._clearHighlightTimeout); delete this._clearHighlightTimeout; - if (this._highlightedLine) - this._codeMirror.setLineClass(this._highlightedLine, null, null); + if (this._highlightedLine) + this._codeMirror.removeLineClass(this._highlightedLine, null, "cm-highlight"); delete this._highlightedLine; }, @@ -243,8 +251,13 @@ WebInspector.CodeMirrorTextEditor.prototype = { return newRange; }, - _onChange: function() + _change: function() { + var widgets = this._elementToWidget.values(); + for (var i = 0; i < widgets.length; ++i) + this._codeMirror.removeLineWidget(widgets[i]); + this._elementToWidget.clear(); + var newRange = this.range(); this._delegate.onTextChanged(this._lastRange, newRange); this._lastRange = newRange; diff --git a/Source/WebCore/inspector/front-end/CompilerScriptMapping.js b/Source/WebCore/inspector/front-end/CompilerScriptMapping.js index f7a502a3c..6259e9a3f 100644 --- a/Source/WebCore/inspector/front-end/CompilerScriptMapping.js +++ b/Source/WebCore/inspector/front-end/CompilerScriptMapping.js @@ -32,10 +32,12 @@ * @constructor * @implements {WebInspector.SourceMapping} * @param {WebInspector.Workspace} workspace + * @param {WebInspector.NetworkWorkspaceProvider} networkWorkspaceProvider */ -WebInspector.CompilerScriptMapping = function(workspace) +WebInspector.CompilerScriptMapping = function(workspace, networkWorkspaceProvider) { this._workspace = workspace; + this._networkWorkspaceProvider = networkWorkspaceProvider; /** @type {Object.<string, WebInspector.SourceMapParser>} */ this._sourceMapForSourceMapURL = {}; /** @type {Object.<string, WebInspector.SourceMapParser>} */ @@ -56,7 +58,7 @@ WebInspector.CompilerScriptMapping.prototype = { */ rawLocationToUILocation: function(rawLocation) { - var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ rawLocation; + var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (rawLocation); var sourceMap = this._sourceMapForScriptId[debuggerModelLocation.scriptId]; var lineNumber = debuggerModelLocation.lineNumber; var columnNumber = debuggerModelLocation.columnNumber || 0; @@ -92,11 +94,10 @@ WebInspector.CompilerScriptMapping.prototype = { { // FIXME: We should only create temporary uiSourceCodes on demand and should set this as a mapping to // relevant uiSourceCodes added by NetworkUISourceCodeProvider. - var originalUISourceCode = new WebInspector.UISourceCode(script.sourceURL, script, false); + var originalUISourceCode = this._workspace.addTemporaryUISourceCode(script.sourceURL, script, false); + originalUISourceCode.setSourceMapping(this); this._originalUISourceCodeForScriptId[script.scriptId] = originalUISourceCode; this._scriptForOriginalUISourceCode.put(originalUISourceCode, script); - originalUISourceCode.setSourceMapping(this); - this._workspace.project().addTemporaryUISourceCode(originalUISourceCode); var sourceMap = this.loadSourceMapForScript(script); @@ -118,9 +119,8 @@ WebInspector.CompilerScriptMapping.prototype = { contentProvider = new WebInspector.StaticContentProvider(WebInspector.resourceTypes.Script, sourceContent); else contentProvider = new WebInspector.CompilerSourceMappingContentProvider(sourceURL); - var uiSourceCode = new WebInspector.UISourceCode(sourceURL, contentProvider, false); - this._workspace.project().addUISourceCode(uiSourceCode); - + this._networkWorkspaceProvider.addFile(sourceURL, contentProvider, true); + var uiSourceCode = this._workspace.uiSourceCodeForURL(sourceURL); uiSourceCode.setSourceMapping(this); uiSourceCode.isContentScript = script.isContentScript; } @@ -146,7 +146,7 @@ WebInspector.CompilerScriptMapping.prototype = { var response = InspectorFrontendHost.loadResourceSynchronously(sourceMapURL); if (response.slice(0, 3) === ")]}") response = response.substring(response.indexOf('\n')); - var payload = /** @type {WebInspector.SourceMapPayload} */ JSON.parse(response); + var payload = /** @type {WebInspector.SourceMapPayload} */ (JSON.parse(response)); sourceMap = new WebInspector.SourceMapParser(sourceMapURL, payload); } catch(e) { console.error(e.message); diff --git a/Source/WebCore/inspector/front-end/ConsoleMessage.js b/Source/WebCore/inspector/front-end/ConsoleMessage.js index 5b1263fda..074fe8600 100644 --- a/Source/WebCore/inspector/front-end/ConsoleMessage.js +++ b/Source/WebCore/inspector/front-end/ConsoleMessage.js @@ -76,6 +76,10 @@ WebInspector.ConsoleMessageImpl.prototype = { case WebInspector.ConsoleMessage.MessageType.Trace: this._messageElement = document.createTextNode("console.trace()"); break; + case WebInspector.ConsoleMessage.MessageType.Clear: + this._messageElement = document.createTextNode(WebInspector.UIString("Console was cleared")); + this._formattedMessage.addStyleClass("console-info"); + break; case WebInspector.ConsoleMessage.MessageType.Assert: var args = [WebInspector.UIString("Assertion failed:")]; if (this._parameters) @@ -355,9 +359,18 @@ WebInspector.ConsoleMessageImpl.prototype = { object.pushNodeToFrontend(printNode.bind(this)); }, + /** + * @param {WebInspector.RemoteObject} array + * @return {boolean} + */ + useArrayPreviewInFormatter: function(array) + { + return !!array.preview; + }, + _formatParameterAsArray: function(array, elem) { - if (array.preview) { + if (this.useArrayPreviewInFormatter(array)) { this._formatParameterAsArrayOrObject(array, "", elem, true); return; } @@ -442,11 +455,25 @@ WebInspector.ConsoleMessageImpl.prototype = { return this._formatParameter(obj, force, false); } - function valueFormatter(obj) + function stringFormatter(obj) { return obj.description; } + function floatFormatter(obj) + { + if (typeof obj.value !== "number") + return "NaN"; + return obj.value; + } + + function integerFormatter(obj) + { + if (typeof obj.value !== "number") + return "NaN"; + return Math.floor(obj.value); + } + function styleFormatter(obj) { var buffer = document.createElement("span"); @@ -470,11 +497,11 @@ WebInspector.ConsoleMessageImpl.prototype = { // Firebug uses %o for formatting objects. formatters.o = parameterFormatter.bind(this, false); - formatters.s = valueFormatter; - formatters.f = valueFormatter; + formatters.s = stringFormatter; + formatters.f = floatFormatter; // Firebug allows both %i and %d for formatting integers. - formatters.i = valueFormatter; - formatters.d = valueFormatter; + formatters.i = integerFormatter; + formatters.d = integerFormatter; // Firebug uses %c for styling the message. formatters.c = styleFormatter; diff --git a/Source/WebCore/inspector/front-end/ConsoleModel.js b/Source/WebCore/inspector/front-end/ConsoleModel.js index fd37e35d9..0a82178a3 100644 --- a/Source/WebCore/inspector/front-end/ConsoleModel.js +++ b/Source/WebCore/inspector/front-end/ConsoleModel.js @@ -231,6 +231,7 @@ WebInspector.ConsoleMessage.MessageType = { Dir: "dir", DirXML: "dirxml", Trace: "trace", + Clear: "clear", StartGroup: "startGroup", StartGroupCollapsed: "startGroupCollapsed", EndGroup: "endGroup", diff --git a/Source/WebCore/inspector/front-end/ConsoleView.js b/Source/WebCore/inspector/front-end/ConsoleView.js index eeb5cf2de..9ea5bca61 100644 --- a/Source/WebCore/inspector/front-end/ConsoleView.js +++ b/Source/WebCore/inspector/front-end/ConsoleView.js @@ -140,7 +140,7 @@ WebInspector.ConsoleView.prototype = { */ _frameAdded: function(event) { - var contextList = /** @type {WebInspector.FrameExecutionContextList} */ event.data; + var contextList = /** @type {WebInspector.FrameExecutionContextList} */ (event.data); this._addFrame(contextList); }, @@ -165,7 +165,7 @@ WebInspector.ConsoleView.prototype = { */ _frameRemoved: function(event) { - var contextList = /** @type {WebInspector.FrameExecutionContextList} */ event.data; + var contextList = /** @type {WebInspector.FrameExecutionContextList} */ (event.data); this._frameSelector.removeOption(contextList._consoleOption); this._frameChanged(); }, @@ -441,7 +441,7 @@ WebInspector.ConsoleView.prototype = { if (event.target.enclosingNodeOrSelfWithNodeName("a")) return; - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); function monitoringXHRItemAction() { @@ -457,7 +457,7 @@ WebInspector.ConsoleView.prototype = { contextMenu.appendSeparator(); contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Clear console" : "Clear Console"), this._requestClearMessages.bind(this)); - contextMenu.show(event); + contextMenu.show(); }, _monitoringXHREnabledSettingChanged: function(event) @@ -485,9 +485,6 @@ WebInspector.ConsoleView.prototype = { var shortcutL = shortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Ctrl); this._shortcuts[shortcutL.key] = this._requestClearMessages.bind(this); - var shortcutM = shortcut.makeDescriptor("m", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta | WebInspector.KeyboardShortcut.Modifiers.Shift); - this._shortcuts[shortcutM.key] = this._dumpMemory.bind(this); - var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Console")); var keys = WebInspector.isMac() ? [ shortcutK.name, shortcutL.name ] : [ shortcutL.name ]; section.addAlternateKeys(keys, WebInspector.UIString("Clear console")); @@ -582,7 +579,7 @@ WebInspector.ConsoleView.prototype = { this._printResult(result, wasThrown, commandMessage); } - WebInspector.runtimeModel.evaluate(text, "console", useCommandLineAPI, false, false, printResult.bind(this)); + WebInspector.runtimeModel.evaluate(text, "console", useCommandLineAPI, false, false, true, printResult.bind(this)); WebInspector.userMetrics.ConsoleEvaluated.record(); }, @@ -592,44 +589,6 @@ WebInspector.ConsoleView.prototype = { return [this.messagesElement]; }, - _dumpMemory: function() - { - function comparator(a, b) - { - if (a.size < b.size) - return 1; - if (a.size > b.size) - return -1; - return a.title.localeCompare(b.title); - } - - function callback(error, groups) - { - var titles = []; - groups.sort(comparator); - for (var i = 0; i < groups.length; ++i) { - var suffix = groups[i].size > 0 ? " [" + groups[i].size + "]" : ""; - titles.push(groups[i].title + suffix + (groups[i].documentURI ? " (" + groups[i].documentURI + ")" : "")); - } - - var counter = 1; - var previousTitle = null; - for (var i = 0; i < titles.length; ++i) { - var title = titles[i]; - if (title === previousTitle) { - counter++; - continue; - } - if (previousTitle) - WebInspector.log(counter > 1 ? counter + " x " + previousTitle : previousTitle); - previousTitle = title; - counter = 1; - } - WebInspector.log(counter > 1 ? counter + " x " + previousTitle : previousTitle); - } - MemoryAgent.getDOMNodeCount(callback); - }, - __proto__: WebInspector.View.prototype } @@ -704,6 +663,16 @@ WebInspector.ConsoleCommandResult = function(result, wasThrown, originatingComma } WebInspector.ConsoleCommandResult.prototype = { + /** + * @override + * @param {WebInspector.RemoteObject} array + * @return {boolean} + */ + useArrayPreviewInFormatter: function(array) + { + return false; + }, + toMessageElement: function() { var element = WebInspector.ConsoleMessageImpl.prototype.toMessageElement.call(this); diff --git a/Source/WebCore/inspector/front-end/ContextMenu.js b/Source/WebCore/inspector/front-end/ContextMenu.js index deecc274e..06535b4b2 100644 --- a/Source/WebCore/inspector/front-end/ContextMenu.js +++ b/Source/WebCore/inspector/front-end/ContextMenu.js @@ -152,8 +152,9 @@ WebInspector.ContextSubMenuItem.prototype = { * @constructor * @extends {WebInspector.ContextSubMenuItem} */ -WebInspector.ContextMenu = function() { +WebInspector.ContextMenu = function(event) { WebInspector.ContextSubMenuItem.call(this, this, ""); + this._event = event; this._handlers = {}; this._id = 0; } @@ -164,15 +165,15 @@ WebInspector.ContextMenu.prototype = { return this._id++; }, - show: function(event) + show: function() { var menuObject = this._buildDescriptor(); if (menuObject.length) { WebInspector._contextMenu = this; - InspectorFrontendHost.showContextMenu(event, menuObject); + InspectorFrontendHost.showContextMenu(this._event, menuObject); } - event.consume(); + this._event.consume(); }, _setHandler: function(id, handler) @@ -203,7 +204,7 @@ WebInspector.ContextMenu.prototype = { for (var i = 0; i < WebInspector.ContextMenu._providers.length; ++i) { var provider = WebInspector.ContextMenu._providers[i]; this.appendSeparator(); - provider.appendApplicableItems(this, target); + provider.appendApplicableItems(this._event, this, target); this.appendSeparator(); } }, @@ -222,7 +223,7 @@ WebInspector.ContextMenu.Provider.prototype = { * @param {WebInspector.ContextMenu} contextMenu * @param {Object} target */ - appendApplicableItems: function(contextMenu, target) { } + appendApplicableItems: function(event, contextMenu, target) { } } /** diff --git a/Source/WebCore/inspector/front-end/CookieItemsView.js b/Source/WebCore/inspector/front-end/CookieItemsView.js index 9fa23547a..31db6f504 100644 --- a/Source/WebCore/inspector/front-end/CookieItemsView.js +++ b/Source/WebCore/inspector/front-end/CookieItemsView.js @@ -74,6 +74,10 @@ WebInspector.CookieItemsView.prototype = { WebInspector.Cookies.getCookiesAsync(this._updateWithCookies.bind(this)); }, + /** + * @param {Array.<WebInspector.Cookie>} allCookies + * @param {boolean} isAdvanced + */ _updateWithCookies: function(allCookies, isAdvanced) { this._cookies = isAdvanced ? this._filterCookiesForDomain(allCookies) : allCookies; @@ -100,6 +104,9 @@ WebInspector.CookieItemsView.prototype = { } }, + /** + * @param {Array.<WebInspector.Cookie>} allCookies + */ _filterCookiesForDomain: function(allCookies) { var cookies = []; @@ -116,7 +123,7 @@ WebInspector.CookieItemsView.prototype = { for (var i = 0; i < allCookies.length; ++i) { var pushed = false; - var size = allCookies[i].size; + var size = allCookies[i].size(); for (var j = 0; j < resourceURLsForDocumentURL.length; ++j) { var resourceURL = resourceURLsForDocumentURL[j]; if (WebInspector.Cookies.cookieMatchesResourceURL(allCookies[i], resourceURL)) { @@ -131,6 +138,9 @@ WebInspector.CookieItemsView.prototype = { return cookies; }, + /** + * @param {WebInspector.Cookie} cookie + */ _deleteCookie: function(cookie) { PageAgent.deleteCookie(cookie.name, this._cookieDomain); @@ -151,9 +161,9 @@ WebInspector.CookieItemsView.prototype = { _contextMenu: function(event) { if (!this._cookies.length) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendItem(WebInspector.UIString("Refresh"), this._update.bind(this)); - contextMenu.show(event); + contextMenu.show(); } }, @@ -180,6 +190,9 @@ WebInspector.SimpleCookiesTable = function() } WebInspector.SimpleCookiesTable.prototype = { + /** + * @param {Array.<WebInspector.Cookie>} cookies + */ setCookies: function(cookies) { this._dataGrid.rootNode().removeChildren(); diff --git a/Source/WebCore/inspector/front-end/CookieParser.js b/Source/WebCore/inspector/front-end/CookieParser.js index a369ac8d9..4a733a622 100644 --- a/Source/WebCore/inspector/front-end/CookieParser.js +++ b/Source/WebCore/inspector/front-end/CookieParser.js @@ -59,7 +59,7 @@ WebInspector.CookieParser.prototype = { /** * @return {Array.<WebInspector.Cookie>} */ - get cookies() + cookies: function() { return this._cookies; }, @@ -122,7 +122,7 @@ WebInspector.CookieParser.prototype = { _flushCookie: function() { if (this._lastCookie) - this._lastCookie.size = this._originalInputLength - this._input.length - this._lastCookiePosition; + this._lastCookie.setSize(this._originalInputLength - this._input.length - this._lastCookiePosition); this._lastCookie = null; }, @@ -168,7 +168,7 @@ WebInspector.CookieParser.prototype = { _addCookie: function(keyValue, type) { if (this._lastCookie) - this._lastCookie.size = keyValue.position - this._lastCookiePosition; + this._lastCookie.setSize(keyValue.position - this._lastCookiePosition); // Mozilla bug 169091: Mozilla, IE and Chrome treat single token (w/o "=") as // specifying a value for a cookie with empty name. this._lastCookie = keyValue.value ? new WebInspector.Cookie(keyValue.key, keyValue.value, type) : @@ -198,6 +198,9 @@ WebInspector.CookieParser.parseSetCookie = function(header) /** * @constructor + * @param {string} name + * @param {string} value + * @param {number=} type */ WebInspector.Cookie = function(name, value, type) { @@ -211,7 +214,7 @@ WebInspector.Cookie.prototype = { /** * @return {boolean} */ - get httpOnly() + httpOnly: function() { return "httponly" in this._attributes; }, @@ -219,7 +222,7 @@ WebInspector.Cookie.prototype = { /** * @return {boolean} */ - get secure() + secure: function() { return "secure" in this._attributes; }, @@ -227,17 +230,17 @@ WebInspector.Cookie.prototype = { /** * @return {boolean} */ - get session() + session: function() { // RFC 2965 suggests using Discard attribute to mark session cookies, but this does not seem to be widely used. - // Check for absence of explicity max-age or expiry date instead. - return !("expires" in this._attributes || "max-age" in this._attributes); + // Check for absence of explicitly max-age or expiry date instead. + return !("expires" in this._attributes || "max-age" in this._attributes); }, /** * @return {string} */ - get path() + path: function() { return this._attributes["path"]; }, @@ -245,31 +248,79 @@ WebInspector.Cookie.prototype = { /** * @return {string} */ - get domain() + port: function() + { + return this._attributes["port"]; + }, + + /** + * @return {string} + */ + domain: function() { return this._attributes["domain"]; }, /** + * @return {string} + */ + expires: function() + { + return this._attributes["expires"]; + }, + + /** + * @return {string} + */ + maxAge: function() + { + return this._attributes["max-age"]; + }, + + /** + * @return {number} + */ + size: function() + { + return this._size; + }, + + /** + * @param {number} size + */ + setSize: function(size) + { + this._size = size; + }, + + /** * @return {Date} */ - expires: function(requestDate) + expiresDate: function(requestDate) { - return this._attributes["expires"] ? new Date(this._attributes["expires"]) : - (this._attributes["max-age"] ? new Date(requestDate.getTime() + 1000 * this._attributes["max-age"]) : null); + // RFC 6265 indicates that the max-age attribute takes precedence over the expires attribute + if (this.maxAge()) { + var targetDate = requestDate === null ? new Date() : requestDate; + return new Date(targetDate.getTime() + 1000 * this.maxAge()); + } + + if (this.expires()) + return new Date(this.expires()); + + return null; }, /** * @return {Object} */ - get attributes() + attributes: function() { return this._attributes; }, /** * @param {string} key - * @param {string} value + * @param {string=} value */ addAttribute: function(key, value) { @@ -286,6 +337,11 @@ WebInspector.Cookies = {} WebInspector.Cookies.getCookiesAsync = function(callback) { + /** + * @param {?Protocol.Error} error + * @param {Array.<WebInspector.Cookie>} cookies + * @param {string} cookiesString + */ function mycallback(error, cookies, cookiesString) { if (error) @@ -293,12 +349,16 @@ WebInspector.Cookies.getCookiesAsync = function(callback) if (cookiesString) callback(WebInspector.Cookies.buildCookiesFromString(cookiesString), false); else - callback(cookies, true); + callback(cookies.map(WebInspector.Cookies.buildCookieProtocolObject), true); } PageAgent.getCookies(mycallback); } +/** + * @param {string} rawCookieString + * @return {Array.<WebInspector.Cookie>} + */ WebInspector.Cookies.buildCookiesFromString = function(rawCookieString) { var rawCookies = rawCookieString.split(/;\s*/); @@ -306,31 +366,61 @@ WebInspector.Cookies.buildCookiesFromString = function(rawCookieString) if (!(/^\s*$/.test(rawCookieString))) { for (var i = 0; i < rawCookies.length; ++i) { - var cookie = rawCookies[i]; - var delimIndex = cookie.indexOf("="); - var name = cookie.substring(0, delimIndex); - var value = cookie.substring(delimIndex + 1); + var rawCookie = rawCookies[i]; + var delimIndex = rawCookie.indexOf("="); + var name = rawCookie.substring(0, delimIndex); + var value = rawCookie.substring(delimIndex + 1); var size = name.length + value.length; - cookies.push({ name: name, value: value, size: size }); + var cookie = new WebInspector.Cookie(name, value); + cookie.setSize(size); + cookies.push(cookie); } } return cookies; } +/** + * @param {Object} protocolCookie + * @return {WebInspector.Cookie} + */ +WebInspector.Cookies.buildCookieProtocolObject = function(protocolCookie) +{ + var cookie = new WebInspector.Cookie(protocolCookie.name, protocolCookie.value); + cookie.addAttribute("domain", protocolCookie["domain"]); + cookie.addAttribute("path", protocolCookie["path"]); + cookie.addAttribute("port", protocolCookie["port"]); + if (protocolCookie["expires"]) + cookie.addAttribute("expires", protocolCookie["expires"]); + if (protocolCookie["httpOnly"]) + cookie.addAttribute("httpOnly"); + if (protocolCookie["secure"]) + cookie.addAttribute("secure"); + cookie.setSize(protocolCookie["size"]); + return cookie; +} + +/** + * @param {WebInspector.Cookie} cookie + * @param {string} resourceURL + */ WebInspector.Cookies.cookieMatchesResourceURL = function(cookie, resourceURL) { var url = resourceURL.asParsedURL(); - if (!url || !WebInspector.Cookies.cookieDomainMatchesResourceDomain(cookie.domain, url.host)) + if (!url || !WebInspector.Cookies.cookieDomainMatchesResourceDomain(cookie.domain(), url.host)) return false; - return (url.path.startsWith(cookie.path) - && (!cookie.port || url.port == cookie.port) - && (!cookie.secure || url.scheme === "https")); + return (url.path.startsWith(cookie.path()) + && (!cookie.port() || url.port == cookie.port()) + && (!cookie.secure() || url.scheme === "https")); } +/** + * @param {string} cookieDomain + * @param {string} resourceDomain + */ WebInspector.Cookies.cookieDomainMatchesResourceDomain = function(cookieDomain, resourceDomain) { if (cookieDomain.charAt(0) !== '.') return resourceDomain === cookieDomain; - return !!resourceDomain.match(new RegExp("^([^\\.]+\\.)?" + cookieDomain.substring(1).escapeForRegExp() + "$"), "i"); + return !!resourceDomain.match(new RegExp("^([^\\.]+\\.)?" + cookieDomain.substring(1).escapeForRegExp() + "$", "i")); } diff --git a/Source/WebCore/inspector/front-end/CookiesTable.js b/Source/WebCore/inspector/front-end/CookiesTable.js index 706b78b8e..3e6bd5521 100644 --- a/Source/WebCore/inspector/front-end/CookiesTable.js +++ b/Source/WebCore/inspector/front-end/CookiesTable.js @@ -31,7 +31,7 @@ /** * @constructor * @extends {WebInspector.View} - * @param {function(PageAgent.Cookie)=} deleteCallback + * @param {function(WebInspector.Cookie)=} deleteCallback * @param {function()=} refreshCallback */ WebInspector.CookiesTable = function(cookieDomain, expandable, deleteCallback, refreshCallback) @@ -55,7 +55,7 @@ WebInspector.CookiesTable = function(cookieDomain, expandable, deleteCallback, r columns[3].title = WebInspector.UIString("Path"); columns[3].sortable = true; columns[3].width = "7%"; - columns[4].title = WebInspector.UIString("Expires"); + columns[4].title = WebInspector.UIString("Expires / Max-Age"); columns[4].sortable = true; columns[4].width = "7%"; columns[5].title = WebInspector.UIString("Size"); @@ -92,6 +92,10 @@ WebInspector.CookiesTable.prototype = { this._rebuildTable(); }, + /** + * @param {string} folderName + * @param {Array.<WebInspector.Cookie>} cookies + */ addCookiesFolder: function(folderName, cookies) { this._data.push({cookies: cookies, folderName: folderName}); @@ -124,7 +128,7 @@ WebInspector.CookiesTable.prototype = { /** * @param {WebInspector.DataGridNode} parentNode - * @param {Array.<PageAgent.Cookie>} cookies + * @param {Array.<WebInspector.Cookie>} cookies */ _populateNode: function(parentNode, cookies) { @@ -146,7 +150,7 @@ WebInspector.CookiesTable.prototype = { { var totalSize = 0; for (var i = 0; cookies && i < cookies.length; ++i) - totalSize += cookies[i].size; + totalSize += cookies[i].size(); return totalSize; }, @@ -166,13 +170,17 @@ WebInspector.CookiesTable.prototype = { function expiresCompare(cookie1, cookie2) { - if (cookie1.session !== cookie2.session) - return sortDirection * (cookie1.session ? 1 : -1); + if (cookie1.session() !== cookie2.session()) + return sortDirection * (cookie1.session() ? 1 : -1); - if (cookie1.session) + if (cookie1.session()) return 0; - return sortDirection * (cookie1.expires - cookie2.expires); + if (cookie1.maxAge() && cookie2.maxAge()) + return sortDirection * (cookie1.maxAge() - cookie2.maxAge()); + if (cookie1.expires() && cookie2.expires()) + return sortDirection * (cookie1.expires() - cookie2.expires()); + return sortDirection * (cookie1.expires() ? 1 : -1); } var comparator; @@ -192,21 +200,27 @@ WebInspector.CookiesTable.prototype = { }, /** - * @param {PageAgent.Cookie} cookie + * @param {WebInspector.Cookie} cookie */ _createGridNode: function(cookie) { var data = {}; data[0] = cookie.name; data[1] = cookie.value; - data[2] = cookie.domain || ""; - data[3] = cookie.path || ""; - data[4] = cookie.type === WebInspector.Cookie.Type.Request ? "" : - (cookie.session ? WebInspector.UIString("Session") : new Date(cookie.expires).toGMTString()); - data[5] = cookie.size; + data[2] = cookie.domain() || ""; + data[3] = cookie.path() || ""; + if (cookie.type === WebInspector.Cookie.Type.Request) + data[4] = ""; + else if (cookie.maxAge()) + data[4] = Number.secondsToString(cookie.maxAge()); + else if (cookie.expires()) + data[4] = new Date(cookie.expires()).toGMTString(); + else + data[4] = WebInspector.UIString("Session"); + data[5] = cookie.size(); const checkmark = "\u2713"; - data[6] = (cookie.httpOnly ? checkmark : ""); - data[7] = (cookie.secure ? checkmark : ""); + data[6] = (cookie.httpOnly() ? checkmark : ""); + data[7] = (cookie.secure() ? checkmark : ""); var node = new WebInspector.DataGridNode(data); node.cookie = cookie; diff --git a/Source/WebCore/inspector/front-end/DOMAgent.js b/Source/WebCore/inspector/front-end/DOMAgent.js index f39a3cbb8..402064173 100644 --- a/Source/WebCore/inspector/front-end/DOMAgent.js +++ b/Source/WebCore/inspector/front-end/DOMAgent.js @@ -772,7 +772,7 @@ WebInspector.DOMDocument = function(domAgent, payload) { WebInspector.DOMNode.call(this, domAgent, this, false, payload); this.documentURL = payload.documentURL || ""; - this.baseURL = /** @type {string} */ payload.baseURL; + this.baseURL = /** @type {string} */ (payload.baseURL); console.assert(this.baseURL); this.xmlVersion = payload.xmlVersion; this._listeners = {}; diff --git a/Source/WebCore/inspector/front-end/DOMBreakpointsSidebarPane.js b/Source/WebCore/inspector/front-end/DOMBreakpointsSidebarPane.js index c1b355e90..e977896d4 100644 --- a/Source/WebCore/inspector/front-end/DOMBreakpointsSidebarPane.js +++ b/Source/WebCore/inspector/front-end/DOMBreakpointsSidebarPane.js @@ -236,7 +236,7 @@ WebInspector.DOMBreakpointsSidebarPane.prototype = { _contextMenu: function(node, type, event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); function removeBreakpoint() { this._removeBreakpoint(node, type); @@ -244,7 +244,7 @@ WebInspector.DOMBreakpointsSidebarPane.prototype = { } contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), removeBreakpoint.bind(this)); contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove all DOM breakpoints" : "Remove All DOM Breakpoints"), this._removeAllBreakpoints.bind(this)); - contextMenu.show(event); + contextMenu.show(); }, _checkboxClicked: function(node, type, event) diff --git a/Source/WebCore/inspector/front-end/DOMExtension.js b/Source/WebCore/inspector/front-end/DOMExtension.js index c8eea3642..483714ac3 100644 --- a/Source/WebCore/inspector/front-end/DOMExtension.js +++ b/Source/WebCore/inspector/front-end/DOMExtension.js @@ -166,10 +166,21 @@ Element.prototype.hasStyleClass = function(className) return this.classList.contains(className); } +/** + * @param {number|undefined} x + * @param {number|undefined} y + */ Element.prototype.positionAt = function(x, y) { - this.style.left = x + "px"; - this.style.top = y + "px"; + if (typeof x === "number") + this.style.setProperty("left", x + "px"); + else + this.style.removeProperty("left"); + + if (typeof y === "number") + this.style.setProperty("top", y + "px"); + else + this.style.removeProperty("top"); } Element.prototype.pruneEmptyTextNodes = function() @@ -189,6 +200,30 @@ Element.prototype.isScrolledToBottom = function() return this.scrollTop + this.clientHeight === this.scrollHeight; } +/** + * @constructor + * @param {number} width + * @param {number} height + */ +function Size(width, height) +{ + this.width = width; + this.height = height; +} + +/** + * @return {Size} + */ +Element.prototype.measurePreferredSize = function() +{ + document.body.appendChild(this); + this.positionAt(0, 0); + var result = new Size(this.offsetWidth, this.offsetHeight); + this.positionAt(undefined, undefined); + document.body.removeChild(this); + return result; +} + Node.prototype.enclosingNodeOrSelfWithNodeNameInArray = function(nameArray) { for (var node = this; node && node !== this.ownerDocument; node = node.parentNode) diff --git a/Source/WebCore/inspector/front-end/DataGrid.js b/Source/WebCore/inspector/front-end/DataGrid.js index 114d82d90..24a349752 100644 --- a/Source/WebCore/inspector/front-end/DataGrid.js +++ b/Source/WebCore/inspector/front-end/DataGrid.js @@ -878,7 +878,7 @@ WebInspector.DataGrid.prototype = { _contextMenuInDataTable: function(event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); var gridNode = this.dataGridNodeFromNode(event.target); if (this._refreshCallback && (!gridNode || gridNode !== this.creationNode)) @@ -896,7 +896,7 @@ WebInspector.DataGrid.prototype = { contextMenu.appendItem(WebInspector.UIString("Delete"), this._deleteCallback.bind(this, gridNode)); } - contextMenu.show(event); + contextMenu.show(); }, _clickInDataTable: function(event) diff --git a/Source/WebCore/inspector/front-end/DebuggerModel.js b/Source/WebCore/inspector/front-end/DebuggerModel.js index 326a9be24..d58da0edb 100644 --- a/Source/WebCore/inspector/front-end/DebuggerModel.js +++ b/Source/WebCore/inspector/front-end/DebuggerModel.js @@ -205,7 +205,7 @@ WebInspector.DebuggerModel.prototype = { function didSetBreakpoint(error, breakpointId, locations) { if (callback) { - var rawLocations = /** @type {Array.<WebInspector.DebuggerModel.Location>} */ locations; + var rawLocations = /** @type {Array.<WebInspector.DebuggerModel.Location>} */ (locations); callback(error ? null : breakpointId, rawLocations); } } @@ -229,7 +229,7 @@ WebInspector.DebuggerModel.prototype = { function didSetBreakpoint(error, breakpointId, actualLocation) { if (callback) { - var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ actualLocation; + var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (actualLocation); callback(error ? null : breakpointId, [rawLocation]); } } @@ -478,9 +478,10 @@ WebInspector.DebuggerModel.prototype = { * @param {boolean} includeCommandLineAPI * @param {boolean} doNotPauseOnExceptionsAndMuteConsole * @param {boolean} returnByValue + * @param {boolean} generatePreview * @param {function(?WebInspector.RemoteObject, boolean, RuntimeAgent.RemoteObject=)} callback */ - evaluateOnSelectedCallFrame: function(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, callback) + evaluateOnSelectedCallFrame: function(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback) { /** * @param {?RuntimeAgent.RemoteObject} result @@ -497,7 +498,7 @@ WebInspector.DebuggerModel.prototype = { this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ConsoleCommandEvaluatedInSelectedCallFrame); } - this.selectedCallFrame().evaluate(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, didEvaluate.bind(this)); + this.selectedCallFrame().evaluate(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, didEvaluate.bind(this)); }, /** @@ -725,7 +726,7 @@ WebInspector.DebuggerModel.CallFrame.prototype = { */ get location() { - var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ this._payload.location; + var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (this._payload.location); return rawLocation; }, @@ -735,9 +736,10 @@ WebInspector.DebuggerModel.CallFrame.prototype = { * @param {boolean} includeCommandLineAPI * @param {boolean} doNotPauseOnExceptionsAndMuteConsole * @param {boolean} returnByValue + * @param {boolean} generatePreview * @param {function(?RuntimeAgent.RemoteObject, boolean=)=} callback */ - evaluate: function(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, callback) + evaluate: function(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback) { /** * @this {WebInspector.DebuggerModel.CallFrame} @@ -754,7 +756,7 @@ WebInspector.DebuggerModel.CallFrame.prototype = { } callback(result, wasThrown); } - DebuggerAgent.evaluateOnCallFrame(this._payload.callFrameId, code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, didEvaluateOnCallFrame.bind(this)); + DebuggerAgent.evaluateOnCallFrame(this._payload.callFrameId, code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, didEvaluateOnCallFrame.bind(this)); }, /** diff --git a/Source/WebCore/inspector/front-end/DebuggerScriptMapping.js b/Source/WebCore/inspector/front-end/DebuggerScriptMapping.js index 12ce92801..d32b2e454 100644 --- a/Source/WebCore/inspector/front-end/DebuggerScriptMapping.js +++ b/Source/WebCore/inspector/front-end/DebuggerScriptMapping.js @@ -30,14 +30,16 @@ /** * @constructor + * @param {WebInspector.Workspace} workspace + * @param {WebInspector.NetworkWorkspaceProvider} networkWorkspaceProvider */ -WebInspector.DebuggerScriptMapping = function(workspace) +WebInspector.DebuggerScriptMapping = function(workspace, networkWorkspaceProvider) { this._mappings = []; this._resourceMapping = new WebInspector.ResourceScriptMapping(workspace); this._mappings.push(this._resourceMapping); - this._compilerMapping = new WebInspector.CompilerScriptMapping(workspace); + this._compilerMapping = new WebInspector.CompilerScriptMapping(workspace, networkWorkspaceProvider); this._mappings.push(this._compilerMapping); this._snippetMapping = WebInspector.scriptSnippetModel.scriptMapping; this._mappings.push(this._snippetMapping); @@ -52,7 +54,7 @@ WebInspector.DebuggerScriptMapping.prototype = { */ _parsedScriptSource: function(event) { - var script = /** @type {WebInspector.Script} */ event.data; + var script = /** @type {WebInspector.Script} */ (event.data); var mapping = this._mappingForScript(script); mapping.addScript(script); }, diff --git a/Source/WebCore/inspector/front-end/DefaultTextEditor.js b/Source/WebCore/inspector/front-end/DefaultTextEditor.js index a41dd8028..d269bc276 100644 --- a/Source/WebCore/inspector/front-end/DefaultTextEditor.js +++ b/Source/WebCore/inspector/front-end/DefaultTextEditor.js @@ -46,6 +46,14 @@ WebInspector.DefaultTextEditor = function(url, delegate) this.element.className = "text-editor monospace"; + // Prevent middle-click pasting in the editor unless it is explicitly enabled for certain component. + this.element.addEventListener("mouseup", preventDefaultOnMouseUp.bind(this), false); + function preventDefaultOnMouseUp(event) + { + if (event.button === 1) + event.consume(true); + } + this._textModel = new WebInspector.TextEditorModel(); this._textModel.addEventListener(WebInspector.TextEditorModel.Events.TextChanged, this._textChanged, this); this._textModel.resetUndoStack(); @@ -63,6 +71,14 @@ WebInspector.DefaultTextEditor = function(url, delegate) this._gutterPanel.element.addEventListener("mousedown", this._onMouseDown.bind(this), true); + // Explicitly enable middle-click pasting in the editor main panel. + this._mainPanel.element.addEventListener("mouseup", consumeMouseUp.bind(this), false); + function consumeMouseUp(event) + { + if (event.button === 1) + event.consume(false); + } + this.element.appendChild(this._mainPanel.element); this.element.appendChild(this._gutterPanel.element); @@ -395,6 +411,8 @@ WebInspector.DefaultTextEditor.prototype = { var handleRedo = this._mainPanel.handleUndoRedo.bind(this._mainPanel, true); this._shortcuts[WebInspector.KeyboardShortcut.makeKey("z", modifiers.CtrlOrMeta)] = handleUndo; this._shortcuts[WebInspector.KeyboardShortcut.makeKey("z", modifiers.Shift | modifiers.CtrlOrMeta)] = handleRedo; + if (!WebInspector.isMac()) + this._shortcuts[WebInspector.KeyboardShortcut.makeKey("y", modifiers.CtrlOrMeta)] = handleRedo; var handleTabKey = this._mainPanel.handleTabKeyPress.bind(this._mainPanel, false); var handleShiftTabKey = this._mainPanel.handleTabKeyPress.bind(this._mainPanel, true); @@ -418,7 +436,7 @@ WebInspector.DefaultTextEditor.prototype = { var anchor = event.target.enclosingNodeOrSelfWithNodeName("a"); if (anchor) return; - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number"); if (target) this._delegate.populateLineGutterContextMenu(contextMenu, target.lineNumber); @@ -426,7 +444,7 @@ WebInspector.DefaultTextEditor.prototype = { target = this._mainPanel._enclosingLineRowOrSelf(event.target); this._delegate.populateTextAreaContextMenu(contextMenu, target && target.lineNumber); } - contextMenu.show(event); + contextMenu.show(); }, _handleScrollChanged: function(event) diff --git a/Source/WebCore/inspector/front-end/DirectoryContentView.js b/Source/WebCore/inspector/front-end/DirectoryContentView.js index 144260fcb..57ae53d3e 100644 --- a/Source/WebCore/inspector/front-end/DirectoryContentView.js +++ b/Source/WebCore/inspector/front-end/DirectoryContentView.js @@ -84,7 +84,7 @@ WebInspector.DirectoryContentView.prototype = { _sort: function() { - var column = /** @type {string} */ this.sortColumnIdentifier; + var column = /** @type {string} */ (this.sortColumnIdentifier); this.sortNodes(WebInspector.DirectoryContentView.Node.comparator(column, this.sortOrder === "descending"), false); }, diff --git a/Source/WebCore/inspector/front-end/DockController.js b/Source/WebCore/inspector/front-end/DockController.js index 5c48cabfe..4b9de5fd5 100644 --- a/Source/WebCore/inspector/front-end/DockController.js +++ b/Source/WebCore/inspector/front-end/DockController.js @@ -40,7 +40,7 @@ WebInspector.DockController = function() if (Preferences.showDockToRight) this._dockToggleButton.makeLongClickEnabled(this._createDockOptions.bind(this)); - this.setDockSide(WebInspector.queryParamsObject["dockSide"]); + this.setDockSide(WebInspector.queryParamsObject["dockSide"] || "bottom"); } WebInspector.DockController.State = { @@ -63,6 +63,9 @@ WebInspector.DockController.prototype = { */ setDockSide: function(dockSide) { + if (this._dockSide === dockSide) + return; + if (this._dockSide) WebInspector.settings.lastDockState.set(this._dockSide); diff --git a/Source/WebCore/inspector/front-end/ElementsPanel.js b/Source/WebCore/inspector/front-end/ElementsPanel.js index b7bb2f6ac..a0e6e1a18 100644 --- a/Source/WebCore/inspector/front-end/ElementsPanel.js +++ b/Source/WebCore/inspector/front-end/ElementsPanel.js @@ -338,7 +338,7 @@ WebInspector.ElementsPanel.prototype = { WebInspector.settings.domWordWrap.set(!WebInspector.settings.domWordWrap.get()); } - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); var populated = this.treeOutline.populateContextMenu(contextMenu, event); if (WebInspector.experimentsSettings.cssRegions.isEnabled()) { @@ -349,7 +349,7 @@ WebInspector.ElementsPanel.prototype = { contextMenu.appendSeparator(); contextMenu.appendCheckboxItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Word wrap" : "Word Wrap"), toggleWordWrap.bind(this), WebInspector.settings.domWordWrap.get()); - contextMenu.show(event); + contextMenu.show(); }, _showNamedFlowCollections: function() @@ -415,7 +415,7 @@ WebInspector.ElementsPanel.prototype = { return; } - var node = /** @type {WebInspector.DOMNode} */ treeElement.representedObject; + var node = /** @type {WebInspector.DOMNode} */ (treeElement.representedObject); if (!node.nodeName() || node.nodeName().toLowerCase() !== "img") { callback(); @@ -604,7 +604,7 @@ WebInspector.ElementsPanel.prototype = { if (name !== "class" && name !== "id") return; - var node = /** @type {WebInspector.DOMNode} */ event.data.node; + var node = /** @type {WebInspector.DOMNode} */ (event.data.node); var crumbs = this.crumbsElement; var crumb = crumbs.firstChild; while (crumb) { @@ -1117,11 +1117,11 @@ WebInspector.ElementsPanel.prototype = { * @param {WebInspector.ContextMenu} contextMenu * @param {Object} target */ - appendApplicableItems: function(contextMenu, target) + appendApplicableItems: function(event, contextMenu, target) { if (!(target instanceof WebInspector.RemoteObject)) return; - var remoteObject = /** @type {WebInspector.RemoteObject} */ target; + var remoteObject = /** @type {WebInspector.RemoteObject} */ (target); if (remoteObject.subtype !== "node") return; diff --git a/Source/WebCore/inspector/front-end/ElementsPanelDescriptor.js b/Source/WebCore/inspector/front-end/ElementsPanelDescriptor.js index b7ced2af3..8f71aae08 100644 --- a/Source/WebCore/inspector/front-end/ElementsPanelDescriptor.js +++ b/Source/WebCore/inspector/front-end/ElementsPanelDescriptor.js @@ -42,14 +42,14 @@ WebInspector.ElementsPanelDescriptor.prototype = { * @param {WebInspector.ContextMenu} contextMenu * @param {Object} target */ - appendApplicableItems: function(contextMenu, target) + appendApplicableItems: function(event, contextMenu, target) { if (!(target instanceof WebInspector.RemoteObject)) return; - var remoteObject = /** @type {WebInspector.RemoteObject} */ target; + var remoteObject = /** @type {WebInspector.RemoteObject} */ (target); if (remoteObject.subtype !== "node") return; - this.panel().appendApplicableItems(contextMenu, target); + this.panel().appendApplicableItems(event, contextMenu, target); }, __proto__: WebInspector.PanelDescriptor.prototype diff --git a/Source/WebCore/inspector/front-end/ElementsTreeOutline.js b/Source/WebCore/inspector/front-end/ElementsTreeOutline.js index 70e3e2525..a87bc923f 100644 --- a/Source/WebCore/inspector/front-end/ElementsTreeOutline.js +++ b/Source/WebCore/inspector/front-end/ElementsTreeOutline.js @@ -359,6 +359,11 @@ WebInspector.ElementsTreeOutline.prototype = { _ondragstart: function(event) { + if (!window.getSelection().isCollapsed) + return false; + if (event.target.nodeName === "A") + return false; + var treeElement = this._treeElementFromEvent(event); if (!treeElement) return false; @@ -484,9 +489,9 @@ WebInspector.ElementsTreeOutline.prototype = { { WebInspector.domAgent.inspectElement(treeElement.representedObject.id); } - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendItem(WebInspector.UIString("Reveal in Elements Panel"), focusElement.bind(this)); - contextMenu.show(event); + contextMenu.show(); }, populateContextMenu: function(contextMenu, event) @@ -1721,7 +1726,7 @@ WebInspector.ElementsTreeElement.prototype = { */ _buildTagDOM: function(parentElement, tagName, isClosingTag, isDistinctTreeElement, linkify) { - var node = /** @type WebInspector.DOMNode */ this.representedObject; + var node = /** @type WebInspector.DOMNode */ (this.representedObject); var classes = [ "webkit-html-tag" ]; if (isClosingTag && isDistinctTreeElement) classes.push("close"); @@ -2003,7 +2008,7 @@ WebInspector.ElementsTreeElement.prototype = { object.callFunction(scrollIntoView); } - var node = /** @type {WebInspector.DOMNode} */ this.representedObject; + var node = /** @type {WebInspector.DOMNode} */ (this.representedObject); WebInspector.RemoteObject.resolveNode(node, "", scrollIntoViewCallback); }, @@ -2039,7 +2044,7 @@ WebInspector.ElementsTreeUpdater.prototype = { if (this._treeOutline._visible) this._updateModifiedNodesSoon(); - var entry = /** @type {WebInspector.ElementsTreeUpdater.UpdateEntry} */ this._recentlyModifiedNodes.get(node); + var entry = /** @type {WebInspector.ElementsTreeUpdater.UpdateEntry} */ (this._recentlyModifiedNodes.get(node)); if (!entry) { entry = new WebInspector.ElementsTreeUpdater.UpdateEntry(isUpdated, parentNode); this._recentlyModifiedNodes.put(node, entry); diff --git a/Source/WebCore/inspector/front-end/ExtensionServer.js b/Source/WebCore/inspector/front-end/ExtensionServer.js index 149c8b8c2..6d0558900 100644 --- a/Source/WebCore/inspector/front-end/ExtensionServer.js +++ b/Source/WebCore/inspector/front-end/ExtensionServer.js @@ -178,7 +178,7 @@ WebInspector.ExtensionServer.prototype = { } for (var name in message.headers) extensionHeaders[name] = message.headers[name]; - var allHeaders = /** @type NetworkAgent.Headers */ {}; + var allHeaders = /** @type NetworkAgent.Headers */ ({}); for (var extension in this._extraHeaders) { var headers = this._extraHeaders[extension]; for (name in headers) { @@ -290,7 +290,7 @@ WebInspector.ExtensionServer.prototype = { _handleOpenURL: function(port, details) { - var url = /** @type {string} */ details.url; + var url = /** @type {string} */ (details.url); var contentProvider = WebInspector.workspace.uiSourceCodeForURL(url) || WebInspector.resourceForURL(url); if (!contentProvider) return false; @@ -482,7 +482,7 @@ WebInspector.ExtensionServer.prototype = { _onGetResourceContent: function(message, port) { - var url = /** @type {string} */ message.url; + var url = /** @type {string} */ (message.url); var contentProvider = WebInspector.workspace.uiSourceCodeForURL(url) || WebInspector.resourceForURL(url); if (!contentProvider) return this._status.E_NOTFOUND(url); @@ -500,7 +500,7 @@ WebInspector.ExtensionServer.prototype = { this._dispatchCallback(message.requestId, port, response); } - var url = /** @type {string} */ message.url; + var url = /** @type {string} */ (message.url); var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(url); if (!uiSourceCode) { var resource = WebInspector.resourceTreeModel.resourceForURL(url); @@ -625,20 +625,20 @@ WebInspector.ExtensionServer.prototype = { _notifyResourceAdded: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data); this._postNotification(WebInspector.extensionAPI.Events.ResourceAdded, this._makeResource(uiSourceCode)); }, _notifyUISourceCodeContentCommitted: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data.uiSourceCode; - var content = /** @type {string} */ event.data.content; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data.uiSourceCode); + var content = /** @type {string} */ (event.data.content); this._postNotification(WebInspector.extensionAPI.Events.ResourceContentCommitted, this._makeResource(uiSourceCode), content); }, _notifyRequestFinished: function(event) { - var request = /** @type {WebInspector.NetworkRequest} */ event.data; + var request = /** @type {WebInspector.NetworkRequest} */ (event.data); this._postNotification(WebInspector.extensionAPI.Events.NetworkRequestFinished, this._requestId(request), (new WebInspector.HAREntry(request)).build()); }, @@ -797,7 +797,7 @@ WebInspector.ExtensionServer.prototype = { return this._status.E_NOTFOUND(securityOrigin); contextId = context.id; } - RuntimeAgent.evaluate(expression, "extension", exposeCommandLineAPI, true, contextId, returnByValue, callback); + RuntimeAgent.evaluate(expression, "extension", exposeCommandLineAPI, true, contextId, returnByValue, false, callback); } } diff --git a/Source/WebCore/inspector/front-end/FileContentView.js b/Source/WebCore/inspector/front-end/FileContentView.js index b8ccd9ad1..7b47c6df0 100644 --- a/Source/WebCore/inspector/front-end/FileContentView.js +++ b/Source/WebCore/inspector/front-end/FileContentView.js @@ -68,7 +68,7 @@ WebInspector.FileContentView.prototype = { if (this._content) { if (!this._content.updateMetadata(metadata)) return; - var sourceFrame = /** @type {WebInspector.SourceFrame} */ this._innerView; + var sourceFrame = /** @type {WebInspector.SourceFrame} */ (this._innerView); this._content.requestContent(sourceFrame.setContent.bind(sourceFrame)); } else { this._innerView.detach(); @@ -124,7 +124,7 @@ WebInspector.FileContentView.FileContentProvider.prototype = { */ requestContent: function(callback) { - var size = /** @type {number} */ this._metadata.size; + var size = /** @type {number} */ (this._metadata.size); this._file.requestFileContent(true, 0, size, this._charset || "", this._fileContentReceived.bind(this, callback)); }, diff --git a/Source/WebCore/inspector/front-end/FileSystemModel.js b/Source/WebCore/inspector/front-end/FileSystemModel.js index ca5bf7300..6b15810b4 100644 --- a/Source/WebCore/inspector/front-end/FileSystemModel.js +++ b/Source/WebCore/inspector/front-end/FileSystemModel.js @@ -56,7 +56,7 @@ WebInspector.FileSystemModel.prototype = { */ _frameAdded: function(event) { - var frame = /** @type {WebInspector.ResourceTreeFrame} */ event.data; + var frame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data); this._attachFrameRecursively(frame); }, @@ -65,7 +65,7 @@ WebInspector.FileSystemModel.prototype = { */ _frameNavigated: function(event) { - var frame = /** @type {WebInspector.ResourceTreeFrame} */ event.data; + var frame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data); this._attachFrameRecursively(frame); }, @@ -74,7 +74,7 @@ WebInspector.FileSystemModel.prototype = { */ _frameDetached: function(event) { - var frame = /** @type {WebInspector.ResourceTreeFrame} */ event.data; + var frame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data); this._detachFrameRecursively(frame); }, diff --git a/Source/WebCore/inspector/front-end/FileSystemView.js b/Source/WebCore/inspector/front-end/FileSystemView.js index 174e3450b..14465f5e0 100644 --- a/Source/WebCore/inspector/front-end/FileSystemView.js +++ b/Source/WebCore/inspector/front-end/FileSystemView.js @@ -135,7 +135,7 @@ WebInspector.FileSystemView.EntryTreeElement.prototype = { if (this._entry.isDirectory) this._view = new WebInspector.DirectoryContentView(); else { - var file = /** @type {WebInspector.FileSystemModel.File} */ this._entry; + var file = /** @type {WebInspector.FileSystemModel.File} */ (this._entry); this._view = new WebInspector.FileContentView(file); } } @@ -211,7 +211,7 @@ WebInspector.FileSystemView.EntryTreeElement.prototype = { { if (!this._entry.isDirectory) { if (this._view && this._view === this._fileSystemView.visibleView) { - var fileContentView = /** @type {WebInspector.FileContentView} */ this._view; + var fileContentView = /** @type {WebInspector.FileContentView} */ (this._view); fileContentView.refresh(); } } else diff --git a/Source/WebCore/inspector/front-end/HAREntry.js b/Source/WebCore/inspector/front-end/HAREntry.js index 22e07f88e..c7a80511b 100644 --- a/Source/WebCore/inspector/front-end/HAREntry.js +++ b/Source/WebCore/inspector/front-end/HAREntry.js @@ -204,11 +204,11 @@ WebInspector.HAREntry.prototype = { return { name: cookie.name, value: cookie.value, - path: cookie.path, - domain: cookie.domain, - expires: cookie.expires(new Date(this._request.startTime * 1000)), - httpOnly: cookie.httpOnly, - secure: cookie.secure + path: cookie.path(), + domain: cookie.domain(), + expires: cookie.expiresDate(new Date(this._request.startTime * 1000)), + httpOnly: cookie.httpOnly(), + secure: cookie.secure() }; }, diff --git a/Source/WebCore/inspector/front-end/HandlerRegistry.js b/Source/WebCore/inspector/front-end/HandlerRegistry.js index 99d1bbfa7..6b9093113 100644 --- a/Source/WebCore/inspector/front-end/HandlerRegistry.js +++ b/Source/WebCore/inspector/front-end/HandlerRegistry.js @@ -94,8 +94,11 @@ WebInspector.HandlerRegistry.prototype = { * @param {WebInspector.ContextMenu} contextMenu * @param {Object} target */ - appendApplicableItems: function(contextMenu, target) + appendApplicableItems: function(event, contextMenu, target) { + if (event.hasBeenHandledByHandlerRegistry) + return; + event.hasBeenHandledByHandlerRegistry = true; this._appendContentProviderItems(contextMenu, target); this._appendHrefItems(contextMenu, target); }, @@ -108,7 +111,7 @@ WebInspector.HandlerRegistry.prototype = { { if (!(target instanceof WebInspector.UISourceCode || target instanceof WebInspector.Resource || target instanceof WebInspector.NetworkRequest)) return; - var contentProvider = /** @type {WebInspector.ContentProvider} */ target; + var contentProvider = /** @type {WebInspector.ContentProvider} */ (target); if (!contentProvider.contentURL()) return; @@ -140,7 +143,7 @@ WebInspector.HandlerRegistry.prototype = { function save(forceSaveAs) { if (contentProvider instanceof WebInspector.UISourceCode) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ contentProvider; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (contentProvider); if (uiSourceCode.isDirty()) { doSave(forceSaveAs, uiSourceCode.workingCopy()); uiSourceCode.commitWorkingCopy(function() { }); @@ -163,7 +166,7 @@ WebInspector.HandlerRegistry.prototype = { { if (!(target instanceof Node)) return; - var targetNode = /** @type {Node} */ target; + var targetNode = /** @type {Node} */ (target); var anchorElement = targetNode.enclosingNodeOrSelfWithClass("webkit-html-resource-link") || targetNode.enclosingNodeOrSelfWithClass("webkit-html-external-link"); if (!anchorElement) diff --git a/Source/WebCore/inspector/front-end/HeapSnapshotLoader.js b/Source/WebCore/inspector/front-end/HeapSnapshotLoader.js index 8725482a5..1a457ce24 100644 --- a/Source/WebCore/inspector/front-end/HeapSnapshotLoader.js +++ b/Source/WebCore/inspector/front-end/HeapSnapshotLoader.js @@ -129,7 +129,7 @@ WebInspector.HeapSnapshotLoader.prototype = { var closingBracketIndex = WebInspector.findBalancedCurlyBrackets(this._json); if (closingBracketIndex === -1) return; - this._snapshot.snapshot = /** @type {HeapSnapshotHeader} */JSON.parse(this._json.slice(0, closingBracketIndex)); + this._snapshot.snapshot = /** @type {HeapSnapshotHeader} */ (JSON.parse(this._json.slice(0, closingBracketIndex))); this._json = this._json.slice(closingBracketIndex); this._state = "find-nodes"; } diff --git a/Source/WebCore/inspector/front-end/HeapSnapshotView.js b/Source/WebCore/inspector/front-end/HeapSnapshotView.js index ccd3630a6..6b718fcc8 100644 --- a/Source/WebCore/inspector/front-end/HeapSnapshotView.js +++ b/Source/WebCore/inspector/front-end/HeapSnapshotView.js @@ -216,7 +216,7 @@ WebInspector.HeapSnapshotView.prototype = { { this._currentSearchResultIndex = -1; this._popoverHelper.hidePopover(); - if (this.helpPopover && this.helpPopover.visible) + if (this.helpPopover && this.helpPopover.isShowing()) this.helpPopover.hide(); }, @@ -368,7 +368,7 @@ WebInspector.HeapSnapshotView.prototype = { return; this._baseProfileUid = this._profiles()[this.baseSelectElement.selectedIndex].uid; - var dataGrid = /** @type {WebInspector.HeapSnapshotDiffDataGrid} */ this.dataGrid; + var dataGrid = /** @type {WebInspector.HeapSnapshotDiffDataGrid} */ (this.dataGrid); // Change set base data source only if main data source is already set. if (dataGrid.snapshot) this.baseProfile.load(dataGrid.setBaseDataSource.bind(dataGrid)); @@ -646,7 +646,7 @@ WebInspector.HeapSnapshotView.prototype = { this._helpPopoverContentElement = contentElement; this.helpPopover = new WebInspector.Popover(); } - if (this.helpPopover.visible) + if (this.helpPopover.isShowing()) this.helpPopover.hide(); else this.helpPopover.show(this._helpPopoverContentElement, this.helpButton.element); @@ -866,7 +866,7 @@ WebInspector.HeapProfileHeader.prototype = { this.sidebarElement.wait = true; ProfilerAgent.getProfile(this.profileType().id, this.uid); } - var loaderProxy = /** @type {WebInspector.HeapSnapshotLoaderProxy} */ this._receiver; + var loaderProxy = /** @type {WebInspector.HeapSnapshotLoaderProxy} */ (this._receiver); loaderProxy.addConsumer(callback); }, @@ -937,7 +937,7 @@ WebInspector.HeapProfileHeader.prototype = { if (snapshotProxy) this._snapshotProxy = snapshotProxy; this._updateSnapshotStatus(); - var worker = /** @type {WebInspector.HeapSnapshotWorker} */ this._snapshotProxy.worker; + var worker = /** @type {WebInspector.HeapSnapshotWorker} */ (this._snapshotProxy.worker); this.isTemporary = false; worker.startCheckingForLongRunningCalls(); }, diff --git a/Source/WebCore/inspector/front-end/ImageView.js b/Source/WebCore/inspector/front-end/ImageView.js index d2c79b30b..d1c4820f9 100644 --- a/Source/WebCore/inspector/front-end/ImageView.js +++ b/Source/WebCore/inspector/front-end/ImageView.js @@ -126,10 +126,10 @@ WebInspector.ImageView.prototype = { _contextMenu: function(event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy image URL" : "Copy Image URL"), this._copyImageURL.bind(this)); contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Open image in new tab" : "Open Image in New Tab"), this._openInNewTab.bind(this)); - contextMenu.show(event); + contextMenu.show(); }, _copyImageURL: function(event) diff --git a/Source/WebCore/inspector/front-end/IndexedDBModel.js b/Source/WebCore/inspector/front-end/IndexedDBModel.js index 0c4fed07e..e6440c1bd 100644 --- a/Source/WebCore/inspector/front-end/IndexedDBModel.js +++ b/Source/WebCore/inspector/front-end/IndexedDBModel.js @@ -198,7 +198,7 @@ WebInspector.IndexedDBModel.prototype = { */ _frameNavigated: function(event) { - var resourceTreeFrame = /** @type {WebInspector.ResourceTreeFrame} */ event.data; + var resourceTreeFrame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data); this._processFrameNavigated(resourceTreeFrame); }, @@ -207,7 +207,7 @@ WebInspector.IndexedDBModel.prototype = { */ _frameDetached: function(event) { - var resourceTreeFrame = /** @type {WebInspector.ResourceTreeFrame} */ event.data; + var resourceTreeFrame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data); this._originRemovedFromFrame(resourceTreeFrame.id); }, diff --git a/Source/WebCore/inspector/front-end/IndexedDBViews.js b/Source/WebCore/inspector/front-end/IndexedDBViews.js index d30006a78..41e2de987 100644 --- a/Source/WebCore/inspector/front-end/IndexedDBViews.js +++ b/Source/WebCore/inspector/front-end/IndexedDBViews.js @@ -183,7 +183,7 @@ WebInspector.IDBDataView.prototype = { } keyColumnHeaderFragment.appendChild(document.createTextNode("]")); } else { - var keyPathString = /** @type {string} */ keyPath; + var keyPathString = /** @type {string} */ (keyPath); keyColumnHeaderFragment.appendChild(this._keyPathStringFragment(keyPathString)); } keyColumnHeaderFragment.appendChild(document.createTextNode(")")); diff --git a/Source/WebCore/inspector/front-end/InspectorFrontendHostStub.js b/Source/WebCore/inspector/front-end/InspectorFrontendHostStub.js index e92348a88..c616c1d19 100644 --- a/Source/WebCore/inspector/front-end/InspectorFrontendHostStub.js +++ b/Source/WebCore/inspector/front-end/InspectorFrontendHostStub.js @@ -239,12 +239,15 @@ WebInspector.ClipboardAccessDeniedScreen.prototype = { * @constructor * @extends {WebInspector.HelpScreen} */ -WebInspector.RemoteDebuggingTerminatedScreen = function() +WebInspector.RemoteDebuggingTerminatedScreen = function(reason) { - WebInspector.HelpScreen.call(this, WebInspector.UIString("Debug target terminated")); + WebInspector.HelpScreen.call(this, WebInspector.UIString("Detached from the target")); var p = this.contentElement.createChild("p"); p.addStyleClass("help-section"); - p.textContent = WebInspector.UIString("This remote debugging client is no longer functional. Please re-attach to the new target."); + p.createChild("span").textContent = "Remote debugging has been terminated with reason: "; + p.createChild("span", "error-message").textContent = reason; + p.createChild("br"); + p.createChild("span").textContent = "Please re-attach to the new target."; } WebInspector.RemoteDebuggingTerminatedScreen.prototype = { diff --git a/Source/WebCore/inspector/front-end/JavaScriptSourceFrame.js b/Source/WebCore/inspector/front-end/JavaScriptSourceFrame.js index b6ab82182..b92824b99 100644 --- a/Source/WebCore/inspector/front-end/JavaScriptSourceFrame.js +++ b/Source/WebCore/inspector/front-end/JavaScriptSourceFrame.js @@ -39,7 +39,6 @@ WebInspector.JavaScriptSourceFrame = function(scriptsPanel, uiSourceCode) this._scriptsPanel = scriptsPanel; this._breakpointManager = WebInspector.breakpointManager; this._uiSourceCode = uiSourceCode; - this._scriptFile = this._uiSourceCode.scriptFile(); var locations = this._breakpointManager.breakpointLocationsForUISourceCode(this._uiSourceCode); for (var i = 0; i < locations.length; ++i) @@ -59,17 +58,12 @@ WebInspector.JavaScriptSourceFrame = function(scriptsPanel, uiSourceCode) this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._onFormattedChanged, this); this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._onWorkingCopyChanged, this); - this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._onWorkingCopyCommitted, this); this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessageAdded, this._consoleMessageAdded, this); this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessageRemoved, this._consoleMessageRemoved, this); this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessagesCleared, this._consoleMessagesCleared, this); - - if (this._scriptFile) { - this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.WillMergeToVM, this._willMergeToVM, this); - this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.DidMergeToVM, this._didMergeToVM, this); - this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.WillDivergeFromVM, this._willDivergeFromVM, this); - this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._didDivergeFromVM, this); - } + this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this); + + this._updateScriptFile(); } WebInspector.JavaScriptSourceFrame.prototype = { @@ -111,7 +105,7 @@ WebInspector.JavaScriptSourceFrame.prototype = { */ _onFormattedChanged: function(event) { - var content = /** @type {string} */ event.data.content; + var content = /** @type {string} */ (event.data.content); this._innerSetContent(content); }, @@ -123,27 +117,12 @@ WebInspector.JavaScriptSourceFrame.prototype = { this._innerSetContent(this._uiSourceCode.workingCopy()); }, - /** - * @param {WebInspector.Event} event - */ - _onWorkingCopyCommitted: function(event) - { - this._innerSetContent(this._uiSourceCode.workingCopy()); - }, - _innerSetContent: function(content) { if (this._isSettingWorkingCopy || this._isCommittingEditing) return; - if (this._uiSourceCode.togglingFormatter()) - this.setContent(content, false, this._uiSourceCode.mimeType()); - else { - var breakpointLocations = this._breakpointManager.breakpointLocationsForUISourceCode(this._uiSourceCode); - for (var i = 0; i < breakpointLocations.length; ++i) - breakpointLocations[i].breakpoint.remove(); - this.setContent(content, false, this._uiSourceCode.mimeType()); - } + this.setContent(content, false, this._uiSourceCode.mimeType()); }, populateLineGutterContextMenu: function(contextMenu, lineNumber) @@ -336,7 +315,7 @@ WebInspector.JavaScriptSourceFrame.prototype = { return; } var selectedCallFrame = WebInspector.debuggerModel.selectedCallFrame(); - selectedCallFrame.evaluate(this._highlightElement.textContent, objectGroupName, false, true, false, showObjectPopover.bind(this)); + selectedCallFrame.evaluate(this._highlightElement.textContent, objectGroupName, false, true, false, false, showObjectPopover.bind(this)); }, _onHidePopover: function() @@ -510,23 +489,23 @@ WebInspector.JavaScriptSourceFrame.prototype = { _breakpointAdded: function(event) { - var uiLocation = /** @type {WebInspector.UILocation} */ event.data.uiLocation; + var uiLocation = /** @type {WebInspector.UILocation} */ (event.data.uiLocation); if (uiLocation.uiSourceCode !== this._uiSourceCode) return; - var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ event.data.breakpoint; + var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint); if (this.loaded) this._addBreakpointDecoration(uiLocation.lineNumber, breakpoint.condition(), breakpoint.enabled(), false); }, _breakpointRemoved: function(event) { - var uiLocation = /** @type {WebInspector.UILocation} */ event.data.uiLocation; + var uiLocation = /** @type {WebInspector.UILocation} */ (event.data.uiLocation); if (uiLocation.uiSourceCode !== this._uiSourceCode) return; - var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ event.data.breakpoint; + var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint); var remainingBreakpoint = this._breakpointManager.findBreakpoint(this._uiSourceCode, uiLocation.lineNumber); if (!remainingBreakpoint && this.loaded) this._removeBreakpointDecoration(uiLocation.lineNumber); @@ -534,14 +513,14 @@ WebInspector.JavaScriptSourceFrame.prototype = { _consoleMessageAdded: function(event) { - var message = /** @type {WebInspector.PresentationConsoleMessage} */ event.data; + var message = /** @type {WebInspector.PresentationConsoleMessage} */ (event.data); if (this.loaded) this.addMessageToSource(message.lineNumber, message.originalMessage); }, _consoleMessageRemoved: function(event) { - var message = /** @type {WebInspector.PresentationConsoleMessage} */ event.data; + var message = /** @type {WebInspector.PresentationConsoleMessage} */ (event.data); if (this.loaded) this.removeMessageFromSource(message.lineNumber, message.originalMessage); }, @@ -551,6 +530,31 @@ WebInspector.JavaScriptSourceFrame.prototype = { this.clearMessages(); }, + /** + * @param {WebInspector.Event} event + */ + _onSourceMappingChanged: function(event) + { + this._updateScriptFile(); + }, + + _updateScriptFile: function() + { + if (this._scriptFile) { + this._scriptFile.removeEventListener(WebInspector.ScriptFile.Events.WillMergeToVM, this._willMergeToVM, this); + this._scriptFile.removeEventListener(WebInspector.ScriptFile.Events.DidMergeToVM, this._didMergeToVM, this); + this._scriptFile.removeEventListener(WebInspector.ScriptFile.Events.WillDivergeFromVM, this._willDivergeFromVM, this); + this._scriptFile.removeEventListener(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._didDivergeFromVM, this); + } + this._scriptFile = this._uiSourceCode.scriptFile(); + if (this._scriptFile) { + this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.WillMergeToVM, this._willMergeToVM, this); + this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.DidMergeToVM, this._didMergeToVM, this); + this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.WillDivergeFromVM, this._willDivergeFromVM, this); + this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._didDivergeFromVM, this); + } + }, + onTextEditorContentLoaded: function() { if (typeof this._executionLineNumber === "number") @@ -578,7 +582,7 @@ WebInspector.JavaScriptSourceFrame.prototype = { return; var lineNumber = event.data.lineNumber; - var eventObject = /** @type {Event} */ event.data.event; + var eventObject = /** @type {Event} */ (event.data.event); if (eventObject.button != 0 || eventObject.altKey || eventObject.ctrlKey || eventObject.metaKey) return; @@ -626,7 +630,7 @@ WebInspector.JavaScriptSourceFrame.prototype = { */ _continueToLine: function(lineNumber) { - var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ this._uiSourceCode.uiLocationToRawLocation(lineNumber, 0); + var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (this._uiSourceCode.uiLocationToRawLocation(lineNumber, 0)); WebInspector.debuggerModel.continueToLocation(rawLocation); }, diff --git a/Source/WebCore/inspector/front-end/Linkifier.js b/Source/WebCore/inspector/front-end/Linkifier.js index c668a45c6..a4b5c9117 100644 --- a/Source/WebCore/inspector/front-end/Linkifier.js +++ b/Source/WebCore/inspector/front-end/Linkifier.js @@ -59,6 +59,7 @@ WebInspector.Linkifier.prototype = { * @param {number} lineNumber * @param {number=} columnNumber * @param {string=} classes + * @return {Element} */ linkifyLocation: function(sourceURL, lineNumber, columnNumber, classes) { @@ -71,6 +72,7 @@ WebInspector.Linkifier.prototype = { /** * @param {WebInspector.DebuggerModel.Location} rawLocation * @param {string=} classes + * @return {Element} */ linkifyRawLocation: function(rawLocation, classes) { @@ -83,6 +85,20 @@ WebInspector.Linkifier.prototype = { return anchor; }, + /** + * @param {WebInspector.CSSRule} rule + * @return {?Element} + */ + linkifyCSSRuleLocation: function(rule) + { + var anchor = WebInspector.linkifyURLAsNode("", "", "", false); + var liveLocation = WebInspector.cssModel.createLiveLocation(rule, this._updateAnchor.bind(this, anchor)); + if (!liveLocation) + return null; + this._liveLocations.push(liveLocation); + return anchor; + }, + reset: function() { for (var i = 0; i < this._liveLocations.length; ++i) @@ -125,7 +141,36 @@ WebInspector.Linkifier.DefaultFormatter.prototype = { if (this._maxLength) text = text.trimMiddle(this._maxLength); anchor.textContent = text; + + var titleText = uiLocation.uiSourceCode.url; + if (typeof uiLocation.lineNumber === "number") + titleText += ":" + (uiLocation.lineNumber + 1); + anchor.title = titleText; }, __proto__: WebInspector.LinkifierFormatter.prototype } + +/** + * @constructor + * @extends {WebInspector.Linkifier.DefaultFormatter} + */ +WebInspector.Linkifier.DefaultCSSFormatter = function() +{ + WebInspector.Linkifier.DefaultFormatter.call(this); +} + +WebInspector.Linkifier.DefaultCSSFormatter.prototype = { + /** + * @param {Element} anchor + * @param {WebInspector.UILocation} uiLocation + */ + formatLiveAnchor: function(anchor, uiLocation) + { + WebInspector.Linkifier.DefaultFormatter.prototype.formatLiveAnchor.call(this, anchor, uiLocation); + anchor.classList.add("webkit-html-resource-link"); + anchor.setAttribute("data-uncopyable", anchor.textContent); + anchor.textContent = ""; + }, + __proto__: WebInspector.Linkifier.DefaultFormatter.prototype +}
\ No newline at end of file diff --git a/Source/WebCore/inspector/front-end/MemoryStatistics.js b/Source/WebCore/inspector/front-end/MemoryStatistics.js index e75cd02b3..30a152cce 100644 --- a/Source/WebCore/inspector/front-end/MemoryStatistics.js +++ b/Source/WebCore/inspector/front-end/MemoryStatistics.js @@ -409,7 +409,7 @@ WebInspector.MemoryStatistics.prototype = { show: function() { - var anchor = /** @type {Element|null} */ this._containerAnchor.nextSibling; + var anchor = /** @type {Element|null} */ (this._containerAnchor.nextSibling); this._memorySplitView.show(this._timelinePanel.element, anchor); this._updateSize(); this._refreshDividers(); diff --git a/Source/WebCore/inspector/front-end/NativeMemorySnapshotView.js b/Source/WebCore/inspector/front-end/NativeMemorySnapshotView.js index 1c0cb67fd..8a5cf157e 100644 --- a/Source/WebCore/inspector/front-end/NativeMemorySnapshotView.js +++ b/Source/WebCore/inspector/front-end/NativeMemorySnapshotView.js @@ -31,35 +31,132 @@ /** * @constructor * @extends {WebInspector.View} + * @param {WebInspector.NativeMemoryProfileHeader} profile */ WebInspector.NativeMemorySnapshotView = function(profile) { WebInspector.View.call(this); this.registerRequiredCSS("nativeMemoryProfiler.css"); - this._profile = profile; - this.element.addStyleClass("memory-chart-view"); - var pieChart = new WebInspector.NativeMemoryPieChart(profile._memoryBlock); - pieChart.element.addStyleClass("fill"); - pieChart.show(this.element); + this.element.addStyleClass("native-snapshot-view"); + this.containmentDataGrid = new WebInspector.NativeSnapshotDataGrid(profile._memoryBlock); + this.containmentDataGrid.show(this.element); } WebInspector.NativeMemorySnapshotView.prototype = { - dispose: function() + __proto__: WebInspector.View.prototype +} + +/** + * @constructor + * @extends {WebInspector.DataGrid} + * @param {MemoryAgent.MemoryBlock} profile + */ +WebInspector.NativeSnapshotDataGrid = function(profile) +{ + var columns = { + object: { title: WebInspector.UIString("Object"), width: "200px", disclosure: true, sortable: false }, + size: { title: WebInspector.UIString("Size"), sortable: false }, + }; + WebInspector.DataGrid.call(this, columns); + this.setRootNode(new WebInspector.DataGridNode(null, true)); + var totalNode = new WebInspector.NativeSnapshotNode(profile, profile); + this.rootNode().appendChild(totalNode); + totalNode.expand(); +} + +WebInspector.NativeSnapshotDataGrid.prototype = { + __proto__: WebInspector.DataGrid.prototype +} + +/** + * @constructor + * @extends {WebInspector.DataGridNode} + * @param {MemoryAgent.MemoryBlock} nodeData + * @param {MemoryAgent.MemoryBlock} profile + */ +WebInspector.NativeSnapshotNode = function(nodeData, profile) +{ + this._nodeData = nodeData; + this._profile = profile; + var viewProperties = WebInspector.MemoryBlockViewProperties._forMemoryBlock(nodeData); + var data = { object: viewProperties._description, size: this._nodeData.size }; + var hasChildren = !!nodeData.children && nodeData.children.length !== 0; + WebInspector.DataGridNode.call(this, data, hasChildren); + this.addEventListener("populate", this._populate, this); +} + +WebInspector.NativeSnapshotNode.prototype = { + /** + * @override + * @param {string} columnIdentifier + * @return {Element} + */ + createCell: function(columnIdentifier) { + var cell = columnIdentifier === "size" ? + this._createSizeCell(columnIdentifier) : + WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier); + return cell; }, - get statusBarItems() + /** + * @param {string} columnIdentifier + * @return {Element} + */ + _createSizeCell: function(columnIdentifier) { - return []; + var node = this; + var viewProperties = null; + while (!viewProperties || viewProperties._fillStyle === "inherit") { + viewProperties = WebInspector.MemoryBlockViewProperties._forMemoryBlock(node._nodeData); + node = node.parent; + } + + var sizeKiB = this._nodeData.size / 1024; + var totalSize = this._profile.size; + var percentage = this._nodeData.size / totalSize * 100; + + var cell = document.createElement("td"); + cell.className = columnIdentifier + "-column"; + + var textDiv = document.createElement("div"); + textDiv.textContent = Number.withThousandsSeparator(sizeKiB.toFixed(0)) + "\u2009" + WebInspector.UIString("KiB"); + textDiv.className = "size-text"; + cell.appendChild(textDiv); + + var barDiv = document.createElement("div"); + barDiv.className = "size-bar"; + barDiv.style.width = percentage + "%"; + barDiv.style.backgroundColor = viewProperties._fillStyle; + // fillerDiv displaces percentage text out of the bar visible area if it doesn't fit. + var fillerDiv = document.createElement("div"); + fillerDiv.className = "percent-text" + barDiv.appendChild(fillerDiv); + var percentDiv = document.createElement("div"); + percentDiv.textContent = percentage.toFixed(1) + "%"; + percentDiv.className = "percent-text" + barDiv.appendChild(percentDiv); + + var barHolderDiv = document.createElement("div"); + barHolderDiv.appendChild(barDiv); + cell.appendChild(barHolderDiv); + + return cell; }, - get profile() - { - return this._profile; + _populate: function() { + function comparator(a, b) { + return b.size - a.size; + } + if (this._nodeData !== this._profile) + this._nodeData.children.sort(comparator); + for (var node in this._nodeData.children) { + this.appendChild(new WebInspector.NativeSnapshotNode(this._nodeData.children[node], this._profile)); + } }, - __proto__: WebInspector.View.prototype + __proto__: WebInspector.DataGridNode.prototype } /** @@ -215,21 +312,19 @@ WebInspector.MemoryBlockViewProperties._initialize = function() { WebInspector.MemoryBlockViewProperties._standardBlocks[name] = new WebInspector.MemoryBlockViewProperties(fillStyle, name, WebInspector.UIString(description)); } - addBlock("hsl( 0, 0%, 100%)", "ProcessPrivateMemory", "Total"); + addBlock("hsl( 0, 0%, 60%)", "ProcessPrivateMemory", "Total"); addBlock("hsl( 0, 0%, 80%)", "OwnersTypePlaceholder", "OwnersTypePlaceholder"); addBlock("hsl( 0, 0%, 80%)", "Other", "Other"); - addBlock("hsl(300, 30%, 80%)", "Page", "Page's structures"); - addBlock("hsl( 90, 60%, 80%)", "JSHeap", "JavaScript heap"); - addBlock("hsl( 90, 80%, 80%)", "JSHeap.Used", "Used JavaScript heap"); - addBlock("hsl( 90, 30%, 80%)", "JSExternalResources", "JavaScript external resources"); - addBlock("hsl( 90, 40%, 80%)", "JSExternalArrays", "JavaScript external arrays"); - addBlock("hsl( 90, 50%, 80%)", "JSExternalStrings", "JavaScript external strings"); - addBlock("hsl(210, 60%, 80%)", "WebInspector", "Inspector data"); - addBlock("hsl( 30, 60%, 80%)", "MemoryCache", "Memory cache resources"); - addBlock("hsl( 40, 60%, 80%)", "GlyphCache", "Glyph cache resources"); - addBlock("hsl( 35, 60%, 80%)", "DOMStorageCache", "DOM storage cache"); - addBlock("hsl( 60, 60%, 80%)", "RenderTree", "Render tree"); - addBlock("hsl( 60, 60%, 80%)", "RenderTree.Used", "Render tree used"); + addBlock("hsl(220, 80%, 70%)", "Page", "Page structures"); + addBlock("hsl(100, 60%, 50%)", "JSHeap", "JavaScript heap"); + addBlock("hsl( 90, 40%, 80%)", "JSExternalResources", "JavaScript external resources"); + addBlock("hsl( 90, 60%, 80%)", "JSExternalArrays", "JavaScript external arrays"); + addBlock("hsl( 90, 60%, 80%)", "JSExternalStrings", "JavaScript external strings"); + addBlock("hsl( 0, 80%, 60%)", "WebInspector", "Inspector data"); + addBlock("hsl( 36, 90%, 50%)", "MemoryCache", "Memory cache resources"); + addBlock("hsl( 40, 80%, 80%)", "GlyphCache", "Glyph cache resources"); + addBlock("hsl( 35, 80%, 80%)", "DOMStorageCache", "DOM storage cache"); + addBlock("hsl( 60, 80%, 60%)", "RenderTree", "Render tree"); } WebInspector.MemoryBlockViewProperties._forMemoryBlock = function(memoryBlock) @@ -238,7 +333,7 @@ WebInspector.MemoryBlockViewProperties._forMemoryBlock = function(memoryBlock) var result = WebInspector.MemoryBlockViewProperties._standardBlocks[memoryBlock.name]; if (result) return result; - return new WebInspector.MemoryBlockViewProperties("rgba(20, 200, 20, 0.8)", memoryBlock.name, memoryBlock.name); + return new WebInspector.MemoryBlockViewProperties("inherit", memoryBlock.name, memoryBlock.name); } @@ -285,7 +380,7 @@ WebInspector.NativeMemoryPieChart.prototype = { var swatchElement = this._memoryBlockList.createChild("div", "item"); swatchElement.createChild("div", "swatch").style.backgroundColor = viewProperties._fillStyle; - swatchElement.createChild("span", "title").textContent = WebInspector.UIString(title); + swatchElement.createChild("span", "title").textContent = title; if (!memoryBlock.children || !includeChildren) return; diff --git a/Source/WebCore/inspector/front-end/NavigatorView.js b/Source/WebCore/inspector/front-end/NavigatorView.js index 97f88e8e5..c9103bba2 100644 --- a/Source/WebCore/inspector/front-end/NavigatorView.js +++ b/Source/WebCore/inspector/front-end/NavigatorView.js @@ -84,25 +84,25 @@ WebInspector.NavigatorView.prototype = { _uiSourceCodeTitleChanged: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.target; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); this._updateScriptTitle(uiSourceCode) }, _uiSourceCodeWorkingCopyChanged: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.target; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); this._updateScriptTitle(uiSourceCode) }, _uiSourceCodeWorkingCopyCommitted: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.target; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); this._updateScriptTitle(uiSourceCode) }, _uiSourceCodeFormattedChanged: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.target; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); this._updateScriptTitle(uiSourceCode); }, @@ -329,9 +329,9 @@ WebInspector.NavigatorView.prototype = { handleContextMenu: function(event, uiSourceCode) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendApplicableItems(uiSourceCode); - contextMenu.show(event); + contextMenu.show(); }, __proto__: WebInspector.View.prototype @@ -613,9 +613,13 @@ WebInspector.NavigatorSourceTreeElement.prototype = { this._navigatorView._scriptSelected(this.uiSourceCode, false); }, - ondblclick: function() + /** + * @param {Event} event + */ + ondblclick: function(event) { - this._navigatorView._scriptSelected(this.uiSourceCode, true); + var middleClick = event.button === 1; + this._navigatorView._scriptSelected(this.uiSourceCode, !middleClick); }, onenter: function() diff --git a/Source/WebCore/inspector/front-end/NetworkLog.js b/Source/WebCore/inspector/front-end/NetworkLog.js index 84ff11021..fc45856b1 100644 --- a/Source/WebCore/inspector/front-end/NetworkLog.js +++ b/Source/WebCore/inspector/front-end/NetworkLog.js @@ -97,7 +97,7 @@ WebInspector.NetworkLog.prototype = { */ _onRequestStarted: function(event) { - var request = /** @type {WebInspector.NetworkRequest} */ event.data; + var request = /** @type {WebInspector.NetworkRequest} */ (event.data); this._requests.push(request); this._requestForId[request.requestId] = request; request.__page = this._currentPageLoad; diff --git a/Source/WebCore/inspector/front-end/NetworkManager.js b/Source/WebCore/inspector/front-end/NetworkManager.js index da29d8951..f0b9e5cc4 100644 --- a/Source/WebCore/inspector/front-end/NetworkManager.js +++ b/Source/WebCore/inspector/front-end/NetworkManager.js @@ -42,10 +42,6 @@ WebInspector.NetworkManager = function() NetworkAgent.enable(); WebInspector.settings.cacheDisabled.addChangeListener(this._cacheDisabledSettingChanged, this); - - if (WebInspector.settings.userAgent.get()) - this._userAgentSettingChanged(); - WebInspector.settings.userAgent.addChangeListener(this._userAgentSettingChanged, this); } WebInspector.NetworkManager.EventTypes = { @@ -62,6 +58,7 @@ WebInspector.NetworkManager._MIMETypes = { "application/xhtml+xml": {"document": true}, "text/css": {"stylesheet": true}, "text/xsl": {"stylesheet": true}, + "image/jpg": {"image": true}, "image/jpeg": {"image": true}, "image/pjpeg": {"image": true}, "image/png": {"image": true}, @@ -107,15 +104,10 @@ WebInspector.NetworkManager.prototype = { */ _cacheDisabledSettingChanged: function(event) { - var enabled = /** @type {boolean} */ event.data; + var enabled = /** @type {boolean} */ (event.data); NetworkAgent.setCacheDisabled(enabled); }, - _userAgentSettingChanged: function() - { - NetworkAgent.setUserAgentOverride(WebInspector.settings.userAgent.get()); - }, - __proto__: WebInspector.Object.prototype } diff --git a/Source/WebCore/inspector/front-end/NetworkPanel.js b/Source/WebCore/inspector/front-end/NetworkPanel.js index 52848639c..4aa583e61 100644 --- a/Source/WebCore/inspector/front-end/NetworkPanel.js +++ b/Source/WebCore/inspector/front-end/NetworkPanel.js @@ -795,7 +795,7 @@ WebInspector.NetworkLogView.prototype = { */ _onRequestUpdated: function(event) { - var request = /** @type {WebInspector.NetworkRequest} */ event.data; + var request = /** @type {WebInspector.NetworkRequest} */ (event.data); this._refreshRequest(request); }, @@ -820,7 +820,7 @@ WebInspector.NetworkLogView.prototype = { if (this._preserveLogToggle.toggled) return; - var frame = /** @type {WebInspector.ResourceTreeFrame} */ event.data; + var frame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data); var loaderId = frame.loaderId; // Preserve provisional load requests. @@ -934,7 +934,7 @@ WebInspector.NetworkLogView.prototype = { _contextMenu: function(event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); var gridNode = this._dataGrid.dataGridNodeFromNode(event.target); var request = gridNode && gridNode._request; @@ -968,7 +968,7 @@ WebInspector.NetworkLogView.prototype = { contextMenu.appendSeparator(); } - contextMenu.show(event); + contextMenu.show(); }, _replayXHR: function(requestId) @@ -1070,7 +1070,7 @@ WebInspector.NetworkLogView.prototype = { if (!this._searchRegExp) return -1; - if ((!request.displayName || !request.displayName.match(this._searchRegExp)) && !request.folder.match(this._searchRegExp)) + if (!request.name().match(this._searchRegExp) && !request.path().match(this._searchRegExp)) return -1; if (request.requestId in this._matchedRequestsMap) @@ -1136,8 +1136,8 @@ WebInspector.NetworkLogView.prototype = { var request = requests[i]; var node = this._requestGridNode(request); if (node) { - var nameMatched = request.displayName && request.displayName.match(regExp); - var pathMatched = request.parsedURL.path && request.folder.match(regExp); + var nameMatched = request.name().match(regExp); + var pathMatched = request.path().match(regExp); if (!nameMatched && pathMatched && !this._largerRequestsButton.toggled) this._toggleLargerRequests(); var highlightedSubstringChanges = node._highlightMatchedSubstring(regExp); @@ -1202,8 +1202,8 @@ WebInspector.NetworkLogView.prototype = { for (var i = 0; i < this._dataGrid.rootNode().children.length; ++i) { var node = this._dataGrid.rootNode().children[i]; node.element.removeStyleClass("filtered-out"); - var nameMatched = node._request.displayName && node._request.displayName.match(filterRegExp); - var pathMatched = node._request.parsedURL.path && node._request.folder.match(filterRegExp); + var nameMatched = node._request.name().match(filterRegExp); + var pathMatched = node._request.path().match(filterRegExp); if (!nameMatched && !pathMatched) { node.element.addStyleClass("filtered-out"); this._filteredOutRequests.put(this._requests[i], true); @@ -1508,7 +1508,7 @@ WebInspector.NetworkPanel.prototype = { * @param {WebInspector.ContextMenu} contextMenu * @param {Object} target */ - appendApplicableItems: function(contextMenu, target) + appendApplicableItems: function(event, contextMenu, target) { if (!(target instanceof WebInspector.NetworkRequest)) return; @@ -1518,7 +1518,7 @@ WebInspector.NetworkPanel.prototype = { function reveal() { WebInspector.inspectorView.setCurrentPanel(this); - this.revealAndHighlightRequest(/** @type {WebInspector.NetworkRequest} */ target); + this.revealAndHighlightRequest(/** @type {WebInspector.NetworkRequest} */ (target)); } contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in network panel" : "Reveal in Network Panel"), reveal.bind(this)); }, @@ -1929,25 +1929,11 @@ WebInspector.NetworkDataGridNode.prototype = { iconElement.className = "icon"; } this._nameCell.appendChild(iconElement); - this._nameCell.appendChild(document.createTextNode(this._fileName())); - - var subtitle = this._request.parsedURL.host === WebInspector.inspectedPageDomain ? "" : this._request.parsedURL.host; - - if (this._request.parsedURL.path) - subtitle += this._request.folder; - - this._appendSubtitle(this._nameCell, subtitle); + this._nameCell.appendChild(document.createTextNode(this._request.name())); + this._appendSubtitle(this._nameCell, this._request.path()); this._nameCell.title = this._request.url; }, - _fileName: function() - { - var fileName = this._request.displayName; - if (this._request.queryString()) - fileName += "?" + this._request.queryString(); - return fileName; - }, - _refreshStatusCell: function() { this._statusCell.removeChildren(); @@ -2173,8 +2159,8 @@ WebInspector.NetworkDataGridNode.prototype = { WebInspector.NetworkDataGridNode.NameComparator = function(a, b) { - var aFileName = a._request.displayName + (a._request.queryString() ? a._request.queryString() : ""); - var bFileName = b._request.displayName + (b._request.queryString() ? b._request.queryString() : ""); + var aFileName = a._request.name(); + var bFileName = b._request.name(); if (aFileName > bFileName) return 1; if (bFileName > aFileName) diff --git a/Source/WebCore/inspector/front-end/NetworkPanelDescriptor.js b/Source/WebCore/inspector/front-end/NetworkPanelDescriptor.js index 1621d77d0..a56758305 100644 --- a/Source/WebCore/inspector/front-end/NetworkPanelDescriptor.js +++ b/Source/WebCore/inspector/front-end/NetworkPanelDescriptor.js @@ -43,11 +43,11 @@ WebInspector.NetworkPanelDescriptor.prototype = { * @param {WebInspector.ContextMenu} contextMenu * @param {Object} target */ - appendApplicableItems: function(contextMenu, target) + appendApplicableItems: function(event, contextMenu, target) { if (!(target instanceof WebInspector.NetworkRequest)) return; - this.panel().appendApplicableItems(contextMenu, target); + this.panel().appendApplicableItems(event, contextMenu, target); }, __proto__: WebInspector.PanelDescriptor.prototype diff --git a/Source/WebCore/inspector/front-end/NetworkRequest.js b/Source/WebCore/inspector/front-end/NetworkRequest.js index 961c55e23..120660c16 100644 --- a/Source/WebCore/inspector/front-end/NetworkRequest.js +++ b/Source/WebCore/inspector/front-end/NetworkRequest.js @@ -97,6 +97,8 @@ WebInspector.NetworkRequest.prototype = { this._url = x; this._parsedURL = new WebInspector.ParsedURL(x); delete this._parsedQueryParameters; + delete this._name; + delete this._path; }, /** @@ -356,6 +358,45 @@ WebInspector.NetworkRequest.prototype = { return this._parsedURL.displayName; }, + name: function() + { + if (this._name) + return this._name; + this._parseNameAndPathFromURL(); + return this._name; + }, + + path: function() + { + if (this._path) + return this._path; + this._parseNameAndPathFromURL(); + return this._path; + }, + + _parseNameAndPathFromURL: function() + { + if (this._parsedURL.isDataURL()) { + this._name = this._parsedURL.dataURLDisplayName(); + this._path = ""; + } else if (this._parsedURL.isAboutBlank()) { + this._name = this._parsedURL.url; + this._path = ""; + } else { + this._path = this._parsedURL.host + this._parsedURL.folderPathComponents; + this._path = this._path.trimURL(WebInspector.inspectedPageDomain ? WebInspector.inspectedPageDomain : ""); + if (this._parsedURL.lastPathComponent || this._parsedURL.queryParams) + this._name = this._parsedURL.lastPathComponent + (this._parsedURL.queryParams ? "?" + this._parsedURL.queryParams : ""); + else if (this._parsedURL.folderPathComponents) { + this._name = this._parsedURL.folderPathComponents.substring(this._parsedURL.folderPathComponents.lastIndexOf("/") + 1) + "/"; + this._path = this._path.substring(0, this._path.lastIndexOf("/")); + } else { + this._name = this._parsedURL.host; + this._path = ""; + } + } + }, + /** * @return {string} */ diff --git a/Source/WebCore/inspector/front-end/NetworkUISourceCodeProvider.js b/Source/WebCore/inspector/front-end/NetworkUISourceCodeProvider.js index ec18dcf32..10fa86c96 100644 --- a/Source/WebCore/inspector/front-end/NetworkUISourceCodeProvider.js +++ b/Source/WebCore/inspector/front-end/NetworkUISourceCodeProvider.js @@ -31,10 +31,12 @@ /** * @constructor * @param {WebInspector.Workspace} workspace + * @param {WebInspector.NetworkWorkspaceProvider} networkWorkspaceProvider */ -WebInspector.NetworkUISourceCodeProvider = function(workspace) +WebInspector.NetworkUISourceCodeProvider = function(workspace, networkWorkspaceProvider) { this._workspace = workspace; + this._networkWorkspaceProvider = networkWorkspaceProvider; WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, this._resourceAdded, this); this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._projectWillReset, this); this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectDidReset, this._projectDidReset, this); @@ -65,7 +67,7 @@ WebInspector.NetworkUISourceCodeProvider.prototype = { */ _parsedScriptSource: function(event) { - var script = /** @type {WebInspector.Script} */ event.data; + var script = /** @type {WebInspector.Script} */ (event.data); if (!script.sourceURL || script.isInlineScript()) return; var isDynamicAnonymousScript; @@ -83,7 +85,7 @@ WebInspector.NetworkUISourceCodeProvider.prototype = { if (!parsedURL.host) return; } - this._addUISourceCode(script.sourceURL, script); + this._addFile(script.sourceURL, script, script.isContentScript); }, /** @@ -91,14 +93,16 @@ WebInspector.NetworkUISourceCodeProvider.prototype = { */ _resourceAdded: function(event) { - var resource = /** @type {WebInspector.Resource} */ event.data; - this._addUISourceCode(resource.url, resource); + var resource = /** @type {WebInspector.Resource} */ (event.data); + this._addFile(resource.url, resource); }, /** + * @param {string} url * @param {WebInspector.ContentProvider} contentProvider + * @param {boolean=} isContentScript */ - _addUISourceCode: function(url, contentProvider) + _addFile: function(url, contentProvider, isContentScript) { var type = contentProvider.contentType(); if (type !== WebInspector.resourceTypes.Stylesheet && type !== WebInspector.resourceTypes.Document && type !== WebInspector.resourceTypes.Script) @@ -107,8 +111,7 @@ WebInspector.NetworkUISourceCodeProvider.prototype = { return; this._processedURLs[url] = true; var isEditable = type !== WebInspector.resourceTypes.Document; - var uiSourceCode = new WebInspector.UISourceCode(url, contentProvider, isEditable); - this._workspace.project().addUISourceCode(uiSourceCode); + this._networkWorkspaceProvider.addFile(url, contentProvider, isEditable, isContentScript); }, _projectWillReset: function() diff --git a/Source/WebCore/inspector/front-end/NetworkWorkspaceProvider.js b/Source/WebCore/inspector/front-end/NetworkWorkspaceProvider.js new file mode 100644 index 000000000..66812adff --- /dev/null +++ b/Source/WebCore/inspector/front-end/NetworkWorkspaceProvider.js @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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. + */ + +/** + * @constructor + * @implements {WebInspector.WorkspaceProvider} + * @extends {WebInspector.Object} + */ +WebInspector.NetworkWorkspaceProvider = function() +{ + /** @type {Object.<string, WebInspector.ContentProvider>} */ + this._contentProviders = {}; +} + +WebInspector.NetworkWorkspaceProvider.prototype = { + /** + * @param {string} path + * @param {function(?string,boolean,string)} callback + */ + requestFileContent: function(path, callback) + { + var contentProvider = this._contentProviders[path]; + contentProvider.requestContent(callback); + }, + + /** + * @param {string} path + * @param {string} newContent + * @param {function(?string)} callback + */ + setFileContent: function(path, newContent, callback) + { + callback(null); + }, + + /** + * @param {string} query + * @param {boolean} caseSensitive + * @param {boolean} isRegex + * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback + */ + searchInFileContent: function(path, query, caseSensitive, isRegex, callback) + { + var contentProvider = this._contentProviders[path]; + contentProvider.searchInContent(query, caseSensitive, isRegex, callback); + }, + + /** + * @param {string} path + * @param {WebInspector.ContentProvider} contentProvider + * @param {boolean} isEditable + * @param {boolean=} isContentScript + * @param {boolean=} isSnippet + */ + addFile: function(path, contentProvider, isEditable, isContentScript, isSnippet) + { + var fileDescriptor = new WebInspector.FileDescriptor(path, contentProvider.contentType(), isEditable, isContentScript, isSnippet); + this._contentProviders[path] = contentProvider; + this.dispatchEventToListeners(WebInspector.WorkspaceProvider.Events.FileAdded, fileDescriptor); + }, + + /** + * @param {string} path + */ + removeFile: function(path) + { + delete this._contentProviders[path]; + this.dispatchEventToListeners(WebInspector.WorkspaceProvider.Events.FileRemoved, path); + }, + + reset: function() + { + this._contentProviders = {}; + }, + + __proto__: WebInspector.Object.prototype +} + +/** + * @type {?WebInspector.NetworkWorkspaceProvider} + */ +WebInspector.networkWorkspaceProvider = null; diff --git a/Source/WebCore/inspector/front-end/ObjectPopoverHelper.js b/Source/WebCore/inspector/front-end/ObjectPopoverHelper.js index 380f4db8f..b0bd431a6 100644 --- a/Source/WebCore/inspector/front-end/ObjectPopoverHelper.js +++ b/Source/WebCore/inspector/front-end/ObjectPopoverHelper.js @@ -90,7 +90,7 @@ WebInspector.ObjectPopoverHelper.prototype = { functionName.textContent = response.name || response.inferredName || response.displayName || WebInspector.UIString("(anonymous function)"); this._linkifier = new WebInspector.Linkifier(); - var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ response.location; + var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (response.location); var link = this._linkifier.linkifyRawLocation(rawLocation, "function-location-link"); if (link) title.appendChild(link); diff --git a/Source/WebCore/inspector/front-end/ObjectPropertiesSection.js b/Source/WebCore/inspector/front-end/ObjectPropertiesSection.js index b64a00be9..12106c4fb 100644 --- a/Source/WebCore/inspector/front-end/ObjectPropertiesSection.js +++ b/Source/WebCore/inspector/front-end/ObjectPropertiesSection.js @@ -58,9 +58,9 @@ WebInspector.ObjectPropertiesSection.prototype = { _contextMenuEventFired: function(event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendApplicableItems(this.object); - contextMenu.show(event); + contextMenu.show(); }, onpopulate: function() @@ -253,10 +253,10 @@ WebInspector.ObjectPropertyTreeElement.prototype = { _contextMenuFired: function(value, event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); this.populateContextMenu(contextMenu); contextMenu.appendApplicableItems(value); - contextMenu.show(event); + contextMenu.show(); }, /** @@ -319,6 +319,14 @@ WebInspector.ObjectPropertyTreeElement.prototype = { proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this, context), false); }, + /** + * @return {boolean} + */ + isEditing: function() + { + return !!this._prompt; + }, + editingEnded: function(context) { this._prompt.detach(); @@ -595,8 +603,7 @@ WebInspector.ArrayGroupingTreeElement._populateRanges = function(treeElement, ob { var count = 0; for (var i = fromIndex; i <= toIndex; ++i) { - var value = this[i]; - if (typeof value !== "undefined") + if (i in this) ++count; } @@ -604,15 +611,14 @@ WebInspector.ArrayGroupingTreeElement._populateRanges = function(treeElement, ob if (count <= bucketThreshold) bucketSize = count; else - bucketSize = Math.pow(bucketThreshold, Math.floor(Math.log(count) / Math.log(bucketThreshold))); + bucketSize = Math.pow(bucketThreshold, Math.ceil(Math.log(count) / Math.log(bucketThreshold)) - 1); var ranges = []; count = 0; var groupStart = -1; var groupEnd = 0; for (var i = fromIndex; i <= toIndex; ++i) { - var value = this[i]; - if (typeof value === "undefined") + if (!(i in this)) continue; if (groupStart === -1) @@ -670,9 +676,8 @@ WebInspector.ArrayGroupingTreeElement._populateAsFragment = function(treeElement { var result = Object.create(null); for (var i = fromIndex; i <= toIndex; ++i) { - var value = this[i]; - if (typeof value !== "undefined") - result[i] = value; + if (i in this) + result[i] = this[i]; } return result; } diff --git a/Source/WebCore/inspector/front-end/ParsedURL.js b/Source/WebCore/inspector/front-end/ParsedURL.js index 49d1fde76..374f8857d 100644 --- a/Source/WebCore/inspector/front-end/ParsedURL.js +++ b/Source/WebCore/inspector/front-end/ParsedURL.js @@ -44,12 +44,12 @@ WebInspector.ParsedURL = function(url) this.lastPathComponent = ""; // RegExp groups: - // 1 - scheme + // 1 - scheme (using the RFC3986 grammar) // 2 - hostname // 3 - ?port // 4 - ?path // 5 - ?fragment - var match = url.match(/^([^:]+):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i); + var match = url.match(/^([A-Za-z][A-Za-z0-9+.-]*):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i); if (match) { this.isValid = true; this.scheme = match[1].toLowerCase(); @@ -69,23 +69,21 @@ WebInspector.ParsedURL = function(url) this.path = this.url; } - if (this.path) { - // First cut the query params. - var path = this.path; - var indexOfQuery = path.indexOf("?"); - if (indexOfQuery !== -1) { - this.queryParams = path.substring(indexOfQuery + 1) - path = path.substring(0, indexOfQuery); - } - - // Then take last path component. - var lastSlashIndex = path.lastIndexOf("/"); - if (lastSlashIndex !== -1) { - this.folderPathComponents = path.substring(0, lastSlashIndex); - this.lastPathComponent = path.substring(lastSlashIndex + 1); - } else - this.lastPathComponent = path; + // First cut the query params. + var path = this.path; + var indexOfQuery = path.indexOf("?"); + if (indexOfQuery !== -1) { + this.queryParams = path.substring(indexOfQuery + 1) + path = path.substring(0, indexOfQuery); } + + // Then take last path component. + var lastSlashIndex = path.lastIndexOf("/"); + if (lastSlashIndex !== -1) { + this.folderPathComponents = path.substring(0, lastSlashIndex); + this.lastPathComponent = path.substring(lastSlashIndex + 1); + } else + this.lastPathComponent = path; } /** @@ -146,12 +144,9 @@ WebInspector.ParsedURL.prototype = { if (this._displayName) return this._displayName; - if (this.scheme === "data") { - this._displayName = this.url.trimEnd(20); - return this._displayName; - } - - if (this.url === "about:blank") + if (this.isDataURL()) + return this.dataURLDisplayName(); + if (this.isAboutBlank()) return this.url; this._displayName = this.lastPathComponent; @@ -162,6 +157,26 @@ WebInspector.ParsedURL.prototype = { if (this._displayName === "/") this._displayName = this.url; return this._displayName; + }, + + dataURLDisplayName: function() + { + if (this._dataURLDisplayName) + return this._dataURLDisplayName; + if (!this.isDataURL()) + return ""; + this._dataURLDisplayName = this.url.trimEnd(20); + return this._dataURLDisplayName; + }, + + isAboutBlank: function() + { + return this.url === "about:blank"; + }, + + isDataURL: function() + { + return this.scheme === "data"; } } diff --git a/Source/WebCore/inspector/front-end/Popover.js b/Source/WebCore/inspector/front-end/Popover.js index ce1e10a53..31aca9dd8 100644 --- a/Source/WebCore/inspector/front-end/Popover.js +++ b/Source/WebCore/inspector/front-end/Popover.js @@ -30,11 +30,13 @@ /** * @constructor + * @extends {WebInspector.View} * @param {WebInspector.PopoverHelper=} popoverHelper */ WebInspector.Popover = function(popoverHelper) { - this.element = document.createElement("div"); + WebInspector.View.call(this); + this.markAsRoot(); this.element.className = "popover custom-popup-vertical-scroll custom-popup-horizontal-scroll"; this._popupArrowElement = document.createElement("div"); @@ -43,37 +45,66 @@ WebInspector.Popover = function(popoverHelper) this._contentDiv = document.createElement("div"); this._contentDiv.className = "content"; - this._visible = false; + this.element.appendChild(this._contentDiv); + this._popoverHelper = popoverHelper; } WebInspector.Popover.prototype = { /** + * @param {Element} element + * @param {Element} anchor + * @param {number=} preferredWidth + * @param {number=} preferredHeight + */ + show: function(element, anchor, preferredWidth, preferredHeight) + { + this._innerShow(null, element, anchor, preferredWidth, preferredHeight); + }, + + /** + * @param {WebInspector.View} view + * @param {Element} anchor + * @param {number=} preferredWidth + * @param {number=} preferredHeight + */ + showView: function(view, anchor, preferredWidth, preferredHeight) + { + this._innerShow(view, view.element, anchor, preferredWidth, preferredHeight); + }, + + /** + * @param {WebInspector.View?} view + * @param {Element} contentElement + * @param {Element} anchor * @param {number=} preferredWidth * @param {number=} preferredHeight */ - show: function(contentElement, anchor, preferredWidth, preferredHeight) + _innerShow: function(view, contentElement, anchor, preferredWidth, preferredHeight) { if (this._disposed) return; this.contentElement = contentElement; // This should not happen, but we hide previous popup to be on the safe side. - if (WebInspector.Popover._popoverElement) - document.body.removeChild(WebInspector.Popover._popoverElement); - WebInspector.Popover._popoverElement = this.element; + if (WebInspector.Popover._popover) + WebInspector.Popover._popover.detach(); + WebInspector.Popover._popover = this; // Temporarily attach in order to measure preferred dimensions. - this.contentElement.positionAt(0, 0); - document.body.appendChild(this.contentElement); - preferredWidth = preferredWidth || this.contentElement.offsetWidth; - preferredHeight = preferredHeight || this.contentElement.offsetHeight; - - this._contentDiv.appendChild(this.contentElement); - this.element.appendChild(this._contentDiv); - document.body.appendChild(this.element); + var preferredSize = view ? view.measurePreferredSize() : this.contentElement.measurePreferredSize(); + preferredWidth = preferredWidth || preferredSize.width; + preferredHeight = preferredHeight || preferredSize.height; + + WebInspector.View.prototype.show.call(this, document.body); + + if (view) + view.show(this._contentDiv); + else + this._contentDiv.appendChild(this.contentElement); + this._positionElement(anchor, preferredWidth, preferredHeight); - this._visible = true; + if (this._popoverHelper) { contentElement.addEventListener("mousemove", this._popoverHelper._killHidePopoverTimer.bind(this._popoverHelper), true); this.element.addEventListener("mouseout", this._popoverHelper._mouseOut.bind(this._popoverHelper), true); @@ -82,16 +113,8 @@ WebInspector.Popover.prototype = { hide: function() { - if (WebInspector.Popover._popoverElement) { - delete WebInspector.Popover._popoverElement; - document.body.removeChild(this.element); - } - this._visible = false; - }, - - get visible() - { - return this._visible; + this.detach(); + delete WebInspector.Popover._popover; }, get disposed() @@ -101,7 +124,7 @@ WebInspector.Popover.prototype = { dispose: function() { - if (this.visible) + if (this.isShowing()) this.hide(); this._disposed = true; }, @@ -187,7 +210,9 @@ WebInspector.Popover.prototype = { this.element.positionAt(newElementPosition.x - borderWidth, newElementPosition.y - borderWidth); this.element.style.width = newElementPosition.width + borderWidth * 2 + "px"; this.element.style.height = newElementPosition.height + borderWidth * 2 + "px"; - } + }, + + __proto__: WebInspector.View.prototype } /** diff --git a/Source/WebCore/inspector/front-end/PresentationConsoleMessageHelper.js b/Source/WebCore/inspector/front-end/PresentationConsoleMessageHelper.js index a0cc97848..25533ed62 100644 --- a/Source/WebCore/inspector/front-end/PresentationConsoleMessageHelper.js +++ b/Source/WebCore/inspector/front-end/PresentationConsoleMessageHelper.js @@ -42,6 +42,7 @@ WebInspector.PresentationConsoleMessageHelper = function(uiSourceCodeProvider) this._uiSourceCodeProvider = uiSourceCodeProvider; WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this); + WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.RepeatCountUpdated, this._consoleMessageAdded, this); WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this); WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this); @@ -55,7 +56,7 @@ WebInspector.PresentationConsoleMessageHelper.prototype = { */ _consoleMessageAdded: function(event) { - var message = /** @type {WebInspector.ConsoleMessage} */ event.data; + var message = /** @type {WebInspector.ConsoleMessage} */ (event.data); if (!message.url || !message.isErrorOrWarning()) return; @@ -92,7 +93,7 @@ WebInspector.PresentationConsoleMessageHelper.prototype = { */ _parsedScriptSource: function(event) { - var script = /** @type {WebInspector.Script} */ event.data; + var script = /** @type {WebInspector.Script} */ (event.data); var messages = this._pendingConsoleMessages[script.sourceURL]; if (!messages) @@ -101,7 +102,7 @@ WebInspector.PresentationConsoleMessageHelper.prototype = { var pendingMessages = []; for (var i = 0; i < messages.length; i++) { var message = messages[i]; - var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ message.location(); + var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (message.location()); if (script.scriptId === rawLocation.scriptId) this._addConsoleMessageToScript(message, rawLocation); else diff --git a/Source/WebCore/inspector/front-end/ProfilesPanel.js b/Source/WebCore/inspector/front-end/ProfilesPanel.js index 1e594e2f0..3e9088582 100644 --- a/Source/WebCore/inspector/front-end/ProfilesPanel.js +++ b/Source/WebCore/inspector/front-end/ProfilesPanel.js @@ -429,11 +429,11 @@ WebInspector.ProfilesPanel.prototype = { return; } if (element !== this.element || event.srcElement === this.sidebarElement) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); if (this.visibleView instanceof WebInspector.HeapSnapshotView) this.visibleView.populateContextMenu(contextMenu, event); contextMenu.appendItem(WebInspector.UIString("Load Heap Snapshot\u2026"), this._fileSelectorElement.click.bind(this._fileSelectorElement)); - contextMenu.show(event); + contextMenu.show(); } }, @@ -1086,12 +1086,12 @@ WebInspector.ProfilesPanel.prototype = { * @param {WebInspector.ContextMenu} contextMenu * @param {Object} target */ - appendApplicableItems: function(contextMenu, target) + appendApplicableItems: function(event, contextMenu, target) { if (WebInspector.inspectorView.currentPanel() !== this) return; - var object = /** @type {WebInspector.RemoteObject} */ target; + var object = /** @type {WebInspector.RemoteObject} */ (target); var objectId = object.objectId; if (!objectId) return; @@ -1248,7 +1248,7 @@ WebInspector.ProfileSidebarTreeElement.prototype = { handleContextMenuEvent: function(event) { var profile = this.profile; - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); var profilesPanel = WebInspector.ProfilesPanel._instance; // FIXME: use context menu provider if (profile.canSaveToFile()) { @@ -1259,7 +1259,7 @@ WebInspector.ProfileSidebarTreeElement.prototype = { contextMenu.appendItem(WebInspector.UIString("Load Heap Snapshot\u2026"), profilesPanel._fileSelectorElement.click.bind(profilesPanel._fileSelectorElement)); contextMenu.appendItem(WebInspector.UIString("Delete profile"), this.ondelete.bind(this)); } - contextMenu.show(event); + contextMenu.show(); }, __proto__: WebInspector.SidebarTreeElement.prototype diff --git a/Source/WebCore/inspector/front-end/RemoteObject.js b/Source/WebCore/inspector/front-end/RemoteObject.js index 324f4edb5..bd59d179b 100644 --- a/Source/WebCore/inspector/front-end/RemoteObject.js +++ b/Source/WebCore/inspector/front-end/RemoteObject.js @@ -248,7 +248,7 @@ WebInspector.RemoteObject.prototype = { setPropertyValueFunction = "function(a) { this[a] = " + result.description + "; }"; delete result.description; // Optimize on traffic. - RuntimeAgent.callFunctionOn(this._objectId, setPropertyValueFunction, [{ value:name }, result], true, undefined, propertySetCallback.bind(this)); + RuntimeAgent.callFunctionOn(this._objectId, setPropertyValueFunction, [{ value:name }, result], true, undefined, undefined, propertySetCallback.bind(this)); if (result._objectId) RuntimeAgent.releaseObject(result._objectId); } @@ -299,7 +299,7 @@ WebInspector.RemoteObject.prototype = { callback((error || wasThrown) ? null : WebInspector.RemoteObject.fromPayload(result)); } - RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, undefined, mycallback); + RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, undefined, undefined, mycallback); }, /** @@ -319,7 +319,7 @@ WebInspector.RemoteObject.prototype = { callback((error || wasThrown) ? null : result.value); } - RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, true, mycallback); + RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, true, false, mycallback); }, release: function() diff --git a/Source/WebCore/inspector/front-end/ResourceScriptMapping.js b/Source/WebCore/inspector/front-end/ResourceScriptMapping.js index bd6b2fa3e..3bf5e301c 100644 --- a/Source/WebCore/inspector/front-end/ResourceScriptMapping.js +++ b/Source/WebCore/inspector/front-end/ResourceScriptMapping.js @@ -56,7 +56,7 @@ WebInspector.ResourceScriptMapping.prototype = { */ rawLocationToUILocation: function(rawLocation) { - var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ rawLocation; + var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (rawLocation); var script = WebInspector.debuggerModel.scriptForId(debuggerModelLocation.scriptId); var uiSourceCode = this._workspaceUISourceCodeForScript(script) || this._getOrCreateTemporaryUISourceCode(script); if (uiSourceCode.scriptFile() && uiSourceCode.scriptFile().hasDivergedFromVM()) @@ -148,7 +148,7 @@ WebInspector.ResourceScriptMapping.prototype = { } if (!originalUISourceCode) return; - this._workspace.project().removeTemporaryUISourceCode(originalUISourceCode); + this._workspace.removeTemporaryUISourceCode(originalUISourceCode); this._scriptIdsForOriginalUISourceCode.remove(originalUISourceCode); }, @@ -165,7 +165,7 @@ WebInspector.ResourceScriptMapping.prototype = { } if (!temporaryUISourceCode) return false; - this._workspace.project().removeTemporaryUISourceCode(temporaryUISourceCode); + this._workspace.removeTemporaryUISourceCode(temporaryUISourceCode); this._scriptIdsForTemporaryUISourceCode.remove(temporaryUISourceCode); return true; }, @@ -177,14 +177,13 @@ WebInspector.ResourceScriptMapping.prototype = { _bindUISourceCodeToScripts: function(uiSourceCode, scripts) { console.assert(scripts.length); - for (var i = 0; i < scripts.length; ++i) - scripts[i].setSourceMapping(this); - uiSourceCode.isContentScript = scripts[0].isContentScript; - uiSourceCode.setSourceMapping(this); if (uiSourceCode.isEditable()) { var scriptFile = new WebInspector.ResourceScriptFile(this, uiSourceCode); uiSourceCode.setScriptFile(scriptFile); } + for (var i = 0; i < scripts.length; ++i) + scripts[i].setSourceMapping(this); + uiSourceCode.setSourceMapping(this); }, /** @@ -225,7 +224,7 @@ WebInspector.ResourceScriptMapping.prototype = { var contentProvider = script.isInlineScript() ? new WebInspector.ConcatenatedScriptsContentProvider(scripts) : script; var isDynamicScript = this._isDynamicScript(script); var url = isDynamicScript ? "" : script.sourceURL; - var temporaryUISourceCode = new WebInspector.UISourceCode(url, contentProvider, !script.isInlineScript() && !divergedVersion); + var temporaryUISourceCode = this._workspace.addTemporaryUISourceCode(url, contentProvider, !script.isInlineScript() && !divergedVersion, script.isContentScript); temporaryUISourceCode.divergedVersion = divergedVersion; return temporaryUISourceCode; }, @@ -248,7 +247,6 @@ WebInspector.ResourceScriptMapping.prototype = { scriptIds.push(scripts[i].scriptId); } this._scriptIdsForTemporaryUISourceCode.put(temporaryUISourceCode, scriptIds); - this._workspace.project().addTemporaryUISourceCode(temporaryUISourceCode); this._bindUISourceCodeToScripts(temporaryUISourceCode, scripts); return temporaryUISourceCode; }, @@ -272,14 +270,13 @@ WebInspector.ResourceScriptMapping.prototype = { scriptIds.push(scripts[i].scriptId); } this._scriptIdsForOriginalUISourceCode.put(originalUISourceCode, scriptIds); - this._workspace.project().addTemporaryUISourceCode(originalUISourceCode); this._bindUISourceCodeToScripts(originalUISourceCode, scripts); return originalUISourceCode; }, _uiSourceCodeAddedToWorkspace: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data); console.assert(!!uiSourceCode.url); var scripts = this._scriptsForUISourceCode(uiSourceCode); @@ -401,7 +398,7 @@ WebInspector.ResourceScriptFile.prototype = { this.dispatchEventToListeners(WebInspector.ScriptFile.Events.DidMergeToVM, this); } - var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ this._uiSourceCode.uiLocationToRawLocation(0, 0); + var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (this._uiSourceCode.uiLocationToRawLocation(0, 0)); if (!rawLocation) return; var script = WebInspector.debuggerModel.scriptForId(rawLocation.scriptId); @@ -410,7 +407,7 @@ WebInspector.ResourceScriptFile.prototype = { _workingCopyChanged: function(event) { - var wasDirty = /** @type {boolean} */ event.data.wasDirty; + var wasDirty = /** @type {boolean} */ (event.data.wasDirty); if (!wasDirty && this._uiSourceCode.isDirty() && !this._hasDivergedFromVM) { this._isDivergingFromVM = true; this.dispatchEventToListeners(WebInspector.ScriptFile.Events.WillDivergeFromVM, this._uiSourceCode); diff --git a/Source/WebCore/inspector/front-end/ResourceTreeModel.js b/Source/WebCore/inspector/front-end/ResourceTreeModel.js index f5527915c..e9e12b9db 100644 --- a/Source/WebCore/inspector/front-end/ResourceTreeModel.js +++ b/Source/WebCore/inspector/front-end/ResourceTreeModel.js @@ -176,7 +176,7 @@ WebInspector.ResourceTreeModel.prototype = { if (!this._cachedResourcesProcessed) return; - var request = /** @type {WebInspector.NetworkRequest} */ event.data; + var request = /** @type {WebInspector.NetworkRequest} */ (event.data); if (request.failed || request.type === WebInspector.resourceTypes.XHR) return; @@ -233,7 +233,7 @@ WebInspector.ResourceTreeModel.prototype = { */ _consoleMessageAdded: function(event) { - var msg = /** @type {WebInspector.ConsoleMessage} */ event.data; + var msg = /** @type {WebInspector.ConsoleMessage} */ (event.data); var resource = msg.url ? this.resourceForURL(msg.url) : null; if (resource) this._addConsoleMessageToResource(msg, resource); diff --git a/Source/WebCore/inspector/front-end/ResourceType.js b/Source/WebCore/inspector/front-end/ResourceType.js index 2f88a6bbb..a3f3110d3 100644 --- a/Source/WebCore/inspector/front-end/ResourceType.js +++ b/Source/WebCore/inspector/front-end/ResourceType.js @@ -108,7 +108,10 @@ WebInspector.ResourceType.prototype = { } } -//Keep these in sync with WebCore::InspectorPageAgent::resourceTypeJson +/** + * Keep these in sync with WebCore::InspectorPageAgent::resourceTypeJson + * @enum {!WebInspector.ResourceType} + */ WebInspector.resourceTypes = { Document: new WebInspector.ResourceType("document", "Document", "Documents", "rgb(47,102,236)", true), Stylesheet: new WebInspector.ResourceType("stylesheet", "Stylesheet", "Stylesheets", "rgb(157,231,119)", true), diff --git a/Source/WebCore/inspector/front-end/ResourceUtils.js b/Source/WebCore/inspector/front-end/ResourceUtils.js index 207bf0b31..01b47f5d1 100644 --- a/Source/WebCore/inspector/front-end/ResourceUtils.js +++ b/Source/WebCore/inspector/front-end/ResourceUtils.js @@ -58,6 +58,10 @@ WebInspector.displayNameForURL = function(url) if (resource) return resource.displayName; + var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(url); + if (uiSourceCode) + return uiSourceCode.parsedURL.displayName; + if (!WebInspector.inspectedPageURL) return url.trimURL(""); @@ -81,7 +85,7 @@ WebInspector.displayNameForURL = function(url) WebInspector.linkifyStringAsFragmentWithCustomLinkifier = function(string, linkifier) { var container = document.createDocumentFragment(); - var linkStringRegEx = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/|www\.)[\w$\-_+*'=\|\/\\(){}[\]%@&#~,:;.!?]{2,}[\w$\-_+*=\|\/\\({%@&#~]/; + var linkStringRegEx = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/|data:|www\.)[\w$\-_+*'=\|\/\\(){}[\]%@&#~,:;.!?]{2,}[\w$\-_+*=\|\/\\({%@&#~]/; var lineColumnRegEx = /:(\d+)(:(\d+))?$/; while (string) { @@ -161,7 +165,7 @@ WebInspector.linkifyStringAsFragment = function(string) * @param {string=} classes * @param {boolean=} isExternal * @param {string=} tooltipText - * @return {Element} + * @return {!Element} */ WebInspector.linkifyURLAsNode = function(url, linkText, classes, isExternal, tooltipText) { diff --git a/Source/WebCore/inspector/front-end/ResourcesPanel.js b/Source/WebCore/inspector/front-end/ResourcesPanel.js index c505c97e0..2da3e9e1d 100644 --- a/Source/WebCore/inspector/front-end/ResourcesPanel.js +++ b/Source/WebCore/inspector/front-end/ResourcesPanel.js @@ -297,7 +297,7 @@ WebInspector.ResourcesPanel.prototype = { */ _databaseAdded: function(event) { - var database = /** @type {WebInspector.Database} */ event.data; + var database = /** @type {WebInspector.Database} */ (event.data); this._addDatabase(database); }, @@ -331,7 +331,7 @@ WebInspector.ResourcesPanel.prototype = { */ _domStorageAdded: function(event) { - var domStorage = /** @type {WebInspector.DOMStorage}*/ event.data; + var domStorage = /** @type {WebInspector.DOMStorage}*/ (event.data); this._addDOMStorage(domStorage); }, @@ -577,7 +577,7 @@ WebInspector.ResourcesPanel.prototype = { */ _domStorageUpdated: function(event) { - var storage = /** @type {WebInspector.DOMStorage}*/ event.data; + var storage = /** @type {WebInspector.DOMStorage}*/ (event.data); var view = this._domStorageViews.get(storage); if (this.visibleView && view === this.visibleView) view.update(); @@ -1217,11 +1217,11 @@ WebInspector.FrameResourceTreeElement.prototype = { _handleContextMenuEvent: function(event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendApplicableItems(this._resource); if (this._resource.request) contextMenu.appendApplicableItems(this._resource.request); - contextMenu.show(event); + contextMenu.show(); }, _setBubbleText: function(x) @@ -1417,9 +1417,9 @@ WebInspector.IndexedDBTreeElement.prototype = { _handleContextMenuEvent: function(event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendItem(WebInspector.UIString("Refresh IndexedDB"), this.refreshIndexedDB.bind(this)); - contextMenu.show(event); + contextMenu.show(); }, _createIndexedDBModel: function() @@ -1446,7 +1446,7 @@ WebInspector.IndexedDBTreeElement.prototype = { */ _indexedDBAdded: function(event) { - var databaseId = /** @type {WebInspector.IndexedDBModel.DatabaseId} */ event.data; + var databaseId = /** @type {WebInspector.IndexedDBModel.DatabaseId} */ (event.data); var idbDatabaseTreeElement = new WebInspector.IDBDatabaseTreeElement(this._storagePanel, this._indexedDBModel, databaseId); this._idbDatabaseTreeElements.push(idbDatabaseTreeElement); @@ -1460,7 +1460,7 @@ WebInspector.IndexedDBTreeElement.prototype = { */ _indexedDBRemoved: function(event) { - var databaseId = /** @type {WebInspector.IndexedDBModel.DatabaseId} */ event.data; + var databaseId = /** @type {WebInspector.IndexedDBModel.DatabaseId} */ (event.data); var idbDatabaseTreeElement = this._idbDatabaseTreeElement(databaseId) if (!idbDatabaseTreeElement) @@ -1476,7 +1476,7 @@ WebInspector.IndexedDBTreeElement.prototype = { */ _indexedDBLoaded: function(event) { - var database = /** @type {WebInspector.IndexedDBModel.Database} */ event.data; + var database = /** @type {WebInspector.IndexedDBModel.Database} */ (event.data); var idbDatabaseTreeElement = this._idbDatabaseTreeElement(database.databaseId) if (!idbDatabaseTreeElement) @@ -1531,21 +1531,21 @@ WebInspector.FileSystemListTreeElement.prototype = { _handleContextMenuEvent: function(event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendItem(WebInspector.UIString("Refresh FileSystem List"), this._refreshFileSystem.bind(this)); - contextMenu.show(event); + contextMenu.show(); }, _fileSystemAdded: function(event) { - var fileSystem = /** @type {WebInspector.FileSystemModel.FileSystem} */ event.data; + var fileSystem = /** @type {WebInspector.FileSystemModel.FileSystem} */ (event.data); var fileSystemTreeElement = new WebInspector.FileSystemTreeElement(this._storagePanel, fileSystem); this.appendChild(fileSystemTreeElement); }, _fileSystemRemoved: function(event) { - var fileSystem = /** @type {WebInspector.FileSystemModel.FileSystem} */ event.data; + var fileSystem = /** @type {WebInspector.FileSystemModel.FileSystem} */ (event.data); var fileSystemTreeElement = this._fileSystemTreeElementByName(fileSystem.name); if (!fileSystemTreeElement) return; @@ -1556,7 +1556,7 @@ WebInspector.FileSystemListTreeElement.prototype = { _fileSystemTreeElementByName: function(fileSystemName) { for (var i = 0; i < this.children.length; ++i) { - var child = /** @type {WebInspector.FileSystemTreeElement} */ this.children[i]; + var child = /** @type {WebInspector.FileSystemTreeElement} */ (this.children[i]); if (child.fileSystemName === fileSystemName) return this.children[i]; } @@ -1606,9 +1606,9 @@ WebInspector.IDBDatabaseTreeElement.prototype = { _handleContextMenuEvent: function(event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendItem(WebInspector.UIString("Refresh IndexedDB"), this._refreshIndexedDB.bind(this)); - contextMenu.show(event); + contextMenu.show(); }, _refreshIndexedDB: function(event) @@ -2211,7 +2211,7 @@ WebInspector.SearchResultsTreeElementsTraverser.prototype = { */ _traverseNext: function(treeElement) { - return /** @type {WebInspector.BaseStorageTreeElement} */ treeElement.traverseNextTreeElement(false, this._root, true); + return /** @type {WebInspector.BaseStorageTreeElement} */ (treeElement.traverseNextTreeElement(false, this._root, true)); }, /** @@ -2220,7 +2220,7 @@ WebInspector.SearchResultsTreeElementsTraverser.prototype = { */ _traversePrevious: function(treeElement) { - return /** @type {WebInspector.BaseStorageTreeElement} */ treeElement.traversePreviousTreeElement(false, true); + return /** @type {WebInspector.BaseStorageTreeElement} */ (treeElement.traversePreviousTreeElement(false, true)); }, /** diff --git a/Source/WebCore/inspector/front-end/RevisionHistoryView.js b/Source/WebCore/inspector/front-end/RevisionHistoryView.js index 56b822676..4592e4ad5 100644 --- a/Source/WebCore/inspector/front-end/RevisionHistoryView.js +++ b/Source/WebCore/inspector/front-end/RevisionHistoryView.js @@ -128,7 +128,7 @@ WebInspector.RevisionHistoryView.prototype = { _revisionAdded: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data.uiSourceCode; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data.uiSourceCode); var uiSourceCodeItem = this._uiSourceCodeItems.get(uiSourceCode); if (!uiSourceCodeItem) { uiSourceCodeItem = this._createUISourceCodeItem(uiSourceCode); @@ -156,7 +156,7 @@ WebInspector.RevisionHistoryView.prototype = { _uiSourceCodeRemoved: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data); this._removeUISourceCode(uiSourceCode); }, diff --git a/Source/WebCore/inspector/front-end/RuntimeModel.js b/Source/WebCore/inspector/front-end/RuntimeModel.js index 6f7f9e39e..b7ba78868 100644 --- a/Source/WebCore/inspector/front-end/RuntimeModel.js +++ b/Source/WebCore/inspector/front-end/RuntimeModel.js @@ -129,12 +129,13 @@ WebInspector.RuntimeModel.prototype = { * @param {boolean} includeCommandLineAPI * @param {boolean} doNotPauseOnExceptionsAndMuteConsole * @param {boolean} returnByValue + * @param {boolean} generatePreview * @param {function(?WebInspector.RemoteObject, boolean, RuntimeAgent.RemoteObject=)} callback */ - evaluate: function(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, callback) + evaluate: function(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback) { if (WebInspector.debuggerModel.selectedCallFrame()) { - WebInspector.debuggerModel.evaluateOnSelectedCallFrame(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, callback); + WebInspector.debuggerModel.evaluateOnSelectedCallFrame(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback); return; } @@ -161,7 +162,7 @@ WebInspector.RuntimeModel.prototype = { else callback(WebInspector.RemoteObject.fromPayload(result), !!wasThrown); } - RuntimeAgent.evaluate(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, this._currentExecutionContext ? this._currentExecutionContext.id : undefined, returnByValue, evalCallback); + RuntimeAgent.evaluate(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, this._currentExecutionContext ? this._currentExecutionContext.id : undefined, returnByValue, generatePreview, evalCallback); }, /** @@ -209,7 +210,7 @@ WebInspector.RuntimeModel.prototype = { if (!expressionString && WebInspector.debuggerModel.selectedCallFrame()) WebInspector.debuggerModel.getSelectedCallFrameVariables(receivedPropertyNames.bind(this)); else - this.evaluate(expressionString, "completion", true, true, false, evaluated.bind(this)); + this.evaluate(expressionString, "completion", true, true, false, false, evaluated.bind(this)); function evaluated(result, wasThrown) { @@ -245,7 +246,7 @@ WebInspector.RuntimeModel.prototype = { if (result.type === "object" || result.type === "function") result.callFunctionJSON(getCompletions, undefined, receivedPropertyNames.bind(this)); else if (result.type === "string" || result.type === "number" || result.type === "boolean") - this.evaluate("(" + getCompletions + ")(\"" + result.type + "\")", "completion", false, true, true, receivedPropertyNamesFromEval.bind(this)); + this.evaluate("(" + getCompletions + ")(\"" + result.type + "\")", "completion", false, true, true, false, receivedPropertyNamesFromEval.bind(this)); } function receivedPropertyNamesFromEval(notRelevant, wasThrown, result) diff --git a/Source/WebCore/inspector/front-end/SASSSourceMapping.js b/Source/WebCore/inspector/front-end/SASSSourceMapping.js index 85b521dce..43bc1e7cf 100644 --- a/Source/WebCore/inspector/front-end/SASSSourceMapping.js +++ b/Source/WebCore/inspector/front-end/SASSSourceMapping.js @@ -32,12 +32,17 @@ * @constructor * @implements {WebInspector.SourceMapping} * @param {WebInspector.Workspace} workspace + * @param {WebInspector.NetworkWorkspaceProvider} networkWorkspaceProvider */ -WebInspector.SASSSourceMapping = function(workspace) +WebInspector.SASSSourceMapping = function(workspace, networkWorkspaceProvider) { this._workspace = workspace; + this._networkWorkspaceProvider = networkWorkspaceProvider; this._uiLocations = {}; + this._cssURLsForSASSURL = {}; + this._timeoutForURL = {}; WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, this._resourceAdded, this); + WebInspector.fileManager.addEventListener(WebInspector.FileManager.EventTypes.SavedURL, this._fileSaveFinished, this); this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._reset, this); } @@ -60,9 +65,46 @@ WebInspector.SASSSourceMapping.prototype = { /** * @param {WebInspector.Event} event */ + _fileSaveFinished: function(event) + { + var sassURL = /** @type {string} */ (event.data); + function callback() + { + delete this._timeoutForURL[sassURL]; + var cssURLs = this._cssURLsForSASSURL[sassURL]; + if (!cssURLs) + return; + for (var i = 0; i < cssURLs.length; ++i) + this._reloadCSS(cssURLs[i]); + } + + var timer = this._timeoutForURL[sassURL]; + if (timer) { + clearTimeout(timer); + delete this._timeoutForURL[sassURL]; + } + if (!WebInspector.settings.cssReloadEnabled.get() || !this._cssURLsForSASSURL[sassURL]) + return; + var timeout = WebInspector.settings.cssReloadTimeout.get(); + if (timeout && isFinite(timeout)) + this._timeoutForURL[sassURL] = setTimeout(callback.bind(this), Number(timeout)); + }, + + _reloadCSS: function(url) + { + var uiSourceCode = this._workspace.uiSourceCodeForURL(url); + if (!uiSourceCode) + return; + var newContent = InspectorFrontendHost.loadResourceSynchronously(url); + uiSourceCode.addRevision(newContent); + }, + + /** + * @param {WebInspector.Event} event + */ _resourceAdded: function(event) { - var resource = /** @type {WebInspector.Resource} */ event.data; + var resource = /** @type {WebInspector.Resource} */ (event.data); if (resource.type !== WebInspector.resourceTypes.Stylesheet) return; @@ -115,12 +157,30 @@ WebInspector.SASSSourceMapping.prototype = { if (!uiSourceCode) { var content = InspectorFrontendHost.loadResourceSynchronously(url); var contentProvider = new WebInspector.StaticContentProvider(WebInspector.resourceTypes.Stylesheet, content, "text/x-scss"); - uiSourceCode = new WebInspector.UISourceCode(url, contentProvider, true); - this._workspace.project().addUISourceCode(uiSourceCode); + this._networkWorkspaceProvider.addFile(url, contentProvider, true); + uiSourceCode = this._workspace.uiSourceCodeForURL(url); WebInspector.cssModel.setSourceMapping(rawURL, this); } var rawLocationString = rawURL + ":" + (rawLine + 1); // Next line after mapping metainfo this._uiLocations[rawLocationString] = new WebInspector.UILocation(uiSourceCode, line - 1, 0); + this._addCSSURLforSASSURL(rawURL, url); + }, + + /** + * @param {string} cssURL + * @param {string} sassURL + */ + _addCSSURLforSASSURL: function(cssURL, sassURL) + { + var cssURLs; + if (this._cssURLsForSASSURL.hasOwnProperty(sassURL)) + cssURLs = this._cssURLsForSASSURL[sassURL]; + else { + cssURLs = []; + this._cssURLsForSASSURL[sassURL] = cssURLs; + } + if (cssURLs.indexOf(cssURL) === -1) + cssURLs.push(cssURL); }, /** @@ -129,7 +189,7 @@ WebInspector.SASSSourceMapping.prototype = { */ rawLocationToUILocation: function(rawLocation) { - var location = /** @type WebInspector.CSSLocation */ rawLocation; + var location = /** @type WebInspector.CSSLocation */ (rawLocation); var uiLocation = this._uiLocations[location.url + ":" + location.lineNumber]; if (!uiLocation) { var uiSourceCode = this._workspace.uiSourceCodeForURL(location.url); diff --git a/Source/WebCore/inspector/front-end/ScopeChainSidebarPane.js b/Source/WebCore/inspector/front-end/ScopeChainSidebarPane.js index ab692e9da..1209526b7 100644 --- a/Source/WebCore/inspector/front-end/ScopeChainSidebarPane.js +++ b/Source/WebCore/inspector/front-end/ScopeChainSidebarPane.js @@ -83,7 +83,7 @@ WebInspector.ScopeChainSidebarPane.prototype = { var exception = details.reason === WebInspector.DebuggerModel.BreakReason.Exception ? details.auxData : 0; if (exception) { extraProperties = extraProperties || []; - var exceptionObject = /** @type {RuntimeAgent.RemoteObject} */ exception; + var exceptionObject = /** @type {RuntimeAgent.RemoteObject} */ (exception); extraProperties.push(new WebInspector.RemoteObjectProperty("<exception>", WebInspector.RemoteObject.fromPayload(exceptionObject))); } } diff --git a/Source/WebCore/inspector/front-end/Script.js b/Source/WebCore/inspector/front-end/Script.js index aaf387aa7..445c5d140 100644 --- a/Source/WebCore/inspector/front-end/Script.js +++ b/Source/WebCore/inspector/front-end/Script.js @@ -224,7 +224,7 @@ WebInspector.Script.Location.prototype = { */ uiLocation: function() { - var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ this.rawLocation(); + var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (this.rawLocation()); return this._script.rawLocationToUILocation(debuggerModelLocation.lineNumber, debuggerModelLocation.columnNumber); }, diff --git a/Source/WebCore/inspector/front-end/ScriptFormatter.js b/Source/WebCore/inspector/front-end/ScriptFormatter.js index b99eab7cd..3eedeba27 100644 --- a/Source/WebCore/inspector/front-end/ScriptFormatter.js +++ b/Source/WebCore/inspector/front-end/ScriptFormatter.js @@ -125,7 +125,7 @@ WebInspector.ScriptFormatter.prototype = { { if (!this._cachedWorker) { this._cachedWorker = new Worker("ScriptFormatterWorker.js"); - this._cachedWorker.onmessage = /** @type {function(this:Worker)} */ this._didFormatContent.bind(this); + this._cachedWorker.onmessage = /** @type {function(this:Worker)} */ (this._didFormatContent.bind(this)); } return this._cachedWorker; } diff --git a/Source/WebCore/inspector/front-end/ScriptSnippetModel.js b/Source/WebCore/inspector/front-end/ScriptSnippetModel.js index a6707c693..bd1f45b8f 100644 --- a/Source/WebCore/inspector/front-end/ScriptSnippetModel.js +++ b/Source/WebCore/inspector/front-end/ScriptSnippetModel.js @@ -32,10 +32,12 @@ * @constructor * @extends {WebInspector.Object} * @param {WebInspector.Workspace} workspace + * @param {WebInspector.NetworkWorkspaceProvider} networkWorkspaceProvider */ -WebInspector.ScriptSnippetModel = function(workspace) +WebInspector.ScriptSnippetModel = function(workspace, networkWorkspaceProvider) { this._workspace = workspace; + this._networkWorkspaceProvider = networkWorkspaceProvider; this._uiSourceCodeForScriptId = {}; this._scriptForUISourceCode = new Map(); this._uiSourceCodeForSnippetId = {}; @@ -82,14 +84,13 @@ WebInspector.ScriptSnippetModel.prototype = { */ _addScriptSnippet: function(snippet) { - var uiSourceCode = new WebInspector.UISourceCode(snippet.name, new WebInspector.SnippetContentProvider(snippet), true); - uiSourceCode.isSnippet = true; + this._networkWorkspaceProvider.addFile(snippet.name, new WebInspector.SnippetContentProvider(snippet), true, false, true); + var uiSourceCode = this._workspace.uiSourceCodeForURL(snippet.name); var scriptFile = new WebInspector.SnippetScriptFile(this, uiSourceCode); uiSourceCode.setScriptFile(scriptFile); this._snippetIdForUISourceCode.put(uiSourceCode, snippet.id); uiSourceCode.setSourceMapping(this._snippetScriptMapping); this._uiSourceCodeForSnippetId[snippet.id] = uiSourceCode; - this._workspace.project().addUISourceCode(uiSourceCode); return uiSourceCode; }, @@ -105,7 +106,7 @@ WebInspector.ScriptSnippetModel.prototype = { this._releaseSnippetScript(uiSourceCode); delete this._uiSourceCodeForSnippetId[snippet.id]; this._snippetIdForUISourceCode.remove(uiSourceCode); - this._workspace.project().removeUISourceCode(uiSourceCode); + this._networkWorkspaceProvider.removeFile(snippet.name); }, /** @@ -321,9 +322,7 @@ WebInspector.ScriptSnippetModel.prototype = { */ _createUISourceCodeForScript: function(script) { - var uiSourceCode = new WebInspector.UISourceCode(script.sourceURL, script, false); - uiSourceCode.isSnippet = true; - this._workspace.project().addTemporaryUISourceCode(uiSourceCode); + var uiSourceCode = this._workspace.addTemporaryUISourceCode(script.sourceURL, script, false, true); uiSourceCode.setSourceMapping(this._snippetScriptMapping); this._uiSourceCodeForScriptId[script.scriptId] = uiSourceCode; this._scriptForUISourceCode.put(uiSourceCode, script); diff --git a/Source/WebCore/inspector/front-end/ScriptsNavigator.js b/Source/WebCore/inspector/front-end/ScriptsNavigator.js index 8477230dd..6e4860920 100644 --- a/Source/WebCore/inspector/front-end/ScriptsNavigator.js +++ b/Source/WebCore/inspector/front-end/ScriptsNavigator.js @@ -210,7 +210,7 @@ WebInspector.SnippetsNavigatorView.prototype = { */ handleContextMenu: function(event, uiSourceCode) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); if (uiSourceCode) { contextMenu.appendItem(WebInspector.UIString("Run"), this._handleEvaluateSnippet.bind(this, uiSourceCode)); contextMenu.appendItem(WebInspector.UIString("Rename"), this._handleRenameSnippet.bind(this, uiSourceCode)); @@ -218,7 +218,7 @@ WebInspector.SnippetsNavigatorView.prototype = { contextMenu.appendSeparator(); } contextMenu.appendItem(WebInspector.UIString("New"), this._handleCreateSnippet.bind(this)); - contextMenu.show(event); + contextMenu.show(); }, /** diff --git a/Source/WebCore/inspector/front-end/ScriptsPanel.js b/Source/WebCore/inspector/front-end/ScriptsPanel.js index a27a45bb1..9a03ad234 100644 --- a/Source/WebCore/inspector/front-end/ScriptsPanel.js +++ b/Source/WebCore/inspector/front-end/ScriptsPanel.js @@ -237,7 +237,7 @@ WebInspector.ScriptsPanel.prototype = { */ _uiSourceCodeAdded: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data); this._addUISourceCode(uiSourceCode); }, @@ -247,7 +247,7 @@ WebInspector.ScriptsPanel.prototype = { _addUISourceCode: function(uiSourceCode) { if (this._toggleFormatSourceButton.toggled) - uiSourceCode.setFormatted(true, this._uiSourceCodeFormatted.bind(this, uiSourceCode)); + uiSourceCode.setFormatted(true); this._navigator.addUISourceCode(uiSourceCode); this._editorContainer.addUISourceCode(uiSourceCode); @@ -255,7 +255,7 @@ WebInspector.ScriptsPanel.prototype = { _uiSourceCodeRemoved: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data); this._editorContainer.removeUISourceCode(uiSourceCode); this._navigator.removeUISourceCode(uiSourceCode); this._removeSourceFrame(uiSourceCode); @@ -517,7 +517,7 @@ WebInspector.ScriptsPanel.prototype = { return; this._editorContainer.addUISourceCode(uiSourceCode); if (uiSourceCode.formatted() !== this._toggleFormatSourceButton.toggled) - uiSourceCode.setFormatted(this._toggleFormatSourceButton.toggled, this._uiSourceCodeFormatted.bind(this, uiSourceCode)); + uiSourceCode.setFormatted(this._toggleFormatSourceButton.toggled); } var sourceFrame = this._showFile(uiSourceCode); sourceFrame.revealLine(uiLocation.lineNumber); @@ -540,7 +540,7 @@ WebInspector.ScriptsPanel.prototype = { _editorClosed: function(event) { this._navigatorController.hideNavigatorOverlay(); - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data); if (this._currentUISourceCode === uiSourceCode) delete this._currentUISourceCode; @@ -552,7 +552,7 @@ WebInspector.ScriptsPanel.prototype = { _editorSelected: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data); var sourceFrame = this._showFile(uiSourceCode); this._navigatorController.hideNavigatorOverlay(); sourceFrame.focus(); @@ -561,7 +561,7 @@ WebInspector.ScriptsPanel.prototype = { _scriptSelected: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data.uiSourceCode; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data.uiSourceCode); var sourceFrame = this._showFile(uiSourceCode); this._navigatorController.hideNavigatorOverlay(); if (sourceFrame && event.data.focusSource) @@ -734,11 +734,11 @@ WebInspector.ScriptsPanel.prototype = { var active = event.data; this._toggleBreakpointsButton.toggled = active; if (active) { - this._toggleBreakpointsButton.title = WebInspector.UIString("Deactivate all breakpoints."); + this._toggleBreakpointsButton.title = WebInspector.UIString("Deactivate breakpoints."); WebInspector.inspectorView.element.removeStyleClass("breakpoints-deactivated"); this.sidebarPanes.jsBreakpoints.listElement.removeStyleClass("breakpoints-list-deactivated"); } else { - this._toggleBreakpointsButton.title = WebInspector.UIString("Activate all breakpoints."); + this._toggleBreakpointsButton.title = WebInspector.UIString("Activate breakpoints."); WebInspector.inspectorView.element.addStyleClass("breakpoints-deactivated"); this.sidebarPanes.jsBreakpoints.listElement.addStyleClass("breakpoints-list-deactivated"); } @@ -795,7 +795,7 @@ WebInspector.ScriptsPanel.prototype = { this.stepOutButton = this._createButtonAndRegisterShortcuts(section, "scripts-step-out", title, handler, shortcuts, WebInspector.UIString("Step out")); debugToolbar.appendChild(this.stepOutButton); - this._toggleBreakpointsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Deactivate all breakpoints."), "toggle-breakpoints"); + this._toggleBreakpointsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Deactivate breakpoints."), "toggle-breakpoints"); this._toggleBreakpointsButton.toggled = true; this._toggleBreakpointsButton.addEventListener("click", this._toggleBreakpointsClicked, this); debugToolbar.appendChild(this._toggleBreakpointsButton.element); @@ -918,7 +918,7 @@ WebInspector.ScriptsPanel.prototype = { */ canSearchAndReplace: function() { - var view = /** @type {WebInspector.SourceFrame} */ this.visibleView; + var view = /** @type {WebInspector.SourceFrame} */ (this.visibleView); return !!view && view.canEditSource(); }, @@ -927,7 +927,7 @@ WebInspector.ScriptsPanel.prototype = { */ replaceSelectionWith: function(text) { - var view = /** @type {WebInspector.SourceFrame} */ this.visibleView; + var view = /** @type {WebInspector.SourceFrame} */ (this.visibleView); view.replaceSearchMatchWith(text); }, @@ -937,21 +937,16 @@ WebInspector.ScriptsPanel.prototype = { */ replaceAllWith: function(query, text) { - var view = /** @type {WebInspector.SourceFrame} */ this.visibleView; + var view = /** @type {WebInspector.SourceFrame} */ (this.visibleView); view.replaceAllWith(query, text); }, - _uiSourceCodeFormatted: function(uiSourceCode) - { - WebInspector.breakpointManager.restoreBreakpoints(uiSourceCode); - }, - _toggleFormatSource: function() { this._toggleFormatSourceButton.toggled = !this._toggleFormatSourceButton.toggled; var uiSourceCodes = this._workspace.uiSourceCodes(); for (var i = 0; i < uiSourceCodes.length; ++i) - uiSourceCodes[i].setFormatted(this._toggleFormatSourceButton.toggled, this._uiSourceCodeFormatted.bind(this, uiSourceCodes[i])); + uiSourceCodes[i].setFormatted(this._toggleFormatSourceButton.toggled); }, addToWatch: function(expression) @@ -966,7 +961,7 @@ WebInspector.ScriptsPanel.prototype = { return; if (sourceFrame instanceof WebInspector.JavaScriptSourceFrame) { - var javaScriptSourceFrame = /** @type {WebInspector.JavaScriptSourceFrame} */ sourceFrame; + var javaScriptSourceFrame = /** @type {WebInspector.JavaScriptSourceFrame} */ (sourceFrame); javaScriptSourceFrame.toggleBreakpointOnCurrentLine(); } }, @@ -1032,8 +1027,8 @@ WebInspector.ScriptsPanel.prototype = { _fileRenamed: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data.uiSourceCode; - var name = /** @type {string} */ event.data.name; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data.uiSourceCode); + var name = /** @type {string} */ (event.data.name); if (!uiSourceCode.isSnippet) return; WebInspector.scriptSnippetModel.renameScriptSnippet(uiSourceCode, name); @@ -1074,7 +1069,7 @@ WebInspector.ScriptsPanel.prototype = { */ _itemRenamingRequested: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data); var shouldHideNavigator = !this._navigatorController.isNavigatorPinned(); if (this._navigatorController.isNavigatorHidden()) @@ -1105,7 +1100,7 @@ WebInspector.ScriptsPanel.prototype = { * @param {WebInspector.ContextMenu} contextMenu * @param {Object} target */ - appendApplicableItems: function(contextMenu, target) + appendApplicableItems: function(event, contextMenu, target) { this._appendUISourceCodeItems(contextMenu, target); this._appendFunctionItems(contextMenu, target); @@ -1120,7 +1115,7 @@ WebInspector.ScriptsPanel.prototype = { if (!(target instanceof WebInspector.UISourceCode)) return; - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ target; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (target); contextMenu.appendItem(WebInspector.UIString("Local modifications..."), this._showLocalHistory.bind(this, uiSourceCode)); var resource = WebInspector.resourceForURL(uiSourceCode.url); if (resource && resource.request) @@ -1135,7 +1130,7 @@ WebInspector.ScriptsPanel.prototype = { { if (!(target instanceof WebInspector.RemoteObject)) return; - var remoteObject = /** @type {WebInspector.RemoteObject} */ target; + var remoteObject = /** @type {WebInspector.RemoteObject} */ (target); if (remoteObject.type !== "function") return; diff --git a/Source/WebCore/inspector/front-end/ScriptsPanelDescriptor.js b/Source/WebCore/inspector/front-end/ScriptsPanelDescriptor.js index 99edfafef..8f387f65b 100644 --- a/Source/WebCore/inspector/front-end/ScriptsPanelDescriptor.js +++ b/Source/WebCore/inspector/front-end/ScriptsPanelDescriptor.js @@ -42,17 +42,17 @@ WebInspector.ScriptsPanelDescriptor.prototype = { * @param {WebInspector.ContextMenu} contextMenu * @param {Object} target */ - appendApplicableItems: function(contextMenu, target) + appendApplicableItems: function(event, contextMenu, target) { var hasApplicableItems = target instanceof WebInspector.UISourceCode; if (!hasApplicableItems && target instanceof WebInspector.RemoteObject) { - var remoteObject = /** @type {WebInspector.RemoteObject} */ target; + var remoteObject = /** @type {WebInspector.RemoteObject} */ (target); if (remoteObject.type !== "function") return; } - this.panel().appendApplicableItems(contextMenu, target); + this.panel().appendApplicableItems(event, contextMenu, target); }, __proto__: WebInspector.PanelDescriptor.prototype diff --git a/Source/WebCore/inspector/front-end/Settings.js b/Source/WebCore/inspector/front-end/Settings.js index cc98b4cd5..cf3532dd4 100644 --- a/Source/WebCore/inspector/front-end/Settings.js +++ b/Source/WebCore/inspector/front-end/Settings.js @@ -55,6 +55,7 @@ var Capabilities = { heapProfilerPresent: false, canOverrideDeviceMetrics: false, timelineSupportsFrameInstrumentation: false, + timelineCanMonitorMainThread: false, canOverrideGeolocation: false, canOverrideDeviceOrientation: false, } @@ -105,6 +106,10 @@ WebInspector.Settings = function() this.searchInContentScripts = this.createSetting("searchInContentScripts", false); this.textEditorIndent = this.createSetting("textEditorIndent", " "); this.lastDockState = this.createSetting("lastDockState", ""); + this.cssReloadEnabled = this.createSetting("cssReloadEnabled", false); + this.cssReloadTimeout = this.createSetting("cssReloadTimeout", 1000); + this.showCpuOnTimelineRuler = this.createSetting("showCpuOnTimelineRuler", false); + // If there are too many breakpoints in a storage, it is likely due to a recent bug that caused // periodical breakpoints duplication leading to inspector slowness. @@ -193,9 +198,6 @@ WebInspector.ExperimentsSettings = function() this.liveNativeMemoryChart = this._createExperiment("liveNativeMemoryChart", "Live native memory chart"); this.fileSystemInspection = this._createExperiment("fileSystemInspection", "FileSystem inspection"); this.canvasInspection = this._createExperiment("canvasInspection ", "Canvas inspection"); - this.mainThreadMonitoring = this._createExperiment("mainThreadMonitoring", "Show CPU activity in Timeline"); - this.geolocationOverride = this._createExperiment("geolocationOverride", "Override Device Geolocation"); - this.deviceOrientationOverride = this._createExperiment("deviceOrientationOverride", "Override Device Orientation"); this.sass = this._createExperiment("sass", "Support for SASS"); this.codemirror = this._createExperiment("codemirror", "Use CodeMirror editor"); this.cssRegions = this._createExperiment("cssRegions", "CSS Regions Support"); diff --git a/Source/WebCore/inspector/front-end/SettingsScreen.js b/Source/WebCore/inspector/front-end/SettingsScreen.js index fe5aab03c..c98361cf9 100644 --- a/Source/WebCore/inspector/front-end/SettingsScreen.js +++ b/Source/WebCore/inspector/front-end/SettingsScreen.js @@ -276,6 +276,8 @@ WebInspector.GenericSettingsTab = function() p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show folders"), WebInspector.settings.showScriptFolders)); p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Search in content scripts"), WebInspector.settings.searchInContentScripts)); p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Enable source maps"), WebInspector.settings.sourceMapsEnabled)); + if (WebInspector.experimentsSettings.isEnabled("sass")) + p.appendChild(this._createCSSAutoReloadControls()); p.appendChild(this._createSelectSetting(WebInspector.UIString("Indentation"), [ [ WebInspector.UIString("2 spaces"), WebInspector.TextEditorModel.Indent.TwoSpaces ], [ WebInspector.UIString("4 spaces"), WebInspector.TextEditorModel.Indent.FourSpaces ], @@ -286,6 +288,9 @@ WebInspector.GenericSettingsTab = function() p = this._appendSection(WebInspector.UIString("Profiler")); p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show objects' hidden properties"), WebInspector.settings.showHeapSnapshotObjectsHiddenProperties)); + p = this._appendSection(WebInspector.UIString("Timeline")); + p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show CPU activity on the ruler"), WebInspector.settings.showCpuOnTimelineRuler)); + p = this._appendSection(WebInspector.UIString("Console")); p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Log XMLHttpRequests"), WebInspector.settings.monitoringXHREnabled)); p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Preserve log upon navigation"), WebInspector.settings.preserveConsoleLog)); @@ -333,6 +338,46 @@ WebInspector.GenericSettingsTab.prototype = { PageAgent.setScriptExecutionDisabled(WebInspector.settings.javaScriptDisabled.get(), this._updateScriptDisabledCheckbox.bind(this)); }, + _createCSSAutoReloadControls: function() + { + var fragment = document.createDocumentFragment(); + var labelElement = fragment.createChild("label"); + var checkboxElement = labelElement.createChild("input"); + checkboxElement.type = "checkbox"; + checkboxElement.checked = WebInspector.settings.cssReloadEnabled.get(); + checkboxElement.addEventListener("click", checkboxClicked, false); + labelElement.appendChild(document.createTextNode(WebInspector.UIString("Auto-reload CSS upon SASS save"))); + + var fieldsetElement = fragment.createChild("fieldset"); + fieldsetElement.disabled = !checkboxElement.checked; + var p = fieldsetElement.createChild("p"); + p.appendChild(document.createTextNode(WebInspector.UIString("Timeout (ms)"))); + p.appendChild(document.createTextNode(" ")); + var timeoutInput = p.createChild("input"); + timeoutInput.value = WebInspector.settings.cssReloadTimeout.get(); + timeoutInput.style.width = "60px"; + timeoutInput.maxLength = 8; + timeoutInput.addEventListener("blur", blurListener, false); + return fragment; + + function checkboxClicked() + { + var reloadEnabled = checkboxElement.checked; + WebInspector.settings.cssReloadEnabled.set(reloadEnabled); + fieldsetElement.disabled = !reloadEnabled; + } + + function blurListener() + { + var value = timeoutInput.value; + if (!isFinite(value) || value <= 0) { + timeoutInput.value = WebInspector.settings.cssReloadTimeout.get(); + return; + } + WebInspector.settings.cssReloadTimeout.set(Number(value)); + } + }, + __proto__: WebInspector.SettingsTab.prototype } @@ -348,9 +393,9 @@ WebInspector.UserAgentSettingsTab = function() p.appendChild(this._createUserAgentControl()); if (Capabilities.canOverrideDeviceMetrics) p.appendChild(this._createDeviceMetricsControl()); - if (Capabilities.canOverrideGeolocation && WebInspector.experimentsSettings.geolocationOverride.isEnabled()) + if (Capabilities.canOverrideGeolocation) p.appendChild(this._createGeolocationOverrideControl()); - if (Capabilities.canOverrideDeviceOrientation && WebInspector.experimentsSettings.deviceOrientationOverride.isEnabled()) + if (Capabilities.canOverrideDeviceOrientation) p.appendChild(this._createDeviceOrientationOverrideControl()); p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Emulate touch events"), WebInspector.settings.emulateTouchEvents)); } @@ -364,7 +409,7 @@ WebInspector.UserAgentSettingsTab.prototype = { var labelElement = p.createChild("label"); var checkboxElement = labelElement.createChild("input"); checkboxElement.type = "checkbox"; - checkboxElement.checked = !!userAgent; + checkboxElement.checked = false; labelElement.appendChild(document.createTextNode(WebInspector.UIString("User Agent"))); p.appendChild(this._createUserAgentSelectRowElement(checkboxElement)); return p; @@ -387,6 +432,11 @@ WebInspector.UserAgentSettingsTab.prototype = { ["Firefox 7 \u2014 Mac", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1"], ["Firefox 4 \u2014 Windows", "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"], ["Firefox 4 \u2014 Mac", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"], + ["Firefox 14 \u2014 Android Mobile", "Mozilla/5.0 (Android; Mobile; rv:14.0) Gecko/14.0 Firefox/14.0"], + ["Firefox 14 \u2014 Android Tablet", "Mozilla/5.0 (Android; Tablet; rv:14.0) Gecko/14.0 Firefox/14.0"], + + ["Chrome \u2014 Android Mobile", "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19"], + ["Chrome \u2014 Android Tablet", "Mozilla/5.0 (Linux; Android 4.1.2; Nexus 7 Build/JZ054K) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19"], ["iPhone \u2014 iOS 5", "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", "640x960x1"], ["iPhone \u2014 iOS 4", "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_2 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8H7 Safari/6533.18.5", "640x960x1"], @@ -454,8 +504,8 @@ WebInspector.UserAgentSettingsTab.prototype = { } else { this._selectElement.disabled = true; this._otherUserAgentElement.disabled = true; - WebInspector.settings.userAgent.set(""); } + WebInspector.userAgentSupport.toggleUserAgentOverride(checkboxElement.checked); } checkboxElement.addEventListener("click", checkboxClicked.bind(this), false); @@ -750,11 +800,14 @@ WebInspector.UserAgentSettingsTab.prototype = { var cellElement = rowElement.createChild("td"); cellElement.appendChild(document.createTextNode(WebInspector.UIString("Geolocation Position") + ":")); cellElement = rowElement.createChild("td"); + cellElement.appendChild(document.createTextNode(WebInspector.UIString("Lat = "))); this._latitudeElement = this._createInput(cellElement, "geolocation-override-latitude", String(geolocation.latitude), this._applyGeolocationUserInput.bind(this)); cellElement.appendChild(document.createTextNode(" , ")); + cellElement.appendChild(document.createTextNode(WebInspector.UIString("Lon = "))); this._longitudeElement = this._createInput(cellElement, "geolocation-override-longitude", String(geolocation.longitude), this._applyGeolocationUserInput.bind(this)); rowElement = tableElement.createChild("tr"); cellElement = rowElement.createChild("td"); + cellElement.colSpan = 2; var geolocationErrorLabelElement = document.createElement("label"); var geolocationErrorCheckboxElement = geolocationErrorLabelElement.createChild("input"); geolocationErrorCheckboxElement.id = "geolocation-error"; diff --git a/Source/WebCore/inspector/front-end/Spectrum.js b/Source/WebCore/inspector/front-end/Spectrum.js index 4e7fdc151..bfeadb517 100644 --- a/Source/WebCore/inspector/front-end/Spectrum.js +++ b/Source/WebCore/inspector/front-end/Spectrum.js @@ -28,20 +28,17 @@ /** * @constructor - * @extends {WebInspector.Object} + * @extends {WebInspector.View} */ WebInspector.Spectrum = function() { - this._popover = new WebInspector.Popover(); - this._popover.setCanShrink(false); - this._popover.element.addEventListener("mousedown", consumeEvent, false); + WebInspector.View.call(this); + this.registerRequiredCSS("spectrum.css"); - this._containerElement = document.createElement('div'); - this._containerElement.className = "spectrum-container"; - this._containerElement.tabIndex = 0; - this._containerElement.addEventListener("keydown", this._onKeyDown.bind(this), false); + this.element.className = "spectrum-container"; + this.element.tabIndex = 0; - var topElement = this._containerElement.createChild("div", "spectrum-top"); + var topElement = this.element.createChild("div", "spectrum-top"); topElement.createChild("div", "spectrum-fill"); var topInnerElement = topElement.createChild("div", "spectrum-top-inner fill"); @@ -51,7 +48,7 @@ WebInspector.Spectrum = function() this._sliderElement = topInnerElement.createChild("div", "spectrum-hue"); this.slideHelper = this._sliderElement.createChild("div", "spectrum-slider"); - var rangeContainer = this._containerElement.createChild("div", "spectrum-range-container"); + var rangeContainer = this.element.createChild("div", "spectrum-range-container"); var alphaLabel = rangeContainer.createChild("label"); alphaLabel.textContent = WebInspector.UIString("\u03B1:"); @@ -65,7 +62,7 @@ WebInspector.Spectrum = function() swatchElement.className = "swatch"; this._swatchInnerElement = swatchElement.createChild("span", "swatch-inner"); - var displayContainer = this._containerElement.createChild("div"); + var displayContainer = this.element.createChild("div"); displayContainer.appendChild(swatchElement); this._displayElement = displayContainer.createChild("span", "source-code spectrum-display-value"); @@ -107,13 +104,10 @@ WebInspector.Spectrum = function() this._onchange(); } - - this._hideProxy = this.hide.bind(this, true); }; WebInspector.Spectrum.Events = { - ColorChanged: "ColorChanged", - Hidden: "Hidden" + ColorChanged: "ColorChanged" }; WebInspector.Spectrum.hsvaToRGBA = function(h, s, v, a) @@ -323,11 +317,6 @@ WebInspector.Spectrum.prototype = { this._displayElement.textContent = text; }, - get visible() - { - return this._popover.visible; - }, - _onchange: function() { this._updateUI(); @@ -378,43 +367,76 @@ WebInspector.Spectrum.prototype = { this._alphaElement.value = this.hsv[3] * 100; }, + wasShown: function() + { + this.slideHeight = this._sliderElement.offsetHeight; + this.dragWidth = this._draggerElement.offsetWidth; + this.dragHeight = this._draggerElement.offsetHeight; + this._dragHelperElementHeight = this._dragHelperElement.offsetHeight / 2; + this.slideHelperHeight = this.slideHelper.offsetHeight / 2; + this._updateUI(); + }, + + __proto__: WebInspector.View.prototype +} + +/** + * @constructor + * @extends {WebInspector.Object} + */ +WebInspector.SpectrumPopupHelper = function() +{ + this._spectrum = new WebInspector.Spectrum(); + this._spectrum.element.addEventListener("keydown", this._onKeyDown.bind(this), false); + + this._popover = new WebInspector.Popover(); + this._popover.setCanShrink(false); + this._popover.element.addEventListener("mousedown", consumeEvent, false); + + this._hideProxy = this.hide.bind(this, true); +} + +WebInspector.SpectrumPopupHelper.Events = { + Hidden: "Hidden" +}; + +WebInspector.SpectrumPopupHelper.prototype = { + /** + * @return {WebInspector.Spectrum} + */ + spectrum: function() + { + return this._spectrum; + }, + toggle: function(element, color, format) { - if (this.visible) + if (this._popover.isShowing()) this.hide(true); else this.show(element, color, format); - return this.visible; + return this._popover.isShowing(); }, show: function(element, color, format) { - if (this.visible) { - if (this.anchorElement === element) + if (this._popover.isShowing()) { + if (this._anchorElement === element) return false; // Reopen the picker for another anchor element. this.hide(true); } + this._anchorElement = element; + + this._spectrum.color = color; + this._spectrum._originalFormat = format || color.format; this.reposition(element); - this.anchorElement = element; document.addEventListener("mousedown", this._hideProxy, false); window.addEventListener("blur", this._hideProxy, false); - - this.slideHeight = this._sliderElement.offsetHeight; - this.dragWidth = this._draggerElement.offsetWidth; - this.dragHeight = this._draggerElement.offsetHeight; - this._dragHelperElementHeight = this._dragHelperElement.offsetHeight / 2; - this.slideHelperHeight = this.slideHelper.offsetHeight / 2; - - this.color = color; - this._originalFormat = format || color.format; - - this._updateUI(); - return true; }, @@ -422,26 +444,28 @@ WebInspector.Spectrum.prototype = { { if (!this._previousFocusElement) this._previousFocusElement = WebInspector.currentFocusElement(); - this._popover.show(this._containerElement, element); - WebInspector.setCurrentFocusElement(this._containerElement); + this._popover.showView(this._spectrum, element); + WebInspector.setCurrentFocusElement(this._spectrum.element); }, /** - * @param {boolean} commitEdit + * @param {boolean=} commitEdit */ hide: function(commitEdit) { + if (!this._popover.isShowing()) + return; this._popover.hide(); document.removeEventListener("mousedown", this._hideProxy, false); window.removeEventListener("blur", this._hideProxy, false); - this.dispatchEventToListeners(WebInspector.Spectrum.Events.Hidden, !!commitEdit); + this.dispatchEventToListeners(WebInspector.SpectrumPopupHelper.Events.Hidden, !!commitEdit); WebInspector.setCurrentFocusElement(this._previousFocusElement); delete this._previousFocusElement; - delete this.anchorElement; + delete this._anchorElement; }, _onKeyDown: function(event) @@ -459,3 +483,26 @@ WebInspector.Spectrum.prototype = { __proto__: WebInspector.Object.prototype } + +/** + * @constructor + */ +WebInspector.ColorSwatch = function() +{ + this.element = document.createElement("span"); + this._swatchInnerElement = this.element.createChild("span", "swatch-inner"); + this.element.title = WebInspector.UIString("Click to open a colorpicker. Shift-click to change color format"); + this.element.className = "swatch"; + this.element.addEventListener("mousedown", consumeEvent, false); + this.element.addEventListener("dblclick", consumeEvent, false); +} + +WebInspector.ColorSwatch.prototype = { + /** + * @param {string} colorString + */ + setColorString: function(colorString) + { + this._swatchInnerElement.style.backgroundColor = colorString; + } +} diff --git a/Source/WebCore/inspector/front-end/StylesSidebarPane.js b/Source/WebCore/inspector/front-end/StylesSidebarPane.js index f6d356e97..29b0e924e 100644 --- a/Source/WebCore/inspector/front-end/StylesSidebarPane.js +++ b/Source/WebCore/inspector/front-end/StylesSidebarPane.js @@ -93,7 +93,8 @@ WebInspector.StylesSidebarPane = function(computedStylePane, setPseudoClassCallb this._sectionsContainer = document.createElement("div"); this.bodyElement.appendChild(this._sectionsContainer); - this._spectrum = new WebInspector.Spectrum(); + this._spectrumHelper = new WebInspector.SpectrumPopupHelper(); + this._linkifier = new WebInspector.Linkifier(new WebInspector.Linkifier.DefaultCSSFormatter()); WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetOrMediaQueryResultChanged, this); WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged, this._styleSheetOrMediaQueryResultChanged, this); @@ -130,14 +131,22 @@ WebInspector.StylesSidebarPane.canonicalPropertyName = function(name) return match[1]; } +WebInspector.StylesSidebarPane.createExclamationMark = function(propertyName) +{ + var exclamationElement = document.createElement("img"); + exclamationElement.className = "exclamation-mark"; + exclamationElement.title = WebInspector.CSSCompletions.cssPropertiesMetainfo.keySet()[propertyName.toLowerCase()] ? WebInspector.UIString("Invalid property value.") : WebInspector.UIString("Unknown property name."); + return exclamationElement; +} + WebInspector.StylesSidebarPane.prototype = { _contextMenuEventFired: function(event) { // We start editing upon click -> default navigation to resources panel is not available // Hence we add a soft context menu for hrefs. - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendApplicableItems(event.target); - contextMenu.show(event); + contextMenu.show(); }, get _forcedPseudoClasses() @@ -161,8 +170,7 @@ WebInspector.StylesSidebarPane.prototype = { update: function(node, forceUpdate) { - if (this._spectrum.visible) - this._spectrum.hide(false); + this._spectrumHelper.hide(); var refresh = false; @@ -353,6 +361,7 @@ WebInspector.StylesSidebarPane.prototype = { { this._sectionsContainer.removeChildren(); this._computedStylePane.bodyElement.removeChildren(); + this._linkifier.reset(); var styleRules = this._rebuildStyleRules(node, styles); var usedProperties = {}; @@ -785,8 +794,7 @@ WebInspector.StylesSidebarPane.prototype = { willHide: function() { - if (this._spectrum.visible) - this._spectrum.hide(false); + this._spectrumHelper.hide(); }, __proto__: WebInspector.SidebarPane.prototype @@ -1191,13 +1199,8 @@ WebInspector.StylePropertiesSection.prototype = { return link; } - if (this.styleRule.sourceURL) { - var uiLocation = this.rule.uiLocation(); - if (uiLocation) - return linkifyUncopyable(uiLocation.url(), uiLocation.lineNumber); - else - return linkifyUncopyable(this.styleRule.sourceURL, this.rule.sourceLine); - } + if (this.styleRule.sourceURL) + return this._parentPane._linkifier.linkifyCSSRuleLocation(this.rule) || linkifyUncopyable(this.styleRule.sourceURL, this.rule.sourceLine); if (!this.rule) return document.createTextNode(""); @@ -1234,6 +1237,9 @@ WebInspector.StylePropertiesSection.prototype = { if (!this.editable) return; + if (!window.getSelection().isCollapsed) + return; + if (this._checkWillCancelEditing()) return; @@ -1456,8 +1462,10 @@ WebInspector.ComputedStylePropertiesSection.prototype = { treeElement.appendChild(childElement); if (property.inactive || section.isPropertyOverloaded(property.name)) childElement.listItemElement.addStyleClass("overloaded"); - if (!property.parsedOk) + if (!property.parsedOk) { childElement.listItemElement.addStyleClass("not-parsed-ok"); + childElement.listItemElement.insertBefore(WebInspector.StylesSidebarPane.createExclamationMark(property.name), childElement.listItemElement.firstChild); + } } } } @@ -1759,32 +1767,21 @@ WebInspector.StylePropertyTreeElement.prototype = { var format = getFormat(); var hasSpectrum = self._parentPane; - var spectrum = hasSpectrum ? self._parentPane._spectrum : null; + var spectrumHelper = hasSpectrum ? self._parentPane._spectrumHelper : null; + var spectrum = spectrumHelper ? spectrumHelper.spectrum() : null; - var swatchElement = document.createElement("span"); - var swatchInnerElement = swatchElement.createChild("span", "swatch-inner"); - swatchElement.title = WebInspector.UIString("Click to open a colorpicker. Shift-click to change color format"); - - swatchElement.className = "swatch"; - - swatchElement.addEventListener("mousedown", consumeEvent, false); - swatchElement.addEventListener("click", swatchClick, false); - swatchElement.addEventListener("dblclick", consumeEvent, false); - - swatchInnerElement.style.backgroundColor = text; + var colorSwatch = new WebInspector.ColorSwatch(); + colorSwatch.setColorString(text); + colorSwatch.element.addEventListener("click", swatchClick, false); var scrollerElement = hasSpectrum ? self._parentPane._computedStylePane.element.parentElement : null; function spectrumChanged(e) { color = e.data; - var colorString = color.toString(); - colorValueElement.textContent = colorString; - spectrum.displayText = colorString; - swatchInnerElement.style.backgroundColor = colorString; - + colorSwatch.setColorString(colorString); self.applyStyleText(nameElement.textContent + ": " + valueElement.textContent, false, false, false); } @@ -1795,7 +1792,7 @@ WebInspector.StylePropertyTreeElement.prototype = { var propertyText = !commitEdit && self.originalPropertyText ? self.originalPropertyText : (nameElement.textContent + ": " + valueElement.textContent); self.applyStyleText(propertyText, true, true, false); spectrum.removeEventListener(WebInspector.Spectrum.Events.ColorChanged, spectrumChanged); - spectrum.removeEventListener(WebInspector.Spectrum.Events.Hidden, spectrumHidden); + spectrumHelper.removeEventListener(WebInspector.SpectrumPopupHelper.Events.Hidden, spectrumHidden); delete self._parentPane._isEditingStyle; delete self.originalPropertyText; @@ -1803,24 +1800,24 @@ WebInspector.StylePropertyTreeElement.prototype = { function repositionSpectrum() { - spectrum.reposition(swatchElement); + spectrumHelper.reposition(colorSwatch.element); } function swatchClick(e) { // Shift + click toggles color formats. // Click opens colorpicker, only if the element is not in computed styles section. - if (!spectrum || e.shiftKey) + if (!spectrumHelper || e.shiftKey) changeColorDisplay(e); else { - var visible = spectrum.toggle(swatchElement, color, format); + var visible = spectrumHelper.toggle(colorSwatch.element, color, format); if (visible) { spectrum.displayText = color.toString(format); self.originalPropertyText = self.property.propertyText; self._parentPane._isEditingStyle = true; spectrum.addEventListener(WebInspector.Spectrum.Events.ColorChanged, spectrumChanged); - spectrum.addEventListener(WebInspector.Spectrum.Events.Hidden, spectrumHidden); + spectrumHelper.addEventListener(WebInspector.SpectrumPopupHelper.Events.Hidden, spectrumHidden); scrollerElement.addEventListener("scroll", repositionSpectrum, false); } @@ -1907,7 +1904,7 @@ WebInspector.StylePropertyTreeElement.prototype = { } var container = document.createElement("nobr"); - container.appendChild(swatchElement); + container.appendChild(colorSwatch.element); container.appendChild(colorValueElement); return container; } @@ -1940,10 +1937,7 @@ WebInspector.StylePropertyTreeElement.prototype = { this.listItemElement.addStyleClass("not-parsed-ok"); // Add a separate exclamation mark IMG element with a tooltip. - var exclamationElement = document.createElement("img"); - exclamationElement.className = "exclamation-mark"; - exclamationElement.title = WebInspector.CSSCompletions.cssPropertiesMetainfo.keySet()[this.property.name.toLowerCase()] ? WebInspector.UIString("Invalid property value.") : WebInspector.UIString("Unknown property name."); - this.listItemElement.insertBefore(exclamationElement, this.listItemElement.firstChild); + this.listItemElement.insertBefore(WebInspector.StylesSidebarPane.createExclamationMark(this.property.name), this.listItemElement.firstChild); } if (this.property.inactive) this.listItemElement.addStyleClass("inactive"); @@ -2048,6 +2042,9 @@ WebInspector.StylePropertyTreeElement.prototype = { _mouseClick: function(event) { + if (!window.getSelection().isCollapsed) + return; + event.consume(true); if (event.target === this.listItemElement) { diff --git a/Source/WebCore/inspector/front-end/StylesSourceMapping.js b/Source/WebCore/inspector/front-end/StylesSourceMapping.js index 4de7509dc..bd7e26c2a 100644 --- a/Source/WebCore/inspector/front-end/StylesSourceMapping.js +++ b/Source/WebCore/inspector/front-end/StylesSourceMapping.js @@ -49,7 +49,7 @@ WebInspector.StylesSourceMapping.prototype = { */ rawLocationToUILocation: function(rawLocation) { - var location = /** @type WebInspector.CSSLocation */ rawLocation; + var location = /** @type WebInspector.CSSLocation */ (rawLocation); var uiSourceCode = this._uiSourceCodeForURL[location.url]; return new WebInspector.UILocation(uiSourceCode, location.lineNumber, 0); }, @@ -67,7 +67,7 @@ WebInspector.StylesSourceMapping.prototype = { _uiSourceCodeAddedToWorkspace: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.data; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data); if (!uiSourceCode.url || this._uiSourceCodeForURL[uiSourceCode.url]) return; if (uiSourceCode.contentType() !== WebInspector.resourceTypes.Stylesheet) diff --git a/Source/WebCore/inspector/front-end/TabbedEditorContainer.js b/Source/WebCore/inspector/front-end/TabbedEditorContainer.js index d4a4e9e0a..056adbca1 100644 --- a/Source/WebCore/inspector/front-end/TabbedEditorContainer.js +++ b/Source/WebCore/inspector/front-end/TabbedEditorContainer.js @@ -127,14 +127,14 @@ WebInspector.TabbedEditorContainer.prototype = { _scrollChanged: function(event) { - var lineNumber = /** @type {number} */ event.data; + var lineNumber = /** @type {number} */ (event.data); this._history.updateScrollLineNumber(this._currentFile.url, lineNumber); this._history.save(this._previouslyViewedFilesSetting); }, _selectionChanged: function(event) { - var range = /** @type {WebInspector.TextRange} */ event.data; + var range = /** @type {WebInspector.TextRange} */ (event.data); this._history.updateSelectionRange(this._currentFile.url, range); this._history.save(this._previouslyViewedFilesSetting); }, @@ -295,8 +295,8 @@ WebInspector.TabbedEditorContainer.prototype = { */ _tabClosed: function(event) { - var tabId = /** @type {string} */ event.data.tabId; - var userGesture = /** @type {boolean} */ event.data.isUserGesture; + var tabId = /** @type {string} */ (event.data.tabId); + var userGesture = /** @type {boolean} */ (event.data.isUserGesture); var uiSourceCode = this._files[tabId]; if (this._currentFile === uiSourceCode) { @@ -319,8 +319,8 @@ WebInspector.TabbedEditorContainer.prototype = { */ _tabSelected: function(event) { - var tabId = /** @type {string} */ event.data.tabId; - var userGesture = /** @type {boolean} */ event.data.isUserGesture; + var tabId = /** @type {string} */ (event.data.tabId); + var userGesture = /** @type {boolean} */ (event.data.isUserGesture); var uiSourceCode = this._files[tabId]; this._innerShowFile(uiSourceCode, userGesture); @@ -362,25 +362,25 @@ WebInspector.TabbedEditorContainer.prototype = { _uiSourceCodeTitleChanged: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.target; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); this._updateFileTitle(uiSourceCode); }, _uiSourceCodeWorkingCopyChanged: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.target; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); this._updateFileTitle(uiSourceCode); }, _uiSourceCodeWorkingCopyCommitted: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.target; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); this._updateFileTitle(uiSourceCode); }, _uiSourceCodeFormattedChanged: function(event) { - var uiSourceCode = /** @type {WebInspector.UISourceCode} */ event.target; + var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); this._updateFileTitle(uiSourceCode); }, diff --git a/Source/WebCore/inspector/front-end/TabbedPane.js b/Source/WebCore/inspector/front-end/TabbedPane.js index 19b787fa9..70d56619e 100644 --- a/Source/WebCore/inspector/front-end/TabbedPane.js +++ b/Source/WebCore/inspector/front-end/TabbedPane.js @@ -45,6 +45,7 @@ WebInspector.TabbedPane = function() this._tabsHistory = []; this._tabsById = {}; this.element.addEventListener("click", this.focus.bind(this), false); + this.element.addEventListener("mouseup", this.onMouseUp.bind(this), false); this._dropDownButton = this._createDropDownButton(); } @@ -93,6 +94,16 @@ WebInspector.TabbedPane.prototype = { }, /** + * @param {Event} event + */ + onMouseUp: function(event) + { + // This is needed to prevent middle-click pasting on linux when tabs are clicked. + if (event.button === 1) + event.consume(true); + }, + + /** * @param {string} id * @param {string} tabTitle * @param {WebInspector.View} view @@ -713,11 +724,11 @@ WebInspector.TabbedPaneTab.prototype = { this._tabbedPane.closeAllTabs(true); } - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendItem(WebInspector.UIString("Close"), close.bind(this)); contextMenu.appendItem(WebInspector.UIString("Close Others"), closeOthers.bind(this)); contextMenu.appendItem(WebInspector.UIString("Close All"), closeAll.bind(this)); - contextMenu.show(event); + contextMenu.show(); }, /** diff --git a/Source/WebCore/inspector/front-end/TimelineModel.js b/Source/WebCore/inspector/front-end/TimelineModel.js index e50cd5b60..924d353a6 100644 --- a/Source/WebCore/inspector/front-end/TimelineModel.js +++ b/Source/WebCore/inspector/front-end/TimelineModel.js @@ -295,7 +295,7 @@ WebInspector.TimelineModelLoader.prototype = { var items; try { - items = /** @type {Array} */ JSON.parse(json); + items = /** @type {Array} */ (JSON.parse(json)); } catch (e) { WebInspector.showErrorMessage("Malformed timeline data."); this._model.reset(); diff --git a/Source/WebCore/inspector/front-end/TimelineOverviewPane.js b/Source/WebCore/inspector/front-end/TimelineOverviewPane.js index 5c8ddc77c..dec588b9a 100644 --- a/Source/WebCore/inspector/front-end/TimelineOverviewPane.js +++ b/Source/WebCore/inspector/front-end/TimelineOverviewPane.js @@ -940,7 +940,8 @@ WebInspector.TimelineCategoryStrips.prototype = { // This bar may be merged with previous -- so just adjust the previous bar. const barsMergeThreshold = 2; if (bar && bar.category === category && bar.end + barsMergeThreshold >= recordStart) { - bar.end = recordEnd; + if (recordEnd > bar.end) + bar.end = recordEnd; return; } if (bar) diff --git a/Source/WebCore/inspector/front-end/TimelinePanel.js b/Source/WebCore/inspector/front-end/TimelinePanel.js index 50f4b6ada..d31c80a9b 100644 --- a/Source/WebCore/inspector/front-end/TimelinePanel.js +++ b/Source/WebCore/inspector/front-end/TimelinePanel.js @@ -131,9 +131,9 @@ WebInspector.TimelinePanel = function() this._headerLineCount = 1; - this._mainThreadTasks = []; + this._mainThreadTasks = /** @type {!Array.<{startTime: number, endTime: number}>} */ ([]); this._mainThreadMonitoringEnabled = false; - if (WebInspector.experimentsSettings.mainThreadMonitoring.isEnabled()) + if (WebInspector.settings.showCpuOnTimelineRuler.get() && Capabilities.timelineCanMonitorMainThread) this._enableMainThreadMonitoring(); this._createFileSelector(); @@ -334,11 +334,11 @@ WebInspector.TimelinePanel.prototype = { _contextMenu: function(event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); if (InspectorFrontendHost.canSave()) contextMenu.appendItem(WebInspector.UIString("Save Timeline data\u2026"), this._saveToFile.bind(this), this._operationInProgress); contextMenu.appendItem(WebInspector.UIString("Load Timeline data\u2026"), this._fileSelectorElement.click.bind(this._fileSelectorElement), this._operationInProgress); - contextMenu.show(event); + contextMenu.show(); }, _saveToFile: function() @@ -553,17 +553,17 @@ WebInspector.TimelinePanel.prototype = { this._automaticallySizeWindow = false; var records = this._model.records; for (var i = 0; i < records.length; ++i) - this._innerAddRecordToTimeline(records[i], this._rootRecord()); + this._innerAddRecordToTimeline(records[i]); this._invalidateAndScheduleRefresh(false); }, _onTimelineEventRecorded: function(event) { - if (this._innerAddRecordToTimeline(event.data, this._rootRecord())) + if (this._innerAddRecordToTimeline(event.data)) this._invalidateAndScheduleRefresh(false); }, - _innerAddRecordToTimeline: function(record, parentRecord) + _innerAddRecordToTimeline: function(record) { if (record.type === WebInspector.TimelineModel.RecordType.Program) { this._mainThreadTasks.push({ @@ -572,7 +572,7 @@ WebInspector.TimelinePanel.prototype = { }); } - var records = this._presentationModel.addRecord(record, parentRecord); + var records = this._presentationModel.addRecord(record); this._allRecordsCount += records.length; var timeStampRecords = this._timeStampRecords; var hasVisibleRecords = false; @@ -875,7 +875,7 @@ WebInspector.TimelinePanel.prototype = { var taskIndex = insertionIndexForObjectInListSortedByFunction(startTime, tasks, compareEndTime); var container = this._cpuBarsElement; - var element = container.firstChild.nextSibling; + var element = container.firstChild; var lastElement; var lastLeft; var lastRight; @@ -893,6 +893,7 @@ WebInspector.TimelinePanel.prototype = { var gap = Math.floor(left) - Math.ceil(lastRight); if (gap < minGap) { lastRight = right; + lastElement._tasksInfo.lastTaskIndex = taskIndex; continue; } lastElement.style.width = (lastRight - lastLeft) + "px"; @@ -902,6 +903,7 @@ WebInspector.TimelinePanel.prototype = { element = container.createChild("div", "timeline-graph-bar"); element.style.left = left + "px"; + element._tasksInfo = {tasks: tasks, firstTaskIndex: taskIndex, lastTaskIndex: taskIndex}; lastLeft = left; lastRight = right; @@ -914,6 +916,7 @@ WebInspector.TimelinePanel.prototype = { while (element) { var nextElement = element.nextSibling; + element._tasksInfo = null; container.removeChild(element); element = nextElement; } @@ -921,12 +924,8 @@ WebInspector.TimelinePanel.prototype = { _enableMainThreadMonitoring: function() { - ++this._headerLineCount; - var container = this._timelineGrid.gridHeaderElement; - this._cpuBarsElement = container.createChild("div", "timeline-cpu-bars timeline-category-program"); - var cpuBarsLabel = this._cpuBarsElement.createChild("span", "timeline-cpu-bars-label"); - cpuBarsLabel.textContent = WebInspector.UIString("CPU"); + this._cpuBarsElement = container.createChild("div", "timeline-cpu-bars"); const headerBorderWidth = 1; const headerMargin = 2; @@ -997,6 +996,8 @@ WebInspector.TimelinePanel.prototype = { } else { if (anchor.row && anchor.row._record) anchor.row._record.generatePopupContent(showCallback); + else if (anchor._tasksInfo) + popover.show(this._presentationModel.generateMainThreadBarPopupContent(anchor._tasksInfo), anchor); } function showCallback(popupContent) diff --git a/Source/WebCore/inspector/front-end/TimelinePresentationModel.js b/Source/WebCore/inspector/front-end/TimelinePresentationModel.js index 7020d1aa3..744ee89f8 100644 --- a/Source/WebCore/inspector/front-end/TimelinePresentationModel.js +++ b/Source/WebCore/inspector/front-end/TimelinePresentationModel.js @@ -274,7 +274,7 @@ WebInspector.TimelinePresentationModel.prototype = { this._frames.push(frame); }, - addRecord: function(record, parentRecord) + addRecord: function(record) { if (this._minimumRecordTime === -1 || record.startTime < this._minimumRecordTime) this._minimumRecordTime = WebInspector.TimelineModel.startTimeInSeconds(record); @@ -288,7 +288,7 @@ WebInspector.TimelinePresentationModel.prototype = { var formattedRecords = []; var recordsCount = records.length; for (var i = 0; i < recordsCount; ++i) - formattedRecords.push(this._innerAddRecord(records[i], parentRecord)); + formattedRecords.push(this._innerAddRecord(records[i], this._rootRecord)); return formattedRecords; }, @@ -442,6 +442,36 @@ WebInspector.TimelinePresentationModel.prototype = { return true; }, + /** + * @param {{tasks: !Array.<{startTime: number, endTime: number}>, firstTaskIndex: number, lastTaskIndex: number}} info + * @return {!Element} + */ + generateMainThreadBarPopupContent: function(info) + { + var firstTaskIndex = info.firstTaskIndex; + var lastTaskIndex = info.lastTaskIndex; + var tasks = info.tasks; + var messageCount = lastTaskIndex - firstTaskIndex + 1; + var cpuTime = 0; + + for (var i = firstTaskIndex; i <= lastTaskIndex; ++i) { + var task = tasks[i]; + cpuTime += task.endTime - task.startTime; + } + var startTime = tasks[firstTaskIndex].startTime; + var endTime = tasks[lastTaskIndex].endTime; + var duration = endTime - startTime; + var offset = this._minimumRecordTime; + + var contentHelper = new WebInspector.TimelinePresentationModel.PopupContentHelper(WebInspector.UIString("CPU")); + var durationText = WebInspector.UIString("%s (at %s)", Number.secondsToString(duration, true), + Number.secondsToString(startTime - offset, true)); + contentHelper._appendTextRow(WebInspector.UIString("Duration"), durationText); + contentHelper._appendTextRow(WebInspector.UIString("CPU time"), Number.secondsToString(cpuTime, true)); + contentHelper._appendTextRow(WebInspector.UIString("Message Count"), messageCount); + return contentHelper._contentTable; + }, + __proto__: WebInspector.Object.prototype } diff --git a/Source/WebCore/inspector/front-end/Toolbar.js b/Source/WebCore/inspector/front-end/Toolbar.js index 5756e73d7..0d2d7285d 100644 --- a/Source/WebCore/inspector/front-end/Toolbar.js +++ b/Source/WebCore/inspector/front-end/Toolbar.js @@ -183,7 +183,6 @@ WebInspector.Toolbar.prototype = { { this._setDropdownVisible(false); - var toolbar = document.getElementById("toolbar"); if (this.element.scrollHeight > this.element.clientHeight) this._dropdownButton.removeStyleClass("hidden"); else @@ -217,7 +216,7 @@ WebInspector.ToolbarDropdown.prototype = { var top = this._arrow.totalOffsetTop() + this._arrow.clientHeight; this._arrow.addStyleClass("dropdown-visible"); this.element.style.top = top + "px"; - this.element.style.left = this._arrow.totalOffsetLeft() + "px"; + this.element.style.right = window.innerWidth - this._arrow.totalOffsetLeft() - this._arrow.clientWidth + "px"; this._contentElement.style.maxHeight = window.innerHeight - top - 20 + "px"; this._toolbar.element.appendChild(this.element); }, diff --git a/Source/WebCore/inspector/front-end/UISourceCode.js b/Source/WebCore/inspector/front-end/UISourceCode.js index 32010a40b..98a514651 100644 --- a/Source/WebCore/inspector/front-end/UISourceCode.js +++ b/Source/WebCore/inspector/front-end/UISourceCode.js @@ -33,17 +33,18 @@ * @constructor * @extends {WebInspector.Object} * @implements {WebInspector.ContentProvider} + * @param {WebInspector.Workspace} workspace * @param {string} url - * @param {WebInspector.ContentProvider} contentProvider + * @param {WebInspector.ResourceType} contentType * @param {boolean} isEditable */ -WebInspector.UISourceCode = function(url, contentProvider, isEditable) +WebInspector.UISourceCode = function(workspace, url, contentType, isEditable) { + this._workspace = workspace; this._url = url; this._parsedURL = new WebInspector.ParsedURL(url); - this._contentProvider = contentProvider; + this._contentType = contentType; this._isEditable = isEditable; - this.isContentScript = false; /** * @type Array.<function(?string,boolean,string)> */ @@ -73,7 +74,8 @@ WebInspector.UISourceCode.Events = { TitleChanged: "TitleChanged", ConsoleMessageAdded: "ConsoleMessageAdded", ConsoleMessageRemoved: "ConsoleMessageRemoved", - ConsoleMessagesCleared: "ConsoleMessagesCleared" + ConsoleMessagesCleared: "ConsoleMessagesCleared", + SourceMappingChanged: "SourceMappingChanged", } WebInspector.UISourceCode.prototype = { @@ -116,7 +118,7 @@ WebInspector.UISourceCode.prototype = { */ contentType: function() { - return this._contentProvider.contentType(); + return this._contentType; }, /** @@ -162,7 +164,7 @@ WebInspector.UISourceCode.prototype = { } this._requestContentCallbacks.push(callback); if (this._requestContentCallbacks.length === 1) - this._contentProvider.requestContent(this._fireContentAvailable.bind(this)); + this._workspace.requestFileContent(this, this._fireContentAvailable.bind(this)); }, /** @@ -170,7 +172,7 @@ WebInspector.UISourceCode.prototype = { */ requestOriginalContent: function(callback) { - this._contentProvider.requestContent(callback); + this._workspace.requestFileContent(this, callback); }, /** @@ -191,7 +193,7 @@ WebInspector.UISourceCode.prototype = { var oldWorkingCopy = this._workingCopy; delete this._workingCopy; this.dispatchEventToListeners(WebInspector.UISourceCode.Events.WorkingCopyCommitted, {oldWorkingCopy: oldWorkingCopy, workingCopy: this.workingCopy()}); - WebInspector.workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeContentCommitted, { uiSourceCode: this, content: this._content }); + this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeContentCommitted, { uiSourceCode: this, content: this._content }); if (this._url && WebInspector.fileManager.isURLSaved(this._url)) { WebInspector.fileManager.save(this._url, this._content, false); WebInspector.fileManager.close(this._url); @@ -371,8 +373,13 @@ WebInspector.UISourceCode.prototype = { searchInContent: function(query, caseSensitive, isRegex, callback) { var content = this.content(); - var provider = content ? new WebInspector.StaticContentProvider(this._contentProvider.contentType(), content) : this._contentProvider; - provider.searchInContent(query, caseSensitive, isRegex, callback); + if (content) { + var provider = new WebInspector.StaticContentProvider(this.contentType(), content); + provider.searchInContent(query, caseSensitive, isRegex, callback); + return; + } + + this._workspace.searchInFileContent(this, query, caseSensitive, isRegex, callback); }, /** @@ -392,16 +399,8 @@ WebInspector.UISourceCode.prototype = { callbacks[i](content, contentEncoded, mimeType); if (this._formatOnLoad) { - function formattedCallback() - { - for (var i = 0; i < this._pendingFormattedCallbacks.length; ++i) - this._pendingFormattedCallbacks[i](); - delete this._pendingFormattedCallbacks; - - } - delete this._formatOnLoad; - this.setFormatted(true, formattedCallback.bind(this)); + this.setFormatted(true); } }, @@ -495,14 +494,6 @@ WebInspector.UISourceCode.prototype = { /** * @return {boolean} */ - togglingFormatter: function() - { - return this._togglingFormatter; - }, - - /** - * @return {boolean} - */ formatted: function() { return !!this._formatted; @@ -510,23 +501,16 @@ WebInspector.UISourceCode.prototype = { /** * @param {boolean} formatted - * @param {function()=} callback */ - setFormatted: function(formatted, callback) + setFormatted: function(formatted) { - callback = callback || function() {}; if (!this.contentLoaded()) { - if (!this._pendingFormattedCallbacks) - this._pendingFormattedCallbacks = []; - this._pendingFormattedCallbacks.push(callback); this._formatOnLoad = formatted; return; } - if (this._formatted === formatted) { - callback(); + if (this._formatted === formatted) return; - } this._formatted = formatted; @@ -557,14 +541,11 @@ WebInspector.UISourceCode.prototype = { */ function formattedChanged(content, formatterMapping) { - this._togglingFormatter = true; this._content = content; delete this._workingCopy; - this.dispatchEventToListeners(WebInspector.UISourceCode.Events.FormattedChanged, {content: content}); - delete this._togglingFormatter; this._formatterMapping = formatterMapping; + this.dispatchEventToListeners(WebInspector.UISourceCode.Events.FormattedChanged, {content: content}); this.updateLiveLocations(); - callback(); } } }, @@ -584,6 +565,7 @@ WebInspector.UISourceCode.prototype = { setSourceMapping: function(sourceMapping) { this._sourceMapping = sourceMapping; + this.dispatchEventToListeners(WebInspector.UISourceCode.Events.SourceMappingChanged, null); }, __proto__: WebInspector.Object.prototype diff --git a/Source/WebCore/inspector/front-end/UISourceCodeFrame.js b/Source/WebCore/inspector/front-end/UISourceCodeFrame.js index 7e0bf51cd..43d762dbe 100644 --- a/Source/WebCore/inspector/front-end/UISourceCodeFrame.js +++ b/Source/WebCore/inspector/front-end/UISourceCodeFrame.js @@ -82,7 +82,7 @@ WebInspector.UISourceCodeFrame.prototype = { */ _onFormattedChanged: function(event) { - var content = /** @type {string} */ event.data.content; + var content = /** @type {string} */ (event.data.content); this._innerSetContent(content); }, diff --git a/Source/WebCore/inspector/front-end/UserAgentSupport.js b/Source/WebCore/inspector/front-end/UserAgentSupport.js index b3edc6677..4859f65fc 100644 --- a/Source/WebCore/inspector/front-end/UserAgentSupport.js +++ b/Source/WebCore/inspector/front-end/UserAgentSupport.js @@ -33,10 +33,12 @@ */ WebInspector.UserAgentSupport = function() { + this._userAgentOverrideEnabled = false; this._deviceMetricsOverrideEnabled = false; this._geolocationPositionOverrideEnabled = false; this._deviceOrientationOverrideEnabled = false; + WebInspector.settings.userAgent.addChangeListener(this._userAgentChanged, this); WebInspector.settings.deviceMetrics.addChangeListener(this._deviceMetricsChanged, this); WebInspector.settings.deviceFitWindow.addChangeListener(this._deviceMetricsChanged, this); WebInspector.settings.geolocationOverride.addChangeListener(this._geolocationPositionChanged, this); @@ -307,6 +309,14 @@ WebInspector.UserAgentSupport.DeviceOrientation.clearDeviceOrientationOverride = } WebInspector.UserAgentSupport.prototype = { + toggleUserAgentOverride: function(enabled) + { + if (enabled === this._userAgentOverrideEnabled) + return; + this._userAgentOverrideEnabled = enabled; + this._userAgentChanged(); + }, + toggleDeviceMetricsOverride: function(enabled) { if (enabled === this._deviceMetricsOverrideEnabled) @@ -331,6 +341,11 @@ WebInspector.UserAgentSupport.prototype = { this._deviceOrientationChanged(); }, + _userAgentChanged: function() + { + NetworkAgent.setUserAgentOverride(this._userAgentOverrideEnabled ? WebInspector.settings.userAgent.get() : ""); + }, + _deviceMetricsChanged: function() { var metrics = WebInspector.UserAgentSupport.DeviceMetrics.parseSetting(this._deviceMetricsOverrideEnabled ? WebInspector.settings.deviceMetrics.get() : ""); diff --git a/Source/WebCore/inspector/front-end/View.js b/Source/WebCore/inspector/front-end/View.js index d3adff99b..f5ff3c95e 100644 --- a/Source/WebCore/inspector/front-end/View.js +++ b/Source/WebCore/inspector/front-end/View.js @@ -360,6 +360,21 @@ WebInspector.View.prototype = { WebInspector.setCurrentFocusElement(element); }, + /** + * @return {Size} + */ + measurePreferredSize: function() + { + this._loadCSSIfNeeded(); + WebInspector.View._originalAppendChild.call(document.body, this.element); + this.element.positionAt(0, 0); + var result = new Size(this.element.offsetWidth, this.element.offsetHeight); + this.element.positionAt(undefined, undefined); + WebInspector.View._originalRemoveChild.call(document.body, this.element); + this._disableCSSIfNeeded(); + return result; + }, + __proto__: WebInspector.Object.prototype } diff --git a/Source/WebCore/inspector/front-end/WatchExpressionsSidebarPane.js b/Source/WebCore/inspector/front-end/WatchExpressionsSidebarPane.js index 1d92b7a0a..0fbe28c11 100644 --- a/Source/WebCore/inspector/front-end/WatchExpressionsSidebarPane.js +++ b/Source/WebCore/inspector/front-end/WatchExpressionsSidebarPane.js @@ -52,16 +52,18 @@ WebInspector.WatchExpressionsSidebarPane.prototype = { this.section = new WebInspector.WatchExpressionsSection(); this.bodyElement.appendChild(this.section.element); - + var refreshButton = document.createElement("button"); refreshButton.className = "pane-title-button refresh"; refreshButton.addEventListener("click", this._refreshButtonClicked.bind(this), false); + refreshButton.title = WebInspector.UIString("Refresh"); this.titleElement.appendChild(refreshButton); - + var addButton = document.createElement("button"); addButton.className = "pane-title-button add"; addButton.addEventListener("click", this._addButtonClicked.bind(this), false); this.titleElement.appendChild(addButton); + addButton.title = WebInspector.UIString("Add watch expression"); this._requiresUpdate = true; if (WebInspector.settings.watchExpressions.get().length > 0) @@ -143,6 +145,7 @@ WebInspector.WatchExpressionsSection = function() this.element.addEventListener("mousemove", this._mouseMove.bind(this), true); this.element.addEventListener("mouseout", this._mouseOut.bind(this), true); this.element.addEventListener("dblclick", this._sectionDoubleClick.bind(this), false); + this.emptyElement.addEventListener("contextmenu", this._emptyElementContextMenu.bind(this), false); } WebInspector.WatchExpressionsSection.NewWatchExpression = "\xA0"; @@ -210,7 +213,7 @@ WebInspector.WatchExpressionsSection.prototype = { if (!expression) continue; - WebInspector.runtimeModel.evaluate(expression, this._watchObjectGroupId, false, true, false, appendResult.bind(this, expression, i)); + WebInspector.runtimeModel.evaluate(expression, this._watchObjectGroupId, false, true, false, false, appendResult.bind(this, expression, i)); } if (!propertyCount) { @@ -326,6 +329,13 @@ WebInspector.WatchExpressionsSection.prototype = { this._lastMouseMovePageY = pageY; }, + _emptyElementContextMenu: function(event) + { + var contextMenu = new WebInspector.ContextMenu(event); + contextMenu.appendItem(WebInspector.UIString("Add watch expression"), this.addNewExpressionAndEdit.bind(this)); + contextMenu.show(); + }, + __proto__: WebInspector.ObjectPropertiesSection.prototype } @@ -390,25 +400,28 @@ WebInspector.WatchExpressionTreeElement.prototype = { this.listItemElement.addEventListener("contextmenu", this._contextMenu.bind(this), false); this.listItemElement.insertBefore(deleteButton, this.listItemElement.firstChild); }, - + /** * @param {WebInspector.ContextMenu} contextMenu * @override */ populateContextMenu: function(contextMenu) { - contextMenu.appendItem(WebInspector.UIString("Delete watch expression"), this._deleteButtonClicked.bind(this)); + if (!this.isEditing()) { + contextMenu.appendItem(WebInspector.UIString("Add watch expression"), this.treeOutline.section.addNewExpressionAndEdit.bind(this.treeOutline.section)); + contextMenu.appendItem(WebInspector.UIString("Delete watch expression"), this._deleteButtonClicked.bind(this)); + } if (this.treeOutline.section.watchExpressions.length > 1) contextMenu.appendItem(WebInspector.UIString("Delete all watch expressions"), this._deleteAllButtonClicked.bind(this)); }, _contextMenu: function(event) { - var contextMenu = new WebInspector.ContextMenu(); + var contextMenu = new WebInspector.ContextMenu(event); this.populateContextMenu(contextMenu); - contextMenu.show(event); + contextMenu.show(); }, - + _deleteAllButtonClicked: function() { this.treeOutline.section._deleteAllExpressions(); diff --git a/Source/WebCore/inspector/front-end/WebKit.qrc b/Source/WebCore/inspector/front-end/WebKit.qrc index 467ee66dd..e57c894db 100644 --- a/Source/WebCore/inspector/front-end/WebKit.qrc +++ b/Source/WebCore/inspector/front-end/WebKit.qrc @@ -112,11 +112,12 @@ <file>NavigatorView.js</file> <file>NetworkItemView.js</file> <file>NetworkLog.js</file> - <file>NetworkUISourceCodeProvider.js</file> <file>NetworkManager.js</file> <file>NetworkPanel.js</file> <file>NetworkPanelDescriptor.js</file> <file>NetworkRequest.js</file> + <file>NetworkUISourceCodeProvider.js</file> + <file>NetworkWorkspaceProvider.js</file> <file>Object.js</file> <file>ObjectPopoverHelper.js</file> <file>ObjectPropertiesSection.js</file> @@ -241,6 +242,7 @@ <file>resourcesPanel.css</file> <file>revisionHistory.css</file> <file>scriptsPanel.css</file> + <file>spectrum.css</file> <file>splitView.css</file> <file>tabbedPane.css</file> <file>textEditor.css</file> diff --git a/Source/WebCore/inspector/front-end/WorkersSidebarPane.js b/Source/WebCore/inspector/front-end/WorkersSidebarPane.js index 3e344680c..1eae6bf83 100644 --- a/Source/WebCore/inspector/front-end/WorkersSidebarPane.js +++ b/Source/WebCore/inspector/front-end/WorkersSidebarPane.js @@ -117,7 +117,7 @@ WebInspector.WorkersSidebarPane.prototype = { _autoattachToWorkersClicked: function(event) { - WorkerAgent.setAutoconnectToWorkers(event.target.checked); + WorkerAgent.setAutoconnectToWorkers(this._enableWorkersCheckbox.checked); }, __proto__: WebInspector.SidebarPane.prototype diff --git a/Source/WebCore/inspector/front-end/Workspace.js b/Source/WebCore/inspector/front-end/Workspace.js index c5515e82b..09abe16c1 100644 --- a/Source/WebCore/inspector/front-end/Workspace.js +++ b/Source/WebCore/inspector/front-end/Workspace.js @@ -44,73 +44,127 @@ WebInspector.WorkspaceController.prototype = { WebInspector.Revision.filterOutStaleRevisions(); this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectWillReset, this._workspace.project()); this._workspace.project().reset(); + this._workspace.reset(); this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectDidReset, this._workspace.project()); }, _frameAdded: function(event) { - var frame = /** @type {WebInspector.ResourceTreeFrame} */ event.data; + var frame = /** @type {WebInspector.ResourceTreeFrame} */ (event.data); if (frame.isMainFrame()) WebInspector.Revision.filterOutStaleRevisions(); } } /** + * @constructor + * @param {string} path + * @param {WebInspector.ResourceType} contentType + * @param {boolean} isEditable + * @param {boolean=} isContentScript + * @param {boolean=} isSnippet + */ +WebInspector.FileDescriptor = function(path, contentType, isEditable, isContentScript, isSnippet) +{ + this.path = path; + this.contentType = contentType; + this.isEditable = isEditable; + this.isContentScript = isContentScript || false; + this.isSnippet = isSnippet || false; +} + +/** + * @interface + */ +WebInspector.WorkspaceProvider = function() { } + +WebInspector.WorkspaceProvider.Events = { + FileAdded: "FileAdded", + FileRemoved: "FileRemoved" +} + +WebInspector.WorkspaceProvider.prototype = { + /** + * @param {string} path + * @param {function(?string,boolean,string)} callback + */ + requestFileContent: function(path, callback) { }, + + /** + * @param {string} query + * @param {boolean} caseSensitive + * @param {boolean} isRegex + * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback + */ + searchInFileContent: function(path, query, caseSensitive, isRegex, callback) { }, + + /** + * @param {string} eventType + * @param {function(WebInspector.Event)} listener + * @param {Object=} thisObject + */ + addEventListener: function(eventType, listener, thisObject) { }, + + /** + * @param {string} eventType + * @param {function(WebInspector.Event)} listener + * @param {Object=} thisObject + */ + removeEventListener: function(eventType, listener, thisObject) { } +} + +/** * @type {?WebInspector.WorkspaceController} */ WebInspector.workspaceController = null; /** + * @param {WebInspector.Workspace} workspace + * @param {WebInspector.WorkspaceProvider} workspaceProvider * @constructor */ -WebInspector.Project = function(workspace) +WebInspector.Project = function(workspace, workspaceProvider) { this._uiSourceCodes = []; this._workspace = workspace; + this._workspaceProvider = workspaceProvider; + this._workspaceProvider.addEventListener(WebInspector.WorkspaceProvider.Events.FileAdded, this._fileAdded, this); + this._workspaceProvider.addEventListener(WebInspector.WorkspaceProvider.Events.FileRemoved, this._fileRemoved, this); } WebInspector.Project.prototype = { reset: function() { + this._workspaceProvider.reset(); this._uiSourceCodes = []; }, - /** - * @param {WebInspector.UISourceCode} uiSourceCode - */ - addUISourceCode: function(uiSourceCode) + _fileAdded: function(event) { + var fileDescriptor = /** @type {WebInspector.FileDescriptor} */ (event.data); + var uiSourceCode = this.uiSourceCodeForURL(fileDescriptor.path); + if (uiSourceCode) { + // FIXME: Implement + return; + } + uiSourceCode = new WebInspector.UISourceCode(this._workspace, fileDescriptor.path, fileDescriptor.contentType, fileDescriptor.isEditable); + uiSourceCode.isContentScript = fileDescriptor.isContentScript; + uiSourceCode.isSnippet = fileDescriptor.isSnippet; this._uiSourceCodes.push(uiSourceCode); this._workspace.dispatchEventToListeners(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, uiSourceCode); }, - /** - * @param {WebInspector.UISourceCode} uiSourceCode - */ - addTemporaryUISourceCode: function(uiSourceCode) - { - uiSourceCode.isTemporary = true; - this._workspace.dispatchEventToListeners(WebInspector.UISourceCodeProvider.Events.TemporaryUISourceCodeAdded, uiSourceCode); - }, - - /** - * @param {WebInspector.UISourceCode} uiSourceCode - */ - removeUISourceCode: function(uiSourceCode) + _fileRemoved: function(event) { + var path = /** @type {string} */ (event.data); + var uiSourceCode = this.uiSourceCodeForURL(path); + if (!uiSourceCode) + return; this._uiSourceCodes.splice(this._uiSourceCodes.indexOf(uiSourceCode), 1); this._workspace.dispatchEventToListeners(WebInspector.UISourceCodeProvider.Events.UISourceCodeRemoved, uiSourceCode); }, /** - * @param {WebInspector.UISourceCode} uiSourceCode - */ - removeTemporaryUISourceCode: function(uiSourceCode) - { - this._workspace.dispatchEventToListeners(WebInspector.UISourceCodeProvider.Events.TemporaryUISourceCodeRemoved, uiSourceCode); - }, - - /** * @param {string} url * @return {?WebInspector.UISourceCode} */ @@ -129,6 +183,26 @@ WebInspector.Project.prototype = { uiSourceCodes: function() { return this._uiSourceCodes; + }, + + /** + * @param {string} path + * @param {function(?string,boolean,string)} callback + */ + requestFileContent: function(path, callback) + { + this._workspaceProvider.requestFileContent(path, callback); + }, + + /** + * @param {string} query + * @param {boolean} caseSensitive + * @param {boolean} isRegex + * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback + */ + searchInFileContent: function(path, query, caseSensitive, isRegex, callback) + { + this._workspaceProvider.searchInFileContent(path, query, caseSensitive, isRegex, callback); } } @@ -139,7 +213,8 @@ WebInspector.Project.prototype = { */ WebInspector.Workspace = function() { - this._project = new WebInspector.Project(this); + /** @type {Object.<string, WebInspector.ContentProvider>} */ + this._temporaryContentProviders = new Map(); } WebInspector.Workspace.Events = { @@ -159,11 +234,20 @@ WebInspector.Workspace.prototype = { }, /** + * @param {string} projectName + * @param {WebInspector.WorkspaceProvider} workspaceProvider + */ + addProject: function(projectName, workspaceProvider) + { + // FIXME: support multiple projects identified by project name. + this._project = new WebInspector.Project(this, workspaceProvider); + }, + + /** * @return {WebInspector.Project} */ project: function() { - // FIXME: support several projects. return this._project; }, @@ -175,6 +259,67 @@ WebInspector.Workspace.prototype = { return this._project.uiSourceCodes(); }, + /** + * @param {string} path + * @param {WebInspector.ContentProvider} contentProvider + * @param {boolean} isEditable + * @param {boolean=} isContentScript + * @param {boolean=} isSnippet + */ + addTemporaryUISourceCode: function(path, contentProvider, isEditable, isContentScript, isSnippet) + { + var uiSourceCode = new WebInspector.UISourceCode(this, path, contentProvider.contentType(), isEditable); + this._temporaryContentProviders.put(uiSourceCode, contentProvider); + uiSourceCode.isContentScript = isContentScript; + uiSourceCode.isSnippet = isSnippet; + uiSourceCode.isTemporary = true; + this.dispatchEventToListeners(WebInspector.UISourceCodeProvider.Events.TemporaryUISourceCodeAdded, uiSourceCode); + return uiSourceCode; + }, + + /** + * @param {WebInspector.UISourceCode} uiSourceCode + */ + removeTemporaryUISourceCode: function(uiSourceCode) + { + this._temporaryContentProviders.remove(uiSourceCode.url); + this.dispatchEventToListeners(WebInspector.UISourceCodeProvider.Events.TemporaryUISourceCodeRemoved, uiSourceCode); + }, + + /** + * @param {WebInspector.UISourceCode} uiSourceCode + * @param {function(?string,boolean,string)} callback + */ + requestFileContent: function(uiSourceCode, callback) + { + if (this._temporaryContentProviders.get(uiSourceCode)) { + this._temporaryContentProviders.get(uiSourceCode).requestContent(callback); + return; + } + this._project.requestFileContent(uiSourceCode.url, callback); + }, + + /** + * @param {WebInspector.UISourceCode} uiSourceCode + * @param {string} query + * @param {boolean} caseSensitive + * @param {boolean} isRegex + * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback + */ + searchInFileContent: function(uiSourceCode, query, caseSensitive, isRegex, callback) + { + if (this._temporaryContentProviders.get(uiSourceCode)) { + this._temporaryContentProviders.get(uiSourceCode).searchInContent(query, caseSensitive, isRegex, callback); + return; + } + this._project.searchInFileContent(uiSourceCode.url, query, caseSensitive, isRegex, callback); + }, + + reset: function() + { + this._temporaryContentProviders = new Map(); + }, + __proto__: WebInspector.Object.prototype } diff --git a/Source/WebCore/inspector/front-end/cm/cmdevtools.css b/Source/WebCore/inspector/front-end/cm/cmdevtools.css index 3e8be97cc..62d7f5882 100644 --- a/Source/WebCore/inspector/front-end/cm/cmdevtools.css +++ b/Source/WebCore/inspector/front-end/cm/cmdevtools.css @@ -1,10 +1,13 @@ .CodeMirror { line-height: 1.2em !important; -} -.CodeMirror-scroll { height: 100% !important; } +.CodeMirror-linenumber { + padding-top: 1px !important; + min-width: 22px !important; +} + .cm-highlight { -webkit-animation: "fadeout" 2s 0s; } @@ -13,11 +16,13 @@ to { background-color: white; } } .cm-breakpoint { - color: white !important; - margin-right: -8px !important; - margin-left: -6px !important; + color: white; + position: absolute; + right: 3px; + margin: 0 -8px 0 -6px !important; border-width: 0 8px 0px 2px !important; -webkit-border-image: url(Images/breakpointBorder.png) 1 14 1 2; + padding: 0 0 1px 100px; } .cm-breakpoint-disabled { opacity: 0.5; @@ -53,33 +58,30 @@ .cm-s-web-inspector-html span.cm-attribute {color: rgb(153, 69, 0);} .cm-s-web-inspector-html span.cm-link {color: #00e;} -.webkit-html-message-bubble { +.CodeMirror .webkit-html-message-bubble { -webkit-box-shadow: black 0px 2px 5px; -webkit-border-radius: 9px; -webkit-border-fit: lines; font-size: 10px; font-family: Lucida Grande, sans-serif; font-weight: bold; - margin: 0px 10px; - padding: 0 7px 1px; + margin: 3px 10px; + padding: 1px 2px 0; z-index: 5; } -.webkit-html-warning-message { + +.CodeMirror .webkit-html-message-bubble img { + position: relative; + top: 1px; + margin-right: 2px; +} + +.CodeMirror .webkit-html-warning-message { background-color: rgb(100%, 62%, 42%); border: 2px solid rgb(100%, 52%, 21%); } -.webkit-html-error-message { + +.CodeMirror .webkit-html-error-message { background-color: rgb(100%, 42%, 42%); border: 2px solid rgb(100%, 31%, 31%); } -.webkit-html-message-line { - padding-left: 23px; - text-indent: -20px; -} -.webkit-html-message-line-hover { - padding-left: 23px; - text-indent: -20px; - white-space: auto; - text-overflow: auto; - overflow: auto; -} diff --git a/Source/WebCore/inspector/front-end/cm/codemirror.css b/Source/WebCore/inspector/front-end/cm/codemirror.css index f0e91b2d7..ea0ecea59 100644 --- a/Source/WebCore/inspector/front-end/cm/codemirror.css +++ b/Source/WebCore/inspector/front-end/cm/codemirror.css @@ -1,173 +1,234 @@ +/* BASICS */ + .CodeMirror { - line-height: 1em; + /* Set height, width, borders, and global font properties here */ font-family: monospace; + height: 300px; +} +.CodeMirror-scroll { + /* Set scrolling behaviour here */ + overflow: auto; +} + +/* PADDING */ + +.CodeMirror-lines { + padding: 4px 0; /* Vertical padding around content */ +} +.CodeMirror pre { + padding: 0 4px; /* Horizontal padding of content */ +} + +.CodeMirror-scrollbar-filler { + background-color: white; /* The little square between H and V scrollbars */ +} + +/* GUTTER */ + +.CodeMirror-gutters { + border-right: 1px solid #ddd; + background-color: #f7f7f7; +} +.CodeMirror-linenumbers {} +.CodeMirror-linenumber { + padding: 0 3px 0 5px; + min-width: 20px; + text-align: right; + color: #999; +} + +/* CURSOR */ + +.CodeMirror pre.CodeMirror-cursor { + border-left: 1px solid black; +} +/* Shown when moving in bi-directional text */ +.CodeMirror pre.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} +.cm-keymap-fat-cursor pre.CodeMirror-cursor { + width: auto; + border: 0; + background: transparent; + background: rgba(0, 200, 0, .4); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800); +} +/* Kludge to turn off filter in ie9+, which also accepts rgba */ +.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) { + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} +/* Can style cursor different in overwrite (non-insert) mode */ +.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {} + +/* DEFAULT THEME */ + +.cm-s-default .cm-keyword {color: #708;} +.cm-s-default .cm-atom {color: #219;} +.cm-s-default .cm-number {color: #164;} +.cm-s-default .cm-def {color: #00f;} +.cm-s-default .cm-variable {color: black;} +.cm-s-default .cm-variable-2 {color: #05a;} +.cm-s-default .cm-variable-3 {color: #085;} +.cm-s-default .cm-property {color: black;} +.cm-s-default .cm-operator {color: black;} +.cm-s-default .cm-comment {color: #a50;} +.cm-s-default .cm-string {color: #a11;} +.cm-s-default .cm-string-2 {color: #f50;} +.cm-s-default .cm-meta {color: #555;} +.cm-s-default .cm-error {color: #f00;} +.cm-s-default .cm-qualifier {color: #555;} +.cm-s-default .cm-builtin {color: #30a;} +.cm-s-default .cm-bracket {color: #997;} +.cm-s-default .cm-tag {color: #170;} +.cm-s-default .cm-attribute {color: #00c;} +.cm-s-default .cm-header {color: blue;} +.cm-s-default .cm-quote {color: #090;} +.cm-s-default .cm-hr {color: #999;} +.cm-s-default .cm-link {color: #00c;} + +.cm-header, .cm-strong {font-weight: bold;} +.cm-em {font-style: italic;} +.cm-emstrong {font-style: italic; font-weight: bold;} +.cm-link {text-decoration: underline;} + +.cm-invalidchar {color: #f00;} + +div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} - /* Necessary so the scrollbar can be absolutely positioned within the wrapper on Lion. */ +/* STOP */ + +/* The rest of this file contains styles related to the mechanics of + the editor. You probably shouldn't touch them. */ + +.CodeMirror { + line-height: 1; position: relative; - /* This prevents unwanted scrollbars from showing up on the body and wrapper in IE. */ overflow: hidden; } .CodeMirror-scroll { - overflow: auto; - height: 300px; - /* This is needed to prevent an IE[67] bug where the scrolled content - is visible outside of the scrolling box. */ + /* 30px is the magic margin used to hide the element's real scrollbars */ + /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */ + margin-bottom: -30px; margin-right: -30px; + padding-bottom: 30px; padding-right: 30px; + height: 100%; + outline: none; /* Prevent dragging from highlighting the element */ + position: relative; +} +.CodeMirror-sizer { position: relative; - outline: none; } -/* Vertical scrollbar */ -.CodeMirror-scrollbar { +/* The fake, visible scrollbars. Used to force redraw during scrolling + before actuall scrolling happens, thus preventing shaking and + flickering artifacts. */ +.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler { position: absolute; + z-index: 6; + display: none; +} +.CodeMirror-vscrollbar { right: 0; top: 0; overflow-x: hidden; overflow-y: scroll; - z-index: 5; -} -.CodeMirror-scrollbar-inner { - /* This needs to have a nonzero width in order for the scrollbar to appear - in Firefox and IE9. */ - width: 1px; -} -.CodeMirror-scrollbar.cm-sb-overlap { - /* Ensure that the scrollbar appears in Lion, and that it overlaps the content - rather than sitting to the right of it. */ - position: absolute; - z-index: 1; - float: none; - right: 0; - min-width: 12px; } -.CodeMirror-scrollbar.cm-sb-nonoverlap { - min-width: 12px; +.CodeMirror-hscrollbar { + bottom: 0; left: 0; + overflow-y: hidden; + overflow-x: scroll; } -.CodeMirror-scrollbar.cm-sb-ie7 { - min-width: 18px; +.CodeMirror-scrollbar-filler { + right: 0; bottom: 0; + z-index: 6; } -.CodeMirror-gutter { +.CodeMirror-gutters { position: absolute; left: 0; top: 0; - z-index: 10; - background-color: #f7f7f7; - border-right: 1px solid #eee; - min-width: 2em; height: 100%; + z-index: 3; } -.CodeMirror-gutter-text { - color: #aaa; - text-align: right; - padding: .4em .2em .4em .4em; - white-space: pre !important; +.CodeMirror-gutter { + height: 100%; + float: left; +} +.CodeMirror-gutter-elt { + position: absolute; cursor: default; + z-index: 4; } + .CodeMirror-lines { - padding: .4em; - white-space: pre; cursor: text; } - .CodeMirror pre { - -moz-border-radius: 0; - -webkit-border-radius: 0; - -o-border-radius: 0; - border-radius: 0; - border-width: 0; margin: 0; padding: 0; background: transparent; + /* Reset some styles that the rest of the page might have set */ + -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0; + border-width: 0; + background: transparent; font-family: inherit; font-size: inherit; - padding: 0; margin: 0; + margin: 0; white-space: pre; word-wrap: normal; line-height: inherit; color: inherit; + z-index: 2; + position: relative; + overflow: visible; } - .CodeMirror-wrap pre { word-wrap: break-word; white-space: pre-wrap; word-break: normal; } +.CodeMirror-linebackground { + position: absolute; + left: 0; right: 0; top: 0; bottom: 0; + z-index: 0; +} + +.CodeMirror-linewidget { + position: relative; + z-index: 2; +} + .CodeMirror-wrap .CodeMirror-scroll { overflow-x: hidden; } -.CodeMirror textarea { - outline: none !important; +.CodeMirror-measure { + position: absolute; + width: 100%; height: 0px; + overflow: hidden; + visibility: hidden; } +.CodeMirror-measure pre { position: static; } .CodeMirror pre.CodeMirror-cursor { - z-index: 10; position: absolute; visibility: hidden; - border-left: 1px solid black; border-right: none; width: 0; } -.cm-keymap-fat-cursor pre.CodeMirror-cursor { - width: auto; - border: 0; - background: transparent; - background: rgba(0, 200, 0, .4); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800); -} -/* Kludge to turn off filter in ie9+, which also accepts rgba */ -.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) { - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} -.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {} .CodeMirror-focused pre.CodeMirror-cursor { visibility: visible; } -div.CodeMirror-selected { background: #d9d9d9; } -.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; } +.CodeMirror-selected { background: #d9d9d9; } +.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } .CodeMirror-searching { background: #ffa; background: rgba(255, 255, 0, .4); } -/* Default theme */ - -.cm-s-default span.cm-keyword {color: #708;} -.cm-s-default span.cm-atom {color: #219;} -.cm-s-default span.cm-number {color: #164;} -.cm-s-default span.cm-def {color: #00f;} -.cm-s-default span.cm-variable {color: black;} -.cm-s-default span.cm-variable-2 {color: #05a;} -.cm-s-default span.cm-variable-3 {color: #085;} -.cm-s-default span.cm-property {color: black;} -.cm-s-default span.cm-operator {color: black;} -.cm-s-default span.cm-comment {color: #a50;} -.cm-s-default span.cm-string {color: #a11;} -.cm-s-default span.cm-string-2 {color: #f50;} -.cm-s-default span.cm-meta {color: #555;} -.cm-s-default span.cm-error {color: #f00;} -.cm-s-default span.cm-qualifier {color: #555;} -.cm-s-default span.cm-builtin {color: #30a;} -.cm-s-default span.cm-bracket {color: #cc7;} -.cm-s-default span.cm-tag {color: #170;} -.cm-s-default span.cm-attribute {color: #00c;} -.cm-s-default span.cm-header {color: blue;} -.cm-s-default span.cm-quote {color: #090;} -.cm-s-default span.cm-hr {color: #999;} -.cm-s-default span.cm-link {color: #00c;} - -span.cm-header, span.cm-strong {font-weight: bold;} -span.cm-em {font-style: italic;} -span.cm-emstrong {font-style: italic; font-weight: bold;} -span.cm-link {text-decoration: underline;} - -span.cm-invalidchar {color: #f00;} - -div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} -div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} +/* IE7 hack to prevent it from returning funny offsetTops on the spans */ +.CodeMirror span { *vertical-align: text-bottom; } @media print { - /* Hide the cursor when printing */ .CodeMirror pre.CodeMirror-cursor { visibility: hidden; } - -}
\ No newline at end of file +} diff --git a/Source/WebCore/inspector/front-end/cm/codemirror.js b/Source/WebCore/inspector/front-end/cm/codemirror.js index d7b8f8c99..5b584c83b 100644 --- a/Source/WebCore/inspector/front-end/cm/codemirror.js +++ b/Source/WebCore/inspector/front-end/cm/codemirror.js @@ -1,2080 +1,2746 @@ -// All functions that need access to the editor's state live inside -// the CodeMirror function. Below that, at the bottom of the file, -// some utilities are defined. - // CodeMirror is the only global var we claim -var CodeMirror = (function() { +window.CodeMirror = (function() { "use strict"; - // This is the function that produces an editor instance. Its - // closure is used to store the editor state. - function CodeMirror(place, givenOptions) { + + // BROWSER SNIFFING + + // Crude, but necessary to handle a number of hard-to-feature-detect + // bugs and behavior differences. + var gecko = /gecko\/\d{7}/i.test(navigator.userAgent); + var ie = /MSIE \d/.test(navigator.userAgent); + var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); + var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); + var ie_lt10 = /MSIE [1-9]\b/.test(navigator.userAgent); + var webkit = /WebKit\//.test(navigator.userAgent); + var chrome = /Chrome\//.test(navigator.userAgent); + var opera = /Opera\//.test(navigator.userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var khtml = /KHTML\//.test(navigator.userAgent); + var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent); + var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent); + var phantom = /PhantomJS/.test(navigator.userAgent); + + var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); + var mac = ios || /Mac/.test(navigator.platform); + var win = /Win/.test(navigator.platform); + + // Optimize some code when these features are not used + var sawReadOnlySpans = false, sawCollapsedSpans = false; + + // CONSTRUCTOR + + function CodeMirror(place, options) { + if (!(this instanceof CodeMirror)) return new CodeMirror(place, options); + + this.options = options = options || {}; // Determine effective options based on given values and defaults. - var options = {}, defaults = CodeMirror.defaults; - for (var opt in defaults) - if (defaults.hasOwnProperty(opt)) - options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; + for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt)) + options[opt] = defaults[opt]; + setGuttersForLineNumbers(options); + + var display = this.display = makeDisplay(place); + display.wrapper.CodeMirror = this; + updateGutters(this); + if (options.autofocus) focusInput(this); - var input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em"); + var doc = new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]); + // The selection. These are always maintained to point at valid + // positions. Inverted is used to remember that the user is + // selecting bottom-to-top. + this.view = { + doc: doc, + // frontier is the point up to which the content has been parsed, + frontier: 0, highlight: new Delayed(), + sel: {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false, shift: false}, + scrollTop: 0, scrollLeft: 0, + overwrite: false, focused: false, + // Tracks the maximum line length so that + // the horizontal scrollbar can be kept + // static when scrolling. + maxLine: getLine(doc, 0), + maxLineLength: 0, + maxLineChanged: false, + suppressEdits: false, + goalColumn: null, + cantEdit: false + }; + loadMode(this); + + // Initialize the content. + this.setValue(options.value || ""); + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie) setTimeout(bind(resetInput, this, true), 20); + this.view.history = makeHistory(); + + registerEventHandlers(this); + // IE throws unspecified error in certain cases, when + // trying to access activeElement before onload + var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { } + if (hasFocus || options.autofocus) setTimeout(bind(onFocus, this), 20); + else onBlur(this); + + operation(this, function() { + for (var opt in optionHandlers) + if (optionHandlers.propertyIsEnumerable(opt)) + optionHandlers[opt](this, options[opt], Init); + for (var i = 0; i < initHooks.length; ++i) initHooks[i](this); + })(); + } + + // DISPLAY CONSTRUCTOR + + function makeDisplay(place) { + var d = {}; + var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;"); input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); // Wraps and hides input textarea - var inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); - // The empty scrollbar content, used solely for managing the scrollbar thumb. - var scrollbarInner = elt("div", null, "CodeMirror-scrollbar-inner"); - // The vertical scrollbar. Horizontal scrolling is handled by the scroller itself. - var scrollbar = elt("div", [scrollbarInner], "CodeMirror-scrollbar"); + d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The actual fake scrollbars. + d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar"); + d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar"); + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); // DIVs containing the selection and the actual code - var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: -1"); + d.lineDiv = elt("div"); + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); // Blinky cursor, and element used to ensure cursor fits at the end of a line - var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"), widthForcer = elt("pre", "\u00a0", "CodeMirror-cursor", "visibility: hidden"); + d.cursor = elt("pre", "\u00a0", "CodeMirror-cursor"); + // Secondary cursor, shown when on a 'jump' in bi-directional text + d.otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"); // Used to measure text size - var measure = elt("div", null, null, "position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden;"); - var lineSpace = elt("div", [measure, cursor, widthForcer, selectionDiv, lineDiv], null, "position: relative; z-index: 0"); - var gutterText = elt("div", null, "CodeMirror-gutter-text"), gutter = elt("div", [gutterText], "CodeMirror-gutter"); + d.measure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor], + null, "position: relative; outline: none"); // Moved around its parent to cover visible view - var mover = elt("div", [gutter, elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative"); + d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative"); + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; // Set to the height of the text, causes scrolling - var sizer = elt("div", [mover], null, "position: relative"); + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers + d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px"); // Provides scrolling - var scroller = elt("div", [sizer], "CodeMirror-scroll"); - scroller.setAttribute("tabIndex", "-1"); + d.scroller = elt("div", [d.sizer, d.heightForcer], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. - var wrapper = elt("div", [inputDiv, scrollbar, scroller], "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "")); - if (place.appendChild) place.appendChild(wrapper); else place(wrapper); + d.wrapper = elt("div", [d.gutters, d.inputDiv, d.scrollbarH, d.scrollbarV, + d.scrollbarFiller, d.scroller], "CodeMirror"); + // Work around IE7 z-index bug + if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper); - themeChanged(); keyMapChanged(); // Needed to hide big blue blinking cursor on Mobile Safari if (ios) input.style.width = "0px"; - if (!webkit) scroller.draggable = true; - lineSpace.style.outline = "none"; - if (options.tabindex != null) input.tabIndex = options.tabindex; - if (options.autofocus) focusInput(); - if (!options.gutter && !options.lineNumbers) gutter.style.display = "none"; + if (!webkit) d.scroller.draggable = true; // Needed to handle Tab key in KHTML - if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute"; - - // Check for OS X >= 10.7. If so, we need to force a width on the scrollbar, and - // make it overlap the content. (But we only do this if the scrollbar doesn't already - // have a natural width. If the mouse is plugged in or the user sets the system pref - // to always show scrollbars, the scrollbar shouldn't overlap.) - if (mac_geLion) { - scrollbar.className += (overlapScrollbars() ? " cm-sb-overlap" : " cm-sb-nonoverlap"); - } else if (ie_lt8) { - // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). - scrollbar.className += " cm-sb-ie7"; - } - - // Check for problem with IE innerHTML not working when we have a - // P (or similar) parent node. - try { charWidth(); } - catch (e) { - if (e.message.match(/runtime/i)) - e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)"); - throw e; - } - - // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval. - var poll = new Delayed(), highlight = new Delayed(), blinker; - - // mode holds a mode API object. doc is the tree of Line objects, - // work an array of lines that should be parsed, and history the - // undo history (instance of History constructor). - var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused; - loadMode(); - // The selection. These are always maintained to point at valid - // positions. Inverted is used to remember that the user is - // selecting bottom-to-top. - var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false}; - // Selection-related flags. shiftSelecting obviously tracks - // whether the user is holding shift. - var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, draggingText, - overwrite = false, suppressEdits = false; - // Variables used by startOperation/endOperation to track what - // happened during the operation. - var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone, - gutterDirty, callbacks; + if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; } + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px"; + // Current visible range (may be bigger than the view window). - var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; - // bracketHighlighted is used to remember that a bracket has been - // marked. - var bracketHighlighted; - // Tracks the maximum line length so that the horizontal scrollbar - // can be kept static when scrolling. - var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true; - var tabCache = {}; - var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll - var goalColumn = null; + d.viewOffset = d.showingFrom = d.showingTo = d.lastSizeC = 0; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // See readInput and resetInput + d.prevInput = ""; + // Set to true when a non-horizontal-scrolling widget is added. As + // an optimization, widget aligning is skipped when d is false. + d.alignWidgets = false; + // Flag that indicates whether we currently expect input to appear + // (after some event like 'keypress' or 'input') and are polling + // intensively. + d.pollingFast = false; + // Self-resetting timeout for the poller + d.poll = new Delayed(); + // True when a drag from the editor is active + d.draggingText = false; + + d.cachedCharWidth = d.cachedTextHeight = null; + d.measureLineCache = []; + d.measureLineCache.pos = 0; + + // Tracks when resetInput has punted to just putting a short + // string instead of the (large) selection. + d.inaccurateSelection = false; + + // Used to adjust overwrite behaviour when a paste has been + // detected + d.pasteIncoming = false; + + return d; + } - // Initialize the content. - operation(function(){setValue(options.value || ""); updateInput = false;})(); - var history = new History(); + // STATE UPDATES - // Register our event handlers. - connect(scroller, "mousedown", operation(onMouseDown)); - connect(scroller, "dblclick", operation(onDoubleClick)); - connect(lineSpace, "selectstart", e_preventDefault); - // Gecko browsers fire contextmenu *after* opening the menu, at - // which point we can't mess with it anymore. Context menu is - // handled in onMouseDown for Gecko. - if (!gecko) connect(scroller, "contextmenu", onContextMenu); - connect(scroller, "scroll", onScrollMain); - connect(scrollbar, "scroll", onScrollBar); - connect(scrollbar, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); - connect(window, "resize", function() {updateDisplay(true);}); - connect(input, "keyup", operation(onKeyUp)); - connect(input, "input", fastPoll); - connect(input, "keydown", operation(onKeyDown)); - connect(input, "keypress", operation(onKeyPress)); - connect(input, "focus", onFocus); - connect(input, "blur", onBlur); + // Used to get the editor into a consistent state again when options change. - function drag_(e) { - if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; - e_stop(e); - } - if (options.dragDrop) { - connect(scroller, "dragstart", onDragStart); - connect(scroller, "dragenter", drag_); - connect(scroller, "dragover", drag_); - connect(scroller, "drop", operation(onDrop)); + function loadMode(cm) { + var doc = cm.view.doc; + cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode); + doc.iter(0, doc.size, function(line) { line.stateAfter = null; }); + cm.view.frontier = 0; + startWorker(cm, 100); + } + + function wrappingChanged(cm) { + var doc = cm.view.doc, th = textHeight(cm.display); + if (cm.options.lineWrapping) { + cm.display.wrapper.className += " CodeMirror-wrap"; + var perLine = cm.display.scroller.clientWidth / charWidth(cm.display) - 3; + doc.iter(0, doc.size, function(line) { + if (line.height == 0) return; + var guess = Math.ceil(line.text.length / perLine) || 1; + if (guess != 1) updateLineHeight(line, guess * th); + }); + cm.display.sizer.style.minWidth = ""; + } else { + cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", ""); + computeMaxLength(cm.view); + doc.iter(0, doc.size, function(line) { + if (line.height != 0) updateLineHeight(line, th); + }); } - connect(scroller, "paste", function(){focusInput(); fastPoll();}); - connect(input, "paste", fastPoll); - connect(input, "cut", operation(function(){ - if (!options.readOnly) replaceSelection(""); - })); + regChange(cm, 0, doc.size); + } - // Needed to handle Tab key in KHTML - if (khtml) connect(sizer, "mouseup", function() { - if (document.activeElement == input) input.blur(); - focusInput(); - }); + function keyMapChanged(cm) { + var style = keyMap[cm.options.keyMap].style; + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") + + (style ? " cm-keymap-" + style : ""); + } - // IE throws unspecified error in certain cases, when - // trying to access activeElement before onload - var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { } - if (hasFocus || options.autofocus) setTimeout(onFocus, 20); - else onBlur(); - - function isLine(l) {return l >= 0 && l < doc.size;} - // The instance object that we'll return. Mostly calls out to - // local functions in the CodeMirror function. Some do some extra - // range checking and/or clipping. operation is used to wrap the - // call so that changes it makes are tracked, and the display is - // updated afterwards. - var instance = wrapper.CodeMirror = { - getValue: getValue, - setValue: operation(setValue), - getSelection: getSelection, - replaceSelection: operation(replaceSelection), - focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();}, - setOption: function(option, value) { - var oldVal = options[option]; - options[option] = value; - if (option == "mode" || option == "indentUnit") loadMode(); - else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();} - else if (option == "readOnly" && !value) {resetInput(true);} - else if (option == "theme") themeChanged(); - else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)(); - else if (option == "tabSize") updateDisplay(true); - else if (option == "keyMap") keyMapChanged(); - if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || - option == "theme" || option == "lineNumberFormatter") { - gutterChanged(); - updateDisplay(true); - } - }, - getOption: function(option) {return options[option];}, - undo: operation(undo), - redo: operation(redo), - indentLine: operation(function(n, dir) { - if (typeof dir != "string") { - if (dir == null) dir = options.smartIndent ? "smart" : "prev"; - else dir = dir ? "add" : "subtract"; - } - if (isLine(n)) indentLine(n, dir); - }), - indentSelection: operation(indentSelected), - historySize: function() {return {undo: history.done.length, redo: history.undone.length};}, - clearHistory: function() {history = new History();}, - setHistory: function(histData) { - history = new History(); - history.done = histData.done; - history.undone = histData.undone; - }, - getHistory: function() { - history.time = 0; - return {done: history.done.concat([]), undone: history.undone.concat([])}; - }, - matchBrackets: operation(function(){matchBrackets(true);}), - getTokenAt: operation(function(pos) { - pos = clipPos(pos); - return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), options.tabSize, pos.ch); - }), - getStateAfter: function(line) { - line = clipLine(line == null ? doc.size - 1: line); - return getStateBefore(line + 1); - }, - cursorCoords: function(start, mode) { - if (start == null) start = sel.inverted; - return this.charCoords(start ? sel.from : sel.to, mode); - }, - charCoords: function(pos, mode) { - pos = clipPos(pos); - if (mode == "local") return localCoords(pos, false); - if (mode == "div") return localCoords(pos, true); - return pageCoords(pos); - }, - coordsChar: function(coords) { - var off = eltOffset(lineSpace); - return coordsChar(coords.x - off.left, coords.y - off.top); - }, - markText: operation(markText), - setBookmark: setBookmark, - findMarksAt: findMarksAt, - setMarker: operation(addGutterMarker), - clearMarker: operation(removeGutterMarker), - setLineClass: operation(setLineClass), - hideLine: operation(function(h) {return setLineHidden(h, true);}), - showLine: operation(function(h) {return setLineHidden(h, false);}), - onDeleteLine: function(line, f) { - if (typeof line == "number") { - if (!isLine(line)) return null; - line = getLine(line); - } - (line.handlers || (line.handlers = [])).push(f); - return line; - }, - lineInfo: lineInfo, - addWidget: function(pos, node, scroll, vert, horiz) { - pos = localCoords(clipPos(pos)); - var top = pos.yBot, left = pos.x; - node.style.position = "absolute"; - sizer.appendChild(node); - if (vert == "over") top = pos.y; - else if (vert == "near") { - var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()), - hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth) - paddingLeft(); - if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight) - top = pos.y - node.offsetHeight; - if (left + node.offsetWidth > hspace) - left = hspace - node.offsetWidth; - } - node.style.top = (top + paddingTop()) + "px"; - node.style.left = node.style.right = ""; - if (horiz == "right") { - left = sizer.clientWidth - node.offsetWidth; - node.style.right = "0px"; - } else { - if (horiz == "left") left = 0; - else if (horiz == "middle") left = (sizer.clientWidth - node.offsetWidth) / 2; - node.style.left = (left + paddingLeft()) + "px"; - } - if (scroll) - scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight); - }, - - lineCount: function() {return doc.size;}, - clipPos: clipPos, - getCursor: function(start) { - if (start == null) start = sel.inverted; - return copyPos(start ? sel.from : sel.to); - }, - somethingSelected: function() {return !posEq(sel.from, sel.to);}, - setCursor: operation(function(line, ch, user) { - if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user); - else setCursor(line, ch, user); - }), - setSelection: operation(function(from, to, user) { - (user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from)); - }), - getLine: function(line) {if (isLine(line)) return getLine(line).text;}, - getLineHandle: function(line) {if (isLine(line)) return getLine(line);}, - setLine: operation(function(line, text) { - if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length}); - }), - removeLine: operation(function(line) { - if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0})); - }), - replaceRange: operation(replaceRange), - getRange: function(from, to, lineSep) {return getRange(clipPos(from), clipPos(to), lineSep);}, - - triggerOnKeyDown: operation(onKeyDown), - execCommand: function(cmd) {return commands[cmd](instance);}, - // Stuff used by commands, probably not much use to outside code. - moveH: operation(moveH), - deleteH: operation(deleteH), - moveV: operation(moveV), - toggleOverwrite: function() { - if(overwrite){ - overwrite = false; - cursor.className = cursor.className.replace(" CodeMirror-overwrite", ""); - } else { - overwrite = true; - cursor.className += " CodeMirror-overwrite"; - } - }, - - posFromIndex: function(off) { - var lineNo = 0, ch; - doc.iter(0, doc.size, function(line) { - var sz = line.text.length + 1; - if (sz > off) { ch = off; return true; } - off -= sz; - ++lineNo; - }); - return clipPos({line: lineNo, ch: ch}); - }, - indexFromPos: function (coords) { - if (coords.line < 0 || coords.ch < 0) return 0; - var index = coords.ch; - doc.iter(0, coords.line, function (line) { - index += line.text.length + 1; - }); - return index; - }, - scrollTo: function(x, y) { - if (x != null) scroller.scrollLeft = x; - if (y != null) scrollbar.scrollTop = scroller.scrollTop = y; - updateDisplay([]); - }, - getScrollInfo: function() { - return {x: scroller.scrollLeft, y: scrollbar.scrollTop, - height: scrollbar.scrollHeight, width: scroller.scrollWidth}; - }, - setSize: function(width, height) { - function interpret(val) { - val = String(val); - return /^\d+$/.test(val) ? val + "px" : val; - } - if (width != null) wrapper.style.width = interpret(width); - if (height != null) scroller.style.height = interpret(height); - instance.refresh(); - }, - - operation: function(f){return operation(f)();}, - compoundChange: function(f){return compoundChange(f);}, - refresh: function(){ - updateDisplay(true, null, lastScrollTop); - if (scrollbar.scrollHeight > lastScrollTop) - scrollbar.scrollTop = lastScrollTop; - }, - getInputField: function(){return input;}, - getWrapperElement: function(){return wrapper;}, - getScrollerElement: function(){return scroller;}, - getGutterElement: function(){return gutter;} - }; + function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + } - function getLine(n) { return getLineAt(doc, n); } - function updateLineHeight(line, height) { - gutterDirty = true; - var diff = height - line.height; - for (var n = line; n; n = n.parent) n.height += diff; + function guttersChanged(cm) { + updateGutters(cm); + updateDisplay(cm, true); + } + + function updateGutters(cm) { + var gutters = cm.display.gutters, specs = cm.options.gutters; + removeChildren(gutters); + for (var i = 0; i < specs.length; ++i) { + var gutterClass = specs[i]; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)); + if (gutterClass == "CodeMirror-linenumbers") { + cm.display.lineGutter = gElt; + gElt.style.width = (cm.display.lineNumWidth || 1) + "px"; + } } + gutters.style.display = i ? "" : "none"; + } - function setValue(code) { - var top = {line: 0, ch: 0}; - updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length}, - splitLines(code), top, top); - updateInput = true; + function lineLength(doc, line) { + if (line.height == 0) return 0; + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(); + cur = getLine(doc, found.from.line); + len += found.from.ch - found.to.ch; } - function getValue(lineSep) { - var text = []; - doc.iter(0, doc.size, function(line) { text.push(line.text); }); - return text.join(lineSep || "\n"); + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found = merged.find(); + len -= cur.text.length - found.from.ch; + cur = getLine(doc, found.to.line); + len += cur.text.length - found.to.ch; } + return len; + } - function onScrollBar(e) { - if (scrollbar.scrollTop != lastScrollTop) { - lastScrollTop = scroller.scrollTop = scrollbar.scrollTop; - updateDisplay([]); + function computeMaxLength(view) { + view.maxLine = getLine(view.doc, 0); + view.maxLineLength = lineLength(view.doc, view.maxLine); + view.maxLineChanged = true; + view.doc.iter(1, view.doc.size, function(line) { + var len = lineLength(view.doc, line); + if (len > view.maxLineLength) { + view.maxLineLength = len; + view.maxLine = line; + } + }); + } + + // Make sure the gutters options contains the element + // "CodeMirror-linenumbers" when the lineNumbers option is true. + function setGuttersForLineNumbers(options) { + var found = false; + for (var i = 0; i < options.gutters.length; ++i) { + if (options.gutters[i] == "CodeMirror-linenumbers") { + if (options.lineNumbers) found = true; + else options.gutters.splice(i--, 1); } } + if (!found && options.lineNumbers) + options.gutters.push("CodeMirror-linenumbers"); + } - function onScrollMain(e) { - if (options.fixedGutter && gutter.style.left != scroller.scrollLeft + "px") - gutter.style.left = scroller.scrollLeft + "px"; - if (scroller.scrollTop != lastScrollTop) { - lastScrollTop = scroller.scrollTop; - if (scrollbar.scrollTop != lastScrollTop) - scrollbar.scrollTop = lastScrollTop; - updateDisplay([]); + // SCROLLBARS + + // Re-synchronize the fake scrollbars with the actual size of the + // content. Optionally force a scrollTop. + function updateScrollbars(d /* display */, docHeight, scrollTop) { + d.sizer.style.minHeight = d.heightForcer.style.top = (docHeight + 2 * paddingTop(d)) + "px"; + var needsH = d.scroller.scrollWidth > d.scroller.clientWidth; + var needsV = d.scroller.scrollHeight > d.scroller.clientHeight; + if (needsV) { + d.scrollbarV.style.display = "block"; + d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0"; + d.scrollbarV.firstChild.style.height = + (d.scroller.scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px"; + if (scrollTop != null) { + d.scrollbarV.scrollTop = d.scroller.scrollTop = scrollTop; + // 'Nudge' the scrollbar to work around a Webkit bug where, + // in some situations, we'd end up with a scrollbar that + // reported its scrollTop (and looked) as expected, but + // *behaved* as if it was still in a previous state (i.e. + // couldn't scroll up, even though it appeared to be at the + // bottom). + if (webkit) setTimeout(function() { + if (d.scrollbarV.scrollTop != scrollTop) return; + d.scrollbarV.scrollTop = scrollTop + (scrollTop ? -1 : 1); + d.scrollbarV.scrollTop = scrollTop; + }, 0); } - if (options.onScroll) options.onScroll(instance); + } else d.scrollbarV.style.display = ""; + if (needsH) { + d.scrollbarH.style.display = "block"; + d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0"; + d.scrollbarH.firstChild.style.width = + (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px"; + } else d.scrollbarH.style.display = ""; + if (needsH && needsV) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px"; + } else d.scrollbarFiller.style.display = ""; + + if (mac_geLion && scrollbarWidth(d.measure) === 0) + d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px"; + } + + function visibleLines(display, doc, viewPort) { + var top = display.scroller.scrollTop, height = display.wrapper.clientHeight; + if (typeof viewPort == "number") top = viewPort; + else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;} + top = Math.floor(top - paddingTop(display)); + var bottom = Math.ceil(top + height); + return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)}; + } + + // LINE NUMBERS + + function alignVertically(display) { + if (!display.alignWidgets && !display.gutters.firstChild) return; + var l = compensateForHScroll(display) + "px"; + for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) { + for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l; } + } - function onMouseDown(e) { - setShift(e_prop(e, "shiftKey")); - // Check whether this is a click in a widget - for (var n = e_target(e); n != wrapper; n = n.parentNode) - if (n.parentNode == sizer && n != mover) return; + function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) return false; + var doc = cm.view.doc, last = lineNumberFor(cm.options, doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding); + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + return true; + } + return false; + } - // See if this is a click in the gutter - for (var n = e_target(e); n != wrapper; n = n.parentNode) - if (n.parentNode == gutterText) { - if (options.onGutterClick) - options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e); - return e_preventDefault(e); - } + function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)); + } + function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left; + } - var start = posFromMouse(e); + // DISPLAY DRAWING - switch (e_button(e)) { - case 3: - if (gecko) onContextMenu(e); - return; - case 2: - if (start) setCursor(start.line, start.ch, true); - setTimeout(focusInput, 20); - e_preventDefault(e); - return; - } - // For button 1, if it was clicked inside the editor - // (posFromMouse returning non-null), we have to adjust the - // selection. - if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;} - - if (!focused) onFocus(); - - var now = +new Date, type = "single"; - if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) { - type = "triple"; - e_preventDefault(e); - setTimeout(focusInput, 20); - selectLine(start.line); - } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) { - type = "double"; - lastDoubleClick = {time: now, pos: start}; - e_preventDefault(e); - var word = findWordAt(start); - setSelectionUser(word.from, word.to); - } else { lastClick = {time: now, pos: start}; } - - function dragEnd(e2) { - if (webkit) scroller.draggable = false; - draggingText = false; - up(); drop(); - if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { - e_preventDefault(e2); - setCursor(start.line, start.ch, true); - focusInput(); + function updateDisplay(cm, changes, viewPort) { + var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo; + var updated = updateDisplayInner(cm, changes, viewPort); + if (updated) { + signalLater(cm, cm, "update", cm); + if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo) + signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo); + } + updateSelection(cm); + updateScrollbars(cm.display, cm.view.doc.height, typeof viewPort == "number" ? viewPort : null); + return updated; + } + + // Uses a set of changes plus the current scroll position to + // determine which DOM updates have to be made, and makes the + // updates. + function updateDisplayInner(cm, changes, viewPort) { + var display = cm.display, doc = cm.view.doc; + if (!display.wrapper.clientWidth) { + display.showingFrom = display.showingTo = display.viewOffset = 0; + return; + } + + // Compute the new visible window + // If scrollTop is specified, use that to determine which lines + // to render instead of the current scrollbar position. + var visible = visibleLines(display, doc, viewPort); + // Bail out if the visible area is already rendered and nothing changed. + if (changes !== true && changes.length == 0 && + visible.from > display.showingFrom && visible.to < display.showingTo) + return; + + if (changes && maybeUpdateLineNumberWidth(cm)) + changes = true; + display.sizer.style.marginLeft = display.scrollbarH.style.left = display.gutters.offsetWidth + "px"; + + // When merged lines are present, the line that needs to be + // redrawn might not be the one that was changed. + if (changes !== true && sawCollapsedSpans) + for (var i = 0; i < changes.length; ++i) { + var ch = changes[i], merged; + while (merged = collapsedSpanAtStart(getLine(doc, ch.from))) { + var from = merged.find().from.line; + if (ch.diff) ch.diff -= ch.from - from; + ch.from = from; } } - var last = start, going; - if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) && - !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") { - // Let the drag handler handle this. - if (webkit) scroller.draggable = true; - var up = connect(document, "mouseup", operation(dragEnd), true); - var drop = connect(scroller, "drop", operation(dragEnd), true); - draggingText = true; - // IE's approach to draggable - if (scroller.dragDrop) scroller.dragDrop(); - return; + + // Used to determine which lines need their line numbers updated + var positionsChangedFrom = changes === true ? 0 : Infinity; + if (cm.options.lineNumbers && changes && changes !== true) + for (var i = 0; i < changes.length; ++i) + if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; } + + var from = Math.max(visible.from - cm.options.viewportMargin, 0); + var to = Math.min(doc.size, visible.to + cm.options.viewportMargin); + if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom; + if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo); + + // Create a range of theoretically intact lines, and punch holes + // in that using the change info. + var intact = changes === true ? [] : + computeIntact([{from: display.showingFrom, to: display.showingTo, domStart: 0}], changes); + // Clip off the parts that won't be visible + var intactLines = 0; + for (var i = 0; i < intact.length; ++i) { + var range = intact[i]; + if (range.from < from) {range.domStart += (from - range.from); range.from = from;} + if (range.to > to) range.to = to; + if (range.from >= range.to) intact.splice(i--, 1); + else intactLines += range.to - range.from; + } + if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) + return; + intact.sort(function(a, b) {return a.domStart - b.domStart;}); + + if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none"; + patchDisplay(cm, from, to, intact, positionsChangedFrom); + display.lineDiv.style.display = ""; + + var different = from != display.showingFrom || to != display.showingTo || + display.lastSizeC != display.wrapper.clientHeight; + // This is just a bogus formula that detects when the editor is + // resized or the font size changes. + if (different) display.lastSizeC = display.wrapper.clientHeight; + display.showingFrom = from; display.showingTo = to; + display.viewOffset = heightAtLine(cm, getLine(doc, from)); + startWorker(cm, 100); + + // Since this is all rather error prone, it is honoured with the + // only assertion in the whole file. + if (display.lineDiv.childNodes.length != display.showingTo - display.showingFrom) + throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (display.showingTo - display.showingFrom) + + " nodes=" + display.lineDiv.childNodes.length); + + // Update line heights for visible lines based on actual DOM + // sizes + var curNode = display.lineDiv.firstChild, relativeTo = curNode.offsetTop; + doc.iter(display.showingFrom, display.showingTo, function(line) { + // Work around bizarro IE7 bug where, sometimes, our curNode + // is magically replaced with a new node in the DOM, leaving + // us with a reference to an orphan (nextSibling-less) node. + if (!curNode) return; + if (!lineIsHidden(line)) { + var end = curNode.offsetHeight + curNode.offsetTop; + var height = end - relativeTo, diff = line.height - height; + if (height < 2) height = textHeight(display); + relativeTo = end; + if (diff > .001 || diff < -.001) + updateLineHeight(line, height); } - e_preventDefault(e); - if (type == "single") setCursor(start.line, start.ch, true); - - var startstart = sel.from, startend = sel.to; - - function doSelect(cur) { - if (type == "single") { - setSelectionUser(start, cur); - } else if (type == "double") { - var word = findWordAt(cur); - if (posLess(cur, startstart)) setSelectionUser(word.from, startend); - else setSelectionUser(startstart, word.to); - } else if (type == "triple") { - if (posLess(cur, startstart)) setSelectionUser(startend, clipPos({line: cur.line, ch: 0})); - else setSelectionUser(startstart, clipPos({line: cur.line + 1, ch: 0})); + curNode = curNode.nextSibling; + }); + + // Position the mover div to align with the current virtual scroll position + display.mover.style.top = display.viewOffset + "px"; + return true; + } + + function computeIntact(intact, changes) { + for (var i = 0, l = changes.length || 0; i < l; ++i) { + var change = changes[i], intact2 = [], diff = change.diff || 0; + for (var j = 0, l2 = intact.length; j < l2; ++j) { + var range = intact[j]; + if (change.to <= range.from && change.diff) + intact2.push({from: range.from + diff, to: range.to + diff, + domStart: range.domStart}); + else if (change.to <= range.from || change.from >= range.to) + intact2.push(range); + else { + if (change.from > range.from) + intact2.push({from: range.from, to: change.from, domStart: range.domStart}); + if (change.to < range.to) + intact2.push({from: change.to + diff, to: range.to + diff, + domStart: range.domStart + (change.to - range.from)}); } } + intact = intact2; + } + return intact; + } + + function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + left[cm.options.gutters[i]] = n.offsetLeft; + width[cm.options.gutters[i]] = n.offsetWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth}; + } - function extend(e) { - var cur = posFromMouse(e, true); - if (cur && !posEq(cur, last)) { - if (!focused) onFocus(); - last = cur; - doSelect(cur); - updateInput = false; - var visible = visibleLines(); - if (cur.line >= visible.to || cur.line < visible.from) - going = setTimeout(operation(function(){extend(e);}), 150); + function patchDisplay(cm, from, to, intact, updateNumbersFrom) { + function killNode(node) { + var tmp = node.nextSibling; + node.parentNode.removeChild(node); + return tmp; + } + var dims = getDimensions(cm); + var display = cm.display, lineNumbers = cm.options.lineNumbers; + // The first pass removes the DOM nodes that aren't intact. + if (!intact.length) { + // old IE does bad things to nodes when .innerHTML = "" is used on a parent + // we still need widgets and markers intact to add back to the new content later + if (ie_lt10) for (var ld = display.lineDiv, tmp = ld.firstChild; tmp; tmp = ld.firstChild) ld.removeChild(tmp); + else removeChildren(display.lineDiv); + } else { + var domPos = 0, curNode = display.lineDiv.firstChild, n; + for (var i = 0; i < intact.length; ++i) { + var cur = intact[i]; + while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;} + for (var j = cur.from, e = cur.to; j < e; ++j) { + if (lineNumbers && updateNumbersFrom <= j && curNode.firstChild) + setTextContent(curNode.firstChild.firstChild, lineNumberFor(cm.options, j)); + curNode = curNode.nextSibling; domPos++; } } + while (curNode) curNode = killNode(curNode); + } + // This pass fills in the lines that actually changed. + var nextIntact = intact.shift(), curNode = display.lineDiv.firstChild; + var j = from; + + cm.view.doc.iter(from, to, function(line) { + if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); + if (!nextIntact || nextIntact.from > j) + display.lineDiv.insertBefore(buildLineElement(cm, line, j, dims), curNode); + else + curNode = curNode.nextSibling; + ++j; + }); + } - function done(e) { - clearTimeout(going); - var cur = posFromMouse(e); - if (cur) doSelect(cur); - e_preventDefault(e); - focusInput(); - updateInput = true; - move(); up(); - } - var move = connect(document, "mousemove", operation(function(e) { - clearTimeout(going); - e_preventDefault(e); - if (!ie && !e_button(e)) done(e); - else extend(e); - }), true); - var up = connect(document, "mouseup", operation(done), true); - } - function onDoubleClick(e) { - for (var n = e_target(e); n != wrapper; n = n.parentNode) - if (n.parentNode == gutterText) return e_preventDefault(e); - e_preventDefault(e); + function buildLineElement(cm, line, lineNo, dims) { + if (line.height == 0) return elt("div"); + + var lineElement = line.height == 0 ? elt("div") : lineContent(cm, line); + var markers = line.gutterMarkers, display = cm.display; + + if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && + (!line.widgets || !line.widgets.length)) return lineElement; + + // Lines with gutter elements or a background class need + // to be wrapped again, and have the extra elements added + // to the wrapper div + + var wrap = elt("div", null, line.wrapClass, "position: relative"); + if (cm.options.lineNumbers || markers) { + var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " + + dims.fixedPos + "px")); + wrap.alignable = [gutterWrap]; + if (cm.options.lineNumbers) + gutterWrap.appendChild(elt("div", lineNumberFor(cm.options, lineNo), + "CodeMirror-linenumber CodeMirror-gutter-elt", + "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: " + + display.lineNumInnerWidth + "px")); + if (markers) + for (var k = 0; k < cm.options.gutters.length; ++k) { + var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]; + if (found) { + gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " + + dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px")); + } + } } - function onDrop(e) { - if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; - e_preventDefault(e); - var pos = posFromMouse(e, true), files = e.dataTransfer.files; - if (!pos || options.readOnly) return; - if (files && files.length && window.FileReader && window.File) { - var n = files.length, text = Array(n), read = 0; - var loadFile = function(file, i) { - var reader = new FileReader; - reader.onload = function() { - text[i] = reader.result; - if (++read == n) { - pos = clipPos(pos); - operation(function() { - var end = replaceRange(text.join(""), pos, pos); - setSelectionUser(pos, end); - })(); - } - }; - reader.readAsText(file); - }; - for (var i = 0; i < n; ++i) loadFile(files[i], i); - } else { - // Don't do a replace if the drop happened inside of the selected text. - if (draggingText && !(posLess(pos, sel.from) || posLess(sel.to, pos))) return; - try { - var text = e.dataTransfer.getData("Text"); - if (text) { - compoundChange(function() { - var curFrom = sel.from, curTo = sel.to; - setSelectionUser(pos, pos); - if (draggingText) replaceRange("", curFrom, curTo); - replaceSelection(text); - focusInput(); - }); + // Kludge to make sure the styled element lies behind the selection (by z-index) + if (line.bgClass) + wrap.appendChild(elt("div", "\u00a0", line.bgClass + " CodeMirror-linebackground")); + wrap.appendChild(lineElement); + if (line.widgets) + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); + node.widget = widget; + if (widget.noHScroll) { + (wrap.alignable || (wrap.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px"; } - catch(e){} + if (widget.above) + wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement); + else + wrap.appendChild(node); } + + if (ie_lt8) wrap.style.zIndex = 2; + return wrap; + } + + // SELECTION / CURSOR + + function selHead(view) { + return view.sel.inverted ? view.sel.from : view.sel.to; + } + + function updateSelection(cm) { + var headPos = posEq(cm.view.sel.from, cm.view.sel.to) ? + updateSelectionCursor(cm) : + updateSelectionRange(cm); + var display = cm.display; + // Move the hidden textarea near the cursor to prevent scrolling artifacts + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)) + "px"; + display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)) + "px"; + } + + // No selection, plain cursor + function updateSelectionCursor(cm) { + var display = cm.display, pos = cursorCoords(cm, cm.view.sel.from, "div"); + display.cursor.style.left = pos.left + "px"; + display.cursor.style.top = pos.top + "px"; + display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + display.cursor.style.display = ""; + display.selectionDiv.style.display = "none"; + + if (pos.other) { + display.otherCursor.style.display = ""; + display.otherCursor.style.left = pos.other.left + "px"; + display.otherCursor.style.top = pos.other.top + "px"; + display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } else { display.otherCursor.style.display = "none"; } + return pos; + } + + // Highlight selection + function updateSelectionRange(cm) { + var display = cm.display, doc = cm.view.doc, sel = cm.view.sel; + var fragment = document.createDocumentFragment(); + var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display); + + function add(left, top, width, bottom) { + if (top < 0) top = 0; + fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + + "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) + + "px; height: " + (bottom - top) + "px")); } - function onDragStart(e) { - var txt = getSelection(); - e.dataTransfer.setData("Text", txt); - // Use dummy image instead of default browsers image. - if (gecko || chrome || opera) { - var img = elt('img'); - img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image - e.dataTransfer.setDragImage(img, 0, 0); + function drawForLine(line, fromArg, toArg, retTop) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity; + function coords(ch) { + return charCoords(cm, {line: line, ch: ch}, "div", lineObj); } + + iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) { + var leftPos = coords(dir == "rtl" ? to - 1 : from); + var rightPos = coords(dir == "rtl" ? from : to - 1); + var left = leftPos.left, right = rightPos.right; + if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part + add(left, leftPos.top, null, leftPos.bottom); + left = pl; + if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top); + } + if (toArg == null && to == lineLen) right = clientWidth; + if (fromArg == null && from == 0) left = pl; + rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal); + if (left < pl + 1) left = pl; + add(left, rightPos.top, right - left, rightPos.bottom); + }); + return rVal; } - function doHandleBinding(bound, dropShift) { - if (typeof bound == "string") { - bound = commands[bound]; - if (!bound) return false; + if (sel.from.line == sel.to.line) { + drawForLine(sel.from.line, sel.from.ch, sel.to.ch); + } else { + var fromObj = getLine(doc, sel.from.line); + var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine; + while (merged = collapsedSpanAtEnd(cur)) { + var found = merged.find(); + path.push(found.from.ch, found.to.line, found.to.ch); + if (found.to.line == sel.to.line) { + path.push(sel.to.ch); + singleLine = true; + break; + } + cur = getLine(doc, found.to.line); } - var prevShift = shiftSelecting; - try { - if (options.readOnly) suppressEdits = true; - if (dropShift) shiftSelecting = null; - bound(instance); - } catch(e) { - if (e != Pass) throw e; - return false; - } finally { - shiftSelecting = prevShift; - suppressEdits = false; + + // This is a single, merged line + if (singleLine) { + for (var i = 0; i < path.length; i += 3) + drawForLine(path[i], path[i+1], path[i+2]); + } else { + var middleTop, middleBot, toObj = getLine(doc, sel.to.line); + if (sel.from.ch) + // Draw the first line of selection. + middleTop = drawForLine(sel.from.line, sel.from.ch, null, false); + else + // Simply include it in the middle block. + middleTop = heightAtLine(cm, fromObj) - display.viewOffset; + + if (!sel.to.ch) + middleBot = heightAtLine(cm, toObj) - display.viewOffset; + else + middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true); + + if (middleTop < middleBot) add(pl, middleTop, null, middleBot); } - return true; } - var maybeTransition; - function handleKeyBinding(e) { - // Handle auto keymap transitions - var startMap = getKeyMap(options.keyMap), next = startMap.auto; - clearTimeout(maybeTransition); - if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() { - if (getKeyMap(options.keyMap) == startMap) { - options.keyMap = (next.call ? next.call(null, instance) : next); - } - }, 50); - - var name = keyNames[e_prop(e, "keyCode")], handled = false; - if (name == null || e.altGraphKey) return false; - if (e_prop(e, "altKey")) name = "Alt-" + name; - if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name; - if (e_prop(e, "metaKey")) name = "Cmd-" + name; - - var stopped = false; - function stop() { stopped = true; } - - if (e_prop(e, "shiftKey")) { - handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap, - function(b) {return doHandleBinding(b, true);}, stop) - || lookupKey(name, options.extraKeys, options.keyMap, function(b) { - if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b); - }, stop); - } else { - handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding, stop); - } - if (stopped) handled = false; - if (handled) { - e_preventDefault(e); - restartBlink(); - if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; } - } - return handled; - } - function handleCharBinding(e, ch) { - var handled = lookupKey("'" + ch + "'", options.extraKeys, - options.keyMap, function(b) { return doHandleBinding(b, true); }); - if (handled) { - e_preventDefault(e); - restartBlink(); - } - return handled; - } - - var lastStoppedKey = null; - function onKeyDown(e) { - if (!focused) onFocus(); - if (ie && e.keyCode == 27) { e.returnValue = false; } - if (pollingFast) { if (readInput()) pollingFast = false; } - if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; - var code = e_prop(e, "keyCode"); - // IE does strange things with escape. - setShift(code == 16 || e_prop(e, "shiftKey")); - // First give onKeyEvent option a chance to handle this. - var handled = handleKeyBinding(e); - if (opera) { - lastStoppedKey = handled ? code : null; - // Opera has no cut event... we try to at least catch the key combo - if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey")) - replaceSelection(""); - } - } - function onKeyPress(e) { - if (pollingFast) readInput(); - if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; - var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode"); - if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} - if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(e)) return; - var ch = String.fromCharCode(charCode == null ? keyCode : charCode); - if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) { - if (mode.electricChars.indexOf(ch) > -1) - setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75); - } - if (handleCharBinding(e, ch)) return; - fastPoll(); - } - function onKeyUp(e) { - if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; - if (e_prop(e, "keyCode") == 16) shiftSelecting = null; - } - - function onFocus() { - if (options.readOnly == "nocursor") return; - if (!focused) { - if (options.onFocus) options.onFocus(instance); - focused = true; - if (scroller.className.search(/\bCodeMirror-focused\b/) == -1) - scroller.className += " CodeMirror-focused"; - if (!leaveInputAlone) resetInput(true); - } - slowPoll(); - restartBlink(); - } - function onBlur() { - if (focused) { - if (options.onBlur) options.onBlur(instance); - focused = false; - if (bracketHighlighted) - operation(function(){ - if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; } - })(); - scroller.className = scroller.className.replace(" CodeMirror-focused", ""); - } - clearInterval(blinker); - setTimeout(function() {if (!focused) shiftSelecting = null;}, 150); - } - - // Replace the range from from to to by the strings in newText. - // Afterwards, set the selection to selFrom, selTo. - function updateLines(from, to, newText, selFrom, selTo) { - if (suppressEdits) return; - if (history) { - var old = []; - doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); }); - history.addChange(from.line, newText.length, old); - while (history.done.length > options.undoDepth) history.done.shift(); - } - updateLinesNoUndo(from, to, newText, selFrom, selTo); - } - function unredoHelper(from, to) { - if (!from.length) return; - var set = from.pop(), out = []; - for (var i = set.length - 1; i >= 0; i -= 1) { - var change = set[i]; - var replaced = [], end = change.start + change.added; - doc.iter(change.start, end, function(line) { replaced.push(line.text); }); - out.push({start: change.start, added: change.old.length, old: replaced}); - var pos = {line: change.start + change.old.length - 1, - ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])}; - updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos); - } - updateInput = true; - to.push(out); - } - function undo() {unredoHelper(history.done, history.undone);} - function redo() {unredoHelper(history.undone, history.done);} - - function updateLinesNoUndo(from, to, newText, selFrom, selTo) { - if (suppressEdits) return; - var recomputeMaxLength = false, maxLineLength = maxLine.text.length; - if (!options.lineWrapping) - doc.iter(from.line, to.line + 1, function(line) { - if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;} - }); - if (from.line != to.line || newText.length > 1) gutterDirty = true; - - var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line); - // First adjust the line structure, taking some care to leave highlighting intact. - if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") { - // This is a whole-line replace. Treated specially to make - // sure line objects move the way they are supposed to. - var added = [], prevLine = null; - if (from.line) { - prevLine = getLine(from.line - 1); - prevLine.fixMarkEnds(lastLine); - } else lastLine.fixMarkStarts(); - for (var i = 0, e = newText.length - 1; i < e; ++i) - added.push(Line.inheritMarks(newText[i], prevLine)); - if (nlines) doc.remove(from.line, nlines, callbacks); - if (added.length) doc.insert(from.line, added); - } else if (firstLine == lastLine) { - if (newText.length == 1) - firstLine.replace(from.ch, to.ch, newText[0]); - else { - lastLine = firstLine.split(to.ch, newText[newText.length-1]); - firstLine.replace(from.ch, null, newText[0]); - firstLine.fixMarkEnds(lastLine); - var added = []; - for (var i = 1, e = newText.length - 1; i < e; ++i) - added.push(Line.inheritMarks(newText[i], firstLine)); - added.push(lastLine); - doc.insert(from.line + 1, added); + + removeChildrenAndAdd(display.selectionDiv, fragment); + display.cursor.style.display = display.otherCursor.style.display = "none"; + display.selectionDiv.style.display = ""; + return cursorCoords(cm, selHead(cm.view), "div"); + } + + // Cursor-blinking + function restartBlink(cm) { + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursor.style.visibility = display.otherCursor.style.visibility = ""; + display.blinker = setInterval(function() { + display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); + } + + // HIGHLIGHT WORKER + + function startWorker(cm, time) { + if (cm.view.frontier < cm.display.showingTo) + cm.view.highlight.set(time, bind(highlightWorker, cm)); + } + + function highlightWorker(cm) { + var view = cm.view, doc = view.doc; + if (view.frontier >= cm.display.showingTo) return; + var end = +new Date + cm.options.workTime; + var state = copyState(view.mode, getStateBefore(cm, view.frontier)); + var changed = [], prevChange; + doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) { + if (view.frontier >= cm.display.showingFrom) { // Visible + if (highlightLine(cm, line, state) && view.frontier >= cm.display.showingFrom) { + if (prevChange && prevChange.end == view.frontier) prevChange.end++; + else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1}); } - } else if (newText.length == 1) { - firstLine.replace(from.ch, null, newText[0]); - lastLine.replace(null, to.ch, ""); - firstLine.append(lastLine); - doc.remove(from.line + 1, nlines, callbacks); + line.stateAfter = copyState(view.mode, state); } else { - var added = []; - firstLine.replace(from.ch, null, newText[0]); - lastLine.replace(null, to.ch, newText[newText.length-1]); - firstLine.fixMarkEnds(lastLine); - for (var i = 1, e = newText.length - 1; i < e; ++i) - added.push(Line.inheritMarks(newText[i], firstLine)); - if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks); - doc.insert(from.line + 1, added); + processLine(cm, line, state); + line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null; } - if (options.lineWrapping) { - var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3); - doc.iter(from.line, from.line + newText.length, function(line) { - if (line.hidden) return; - var guess = Math.ceil(line.text.length / perLine) || 1; - if (guess != line.height) updateLineHeight(line, guess); - }); - } else { - doc.iter(from.line, from.line + newText.length, function(line) { - var l = line.text; - if (!line.hidden && l.length > maxLineLength) { - maxLine = line; maxLineLength = l.length; maxLineChanged = true; - recomputeMaxLength = false; - } - }); - if (recomputeMaxLength) updateMaxLine = true; - } - - // Add these lines to the work array, so that they will be - // highlighted. Adjust work lines if lines were added/removed. - var newWork = [], lendiff = newText.length - nlines - 1; - for (var i = 0, l = work.length; i < l; ++i) { - var task = work[i]; - if (task < from.line) newWork.push(task); - else if (task > to.line) newWork.push(task + lendiff); - } - var hlEnd = from.line + Math.min(newText.length, 500); - highlightLines(from.line, hlEnd); - newWork.push(hlEnd); - work = newWork; - startWorker(100); - // Remember that these lines changed, for updating the display - changes.push({from: from.line, to: to.line + 1, diff: lendiff}); - var changeObj = {from: from, to: to, text: newText}; - if (textChanged) { - for (var cur = textChanged; cur.next; cur = cur.next) {} - cur.next = changeObj; - } else textChanged = changeObj; + ++view.frontier; + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true; + } + }); + if (changed.length) + operation(cm, function() { + for (var i = 0; i < changed.length; ++i) + regChange(this, changed[i].start, changed[i].end); + })(); + } - // Update the selection - function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;} - setSelection(clipPos(selFrom), clipPos(selTo), - updateLine(sel.from.line), updateLine(sel.to.line)); + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(cm, n) { + var minindent, minline, doc = cm.view.doc; + for (var search = n, lim = n - 100; search > lim; --search) { + if (search == 0) return 0; + var line = getLine(doc, search-1); + if (line.stateAfter) return search; + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } } + return minline; + } - function needsScrollbar() { - var realHeight = doc.height * textHeight() + 2 * paddingTop(); - return realHeight - 1 > scroller.offsetHeight ? realHeight : false; + function getStateBefore(cm, n) { + var view = cm.view; + var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter; + if (!state) state = startState(view.mode); + else state = copyState(view.mode, state); + view.doc.iter(pos, n, function(line) { + processLine(cm, line, state); + var save = pos == n - 1 || pos % 5 == 0 || pos >= view.showingFrom && pos < view.showingTo; + line.stateAfter = save ? copyState(view.mode, state) : null; + ++pos; + }); + return state; + } + + // POSITION MEASUREMENT + + function paddingTop(display) {return display.lineSpace.offsetTop;} + function paddingLeft(display) { + var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x")); + return e.offsetLeft; + } + + function measureChar(cm, line, ch, data) { + var data = data || measureLine(cm, line); + var dir = -1, len = line.text.length; + for (var pos = ch;; pos += dir) { + var r = data[pos]; + if (r) break; + if (dir < 0 && pos == 0) dir = 1; } + return {left: pos < ch ? r.right : r.left, + right: pos > ch ? r.left : r.right, + top: r.top, bottom: r.bottom}; + } - function updateVerticalScroll(scrollTop) { - var scrollHeight = needsScrollbar(); - scrollbar.style.display = scrollHeight ? "block" : "none"; - if (scrollHeight) { - scrollbarInner.style.height = sizer.style.minHeight = scrollHeight + "px"; - scrollbar.style.height = scroller.clientHeight + "px"; - if (scrollTop != null) scrollbar.scrollTop = scroller.scrollTop = scrollTop; - } else { - sizer.style.minHeight = ""; + function measureLine(cm, line) { + // First look in the cache + var display = cm.display, cache = cm.display.measureLineCache; + for (var i = 0; i < cache.length; ++i) { + var memo = cache[i]; + if (memo.text == line.text && memo.markedSpans == line.markedSpans && + display.scroller.clientWidth == memo.width) + return memo.measure; + } + + var measure = measureLineInner(cm, line); + // Store result in the cache + var memo = {text: line.text, width: display.scroller.clientWidth, + markedSpans: line.markedSpans, measure: measure}; + if (cache.length == 16) cache[++cache.pos % 16] = memo; + else cache.push(memo); + return measure; + } + + function measureLineInner(cm, line) { + var display = cm.display, measure = emptyArray(line.text.length); + var pre = lineContent(cm, line, measure); + removeChildrenAndAdd(display.measure, pre); + + var outer = display.lineDiv.getBoundingClientRect(); + var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight; + for (var i = 0, elt; i < measure.length; ++i) if (elt = measure[i]) { + var size = measure[i].getBoundingClientRect(); + var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot); + for (var j = 0; j < vranges.length; j += 2) { + var rtop = vranges[j], rbot = vranges[j+1]; + if (rtop > bot || rbot < top) continue; + if (rtop <= top && rbot >= bot || + top <= rtop && bot >= rbot || + Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) { + vranges[j] = Math.min(top, rtop); + vranges[j+1] = Math.max(bot, rbot); + break; + } } - // Position the mover div to align with the current virtual scroll position - mover.style.top = displayOffset * textHeight() + "px"; + if (j == vranges.length) vranges.push(top, bot); + data[i] = {left: size.left - outer.left, right: size.right - outer.left, top: j}; } + for (var i = 0, elt; i < data.length; ++i) if (elt = data[i]) { + var vr = elt.top; + elt.top = vranges[vr]; elt.bottom = vranges[vr+1]; + } + return data; + } - // On Mac OS X Lion and up, detect whether the mouse is plugged in by measuring - // the width of a div with a scrollbar in it. If the width is <= 1, then - // the mouse isn't plugged in and scrollbars should overlap the content. - function overlapScrollbars() { - var tmpSbInner = elt("div", null, "CodeMirror-scrollbar-inner", "height: 200px"); - var tmpSb = elt("div", [tmpSbInner], "CodeMirror-scrollbar", "position: absolute; left: -9999px; height: 100px;"); - document.body.appendChild(tmpSb); - var result = (tmpSb.offsetWidth <= 1); - document.body.removeChild(tmpSb); - return result; + // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page" + function intoCoordSystem(cm, lineObj, pos, rect, context) { + if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { + var size = lineObj.widgets[i].node.offsetHeight; + rect.top += size; rect.bottom += size; + } + if (context == "line") return rect; + if (!context) context = "local"; + var yOff = heightAtLine(cm, lineObj); + if (context != "local") yOff -= cm.display.viewOffset; + if (context == "page") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop); + var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft); + rect.left += xOff; rect.right += xOff; } + rect.top += yOff; rect.bottom += yOff; + return rect; + } - function computeMaxLength() { - maxLine = getLine(0); maxLineChanged = true; - var maxLineLength = maxLine.text.length; - doc.iter(1, doc.size, function(line) { - var l = line.text; - if (!line.hidden && l.length > maxLineLength) { - maxLineLength = l.length; maxLine = line; - } - }); - updateMaxLine = false; + function charCoords(cm, pos, context, lineObj) { + if (!lineObj) lineObj = getLine(cm.view.doc, pos.line); + return intoCoordSystem(cm, lineObj, pos, measureChar(cm, lineObj, pos.ch), context); + } + + function cursorCoords(cm, pos, context, lineObj, measurement) { + lineObj = lineObj || getLine(cm.view.doc, pos.line); + if (!measurement) measurement = measureLine(cm, lineObj); + function get(ch, right) { + var m = measureChar(cm, lineObj, ch, measurement); + if (right) m.left = m.right; else m.right = m.left; + return intoCoordSystem(cm, lineObj, pos, m, context); + } + var order = getOrder(lineObj), ch = pos.ch; + if (!order) return get(ch); + var main, other, linedir = order[0].level; + for (var i = 0; i < order.length; ++i) { + var part = order[i], rtl = part.level % 2, nb, here; + if (part.from < ch && part.to > ch) return get(ch, rtl); + var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to; + if (left == ch) { + // Opera and IE return bogus offsets and widths for edges + // where the direction flips, but only for the side with the + // lower level. So we try to use the side with the higher + // level. + if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true); + else here = get(rtl && part.from != part.to ? ch - 1 : ch); + if (rtl == linedir) main = here; else other = here; + } else if (right == ch) { + var nb = i < order.length - 1 && order[i+1]; + if (!rtl && nb && nb.from == nb.to) continue; + if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from); + else here = get(rtl ? ch : ch - 1, true); + if (rtl == linedir) main = here; else other = here; + } } + if (linedir && !ch) other = get(order[0].to - 1); + if (!main) return other; + if (other) main.other = other; + return main; + } - function replaceRange(code, from, to) { - from = clipPos(from); - if (!to) to = from; else to = clipPos(to); - code = splitLines(code); - function adjustPos(pos) { - if (posLess(pos, from)) return pos; - if (!posLess(to, pos)) return end; - var line = pos.line + code.length - (to.line - from.line) - 1; - var ch = pos.ch; - if (pos.line == to.line) - ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0)); - return {line: line, ch: ch}; + // Coords must be lineSpace-local + function coordsChar(cm, x, y) { + var doc = cm.view.doc; + y += cm.display.viewOffset; + if (y < 0) return {line: 0, ch: 0, outside: true}; + var lineNo = lineAtHeight(doc, y); + if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length}; + if (x < 0) x = 0; + + for (;;) { + var lineObj = getLine(doc, lineNo); + var found = coordsCharInner(cm, lineObj, lineNo, x, y); + var merged = collapsedSpanAtEnd(lineObj); + if (merged && found.ch == lineRight(lineObj)) + lineNo = merged.find().to.line; + else + return found; + } + } + + function coordsCharInner(cm, lineObj, lineNo, x, y) { + var doc = cm.view.doc; + var innerOff = y - heightAtLine(cm, lineObj); + var wrongLine = false, cWidth = cm.display.wrapper.clientWidth; + var measurement = measureLine(cm, lineObj); + + function getX(ch) { + var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line", + lineObj, measurement); + wrongLine = true; + if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth); + else if (innerOff < sp.top) return sp.left + cWidth; + else wrongLine = false; + return sp.left; + } + + var bidi = getOrder(lineObj), dist = lineObj.text.length; + var from = lineLeft(lineObj), to = lineRight(lineObj), fromX = paddingLeft(cm.display), toX; + if (!bidi) { + // Guess a suitable upper bound for our search. + var estimated = Math.min(to, Math.ceil((x + Math.floor(innerOff / textHeight(cm.display)) * + cWidth * .9) / charWidth(cm.display))); + for (;;) { + var estX = getX(estimated); + if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); + else {toX = estX; to = estimated; break;} } - var end; - replaceRange1(code, from, to, function(end1) { - end = end1; - return {from: adjustPos(sel.from), to: adjustPos(sel.to)}; - }); - return end; + // Try to guess a suitable lower bound as well. + estimated = Math.floor(to * 0.8); estX = getX(estimated); + if (estX < x) {from = estimated; fromX = estX;} + dist = to - from; + } else toX = getX(to); + if (x > toX) return {line: lineNo, ch: to, outside: wrongLine}; + // Do a binary search between these bounds. + for (;;) { + if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { + var after = x - fromX < toX - x, ch = after ? from : to; + while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch; + return {line: lineNo, ch: ch, after: after, outside: wrongLine}; + } + var step = Math.ceil(dist / 2), middle = from + step; + if (bidi) { + middle = from; + for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1); + } + var middleX = getX(middle); + if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;} + else {from = middle; fromX = middleX; dist = step;} } - function replaceSelection(code, collapse) { - replaceRange1(splitLines(code), sel.from, sel.to, function(end) { - if (collapse == "end") return {from: end, to: end}; - else if (collapse == "start") return {from: sel.from, to: sel.from}; - else return {from: sel.from, to: end}; - }); + } + + var measureText; + function textHeight(display) { + if (display.cachedTextHeight != null) return display.cachedTextHeight; + if (measureText == null) { + measureText = elt("pre"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) display.cachedTextHeight = height; + removeChildren(display.measure); + return height || 1; + } + + function charWidth(display) { + if (display.cachedCharWidth != null) return display.cachedCharWidth; + var anchor = elt("span", "x"); + var pre = elt("pre", [anchor]); + removeChildrenAndAdd(display.measure, pre); + var width = anchor.offsetWidth; + if (width > 2) display.cachedCharWidth = width; + return width || 10; + } + + // OPERATIONS + + // Operations are used to wrap changes in such a way that each + // change won't have to update the cursor and display (which would + // be awkward, slow, and error-prone), but instead updates are + // batched and then all combined and executed at once. + + function startOperation(cm) { + if (cm.curOp) ++cm.curOp.depth; + else cm.curOp = { + // Nested operations delay update until the outermost one + // finishes. + depth: 1, + // An array of ranges of lines that have to be updated. See + // updateDisplay. + changes: [], + delayedCallbacks: [], + updateInput: null, + userSelChange: null, + textChanged: null, + selectionChanged: false, + updateMaxLine: false + }; + } + + function endOperation(cm) { + var op = cm.curOp; + if (--op.depth) return; + cm.curOp = null; + var view = cm.view, display = cm.display; + if (op.updateMaxLine) computeMaxLength(view); + if (view.maxLineChanged && !cm.options.lineWrapping) { + var width = measureChar(cm, view.maxLine, view.maxLine.text.length).right; + display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px"; + view.maxLineChanged = false; } - function replaceRange1(code, from, to, computeSel) { - var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length; - var newSel = computeSel({line: from.line + code.length - 1, ch: endch}); - updateLines(from, to, code, newSel.from, newSel.to); + var newScrollPos, updated; + if (op.selectionChanged) { + var coords = cursorCoords(cm, selHead(view)); + newScrollPos = calculateScrollPos(display, coords.left, coords.top, coords.left, coords.bottom); } + if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) + updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop); + if (!updated && op.selectionChanged) updateSelection(cm); + if (newScrollPos) scrollCursorIntoView(cm); + if (op.selectionChanged) restartBlink(cm); + + if (view.focused && op.updateInput) + resetInput(cm, op.userSelChange); + + if (op.textChanged) + signal(cm, "change", cm, op.textChanged); + if (op.selectionChanged) signal(cm, "cursorActivity", cm); + for (var i = 0; i < op.delayedCallbacks.length; ++i) op.delayedCallbacks[i](cm); + } - function getRange(from, to, lineSep) { - var l1 = from.line, l2 = to.line; - if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch); - var code = [getLine(l1).text.slice(from.ch)]; - doc.iter(l1 + 1, l2, function(line) { code.push(line.text); }); - code.push(getLine(l2).text.slice(0, to.ch)); - return code.join(lineSep || "\n"); + // Wraps a function in an operation. Returns the wrapped function. + function operation(cm1, f) { + return function() { + var cm = cm1 || this; + startOperation(cm); + try {var result = f.apply(cm, arguments);} + finally {endOperation(cm);} + return result; + }; + } + + function regChange(cm, from, to, lendiff) { + cm.curOp.changes.push({from: from, to: to, diff: lendiff}); + } + + // INPUT HANDLING + + function slowPoll(cm) { + if (cm.view.pollingFast) return; + cm.display.poll.set(cm.options.pollInterval, function() { + readInput(cm); + if (cm.view.focused) slowPoll(cm); + }); + } + + function fastPoll(cm) { + var missed = false; + cm.display.pollingFast = true; + function p() { + var changed = readInput(cm); + if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);} + else {cm.display.pollingFast = false; slowPoll(cm);} } - function getSelection(lineSep) { - return getRange(sel.from, sel.to, lineSep); + cm.display.poll.set(20, p); + } + + // prevInput is a hack to work with IME. If we reset the textarea + // on every change, that breaks IME. So we look for changes + // compared to the previous content instead. (Modern browsers have + // events that indicate IME taking place, but these are not widely + // supported or compatible enough yet to rely on.) + function readInput(cm) { + var input = cm.display.input, prevInput = cm.display.prevInput, view = cm.view, sel = view.sel; + if (!view.focused || hasSelection(input) || isReadOnly(cm)) return false; + var text = input.value; + if (text == prevInput && posEq(sel.from, sel.to)) return false; + startOperation(cm); + view.sel.shift = null; + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput[same] == text[same]) ++same; + if (same < prevInput.length) + sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)}; + else if (view.overwrite && posEq(sel.from, sel.to) && !cm.display.pasteIncoming) + sel.to = {line: sel.to.line, ch: Math.min(getLine(cm.view.doc, sel.to.line).text.length, sel.to.ch + (text.length - same))}; + var updateInput = cm.curOp.updateInput; + cm.replaceSelection(text.slice(same), "end"); + cm.curOp.updateInput = updateInput; + if (text.length > 1000) { input.value = cm.display.prevInput = ""; } + else cm.display.prevInput = text; + endOperation(cm); + cm.display.pasteIncoming = false; + return true; + } + + function resetInput(cm, user) { + var view = cm.view, minimal, selected; + if (!posEq(view.sel.from, view.sel.to)) { + cm.display.prevInput = ""; + minimal = hasCopyEvent && + (view.sel.to.line - view.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000); + if (minimal) cm.display.input.value = "-"; + else cm.display.input.value = selected || cm.getSelection(); + if (view.focused) selectInput(cm.display.input); + } else if (user) cm.display.prevInput = cm.display.input.value = ""; + cm.display.inaccurateSelection = minimal; + } + + function focusInput(cm) { + if (cm.options.readOnly != "nocursor") cm.display.input.focus(); + } + + function isReadOnly(cm) { + return cm.options.readOnly || cm.view.cantEdit; + } + + // EVENT HANDLERS + + function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + on(d.gutters, "mousedown", operation(cm, clickInGutter)); + on(d.scroller, "dblclick", operation(cm, e_preventDefault)); + on(d.lineSpace, "selectstart", function(e) { + if (!mouseEventInWidget(d, e)) e_preventDefault(e); + }); + // Gecko browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for Gecko. + if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); + + on(d.scroller, "scroll", function() { + setScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft); + signal(cm, "scroll", cm); + }); + on(d.scrollbarV, "scroll", function() { + setScrollTop(cm, d.scrollbarV.scrollTop); + }); + on(d.scrollbarH, "scroll", function() { + setScrollLeft(cm, d.scrollbarH.scrollLeft); + }); + + on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); + on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); + + function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); } + on(d.scrollbarH, "mousedown", reFocus); + on(d.scrollbarV, "mousedown", reFocus); + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + on(window, "resize", function resizeHandler() { + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = null; + d.measureLineCache.length = d.measureLineCache.pos = 0; + if (d.wrapper.parentNode) updateDisplay(cm, true); + else off(window, "resize", resizeHandler); + }); + + on(d.input, "keyup", operation(cm, function(e) { + if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + if (e_prop(e, "keyCode") == 16) cm.view.sel.shift = null; + })); + on(d.input, "input", bind(fastPoll, cm)); + on(d.input, "keydown", operation(cm, onKeyDown)); + on(d.input, "keypress", operation(cm, onKeyPress)); + on(d.input, "focus", bind(onFocus, cm)); + on(d.input, "blur", bind(onBlur, cm)); + + function drag_(e) { + if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return; + e_stop(e); + } + if (cm.options.dragDrop) { + on(d.scroller, "dragstart", function(e){onDragStart(cm, e);}); + on(d.scroller, "dragenter", drag_); + on(d.scroller, "dragover", drag_); + on(d.scroller, "drop", operation(cm, onDrop)); } + on(d.scroller, "paste", function(){focusInput(cm); fastPoll(cm);}); + on(d.input, "paste", function() { + d.pasteIncoming = true; + fastPoll(cm); + }); - function slowPoll() { - if (pollingFast) return; - poll.set(options.pollInterval, function() { - startOperation(); - readInput(); - if (focused) slowPoll(); - endOperation(); - }); + function prepareCopy() { + if (d.inaccurateSelection) { + d.prevInput = ""; + d.inaccurateSelection = false; + d.input.value = cm.getSelection(); + selectInput(d.input); + } } - function fastPoll() { - var missed = false; - pollingFast = true; - function p() { - startOperation(); - var changed = readInput(); - if (!changed && !missed) {missed = true; poll.set(60, p);} - else {pollingFast = false; slowPoll();} - endOperation(); - } - poll.set(20, p); - } - - // Previnput is a hack to work with IME. If we reset the textarea - // on every change, that breaks IME. So we look for changes - // compared to the previous content instead. (Modern browsers have - // events that indicate IME taking place, but these are not widely - // supported or compatible enough yet to rely on.) - var prevInput = ""; - function readInput() { - if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false; - var text = input.value; - if (text == prevInput) return false; - shiftSelecting = null; - var same = 0, l = Math.min(prevInput.length, text.length); - while (same < l && prevInput[same] == text[same]) ++same; - if (same < prevInput.length) - sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)}; - else if (overwrite && posEq(sel.from, sel.to)) - sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))}; - replaceSelection(text.slice(same), "end"); - if (text.length > 1000) { input.value = prevInput = ""; } - else prevInput = text; - return true; + on(d.input, "cut", prepareCopy); + on(d.input, "copy", prepareCopy); + + // Needed to handle Tab key in KHTML + if (khtml) on(d.sizer, "mouseup", function() { + if (document.activeElement == d.input) d.input.blur(); + focusInput(cm); + }); + } + + function mouseEventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) + if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) || + n.parentNode == display.sizer && n != display.mover) return true; + } + + function posFromMouse(cm, e, liberal) { + var display = cm.display; + if (!liberal) { + var target = e_target(e); + if (target == display.scrollbarH || target == display.scrollbarH.firstChild || + target == display.scrollbarV || target == display.scrollbarV.firstChild || + target == display.scrollbarFiller) return null; } - function resetInput(user) { - if (!posEq(sel.from, sel.to)) { - prevInput = ""; - input.value = getSelection(); - selectInput(input); - } else if (user) prevInput = input.value = ""; - } - - function focusInput() { - if (options.readOnly != "nocursor") input.focus(); - } - - function scrollEditorIntoView() { - var rect = cursor.getBoundingClientRect(); - // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden - if (ie && rect.top == rect.bottom) return; - var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); - if (rect.top < 0 || rect.bottom > winH) scrollCursorIntoView(); - } - function scrollCursorIntoView() { - var coords = calculateCursorCoords(); - scrollIntoView(coords.x, coords.y, coords.x, coords.yBot); - } - function calculateCursorCoords() { - var cursor = localCoords(sel.inverted ? sel.from : sel.to); - var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x; - return {x: x, y: cursor.y, yBot: cursor.yBot}; - } - function scrollIntoView(x1, y1, x2, y2) { - var scrollPos = calculateScrollPos(x1, y1, x2, y2); - if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft;} - if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scroller.scrollTop = scrollPos.scrollTop;} - } - function calculateScrollPos(x1, y1, x2, y2) { - var pl = paddingLeft(), pt = paddingTop(); - y1 += pt; y2 += pt; x1 += pl; x2 += pl; - var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {}; - var docBottom = needsScrollbar() || Infinity; - var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; - if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1); - else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen; - - var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft; - var gutterw = options.fixedGutter ? gutter.clientWidth : 0; - var atLeft = x1 < gutterw + pl + 10; - if (x1 < screenleft + gutterw || atLeft) { - if (atLeft) x1 = 0; - result.scrollLeft = Math.max(0, x1 - 10 - gutterw); - } else if (x2 > screenw + screenleft - 3) { - result.scrollLeft = x2 + 10 - screenw; + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX; y = e.clientY; } catch (e) { return null; } + return coordsChar(cm, x - space.left, y - space.top); + } + + var lastClick, lastDoubleClick; + function onMouseDown(e) { + var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc; + setShift(cm.view, e_prop(e, "shiftKey")); + + if (mouseEventInWidget(display, e)) { + if (!webkit) { + display.scroller.draggable = false; + setTimeout(function(){display.scroller.draggable = true;}, 100); } - return result; + return; } + if (clickInGutter.call(cm, e)) return; + var start = posFromMouse(cm, e); + + switch (e_button(e)) { + case 3: + if (gecko) onContextMenu.call(cm, cm, e); + return; + case 2: + if (start) setSelectionUser(cm, start, start); + setTimeout(bind(focusInput, cm), 20); + e_preventDefault(e); + return; + } + // For button 1, if it was clicked inside the editor + // (posFromMouse returning non-null), we have to adjust the + // selection. + if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;} + + if (!view.focused) onFocus(cm); - function visibleLines(scrollTop) { - var lh = textHeight(), top = (scrollTop != null ? scrollTop : scrollbar.scrollTop) - paddingTop(); - var fromHeight = Math.max(0, Math.floor(top / lh)); - var toHeight = Math.ceil((top + scroller.clientHeight) / lh); - return {from: lineAtHeight(doc, fromHeight), - to: lineAtHeight(doc, toHeight)}; + var now = +new Date, type = "single"; + if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) { + type = "triple"; + e_preventDefault(e); + setTimeout(bind(focusInput, cm), 20); + selectLine(cm, start.line); + } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) { + type = "double"; + lastDoubleClick = {time: now, pos: start}; + e_preventDefault(e); + var word = findWordAt(getLine(doc, start.line).text, start); + setSelectionUser(cm, word.from, word.to); + } else { lastClick = {time: now, pos: start}; } + + var last = start; + if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) && + !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") { + var dragEnd = operation(cm, function(e2) { + if (webkit) display.scroller.draggable = false; + view.draggingText = false; + off(document, "mouseup", dragEnd); + off(display.scroller, "drop", dragEnd); + if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { + e_preventDefault(e2); + setSelectionUser(cm, start, start); + focusInput(cm); + } + }); + // Let the drag handler handle this. + if (webkit) display.scroller.draggable = true; + view.draggingText = dragEnd; + // IE's approach to draggable + if (display.scroller.dragDrop) display.scroller.dragDrop(); + on(document, "mouseup", dragEnd); + on(display.scroller, "drop", dragEnd); + return; } - // Uses a set of changes plus the current scroll position to - // determine which DOM updates have to be made, and makes the - // updates. - function updateDisplay(changes, suppressCallback, scrollTop) { - if (!scroller.clientWidth) { - showingFrom = showingTo = displayOffset = 0; - return; + e_preventDefault(e); + if (type == "single") setSelectionUser(cm, start, start); + + var startstart = sel.from, startend = sel.to; + + function doSelect(cur) { + if (type == "single") { + setSelectionUser(cm, start, cur); + } else if (type == "double") { + var word = findWordAt(getLine(doc, cur.line).text, cur); + if (posLess(cur, startstart)) setSelectionUser(cm, word.from, startend); + else setSelectionUser(cm, startstart, word.to); + } else if (type == "triple") { + if (posLess(cur, startstart)) setSelectionUser(cm, startend, clipPos(doc, {line: cur.line, ch: 0})); + else setSelectionUser(cm, startstart, clipPos(doc, {line: cur.line + 1, ch: 0})); } - // Compute the new visible window - // If scrollTop is specified, use that to determine which lines - // to render instead of the current scrollbar position. - var visible = visibleLines(scrollTop); - // Bail out if the visible area is already rendered and nothing changed. - if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) { - updateVerticalScroll(scrollTop); - return; + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true); + if (!cur) return; + if (!posEq(cur, last)) { + if (!view.focused) onFocus(cm); + last = cur; + doSelect(cur); + var visible = visibleLines(display, doc); + if (cur.line >= visible.to || cur.line < visible.from) + setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150); + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) setTimeout(operation(cm, function() { + if (counter != curCount) return; + display.scroller.scrollTop += outside; + extend(e); + }), 50); } - var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100); - if (showingFrom < from && from - showingFrom < 20) from = showingFrom; - if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo); + } - // Create a range of theoretically intact lines, and punch holes - // in that using the change info. - var intact = changes === true ? [] : - computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes); - // Clip off the parts that won't be visible - var intactLines = 0; - for (var i = 0; i < intact.length; ++i) { - var range = intact[i]; - if (range.from < from) {range.domStart += (from - range.from); range.from = from;} - if (range.to > to) range.to = to; - if (range.from >= range.to) intact.splice(i--, 1); - else intactLines += range.to - range.from; - } - if (intactLines == to - from && from == showingFrom && to == showingTo) { - updateVerticalScroll(scrollTop); + function done(e) { + counter = Infinity; + var cur = posFromMouse(cm, e); + if (cur) doSelect(cur); + e_preventDefault(e); + focusInput(cm); + off(document, "mousemove", move); + off(document, "mouseup", up); + } + + var move = operation(cm, function(e) { + e_preventDefault(e); + if (!ie && !e_button(e)) done(e); + else extend(e); + }); + var up = operation(cm, done); + on(document, "mousemove", move); + on(document, "mouseup", up); + } + + function onDrop(e) { + var cm = this; + if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return; + e_preventDefault(e); + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || isReadOnly(cm)) return; + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var loadFile = function(file, i) { + var reader = new FileReader; + reader.onload = function() { + text[i] = reader.result; + if (++read == n) { + pos = clipPos(cm.view.doc, pos); + operation(cm, function() { + var end = replaceRange(cm, text.join(""), pos, pos); + setSelectionUser(cm, pos, end); + })(); + } + }; + reader.readAsText(file); + }; + for (var i = 0; i < n; ++i) loadFile(files[i], i); + } else { + // Don't do a replace if the drop happened inside of the selected text. + if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) { + cm.view.draggingText(e); + if (ie) setTimeout(bind(focusInput, cm), 50); return; } - intact.sort(function(a, b) {return a.domStart - b.domStart;}); - - var th = textHeight(), gutterDisplay = gutter.style.display; - lineDiv.style.display = "none"; - patchDisplay(from, to, intact); - lineDiv.style.display = gutter.style.display = ""; - - var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th; - // This is just a bogus formula that detects when the editor is - // resized or the font size changes. - if (different) lastSizeC = scroller.clientHeight + th; - showingFrom = from; showingTo = to; - displayOffset = heightAtLine(doc, from); - - // Since this is all rather error prone, it is honoured with the - // only assertion in the whole file. - if (lineDiv.childNodes.length != showingTo - showingFrom) - throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) + - " nodes=" + lineDiv.childNodes.length); - - function checkHeights() { - var curNode = lineDiv.firstChild, heightChanged = false; - doc.iter(showingFrom, showingTo, function(line) { - // Work around bizarro IE7 bug where, sometimes, our curNode - // is magically replaced with a new node in the DOM, leaving - // us with a reference to an orphan (nextSibling-less) node. - if (!curNode) return; - if (!line.hidden) { - var height = Math.round(curNode.offsetHeight / th) || 1; - if (line.height != height) { - updateLineHeight(line, height); - gutterDirty = heightChanged = true; - } - } - curNode = curNode.nextSibling; - }); - return heightChanged; - } - - if (options.lineWrapping) { - checkHeights(); - var scrollHeight = needsScrollbar(); - var shouldHaveScrollbar = scrollHeight ? "block" : "none"; - if (scrollbar.style.display != shouldHaveScrollbar) { - scrollbar.style.display = shouldHaveScrollbar; - if (scrollHeight) scrollbarInner.style.height = scrollHeight + "px"; - checkHeights(); + try { + var text = e.dataTransfer.getData("Text"); + if (text) { + compoundChange(cm, function() { + var curFrom = cm.view.sel.from, curTo = cm.view.sel.to; + setSelectionUser(cm, pos, pos); + if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo); + cm.replaceSelection(text); + focusInput(cm); + onFocus(cm); + }); } } + catch(e){} + } + } - gutter.style.display = gutterDisplay; - if (different || gutterDirty) { - // If the gutter grew in size, re-check heights. If those changed, re-draw gutter. - updateGutter() && options.lineWrapping && checkHeights() && updateGutter(); + function clickInGutter(e) { + var cm = this, display = cm.display; + try { var mX = e.clientX, mY = e.clientY; } + catch(e) { return false; } + + if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false; + e_preventDefault(e); + if (!hasHandler(cm, "gutterClick")) return true; + + var lineBox = display.lineDiv.getBoundingClientRect(); + if (mY > lineBox.bottom) return true; + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.options.gutters.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.view.doc, mY); + var gutter = cm.options.gutters[i]; + signalLater(cm, cm, "gutterClick", cm, line, gutter, e); + break; } - updateVerticalScroll(scrollTop); - updateSelection(); - if (!suppressCallback && options.onUpdate) options.onUpdate(instance); - return true; } + return true; + } - function computeIntact(intact, changes) { - for (var i = 0, l = changes.length || 0; i < l; ++i) { - var change = changes[i], intact2 = [], diff = change.diff || 0; - for (var j = 0, l2 = intact.length; j < l2; ++j) { - var range = intact[j]; - if (change.to <= range.from && change.diff) - intact2.push({from: range.from + diff, to: range.to + diff, - domStart: range.domStart}); - else if (change.to <= range.from || change.from >= range.to) - intact2.push(range); - else { - if (change.from > range.from) - intact2.push({from: range.from, to: change.from, domStart: range.domStart}); - if (change.to < range.to) - intact2.push({from: change.to + diff, to: range.to + diff, - domStart: range.domStart + (change.to - range.from)}); - } - } - intact = intact2; + function onDragStart(cm, e) { + var txt = cm.getSelection(); + e.dataTransfer.setData("Text", txt); + + // Use dummy image instead of default browsers image. + if (e.dataTransfer.setDragImage) + e.dataTransfer.setDragImage(elt('img'), 0, 0); + } + + function setScrollTop(cm, val) { + if (Math.abs(cm.view.scrollTop - val) < 2) return; + cm.view.scrollTop = val; + if (!gecko) updateDisplay(cm, [], val); + if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val; + if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val; + if (gecko) updateDisplay(cm, []); + } + function setScrollLeft(cm, val) { + if (Math.abs(cm.view.scrollLeft - val) < 2) return; + cm.view.scrollLeft = val; + if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val; + if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val; + alignVertically(cm.display); + } + + // Since the delta values reported on mouse wheel events are + // unstandardized between browsers and even browser versions, and + // generally horribly unpredictable, this code starts by measuring + // the scroll effect that the first few mouse wheel events have, + // and, from that, detects the way it can convert deltas to pixel + // offsets afterwards. + // + // The reason we want to know the amount a wheel event will scroll + // is that it gives us a chance to update the display before the + // actual scrolling happens, reducing flickering. + + var wheelSamples = 0, wheelDX, wheelDY, wheelStartX, wheelStartY, wheelPixelsPerUnit = null; + // Fill in a browser-detected starting value on browsers where we + // know one. These don't have to be accurate -- the result of them + // being wrong would just be a slight flicker on the first wheel + // scroll (if it is large enough). + if (ie) wheelPixelsPerUnit = -.53; + else if (gecko) wheelPixelsPerUnit = 15; + else if (chrome) wheelPixelsPerUnit = -.7; + else if (safari) wheelPixelsPerUnit = -1/3; + + function onScrollWheel(cm, e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail; + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail; + else if (dy == null) dy = e.wheelDelta; + + var scroll = cm.display.scroller; + if (wheelPixelsPerUnit != null) { + var pixels = dy * wheelPixelsPerUnit; + var top = cm.view.scrollTop, bot = top + cm.display.wrapper.clientHeight; + if (pixels < 0) top = Math.max(0, top + pixels); + else bot = Math.min(cm.view.doc.height, bot + pixels); + updateDisplay(cm, [], {top: top, bottom: bot}); + } + if (wheelSamples < 20) { + if (wheelStartX == null) { + wheelStartX = scroll.scrollLeft; wheelStartY = scroll.scrollTop; + wheelDX = dx; wheelDY = dy; + setTimeout(function() { + var movedX = scroll.scrollLeft - wheelStartX; + var movedY = scroll.scrollTop - wheelStartY; + var sample = (movedY && wheelDY && movedY / wheelDY) || + (movedX && wheelDX && movedX / wheelDX); + wheelStartX = wheelStartY = null; + if (!sample) return; + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + wheelDX += dx; wheelDY += dy; } - return intact; } + } - function patchDisplay(from, to, intact) { - function killNode(node) { - var tmp = node.nextSibling; - node.parentNode.removeChild(node); - return tmp; + function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) return false; + } + var view = cm.view, prevShift = view.sel.shift; + try { + if (isReadOnly(cm)) view.suppressEdits = true; + if (dropShift) view.sel.shift = null; + bound(cm); + } catch(e) { + if (e != Pass) throw e; + return false; + } finally { + view.sel.shift = prevShift; + view.suppressEdits = false; + } + return true; + } + + var maybeTransition; + function handleKeyBinding(cm, e) { + // Handle auto keymap transitions + var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto; + clearTimeout(maybeTransition); + if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() { + if (getKeyMap(cm.options.keyMap) == startMap) + cm.options.keyMap = (next.call ? next.call(null, cm) : next); + }, 50); + + var name = keyNames[e_prop(e, "keyCode")], handled = false; + var flipCtrlCmd = opera && mac; + if (name == null || e.altGraphKey) return false; + if (e_prop(e, "altKey")) name = "Alt-" + name; + if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name; + if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name; + + var stopped = false; + function stop() { stopped = true; } + + if (e_prop(e, "shiftKey")) { + handled = lookupKey("Shift-" + name, cm.options.extraKeys, cm.options.keyMap, + function(b) {return doHandleBinding(cm, b, true);}, stop) + || lookupKey(name, cm.options.extraKeys, cm.options.keyMap, function(b) { + if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b); + }, stop); + } else { + handled = lookupKey(name, cm.options.extraKeys, cm.options.keyMap, + function(b) { return doHandleBinding(cm, b); }, stop); + } + if (stopped) handled = false; + if (handled) { + e_preventDefault(e); + restartBlink(cm); + if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; } + } + return handled; + } + + function handleCharBinding(cm, e, ch) { + var handled = lookupKey("'" + ch + "'", cm.options.extraKeys, cm.options.keyMap, + function(b) { return doHandleBinding(cm, b, true); }); + if (handled) { + e_preventDefault(e); + restartBlink(cm); + } + return handled; + } + + var lastStoppedKey = null; + function onKeyDown(e) { + var cm = this; + if (!cm.view.focused) onFocus(cm); + if (ie && e.keyCode == 27) { e.returnValue = false; } + if (cm.display.pollingFast) { if (readInput(cm)) cm.display.pollingFast = false; } + if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + var code = e_prop(e, "keyCode"); + // IE does strange things with escape. + setShift(cm.view, code == 16 || e_prop(e, "shiftKey")); + // First give onKeyEvent option a chance to handle this. + var handled = handleKeyBinding(cm, e); + if (opera) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey")) + cm.replaceSelection(""); + } + } + + function onKeyPress(e) { + var cm = this; + if (cm.display.pollingFast) readInput(cm); + if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode"); + if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} + if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return; + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + if (this.options.electricChars && this.view.mode.electricChars && + this.options.smartIndent && !isReadOnly(this) && + this.view.mode.electricChars.indexOf(ch) > -1) + setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75); + if (handleCharBinding(cm, e, ch)) return; + fastPoll(cm); + } + + function onFocus(cm) { + if (cm.options.readOnly == "nocursor") return; + if (!cm.view.focused) { + signal(cm, "focus", cm); + cm.view.focused = true; + if (cm.display.scroller.className.search(/\bCodeMirror-focused\b/) == -1) + cm.display.scroller.className += " CodeMirror-focused"; + resetInput(cm, true); + } + slowPoll(cm); + restartBlink(cm); + } + function onBlur(cm) { + if (cm.view.focused) { + signal(cm, "blur", cm); + cm.view.focused = false; + cm.display.scroller.className = cm.display.scroller.className.replace(" CodeMirror-focused", ""); + } + clearInterval(cm.display.blinker); + setTimeout(function() {if (!cm.view.focused) cm.view.sel.shift = null;}, 150); + } + + var detectingSelectAll; + function onContextMenu(cm, e) { + var display = cm.display, sel = cm.view.sel; + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || opera) return; // Opera is difficult. + if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)) + operation(cm, setSelection)(cm, pos, pos); + + var oldCSS = display.input.style.cssText; + display.inputDiv.style.position = "absolute"; + display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + + "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" + + "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + focusInput(cm); + resetInput(cm, true); + // Adds "Select all" to context menu in FF + if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " "; + + function rehide() { + display.inputDiv.style.position = "relative"; + display.input.style.cssText = oldCSS; + if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos; + slowPoll(cm); + + // Try to detect the user choosing select-all + if (display.input.selectionStart != null) { + clearTimeout(detectingSelectAll); + var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0; + display.prevInput = " "; + display.input.selectionStart = 1; display.input.selectionEnd = extval.length; + detectingSelectAll = setTimeout(function poll(){ + if (display.prevInput == " " && display.input.selectionStart == 0) + operation(cm, commands.selectAll)(cm); + else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500); + else resetInput(cm); + }, 200); } - // The first pass removes the DOM nodes that aren't intact. - if (!intact.length) removeChildren(lineDiv); - else { - var domPos = 0, curNode = lineDiv.firstChild, n; - for (var i = 0; i < intact.length; ++i) { - var cur = intact[i]; - while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;} - for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;} - } - while (curNode) curNode = killNode(curNode); - } - // This pass fills in the lines that actually changed. - var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from; - doc.iter(from, to, function(line) { - if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); - if (!nextIntact || nextIntact.from > j) { - if (line.hidden) var lineElement = elt("pre"); - else { - var lineElement = line.getElement(makeTab); - if (line.className) lineElement.className = line.className; - // Kludge to make sure the styled element lies behind the selection (by z-index) - if (line.bgClassName) { - var pre = elt("pre", "\u00a0", line.bgClassName, "position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2"); - lineElement = elt("div", [pre, lineElement], null, "position: relative"); - } - } - lineDiv.insertBefore(lineElement, curNode); - } else { - curNode = curNode.nextSibling; - } - ++j; + } + + if (gecko) { + e_stop(e); + on(window, "mouseup", function mouseup() { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); }); + } else { + setTimeout(rehide, 50); } + } - function updateGutter() { - if (!options.gutter && !options.lineNumbers) return; - var hText = mover.offsetHeight, hEditor = scroller.clientHeight; - gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px"; - var fragment = document.createDocumentFragment(), i = showingFrom, normalNode; - doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) { - if (line.hidden) { - fragment.appendChild(elt("pre")); - } else { - var marker = line.gutterMarker; - var text = options.lineNumbers ? options.lineNumberFormatter(i + options.firstLineNumber) : null; - if (marker && marker.text) - text = marker.text.replace("%N%", text != null ? text : ""); - else if (text == null) - text = "\u00a0"; - var markerElement = fragment.appendChild(elt("pre", null, marker && marker.style)); - markerElement.innerHTML = text; - for (var j = 1; j < line.height; ++j) { - markerElement.appendChild(elt("br")); - markerElement.appendChild(document.createTextNode("\u00a0")); - } - if (!marker) normalNode = i; + // UPDATING + + // Replace the range from from to to by the strings in newText. + // Afterwards, set the selection to selFrom, selTo. + function updateDoc(cm, from, to, newText, selUpdate) { + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && + removeReadOnlyRanges(cm.view.doc, from, to); + if (split) { + for (var i = split.length - 1; i >= 1; --i) + updateDocInner(cm, split[i].from, split[i].to, [""]); + if (split.length) + return updateDocInner(cm, split[0].from, split[0].to, newText, selUpdate); + } else { + return updateDocInner(cm, from, to, newText, selUpdate); + } + } + + function updateDocInner(cm, from, to, newText, selUpdate) { + if (cm.view.suppressEdits) return; + + var view = cm.view, doc = view.doc, old = []; + doc.iter(from.line, to.line + 1, function(line) { + old.push(newHL(line.text, line.markedSpans)); + }); + if (view.history) { + addChange(view.history, from.line, newText.length, old); + while (view.history.done.length > cm.options.undoDepth) view.history.done.shift(); + } + var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText); + return updateDocNoUndo(cm, from, to, lines, selUpdate); + } + + function unredoHelper(cm, from, to, dir) { + var doc = cm.view.doc, hist = cm.view.history; + if (!from.length) return; + var set = from.pop(), out = []; + for (var i = set.length - 1; i >= 0; i -= 1) { + hist.dirtyCounter += dir; + var change = set[i]; + var replaced = [], end = change.start + change.added; + doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); }); + out.push({start: change.start, added: change.old.length, old: replaced}); + var pos = {line: change.start + change.old.length - 1, + ch: editEnd(hlText(lst(replaced)), hlText(lst(change.old)))}; + updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length}, + change.old, pos); + } + to.push(out); + } + + function updateDocNoUndo(cm, from, to, lines, selUpdate) { + var view = cm.view, doc = view.doc, display = cm.display; + if (view.suppressEdits) return; + + var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + for (var cur = firstLine, merged; merged = collapsedSpanAtStart(cur);) { + checkWidthStart = merged.find().from.line; + cur = getLine(doc, checkWidthStart); + } + doc.iter(checkWidthStart, to.line + 1, function(line) { + if (lineLength(doc, line) == view.maxLineLength) { + recomputeMaxLength = true; + return true; } - ++i; }); - gutter.style.display = "none"; - removeChildrenAndAdd(gutterText, fragment); - // Make sure scrolling doesn't cause number gutter size to pop - if (normalNode != null && options.lineNumbers) { - var node = gutterText.childNodes[normalNode - showingFrom]; - var minwidth = String(doc.size).length, val = eltText(node.firstChild), pad = ""; - while (val.length + pad.length < minwidth) pad += "\u00a0"; - if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild); - } - gutter.style.display = ""; - var resized = Math.abs((parseInt(lineSpace.style.marginLeft) || 0) - gutter.offsetWidth) > 2; - lineSpace.style.marginLeft = gutter.offsetWidth + "px"; - gutterDirty = false; - return resized; - } - function updateSelection() { - var collapsed = posEq(sel.from, sel.to); - var fromPos = localCoords(sel.from, true); - var toPos = collapsed ? fromPos : localCoords(sel.to, true); - var headPos = sel.inverted ? fromPos : toPos, th = textHeight(); - var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv); - inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px"; - inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px"; - if (collapsed) { - cursor.style.top = headPos.y + "px"; - cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px"; - cursor.style.display = ""; - selectionDiv.style.display = "none"; + } + + var lastHL = lst(lines), th = textHeight(display); + + // First adjust the line structure + if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = [], prevLine = null; + for (var i = 0, e = lines.length - 1; i < e; ++i) + added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th)); + updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL)); + if (nlines) doc.remove(from.line, nlines, cm); + if (added.length) doc.insert(from.line, added); + } else if (firstLine == lastLine) { + if (lines.length == 1) { + updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) + + firstLine.text.slice(to.ch), hlSpans(lines[0])); } else { - var sameLine = fromPos.y == toPos.y, fragment = document.createDocumentFragment(); - var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth; - var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight; - var add = function(left, top, right, height) { - var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px" - : "right: " + right + "px"; - fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + - "px; top: " + top + "px; " + rstyle + "; height: " + height + "px")); - }; - if (sel.from.ch && fromPos.y >= 0) { - var right = sameLine ? clientWidth - toPos.x : 0; - add(fromPos.x, fromPos.y, right, th); - } - var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0)); - var middleHeight = Math.min(toPos.y, clientHeight) - middleStart; - if (middleHeight > 0.2 * th) - add(0, middleStart, 0, middleHeight); - if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th) - add(0, toPos.y, clientWidth - toPos.x, th); - removeChildrenAndAdd(selectionDiv, fragment); - cursor.style.display = "none"; - selectionDiv.style.display = ""; - } - } - - function setShift(val) { - if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from); - else shiftSelecting = null; - } - function setSelectionUser(from, to) { - var sh = shiftSelecting && clipPos(shiftSelecting); - if (sh) { - if (posLess(sh, from)) from = sh; - else if (posLess(to, sh)) to = sh; - } - setSelection(from, to); - userSelChange = true; - } - // Update the selection. Last two args are only used by - // updateLines, since they have to be expressed in the line - // numbers before the update. - function setSelection(from, to, oldFrom, oldTo) { - goalColumn = null; - if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;} - if (posEq(sel.from, from) && posEq(sel.to, to)) return; - if (posLess(to, from)) {var tmp = to; to = from; from = tmp;} - - // Skip over hidden lines. - if (from.line != oldFrom) { - var from1 = skipHidden(from, oldFrom, sel.from.ch); - // If there is no non-hidden line left, force visibility on current line - if (!from1) setLineHidden(from.line, false); - else from = from1; - } - if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch); - - if (posEq(from, to)) sel.inverted = false; - else if (posEq(from, sel.to)) sel.inverted = false; - else if (posEq(to, sel.from)) sel.inverted = true; - - if (options.autoClearEmptyLines && posEq(sel.from, sel.to)) { - var head = sel.inverted ? from : to; - if (head.line != sel.from.line && sel.from.line < doc.size) { - var oldLine = getLine(sel.from.line); - if (/^\s+$/.test(oldLine.text)) - setTimeout(operation(function() { - if (oldLine.parent && /^\s+$/.test(oldLine.text)) { - var no = lineNo(oldLine); - replaceRange("", {line: no, ch: 0}, {line: no, ch: oldLine.text.length}); - } - }, 10)); - } + for (var added = [], i = 1, e = lines.length - 1; i < e; ++i) + added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th)); + added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th)); + updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0])); + doc.insert(from.line + 1, added); } - - sel.from = from; sel.to = to; - selectionChanged = true; + } else if (lines.length == 1) { + updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) + + lastLine.text.slice(to.ch), hlSpans(lines[0])); + doc.remove(from.line + 1, nlines, cm); + } else { + var added = []; + updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0])); + updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL)); + for (var i = 1, e = lines.length - 1; i < e; ++i) + added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th)); + if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm); + doc.insert(from.line + 1, added); } - function skipHidden(pos, oldLine, oldCh) { - function getNonHidden(dir) { - var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1; - while (lNo != end) { - var line = getLine(lNo); - if (!line.hidden) { - var ch = pos.ch; - if (toEnd || ch > oldCh || ch > line.text.length) ch = line.text.length; - return {line: lNo, ch: ch}; - } - lNo += dir; + + if (cm.options.lineWrapping) { + var perLine = Math.max(5, display.scroller.clientWidth / charWidth(display) - 3); + doc.iter(from.line, from.line + lines.length, function(line) { + if (line.height == 0) return; + var guess = (Math.ceil(line.text.length / perLine) || 1) * th; + if (guess != line.height) updateLineHeight(line, guess); + }); + } else { + doc.iter(checkWidthStart, from.line + lines.length, function(line) { + var len = lineLength(doc, line); + if (len > view.maxLineLength) { + view.maxLine = line; + view.maxLineLength = len; + view.maxLineChanged = true; + recomputeMaxLength = false; } + }); + if (recomputeMaxLength) cm.curOp.updateMaxLine = true; + } + + // Adjust frontier, schedule worker + view.frontier = Math.min(view.frontier, from.line); + startWorker(cm, 400); + + var lendiff = lines.length - nlines - 1; + // Remember that these lines changed, for updating the display + regChange(cm, from.line, to.line + 1, lendiff); + if (hasHandler(cm, "change")) { + // Normalize lines to contain only strings, since that's what + // the change event handler expects + for (var i = 0; i < lines.length; ++i) + if (typeof lines[i] != "string") lines[i] = lines[i].text; + var changeObj = {from: from, to: to, text: lines}; + if (cm.curOp.textChanged) { + for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {} + cur.next = changeObj; + } else cm.curOp.textChanged = changeObj; + } + + // Update the selection + var newSelFrom, newSelTo, end = {line: from.line + lines.length - 1, + ch: hlText(lastHL).length + (lines.length == 1 ? from.ch : 0)}; + if (typeof selUpdate == "object" && selUpdate.line != null) { + newSelFrom = newSelTo = selUpdate; + } else if (selUpdate == "end") { + newSelFrom = newSelTo = end; + } else if (selUpdate == "start") { + newSelFrom = newSelTo = from; + } else if (selUpdate == "around") { + newSelFrom = from; newSelTo = end; + } else { + var adjustPos = function(pos) { + if (posLess(pos, from)) return pos; + if (!posLess(to, pos)) return end; + var line = pos.line + lendiff; + var ch = pos.ch; + if (pos.line == to.line) + ch += hlText(lastHL).length - (to.ch - (to.line == from.line ? from.ch : 0)); + return {line: line, ch: ch}; + }; + newSelFrom = adjustPos(view.sel.from); + newSelTo = adjustPos(view.sel.to); + } + setSelection(cm, newSelFrom, newSelTo, null, true); + return end; + } + + function replaceRange(cm, code, from, to) { + if (!to) to = from; + if (posLess(to, from)) { var tmp = to; to = from; from = tmp; } + code = splitLines(code); + function adjustPos(pos) { + if (posLess(pos, from)) return pos; + if (!posLess(to, pos)) return end; + var line = pos.line + code.length - (to.line - from.line) - 1; + var ch = pos.ch; + if (pos.line == to.line) + ch += lst(code).length - (to.ch - (to.line == from.line ? from.ch : 0)); + return {line: line, ch: ch}; + } + return updateDoc(cm, from, to, code); + } + + // SELECTION + + function posEq(a, b) {return a.line == b.line && a.ch == b.ch;} + function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} + function copyPos(x) {return {line: x.line, ch: x.ch};} + + function clipLine(doc, n) {return Math.max(0, Math.min(n, doc.size-1));} + function clipPos(doc, pos) { + if (pos.line < 0) return {line: 0, ch: 0}; + if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc, doc.size-1).text.length}; + var ch = pos.ch, linelen = getLine(doc, pos.line).text.length; + if (ch == null || ch > linelen) return {line: pos.line, ch: linelen}; + else if (ch < 0) return {line: pos.line, ch: 0}; + else return pos; + } + function isLine(doc, l) {return l >= 0 && l < doc.size;} + + function setShift(view, val) { + if (val) view.sel.shift = view.sel.shift || selHead(view); + else view.sel.shift = null; + } + function setSelectionUser(cm, from, to, bias) { + var view = cm.view, sh = view.sel.shift; + if (sh) { + sh = clipPos(view.doc, sh); + if (posLess(sh, from)) from = sh; + else if (posLess(to, sh)) to = sh; + } + setSelection(cm, from, to, bias); + cm.curOp.userSelChange = true; + } + + // Update the selection. Last two args are only used by + // updateDoc, since they have to be expressed in the line + // numbers before the update. + function setSelection(cm, from, to, bias, checkAtomic) { + cm.curOp.updateInput = true; + var sel = cm.view.sel, doc = cm.view.doc; + cm.view.goalColumn = null; + if (!checkAtomic && posEq(sel.from, from) && posEq(sel.to, to)) return; + if (posLess(to, from)) {var tmp = to; to = from; from = tmp;} + + // Skip over atomic spans. + if (checkAtomic || !posEq(from, sel.from)) + from = skipAtomic(cm, from, bias, checkAtomic != "push"); + if (checkAtomic || !posEq(to, sel.to)) + to = skipAtomic(cm, to, bias, checkAtomic != "push"); + + if (posEq(from, to)) sel.inverted = false; + else if (posEq(from, sel.to)) sel.inverted = false; + else if (posEq(to, sel.from)) sel.inverted = true; + + if (cm.options.autoClearEmptyLines && posEq(sel.from, sel.to)) { + var head = selHead(cm.view); + if (head.line != sel.from.line && sel.from.line < doc.size) { + var oldLine = getLine(doc, sel.from.line); + if (/^\s+$/.test(oldLine.text)) + setTimeout(operation(cm, function() { + if (oldLine.parent && /^\s+$/.test(oldLine.text)) { + var no = lineNo(oldLine); + replaceRange(cm, "", {line: no, ch: 0}, {line: no, ch: oldLine.text.length}); + } + }, 10)); } - var line = getLine(pos.line); - var toEnd = pos.ch == line.text.length && pos.ch != oldCh; - if (!line.hidden) return pos; - if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1); - else return getNonHidden(-1) || getNonHidden(1); - } - function setCursor(line, ch, user) { - var pos = clipPos({line: line, ch: ch || 0}); - (user ? setSelectionUser : setSelection)(pos, pos); - } - - function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));} - function clipPos(pos) { - if (pos.line < 0) return {line: 0, ch: 0}; - if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length}; - var ch = pos.ch, linelen = getLine(pos.line).text.length; - if (ch == null || ch > linelen) return {line: pos.line, ch: linelen}; - else if (ch < 0) return {line: pos.line, ch: 0}; - else return pos; - } - - function findPosH(dir, unit) { - var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch; - var lineObj = getLine(line); - function findNextLine() { - for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) { - var lo = getLine(l); - if (!lo.hidden) { line = l; lineObj = lo; return true; } + } + + sel.from = from; sel.to = to; + cm.curOp.selectionChanged = true; + } + + function reCheckSelection(cm) { + setSelection(cm, cm.view.sel.from, cm.view.sel.to, null, "push"); + } + + function skipAtomic(cm, pos, bias, mayClear) { + var doc = cm.view.doc, flipped = false, curPos = pos; + var dir = bias || 1; + cm.view.cantEdit = false; + search: for (;;) { + var line = getLine(doc, curPos.line), toClear; + if (line.markedSpans) { + for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) && + (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) { + if (mayClear && m.clearOnEnter) { + (toClear || (toClear = [])).push(m); + continue; + } else if (!m.atomic) continue; + var newPos = m.find()[dir < 0 ? "from" : "to"]; + if (posEq(newPos, curPos)) { + newPos.ch += dir; + if (newPos.ch < 0) { + if (newPos.line) newPos = clipPos(doc, {line: newPos.line - 1}); + else newPos = null; + } else if (newPos.ch > line.text.length) { + if (newPos.line < doc.size - 1) newPos = {line: newPos.line + 1, ch: 0}; + else newPos = null; + } + if (!newPos) { + if (flipped) { + // Driven in a corner -- no valid cursor position found at all + // -- try again *with* clearing, if we didn't already + if (!mayClear) return skipAtomic(cm, pos, bias, true); + // Otherwise, turn off editing until further notice, and return the start of the doc + cm.view.cantEdit = true; + return {line: 0, ch: 0}; + } + flipped = true; newPos = pos; dir = -dir; + } + } + curPos = newPos; + continue search; + } } + if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear(); } - function moveOnce(boundToLine) { - if (ch == (dir < 0 ? 0 : lineObj.text.length)) { - if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0; - else return false; - } else ch += dir; - return true; + return curPos; + } + } + + // SCROLLING + + function scrollCursorIntoView(cm) { + var view = cm.view, coords = cursorCoords(cm, selHead(view)); + scrollIntoView(cm.display, coords.left, coords.top, coords.left, coords.bottom); + if (!view.focused) return; + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + if (coords.top + box.top < 0) doScroll = true; + else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; + if (doScroll != null && !phantom) { + var hidden = display.cursor.style.display == "none"; + if (hidden) { + display.cursor.style.display = ""; + display.cursor.style.left = coords.left + "px"; + display.cursor.style.top = (coords.top - display.viewOffset) + "px"; } - if (unit == "char") moveOnce(); - else if (unit == "column") moveOnce(true); - else if (unit == "word") { - var sawWord = false; - for (;;) { - if (dir < 0) if (!moveOnce()) break; - if (isWordChar(lineObj.text.charAt(ch))) sawWord = true; - else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;} - if (dir > 0) if (!moveOnce()) break; - } + display.cursor.scrollIntoView(doScroll); + if (hidden) display.cursor.style.display = "none"; + } + } + + function scrollIntoView(display, x1, y1, x2, y2) { + var scrollPos = calculateScrollPos(display, x1, y1, x2, y2); + if (scrollPos.scrollLeft != null) {display.scrollbarH.scrollLeft = display.scroller.scrollLeft = scrollPos.scrollLeft;} + if (scrollPos.scrollTop != null) {display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos.scrollTop;} + } + + function calculateScrollPos(display, x1, y1, x2, y2) { + var pt = paddingTop(display); + y1 += pt; y2 += pt; + var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {}; + var docBottom = display.scroller.scrollHeight - scrollerCutOff; + var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; + if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1); + else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2 - screen); + + var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft; + x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth; + var gutterw = display.gutters.offsetWidth; + var atLeft = x1 < gutterw + 10; + if (x1 < screenleft + gutterw || atLeft) { + if (atLeft) x1 = 0; + result.scrollLeft = Math.max(0, x1 - 10 - gutterw); + } else if (x2 > screenw + screenleft - 3) { + result.scrollLeft = x2 + 10 - screenw; + } + return result; + } + + // API UTILITIES + + function indentLine(cm, n, how) { + var doc = cm.view.doc; + if (!how) how = "add"; + if (how == "smart") { + if (!cm.view.mode.indent) how = "prev"; + else var state = getStateBefore(cm, n); + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (how == "smart") { + indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass) how = "prev"; + } + if (how == "prev") { + if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize); + else indentation = 0; + } + else if (how == "add") indentation = curSpace + cm.options.indentUnit; + else if (how == "subtract") indentation = curSpace - cm.options.indentUnit; + indentation = Math.max(0, indentation); + var diff = indentation - curSpace; + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} + if (pos < indentation) indentString += spaceStr(indentation - pos); + + if (indentString != curSpaceString) + replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); + line.stateAfter = null; + } + + function changeLine(cm, handle, op) { + var no = handle, line = handle, doc = cm.view.doc; + if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)); + else no = lineNo(handle); + if (no == null) return null; + if (op(line, no)) regChange(cm, no, no + 1); + else return null; + return line; + } + + function findPosH(cm, dir, unit, visually) { + var doc = cm.view.doc, end = selHead(cm.view), line = end.line, ch = end.ch; + var lineObj = getLine(doc, line); + function findNextLine() { + var l = line + dir; + if (l < 0 || l == doc.size) return false; + line = l; + return lineObj = getLine(doc, l); + } + function moveOnce(boundToLine) { + var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true); + if (next == null) { + if (!boundToLine && findNextLine()) { + if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj); + else ch = dir < 0 ? lineObj.text.length : 0; + } else return false; + } else ch = next; + return true; + } + if (unit == "char") moveOnce(); + else if (unit == "column") moveOnce(true); + else if (unit == "word") { + var sawWord = false; + for (;;) { + if (dir < 0) if (!moveOnce()) break; + if (isWordChar(lineObj.text.charAt(ch))) sawWord = true; + else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;} + if (dir > 0) if (!moveOnce()) break; } - return {line: line, ch: ch}; } - function moveH(dir, unit) { - var pos = dir < 0 ? sel.from : sel.to; - if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit); - setCursor(pos.line, pos.ch, true); - } - function deleteH(dir, unit) { - if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to); - else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to); - else replaceRange("", sel.from, findPosH(dir, unit)); - userSelChange = true; - } - function moveV(dir, unit) { - var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true); - if (goalColumn != null) pos.x = goalColumn; - if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight); - else if (unit == "line") dist = textHeight(); - var target = coordsChar(pos.x, pos.y + dist * dir + 2); - if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y; - setCursor(target.line, target.ch, true); - goalColumn = pos.x; - } - - function findWordAt(pos) { - var line = getLine(pos.line).text; - var start = pos.ch, end = pos.ch; - if (line) { - if (pos.after === false || end == line.length) --start; else ++end; - var startChar = line.charAt(start); - var check = isWordChar(startChar) ? isWordChar : - /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} : - function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; - while (start > 0 && check(line.charAt(start - 1))) --start; - while (end < line.length && check(line.charAt(end))) ++end; - } - return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}}; - } - function selectLine(line) { - setSelectionUser({line: line, ch: 0}, clipPos({line: line + 1, ch: 0})); - } - function indentSelected(mode) { - if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode); + return skipAtomic(cm, {line: line, ch: ch}, dir, true); + } + + function findWordAt(line, pos) { + var start = pos.ch, end = pos.ch; + if (line) { + if (pos.after === false || end == line.length) --start; else ++end; + var startChar = line.charAt(start); + var check = isWordChar(startChar) ? isWordChar : + /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} : + function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; + while (start > 0 && check(line.charAt(start - 1))) --start; + while (end < line.length && check(line.charAt(end))) ++end; + } + return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}}; + } + + function selectLine(cm, line) { + setSelectionUser(cm, {line: line, ch: 0}, clipPos(cm.view.doc, {line: line + 1, ch: 0})); + } + + // PROTOTYPE + + // The publicly visible API. Note that operation(null, f) means + // 'wrap f in an operation, performed on its `this` parameter' + + CodeMirror.prototype = { + getValue: function(lineSep) { + var text = [], doc = this.view.doc; + doc.iter(0, doc.size, function(line) { text.push(line.text); }); + return text.join(lineSep || "\n"); + }, + + setValue: operation(null, function(code) { + var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length; + updateDocInner(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top); + }), + + getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); }, + + replaceSelection: operation(null, function(code, collapse) { + var sel = this.view.sel; + updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around"); + }), + + focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") return; + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + operation(this, optionHandlers[option])(this, value, old); + }, + + getOption: function(option) {return this.options[option];}, + + getMode: function() {return this.view.mode;}, + + undo: operation(null, function() { + var hist = this.view.history; + unredoHelper(this, hist.done, hist.undone, -1); + }), + redo: operation(null, function() { + var hist = this.view.history; + unredoHelper(this, hist.undone, hist.done, 1); + }), + + indentLine: operation(null, function(n, dir) { + if (typeof dir != "string") { + if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; + else dir = dir ? "add" : "subtract"; + } + if (isLine(this.view.doc, n)) indentLine(this, n, dir); + }), + + indentSelection: operation(null, function(how) { + var sel = this.view.sel; + if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how); var e = sel.to.line - (sel.to.ch ? 0 : 1); - for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode); - } - - function indentLine(n, how) { - if (!how) how = "add"; - if (how == "smart") { - if (!mode.indent) how = "prev"; - else var state = getStateBefore(n); - } - - var line = getLine(n), curSpace = line.indentation(options.tabSize), - curSpaceString = line.text.match(/^\s*/)[0], indentation; - if (how == "smart") { - indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text); - if (indentation == Pass) how = "prev"; - } - if (how == "prev") { - if (n) indentation = getLine(n-1).indentation(options.tabSize); - else indentation = 0; - } - else if (how == "add") indentation = curSpace + options.indentUnit; - else if (how == "subtract") indentation = curSpace - options.indentUnit; - indentation = Math.max(0, indentation); - var diff = indentation - curSpace; - - var indentString = "", pos = 0; - if (options.indentWithTabs) - for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";} - while (pos < indentation) {++pos; indentString += " ";} - - replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); - } - - function loadMode() { - mode = CodeMirror.getMode(options, options.mode); - doc.iter(0, doc.size, function(line) { line.stateAfter = null; }); - work = [0]; - startWorker(); - } - function gutterChanged() { - var visible = options.gutter || options.lineNumbers; - gutter.style.display = visible ? "" : "none"; - if (visible) gutterDirty = true; - else lineDiv.parentNode.style.marginLeft = 0; - } - function wrappingChanged(from, to) { - if (options.lineWrapping) { - wrapper.className += " CodeMirror-wrap"; - var perLine = scroller.clientWidth / charWidth() - 3; - doc.iter(0, doc.size, function(line) { - if (line.hidden) return; - var guess = Math.ceil(line.text.length / perLine) || 1; - if (guess != 1) updateLineHeight(line, guess); - }); - lineSpace.style.minWidth = widthForcer.style.left = ""; - } else { - wrapper.className = wrapper.className.replace(" CodeMirror-wrap", ""); - computeMaxLength(); - doc.iter(0, doc.size, function(line) { - if (line.height != 1 && !line.hidden) updateLineHeight(line, 1); - }); - } - changes.push({from: 0, to: doc.size}); - } - function makeTab(col) { - var w = options.tabSize - col % options.tabSize, cached = tabCache[w]; - if (cached) return cached; - for (var str = "", i = 0; i < w; ++i) str += " "; - var span = elt("span", str, "cm-tab"); - return (tabCache[w] = {element: span, width: w}); - } - function themeChanged() { - scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") + - options.theme.replace(/(^|\s)\s*/g, " cm-s-"); - } - function keyMapChanged() { - var style = keyMap[options.keyMap].style; - wrapper.className = wrapper.className.replace(/\s*cm-keymap-\S+/g, "") + - (style ? " cm-keymap-" + style : ""); - } - - function TextMarker() { this.set = []; } - TextMarker.prototype.clear = operation(function() { - var min = Infinity, max = -Infinity; - for (var i = 0, e = this.set.length; i < e; ++i) { - var line = this.set[i], mk = line.marked; - if (!mk || !line.parent) continue; - var lineN = lineNo(line); - min = Math.min(min, lineN); max = Math.max(max, lineN); - for (var j = 0; j < mk.length; ++j) - if (mk[j].marker == this) mk.splice(j--, 1); - } - if (min != Infinity) - changes.push({from: min, to: max + 1}); - }); - TextMarker.prototype.find = function() { - var from, to; - for (var i = 0, e = this.set.length; i < e; ++i) { - var line = this.set[i], mk = line.marked; - for (var j = 0; j < mk.length; ++j) { - var mark = mk[j]; - if (mark.marker == this) { - if (mark.from != null || mark.to != null) { - var found = lineNo(line); - if (found != null) { - if (mark.from != null) from = {line: found, ch: mark.from}; - if (mark.to != null) to = {line: found, ch: mark.to}; - } - } + for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how); + }), + + historySize: function() { + var hist = this.view.history; + return {undo: hist.done.length, redo: hist.undone.length}; + }, + + clearHistory: function() {this.view.history = makeHistory();}, + + markClean: function() { + this.view.history.dirtyCounter = 0; + this.view.history.closed = true; + }, + + isClean: function () {return this.view.history.dirtyCounter == 0;}, + + getHistory: function() { + var hist = this.view.history; + function cp(arr) { + for (var i = 0, nw = [], nwelt; i < arr.length; ++i) { + nw.push(nwelt = []); + for (var j = 0, elt = arr[i]; j < elt.length; ++j) { + var old = [], cur = elt[j]; + nwelt.push({start: cur.start, added: cur.added, old: old}); + for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k])); } } + return nw; } - return {from: from, to: to}; - }; + return {done: cp(hist.done), undone: cp(hist.undone)}; + }, - function markText(from, to, className) { - from = clipPos(from); to = clipPos(to); - var tm = new TextMarker(); - if (!posLess(from, to)) return tm; - function add(line, from, to, className) { - getLine(line).addMark(new MarkedText(from, to, className, tm)); - } - if (from.line == to.line) add(from.line, from.ch, to.ch, className); - else { - add(from.line, from.ch, null, className); - for (var i = from.line + 1, e = to.line; i < e; ++i) - add(i, null, null, className); - add(to.line, null, to.ch, className); - } - changes.push({from: from.line, to: to.line + 1}); - return tm; - } - - function setBookmark(pos) { - pos = clipPos(pos); - var bm = new Bookmark(pos.ch); - getLine(pos.line).addMark(bm); - return bm; - } - - function findMarksAt(pos) { - pos = clipPos(pos); - var markers = [], marked = getLine(pos.line).marked; - if (!marked) return markers; - for (var i = 0, e = marked.length; i < e; ++i) { - var m = marked[i]; - if ((m.from == null || m.from <= pos.ch) && - (m.to == null || m.to >= pos.ch)) - markers.push(m.marker || m); + setHistory: function(histData) { + var hist = this.view.history = makeHistory(); + hist.done = histData.done; + hist.undone = histData.undone; + }, + + getTokenAt: function(pos) { + var doc = this.view.doc; + pos = clipPos(doc, pos); + return getTokenAt(this, getLine(doc, pos.line), getStateBefore(this, pos.line), pos.ch); + }, + + getStateAfter: function(line) { + var doc = this.view.doc; + line = clipLine(doc, line == null ? doc.size - 1: line); + return getStateBefore(this, line + 1); + }, + + cursorCoords: function(start, mode) { + var pos, sel = this.view.sel; + if (start == null) start = sel.inverted; + if (typeof start == "object") pos = clipPos(this.view.doc, start); + else pos = start ? sel.from : sel.to; + return cursorCoords(this, pos, mode || "page"); + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.view.doc, pos), mode || "page"); + }, + + coordsChar: function(coords) { + var off = this.display.lineSpace.getBoundingClientRect(); + return coordsChar(this, coords.left - off.left, coords.top - off.top); + }, + + defaultTextHeight: function() { return textHeight(this.display); }, + + markText: operation(null, function(from, to, options) { + return markText(this, clipPos(this.view.doc, from), clipPos(this.view.doc, to), + options, "range"); + }), + + setBookmark: operation(null, function(pos, widget) { + pos = clipPos(this.view.doc, pos); + return markText(this, pos, pos, widget ? {replacedWith: widget} : {}, "bookmark"); + }), + + findMarksAt: function(pos) { + var doc = this.view.doc; + pos = clipPos(doc, pos); + var markers = [], spans = getLine(doc, pos.line).markedSpans; + if (spans) for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + markers.push(span.marker); } return markers; - } + }, - function addGutterMarker(line, text, className) { - if (typeof line == "number") line = getLine(clipLine(line)); - line.gutterMarker = {text: text, style: className}; - gutterDirty = true; - return line; - } - function removeGutterMarker(line) { - if (typeof line == "number") line = getLine(clipLine(line)); - line.gutterMarker = null; - gutterDirty = true; - } - - function changeLine(handle, op) { - var no = handle, line = handle; - if (typeof handle == "number") line = getLine(clipLine(handle)); - else no = lineNo(handle); - if (no == null) return null; - if (op(line, no)) changes.push({from: no, to: no + 1}); - else return null; - return line; - } - function setLineClass(handle, className, bgClassName) { - return changeLine(handle, function(line) { - if (line.className != className || line.bgClassName != bgClassName) { - line.className = className; - line.bgClassName = bgClassName; - return true; + setGutterMarker: operation(null, function(line, gutterID, value) { + return changeLine(this, line, function(line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) line.gutterMarkers = null; + return true; + }); + }), + + clearGutter: operation(null, function(gutterID) { + var i = 0, cm = this, doc = cm.view.doc; + doc.iter(0, doc.size, function(line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + line.gutterMarkers[gutterID] = null; + regChange(cm, i, i + 1); + if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null; } + ++i; }); - } - function setLineHidden(handle, hidden) { - return changeLine(handle, function(line, no) { - if (line.hidden != hidden) { - line.hidden = hidden; - if (!options.lineWrapping) { - if (hidden && line.text.length == maxLine.text.length) { - updateMaxLine = true; - } else if (!hidden && line.text.length > maxLine.text.length) { - maxLine = line; updateMaxLine = false; - } - } - updateLineHeight(line, hidden ? 0 : 1); - var fline = sel.from.line, tline = sel.to.line; - if (hidden && (fline == no || tline == no)) { - var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from; - var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to; - // Can't hide the last visible line, we'd have no place to put the cursor - if (!to) return; - setSelection(from, to); - } - return (gutterDirty = true); + }), + + addLineClass: operation(null, function(handle, where, cls) { + return changeLine(this, handle, function(line) { + var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass"; + if (!line[prop]) line[prop] = cls; + else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false; + else line[prop] += " " + cls; + return true; + }); + }), + + removeLineClass: operation(null, function(handle, where, cls) { + return changeLine(this, handle, function(line) { + var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) return false; + else if (cls == null) line[prop] = null; + else { + var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), ""); + if (upd == cur) return false; + line[prop] = upd || null; } + return true; }); - } + }), + + addLineWidget: operation(null, function addLineWidget(handle, node, options) { + var widget = options || {}; + widget.node = node; + if (widget.noHScroll) this.display.alignWidgets = true; + changeLine(this, handle, function(line) { + (line.widgets || (line.widgets = [])).push(widget); + widget.line = line; + return true; + }); + return widget; + }), - function lineInfo(line) { + removeLineWidget: operation(null, function(widget) { + var ws = widget.line.widgets, no = lineNo(widget.line); + if (no == null) return; + for (var i = 0; i < ws.length; ++i) if (ws[i] == widget) ws.splice(i--, 1); + regChange(this, no, no + 1); + }), + + lineInfo: function(line) { if (typeof line == "number") { - if (!isLine(line)) return null; + if (!isLine(this.view.doc, line)) return null; var n = line; - line = getLine(line); + line = getLine(this.view.doc, line); if (!line) return null; } else { var n = lineNo(line); if (n == null) return null; } - var marker = line.gutterMarker; - return {line: n, handle: line, text: line.text, markerText: marker && marker.text, - markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName}; - } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets}; + }, - // These are used to go from pixel positions to character - // positions, taking varying character widths into account. - function charFromX(line, x) { - if (x <= 0) return 0; - var lineObj = getLine(line), text = lineObj.text; - function getX(len) { - return measureLine(lineObj, len).left; - } - var from = 0, fromX = 0, to = text.length, toX; - // Guess a suitable upper bound for our search. - var estimated = Math.min(to, Math.ceil(x / charWidth())); - for (;;) { - var estX = getX(estimated); - if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); - else {toX = estX; to = estimated; break;} - } - if (x > toX) return to; - // Try to guess a suitable lower bound as well. - estimated = Math.floor(to * 0.8); estX = getX(estimated); - if (estX < x) {from = estimated; fromX = estX;} - // Do a binary search between these bounds. - for (;;) { - if (to - from <= 1) return (toX - x > x - fromX) ? from : to; - var middle = Math.ceil((from + to) / 2), middleX = getX(middle); - if (middleX > x) {to = middle; toX = middleX;} - else {from = middle; fromX = middleX;} - } - } - - function measureLine(line, ch) { - if (ch == 0) return {top: 0, left: 0}; - var wbr = options.lineWrapping && ch < line.text.length && - spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1)); - var pre = line.getElement(makeTab, ch, wbr); - removeChildrenAndAdd(measure, pre); - var anchor = pre.anchor; - var top = anchor.offsetTop, left = anchor.offsetLeft; - // Older IEs report zero offsets for spans directly after a wrap - if (ie && top == 0 && left == 0) { - var backup = elt("span", "x"); - anchor.parentNode.insertBefore(backup, anchor.nextSibling); - top = backup.offsetTop; - } - return {top: top, left: left}; - } - function localCoords(pos, inLineWrap) { - var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0)); - if (pos.ch == 0) x = 0; - else { - var sp = measureLine(getLine(pos.line), pos.ch); - x = sp.left; - if (options.lineWrapping) y += Math.max(0, sp.top); - } - return {x: x, y: y, yBot: y + lh}; - } - // Coords must be lineSpace-local - function coordsChar(x, y) { - if (y < 0) return {line: 0, ch: 0}; - var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th); - var lineNo = lineAtHeight(doc, heightPos); - if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length}; - var lineObj = getLine(lineNo), text = lineObj.text; - var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; - if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0}; - var wrongLine = false; - function getX(len) { - var sp = measureLine(lineObj, len); - if (tw) { - var off = Math.round(sp.top / th); - wrongLine = off != innerOff; - return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth); - } - return sp.left; - } - var from = 0, fromX = 0, to = text.length, toX; - // Guess a suitable upper bound for our search. - var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw)); - for (;;) { - var estX = getX(estimated); - if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); - else {toX = estX; to = estimated; break;} + getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.view.doc, pos)); + var top = pos.top, left = pos.left; + node.style.position = "absolute"; + display.sizer.appendChild(node); + if (vert == "over") top = pos.top; + else if (vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.view.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight) + top = pos.top - node.offsetHeight; + if (left + node.offsetWidth > hspace) + left = hspace - node.offsetWidth; } - if (x > toX) return {line: lineNo, ch: to}; - // Try to guess a suitable lower bound as well. - estimated = Math.floor(to * 0.8); estX = getX(estimated); - if (estX < x) {from = estimated; fromX = estX;} - // Do a binary search between these bounds. - for (;;) { - if (to - from <= 1) { - var after = x - fromX < toX - x; - return {line: lineNo, ch: after ? from : to, after: after}; - } - var middle = Math.ceil((from + to) / 2), middleX = getX(middle); - if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; } - else {from = middle; fromX = middleX;} - } - } - function pageCoords(pos) { - var local = localCoords(pos, true), off = eltOffset(lineSpace); - return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot}; - } - - var cachedHeight, cachedHeightFor, measurePre; - function textHeight() { - if (measurePre == null) { - measurePre = elt("pre"); - for (var i = 0; i < 49; ++i) { - measurePre.appendChild(document.createTextNode("x")); - measurePre.appendChild(elt("br")); - } - measurePre.appendChild(document.createTextNode("x")); - } - var offsetHeight = lineDiv.clientHeight; - if (offsetHeight == cachedHeightFor) return cachedHeight; - cachedHeightFor = offsetHeight; - removeChildrenAndAdd(measure, measurePre.cloneNode(true)); - cachedHeight = measure.firstChild.offsetHeight / 50 || 1; - removeChildren(measure); - return cachedHeight; - } - var cachedWidth, cachedWidthFor = 0; - function charWidth() { - if (scroller.clientWidth == cachedWidthFor) return cachedWidth; - cachedWidthFor = scroller.clientWidth; - var anchor = elt("span", "x"); - var pre = elt("pre", [anchor]); - removeChildrenAndAdd(measure, pre); - return (cachedWidth = anchor.offsetWidth || 10); - } - function paddingTop() {return lineSpace.offsetTop;} - function paddingLeft() {return lineSpace.offsetLeft;} - - function posFromMouse(e, liberal) { - var offW = eltOffset(scroller, true), x, y; - // Fails unpredictably on IE[67] when mouse is dragged around quickly. - try { x = e.clientX; y = e.clientY; } catch (e) { return null; } - // This is a mess of a heuristic to try and determine whether a - // scroll-bar was clicked or not, and to return null if one was - // (and !liberal). - if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight)) - return null; - var offL = eltOffset(lineSpace, true); - return coordsChar(x - offL.left, y - offL.top); - } - function onContextMenu(e) { - var pos = posFromMouse(e), scrollPos = scrollbar.scrollTop; - if (!pos || opera) return; // Opera is difficult. - if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)) - operation(setCursor)(pos.line, pos.ch); - - var oldCSS = input.style.cssText; - inputDiv.style.position = "absolute"; - input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + - "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " + - "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; - leaveInputAlone = true; - var val = input.value = getSelection(); - focusInput(); - selectInput(input); - function rehide() { - var newVal = splitLines(input.value).join("\n"); - if (newVal != val && !options.readOnly) operation(replaceSelection)(newVal, "end"); - inputDiv.style.position = "relative"; - input.style.cssText = oldCSS; - if (ie_lt9) scrollbar.scrollTop = scrollPos; - leaveInputAlone = false; - resetInput(true); - slowPoll(); - } - - if (gecko) { - e_stop(e); - var mouseup = connect(window, "mouseup", function() { - mouseup(); - setTimeout(rehide, 20); - }, true); + node.style.top = (top + paddingTop(display)) + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; } else { - setTimeout(rehide, 50); - } - } - - // Cursor-blinking - function restartBlink() { - clearInterval(blinker); - var on = true; - cursor.style.visibility = ""; - blinker = setInterval(function() { - cursor.style.visibility = (on = !on) ? "" : "hidden"; - }, 650); - } - - var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; - function matchBrackets(autoclear) { - var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1; - var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; - if (!match) return; - var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles; - for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2) - if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;} - - var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; - function scan(line, from, to) { - if (!line.text) return; - var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur; - for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) { - var text = st[i]; - if (st[i+1] != style) {pos += d * text.length; continue;} - for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) { - if (pos >= from && pos < to && re.test(cur = text.charAt(j))) { - var match = matching[cur]; - if (match.charAt(1) == ">" == forward) stack.push(cur); - else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; - else if (!stack.length) return {pos: pos, match: true}; - } - } - } + if (horiz == "left") left = 0; + else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2; + node.style.left = left + "px"; } - for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) { - var line = getLine(i), first = i == head.line; - var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length); - if (found) break; - } - if (!found) found = {pos: null, match: false}; - var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; - var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style), - two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style); - var clear = operation(function(){one.clear(); two && two.clear();}); - if (autoclear) setTimeout(clear, 800); - else bracketHighlighted = clear; - } - - // Finds the line to start with when starting a parse. Tries to - // find a line with a stateAfter, so that it can start with a - // valid state. If that fails, it returns the line with the - // smallest indentation, which tends to need the least context to - // parse correctly. - function findStartLine(n) { - var minindent, minline; - for (var search = n, lim = n - 40; search > lim; --search) { - if (search == 0) return 0; - var line = getLine(search-1); - if (line.stateAfter) return search; - var indented = line.indentation(options.tabSize); - if (minline == null || minindent > indented) { - minline = search - 1; - minindent = indented; - } + if (scroll) + scrollIntoView(display, left, top, left + node.offsetWidth, top + node.offsetHeight); + }, + + lineCount: function() {return this.view.doc.size;}, + + clipPos: function(pos) {return clipPos(this.view.doc, pos);}, + + getCursor: function(start) { + var sel = this.view.sel; + if (start == null) start = sel.inverted; + return copyPos(start ? sel.from : sel.to); + }, + + somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);}, + + setCursor: operation(null, function(line, ch, user) { + var pos = typeof line == "number" ? {line: line, ch: ch || 0} : line; + (user ? setSelectionUser : setSelection)(this, pos, pos); + }), + + setSelection: operation(null, function(from, to, user) { + var doc = this.view.doc; + (user ? setSelectionUser : setSelection)(this, clipPos(doc, from), clipPos(doc, to || from)); + }), + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;}, + + getLineHandle: function(line) { + var doc = this.view.doc; + if (isLine(doc, line)) return getLine(doc, line); + }, + + getLineNumber: function(line) {return lineNo(line);}, + + setLine: operation(null, function(line, text) { + if (isLine(this.view.doc, line)) + replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length}); + }), + + removeLine: operation(null, function(line) { + if (isLine(this.view.doc, line)) + replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0})); + }), + + replaceRange: operation(null, function(code, from, to) { + var doc = this.view.doc; + from = clipPos(doc, from); + to = to ? clipPos(doc, to) : from; + return replaceRange(this, code, from, to); + }), + + getRange: function(from, to, lineSep) { + var doc = this.view.doc; + from = clipPos(doc, from); to = clipPos(doc, to); + var l1 = from.line, l2 = to.line; + if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch); + var code = [getLine(doc, l1).text.slice(from.ch)]; + doc.iter(l1 + 1, l2, function(line) { code.push(line.text); }); + code.push(getLine(doc, l2).text.slice(0, to.ch)); + return code.join(lineSep || "\n"); + }, + + triggerOnKeyDown: operation(null, onKeyDown), + + execCommand: function(cmd) {return commands[cmd](this);}, + + // Stuff used by commands, probably not much use to outside code. + moveH: operation(null, function(dir, unit) { + var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to; + if (sel.shift || posEq(sel.from, sel.to)) pos = findPosH(this, dir, unit, true); + setSelectionUser(this, pos, pos, dir); + }), + + deleteH: operation(null, function(dir, unit) { + var sel = this.view.sel; + if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to); + else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false)); + this.curOp.userSelChange = true; + }), + + moveV: operation(null, function(dir, unit) { + var view = this.view, doc = view.doc, display = this.display; + var dist = 0, cur = selHead(view), pos = cursorCoords(this, cur, "div"); + var x = pos.left, y; + if (view.goalColumn != null) x = view.goalColumn; + if (unit == "page") { + var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); + y = pos.top + dir * pageSize; + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } - return minline; - } - function getStateBefore(n) { - var start = findStartLine(n), state = start && getLine(start-1).stateAfter; - if (!state) state = startState(mode); - else state = copyState(mode, state); - doc.iter(start, n, function(line) { - line.highlight(mode, state, options.tabSize); - line.stateAfter = copyState(mode, state); + do { + var target = coordsChar(this, x, y); + y += dir * 5; + } while (target.outside && (dir < 0 ? y > 0 : y < doc.height)); + + if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top; + setSelectionUser(this, target, target, dir); + view.goalColumn = x; + }), + + toggleOverwrite: function() { + if (this.view.overwrite = !this.view.overwrite) + this.display.cursor.className += " CodeMirror-overwrite"; + else + this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", ""); + }, + + posFromIndex: function(off) { + var lineNo = 0, ch, doc = this.view.doc; + doc.iter(0, doc.size, function(line) { + var sz = line.text.length + 1; + if (sz > off) { ch = off; return true; } + off -= sz; + ++lineNo; }); - if (start < n) changes.push({from: start, to: n}); - if (n < doc.size && !getLine(n).stateAfter) work.push(n); - return state; - } - function highlightLines(start, end) { - var state = getStateBefore(start); - doc.iter(start, end, function(line) { - line.highlight(mode, state, options.tabSize); - line.stateAfter = copyState(mode, state); + return clipPos(doc, {line: lineNo, ch: ch}); + }, + indexFromPos: function (coords) { + if (coords.line < 0 || coords.ch < 0) return 0; + var index = coords.ch; + this.view.doc.iter(0, coords.line, function (line) { + index += line.text.length + 1; }); - } - function highlightWorker() { - var end = +new Date + options.workTime; - var foundWork = work.length; - while (work.length) { - if (!getLine(showingFrom).stateAfter) var task = showingFrom; - else var task = work.pop(); - if (task >= doc.size) continue; - var start = findStartLine(task), state = start && getLine(start-1).stateAfter; - if (state) state = copyState(mode, state); - else state = startState(mode); - - var unchanged = 0, compare = mode.compareStates, realChange = false, - i = start, bail = false; - doc.iter(i, doc.size, function(line) { - var hadState = line.stateAfter; - if (+new Date > end) { - work.push(i); - startWorker(options.workDelay); - if (realChange) changes.push({from: task, to: i + 1}); - return (bail = true); - } - var changed = line.highlight(mode, state, options.tabSize); - if (changed) realChange = true; - line.stateAfter = copyState(mode, state); - var done = null; - if (compare) { - var same = hadState && compare(hadState, state); - if (same != Pass) done = !!same; - } - if (done == null) { - if (changed !== false || !hadState) unchanged = 0; - else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, ""))) - done = true; - } - if (done) return true; - ++i; - }); - if (bail) return; - if (realChange) changes.push({from: task, to: i + 1}); - } - if (foundWork && options.onHighlightComplete) - options.onHighlightComplete(instance); - } - function startWorker(time) { - if (!work.length) return; - highlight.set(time, operation(highlightWorker)); - } - - // Operations are used to wrap changes in such a way that each - // change won't have to update the cursor and display (which would - // be awkward, slow, and error-prone), but instead updates are - // batched and then all combined and executed at once. - function startOperation() { - updateInput = userSelChange = textChanged = null; - changes = []; selectionChanged = false; callbacks = []; - } - function endOperation() { - if (updateMaxLine) computeMaxLength(); - if (maxLineChanged && !options.lineWrapping) { - var cursorWidth = widthForcer.offsetWidth, left = measureLine(maxLine, maxLine.text.length).left; - widthForcer.style.left = left + "px"; - lineSpace.style.minWidth = (left + cursorWidth) + "px"; - maxLineChanged = false; - } - var newScrollPos, updated; - if (selectionChanged) { - var coords = calculateCursorCoords(); - newScrollPos = calculateScrollPos(coords.x, coords.y, coords.x, coords.yBot); - } - if (changes.length || newScrollPos && newScrollPos.scrollTop != null) - updated = updateDisplay(changes, true, newScrollPos && newScrollPos.scrollTop); - if (!updated) { - if (selectionChanged) updateSelection(); - if (gutterDirty) updateGutter(); - } - if (newScrollPos) scrollCursorIntoView(); - if (selectionChanged) {scrollEditorIntoView(); restartBlink();} - - if (focused && !leaveInputAlone && - (updateInput === true || (updateInput !== false && selectionChanged))) - resetInput(userSelChange); - - if (selectionChanged && options.matchBrackets) - setTimeout(operation(function() { - if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;} - if (posEq(sel.from, sel.to)) matchBrackets(false); - }), 20); - var sc = selectionChanged, cbs = callbacks; // these can be reset by callbacks - if (textChanged && options.onChange && instance) - options.onChange(instance, textChanged); - if (sc && options.onCursorActivity) - options.onCursorActivity(instance); - for (var i = 0; i < cbs.length; ++i) cbs[i](instance); - if (updated && options.onUpdate) options.onUpdate(instance); - } - var nestedOperation = 0; - function operation(f) { - return function() { - if (!nestedOperation++) startOperation(); - try {var result = f.apply(this, arguments);} - finally {if (!--nestedOperation) endOperation();} - return result; - }; - } + return index; + }, - function compoundChange(f) { - history.startCompound(); - try { return f(); } finally { history.endCompound(); } - } + scrollTo: function(x, y) { + if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x; + if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y; + updateDisplay(this, []); + }, + getScrollInfo: function() { + var scroller = this.display.scroller, co = scrollerCutOff; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - co, width: scroller.scrollWidth - co, + clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co}; + }, - for (var ext in extensions) - if (extensions.propertyIsEnumerable(ext) && - !instance.propertyIsEnumerable(ext)) - instance[ext] = extensions[ext]; - return instance; - } // (end of function CodeMirror) + scrollIntoView: function(pos) { + pos = pos ? clipPos(this.view.doc, pos) : selHead(this.view); + var coords = cursorCoords(this, pos); + scrollIntoView(this.display, coords.left, coords.top, coords.left, coords.bottom); + }, - // The default configuration options. - CodeMirror.defaults = { - value: "", - mode: null, - theme: "default", - indentUnit: 2, - indentWithTabs: false, - smartIndent: true, - tabSize: 4, - keyMap: "default", - extraKeys: null, - electricChars: true, - autoClearEmptyLines: false, - onKeyEvent: null, - onDragEvent: null, - lineWrapping: false, - lineNumbers: false, - gutter: false, - fixedGutter: false, - firstLineNumber: 1, - readOnly: false, - dragDrop: true, - onChange: null, - onCursorActivity: null, - onGutterClick: null, - onHighlightComplete: null, - onUpdate: null, - onFocus: null, onBlur: null, onScroll: null, - matchBrackets: false, - workTime: 100, - workDelay: 200, - pollInterval: 100, - undoDepth: 40, - tabindex: null, - autofocus: null, - lineNumberFormatter: function(integer) { return integer; } + setSize: function(width, height) { + function interpret(val) { + return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; + } + if (width != null) this.display.wrapper.style.width = interpret(width); + if (height != null) this.display.wrapper.style.height = interpret(height); + this.refresh(); + }, + + on: function(type, f) {on(this, type, f);}, + off: function(type, f) {off(this, type, f);}, + + operation: function(f){return operation(this, f)();}, + compoundChange: function(f){return compoundChange(this, f);}, + + refresh: function(){ + updateDisplay(this, true, this.view.scrollTop); + if (this.display.scrollbarV.scrollHeight > this.view.scrollTop) + this.display.scrollbarV.scrollTop = this.view.scrollTop; + }, + + getInputField: function(){return this.display.input;}, + getWrapperElement: function(){return this.display.wrapper;}, + getScrollerElement: function(){return this.display.scroller;}, + getGutterElement: function(){return this.display.gutters;} }; - var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); - var mac = ios || /Mac/.test(navigator.platform); - var win = /Win/.test(navigator.platform); + // OPTION DEFAULTS + + var optionHandlers = CodeMirror.optionHandlers = {}; + + // The default configuration options. + var defaults = CodeMirror.defaults = {}; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) optionHandlers[name] = + notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle; + } + + var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}}; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function(cm, val) {cm.setValue(val);}, true); + option("mode", null, loadMode, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function(cm) {loadMode(cm); updateDisplay(cm, true);}, true); + option("electricChars", true); + option("autoClearEmptyLines", false); + + option("theme", "default", function(cm, val, old) { + themeChanged(cm); + if (old != Init) guttersChanged(cm); + }); + option("keyMap", "default", keyMapChanged); + option("extraKeys", null); + + option("onKeyEvent", null); + option("onDragEvent", null); + + option("lineWrapping", false, wrappingChanged); + option("gutters", [], function(cm) { + setGuttersForLineNumbers(cm.options); + guttersChanged(cm); + }, true); + option("lineNumbers", false, function(cm) { + setGuttersForLineNumbers(cm.options); + guttersChanged(cm); + }, true); + option("firstLineNumber", 1, guttersChanged, false); + option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, false); + + option("readOnly", false, function(cm, val) { + if (val == "nocursor") {onBlur(cm); cm.display.input.blur();} + else if (!val) resetInput(cm, true); + }); + option("dragDrop", true); + + option("cursorBlinkRate", 530); + option("cursorHeight", 1); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true); + option("pollInterval", 100); + option("undoDepth", 40); + option("viewportMargin", 10, function(cm){cm.refresh();}, true); + + option("tabindex", null, function(cm, val) { + cm.display.input.tabIndex = val || ""; + }); + option("autofocus", null); + + // MODE DEFINITION AND QUERYING // Known modes, by name and by MIME var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; + CodeMirror.defineMode = function(name, mode) { if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name; if (arguments.length > 2) { @@ -2083,9 +2749,11 @@ var CodeMirror = (function() { } modes[name] = mode; }; + CodeMirror.defineMIME = function(mime, spec) { mimeModes[mime] = spec; }; + CodeMirror.resolveMode = function(spec) { if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) spec = mimeModes[spec]; @@ -2094,35 +2762,82 @@ var CodeMirror = (function() { if (typeof spec == "string") return {name: spec}; else return spec || {name: "null"}; }; + CodeMirror.getMode = function(options, spec) { var spec = CodeMirror.resolveMode(spec); var mfactory = modes[spec.name]; if (!mfactory) return CodeMirror.getMode(options, "text/plain"); - return mfactory(options, spec); - }; - CodeMirror.listModes = function() { - var list = []; - for (var m in modes) - if (modes.propertyIsEnumerable(m)) list.push(m); - return list; + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) if (exts.hasOwnProperty(prop)) modeObj[prop] = exts[prop]; + } + modeObj.name = spec.name; + return modeObj; }; - CodeMirror.listMIMEs = function() { - var list = []; - for (var m in mimeModes) - if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]}); - return list; + + CodeMirror.defineMode("null", function() { + return {token: function(stream) {stream.skipToEnd();}}; + }); + CodeMirror.defineMIME("text/plain", "null"); + + var modeExtensions = CodeMirror.modeExtensions = {}; + CodeMirror.extendMode = function(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + for (var prop in properties) if (properties.hasOwnProperty(prop)) + exts[prop] = properties[prop]; }; - var extensions = CodeMirror.extensions = {}; + // EXTENSIONS + CodeMirror.defineExtension = function(name, func) { - extensions[name] = func; + CodeMirror.prototype[name] = func; }; + CodeMirror.defineOption = option; + + var initHooks = []; + CodeMirror.defineInitHook = function(f) {initHooks.push(f);}; + + // MODE STATE HANDLING + + // Utility functions for working with state. Exported because modes + // sometimes need to do this. + function copyState(mode, state) { + if (state === true) return state; + if (mode.copyState) return mode.copyState(state); + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) val = val.concat([]); + nstate[n] = val; + } + return nstate; + } + CodeMirror.copyState = copyState; + + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true; + } + CodeMirror.startState = startState; + + CodeMirror.innerMode = function(mode, state) { + while (mode.innerMode) { + var info = mode.innerMode(state); + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state}; + }; + + // STANDARD COMMANDS + var commands = CodeMirror.commands = { selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});}, killLine: function(cm) { var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to); - if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0}); + if (!sel && cm.getLine(from.line).length == from.ch) + cm.replaceRange("", from, {line: from.line + 1, ch: 0}); else cm.replaceRange("", from, sel ? to : {line: from.line}); }, deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});}, @@ -2130,13 +2845,22 @@ var CodeMirror = (function() { redo: function(cm) {cm.redo();}, goDocStart: function(cm) {cm.setCursor(0, 0, true);}, goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);}, - goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);}, + goLineStart: function(cm) { + cm.setCursor(lineStart(cm, cm.getCursor().line), null, true); + }, goLineStartSmart: function(cm) { - var cur = cm.getCursor(); - var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/)); - cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true); + var cur = cm.getCursor(), start = lineStart(cm, cur.line); + var line = cm.getLineHandle(start.line); + var order = getOrder(line); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(0, line.text.search(/\S/)); + var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch; + cm.setCursor(start.line, inWS ? 0 : firstNonWS, true); + } else cm.setCursor(start, null, true); + }, + goLineEnd: function(cm) { + cm.setCursor(lineEnd(cm, cm.getCursor().line), null, true); }, - goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);}, goLineUp: function(cm) {cm.moveV(-1, "line");}, goLineDown: function(cm) {cm.moveV(1, "line");}, goPageUp: function(cm) {cm.moveV(-1, "page");}, @@ -2147,10 +2871,10 @@ var CodeMirror = (function() { goColumnRight: function(cm) {cm.moveH(1, "column");}, goWordLeft: function(cm) {cm.moveH(-1, "word");}, goWordRight: function(cm) {cm.moveH(1, "word");}, - delCharLeft: function(cm) {cm.deleteH(-1, "char");}, - delCharRight: function(cm) {cm.deleteH(1, "char");}, - delWordLeft: function(cm) {cm.deleteH(-1, "word");}, - delWordRight: function(cm) {cm.deleteH(1, "word");}, + delCharBefore: function(cm) {cm.deleteH(-1, "char");}, + delCharAfter: function(cm) {cm.deleteH(1, "char");}, + delWordBefore: function(cm) {cm.deleteH(-1, "word");}, + delWordAfter: function(cm) {cm.deleteH(1, "word");}, indentAuto: function(cm) {cm.indentSelection("smart");}, indentMore: function(cm) {cm.indentSelection("add");}, indentLess: function(cm) {cm.indentSelection("subtract");}, @@ -2172,11 +2896,13 @@ var CodeMirror = (function() { toggleOverwrite: function(cm) {cm.toggleOverwrite();} }; + // STANDARD KEYMAPS + var keyMap = CodeMirror.keyMap = {}; keyMap.basic = { "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", - "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto", "Enter": "newlineAndIndent", "Insert": "toggleOverwrite" }; // Note that the save and find-related commands aren't defined by @@ -2185,7 +2911,7 @@ var CodeMirror = (function() { "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd", "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", - "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find", "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", fallthrough: "basic" @@ -2193,8 +2919,8 @@ var CodeMirror = (function() { keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft", - "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft", - "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find", + "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore", + "Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", "Cmd-[": "indentLess", "Cmd-]": "indentMore", fallthrough: ["basic", "emacsy"] @@ -2203,14 +2929,17 @@ var CodeMirror = (function() { keyMap.emacsy = { "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", - "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft", - "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars" + "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", + "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars" }; + // KEYMAP DISPATCH + function getKeyMap(val) { if (typeof val == "string") return keyMap[val]; else return val; } + function lookupKey(name, extraMap, map, handle, stop) { function lookup(map) { map = getKeyMap(map); @@ -2240,19 +2969,29 @@ var CodeMirror = (function() { var name = keyNames[e_prop(event, "keyCode")]; return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; } + CodeMirror.isModifierKey = isModifierKey; + + // FROMTEXTAREA CodeMirror.fromTextArea = function(textarea, options) { if (!options) options = {}; options.value = textarea.value; if (!options.tabindex && textarea.tabindex) options.tabindex = textarea.tabindex; - if (options.autofocus == null && textarea.getAttribute("autofocus") != null) - options.autofocus = true; + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = document.body; + // doc.activeElement occasionally throws on IE + try { hasFocus = document.activeElement; } catch(e) {} + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } - function save() {textarea.value = instance.getValue();} + function save() {textarea.value = cm.getValue();} if (textarea.form) { // Deplorable hack to make the submit method do the right thing. - var rmSubmit = connect(textarea.form, "submit", save, true); + on(textarea.form, "submit", save); if (typeof textarea.form.submit == "function") { var realSubmit = textarea.form.submit; textarea.form.submit = function wrappedSubmit() { @@ -2265,54 +3004,28 @@ var CodeMirror = (function() { } textarea.style.display = "none"; - var instance = CodeMirror(function(node) { + var cm = CodeMirror(function(node) { textarea.parentNode.insertBefore(node, textarea.nextSibling); }, options); - instance.save = save; - instance.getTextArea = function() { return textarea; }; - instance.toTextArea = function() { + cm.save = save; + cm.getTextArea = function() { return textarea; }; + cm.toTextArea = function() { save(); - textarea.parentNode.removeChild(instance.getWrapperElement()); + textarea.parentNode.removeChild(cm.getWrapperElement()); textarea.style.display = ""; if (textarea.form) { - rmSubmit(); + off(textarea.form, "submit", save); if (typeof textarea.form.submit == "function") textarea.form.submit = realSubmit; } }; - return instance; + return cm; }; - var gecko = /gecko\/\d{7}/i.test(navigator.userAgent); - var ie = /MSIE \d/.test(navigator.userAgent); - var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); - var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); - var quirksMode = ie && document.documentMode == 5; - var webkit = /WebKit\//.test(navigator.userAgent); - var chrome = /Chrome\//.test(navigator.userAgent); - var opera = /Opera\//.test(navigator.userAgent); - var safari = /Apple Computer/.test(navigator.vendor); - var khtml = /KHTML\//.test(navigator.userAgent); - var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent); + // STRING STREAM - // Utility functions for working with state. Exported because modes - // sometimes need to do this. - function copyState(mode, state) { - if (state === true) return state; - if (mode.copyState) return mode.copyState(state); - var nstate = {}; - for (var n in state) { - var val = state[n]; - if (val instanceof Array) val = val.concat([]); - nstate[n] = val; - } - return nstate; - } - CodeMirror.copyState = copyState; - function startState(mode, a1, a2) { - return mode.startState ? mode.startState(a1, a2) : true; - } - CodeMirror.startState = startState; + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. // The character stream used by a mode's parser. function StringStream(string, tabSize) { @@ -2320,10 +3033,11 @@ var CodeMirror = (function() { this.string = string; this.tabSize = tabSize || 8; } + StringStream.prototype = { eol: function() {return this.pos >= this.string.length;}, sol: function() {return this.pos == 0;}, - peek: function() {return this.string.charAt(this.pos);}, + peek: function() {return this.string.charAt(this.pos) || undefined;}, next: function() { if (this.pos < this.string.length) return this.string.charAt(this.pos++); @@ -2361,6 +3075,7 @@ var CodeMirror = (function() { } } else { var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) return null; if (match && consume !== false) this.pos += match[0].length; return match; } @@ -2369,392 +3084,546 @@ var CodeMirror = (function() { }; CodeMirror.StringStream = StringStream; - function MarkedText(from, to, className, marker) { - this.from = from; this.to = to; this.style = className; this.marker = marker; + // TEXTMARKERS + + function TextMarker(cm, type) { + this.lines = []; + this.type = type; + this.cm = cm; } - MarkedText.prototype = { - attach: function(line) { this.marker.set.push(line); }, - detach: function(line) { - var ix = indexOf(this.marker.set, line); - if (ix > -1) this.marker.set.splice(ix, 1); - }, - split: function(pos, lenBefore) { - if (this.to <= pos && this.to != null) return null; - var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore; - var to = this.to == null ? null : this.to - pos + lenBefore; - return new MarkedText(from, to, this.style, this.marker); - }, - dup: function() { return new MarkedText(null, null, this.style, this.marker); }, - clipTo: function(fromOpen, from, toOpen, to, diff) { - if (fromOpen && to > this.from && (to < this.to || this.to == null)) - this.from = null; - else if (this.from != null && this.from >= from) - this.from = Math.max(to, this.from) + diff; - if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null)) - this.to = null; - else if (this.to != null && this.to > from) - this.to = to < this.to ? this.to + diff : from; - }, - isDead: function() { return this.from != null && this.to != null && this.from >= this.to; }, - sameSet: function(x) { return this.marker == x.marker; } + + TextMarker.prototype.clear = function() { + if (this.explicitlyCleared) return; + startOperation(this.cm); + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.to != null) max = lineNo(line); + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from != null) + min = lineNo(line); + else if (this.collapsed && !lineIsHidden(line)) + updateLineHeight(line, textHeight(this.cm.display)); + } + if (min != null) regChange(this.cm, min, max + 1); + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.collapsed && this.cm.view.cantEdit) { + this.cm.view.cantEdit = false; + reCheckSelection(this.cm); + } + endOperation(this.cm); + signalLater(this.cm, this, "clear"); }; - function Bookmark(pos) { - this.from = pos; this.to = pos; this.line = null; - } - Bookmark.prototype = { - attach: function(line) { this.line = line; }, - detach: function(line) { if (this.line == line) this.line = null; }, - split: function(pos, lenBefore) { - if (pos < this.from) { - this.from = this.to = (this.from - pos) + lenBefore; - return this; - } - }, - isDead: function() { return this.from > this.to; }, - clipTo: function(fromOpen, from, toOpen, to, diff) { - if ((fromOpen || from < this.from) && (toOpen || to > this.to)) { - this.from = 0; this.to = -1; - } else if (this.from > from) { - this.from = this.to = Math.max(to, this.from) + diff; - } - }, - sameSet: function(x) { return false; }, - find: function() { - if (!this.line || !this.line.parent) return null; - return {line: lineNo(this.line), ch: this.from}; - }, - clear: function() { - if (this.line) { - var found = indexOf(this.line.marked, this); - if (found != -1) this.line.marked.splice(found, 1); - this.line = null; + TextMarker.prototype.find = function() { + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null || span.to != null) { + var found = lineNo(line); + if (span.from != null) from = {line: found, ch: span.from}; + if (span.to != null) to = {line: found, ch: span.to}; } } + if (this.type == "bookmark") return from; + return from && {from: from, to: to}; }; - // When measuring the position of the end of a line, different - // browsers require different approaches. If an empty span is added, - // many browsers report bogus offsets. Of those, some (Webkit, - // recent IE) will accept a space without moving the whole span to - // the next line when wrapping it, others work with a zero-width - // space. - var eolSpanContent = " "; - if (gecko || (ie && !ie_lt8)) eolSpanContent = "\u200b"; - else if (opera) eolSpanContent = ""; + function markText(cm, from, to, options, type) { + var doc = cm.view.doc; + var marker = new TextMarker(cm, type); + if (type == "range" && !posLess(from, to)) return marker; + if (options) for (var opt in options) if (options.hasOwnProperty(opt)) + marker[opt] = options[opt]; + if (marker.replacedWith) { + marker.collapsed = true; + marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget"); + } + if (marker.collapsed) sawCollapsedSpans = true; + + var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd; + doc.iter(curLine, to.line + 1, function(line) { + var span = {from: null, to: null, marker: marker}; + size += line.text.length; + if (curLine == from.line) {span.from = from.ch; size -= from.ch;} + if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;} + if (marker.collapsed) { + if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch); + if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch); + else updateLineHeight(line, 0); + } + addMarkedSpan(line, span); + if (marker.collapsed && curLine == from.line && lineIsHidden(line)) + updateLineHeight(line, 0); + ++curLine; + }); - // Line objects. These hold state related to a line, including - // highlighting info (the styles array). - function Line(text, styles) { - this.styles = styles || [text, null]; - this.text = text; - this.height = 1; - } - Line.inheritMarks = function(text, orig) { - var ln = new Line(text), mk = orig && orig.marked; - if (mk) { - for (var i = 0; i < mk.length; ++i) { - if (mk[i].to == null && mk[i].style) { - var newmk = ln.marked || (ln.marked = []), mark = mk[i]; - var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln); - } + if (marker.readOnly) { + sawReadOnlySpans = true; + if (cm.view.history.done.length || cm.view.history.undone.length) + cm.clearHistory(); + } + if (marker.collapsed) { + if (collapsedAtStart != collapsedAtEnd) + throw new Error("Inserting collapsed marker overlapping an existing one"); + marker.size = size; + marker.atomic = true; + } + if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed) + regChange(cm, from.line, to.line + 1); + if (marker.atomic) reCheckSelection(cm); + return marker; + } + + // TEXTMARKER SPANS + + function getMarkedSpanFor(spans, marker) { + if (spans) for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) return span; + } + } + function removeMarkedSpan(spans, span) { + for (var r, i = 0; i < spans.length; ++i) + if (spans[i] != span) (r || (r = [])).push(spans[i]); + return r; + } + function addMarkedSpan(line, span) { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + span.marker.lines.push(line); + } + + function markedSpansBefore(old, startCh) { + if (old) for (var i = 0, nw; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || marker.type == "bookmark" && span.from == startCh) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh); + (nw || (nw = [])).push({from: span.from, + to: endsAfter ? null : span.to, + marker: marker}); } } - return ln; - }; - Line.prototype = { - // Replace a piece of a line, keeping the styles around it intact. - replace: function(from, to_, text) { - var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_; - copyStyles(0, from, this.styles, st); - if (text) st.push(text, null); - copyStyles(to, this.text.length, this.styles, st); - this.styles = st; - this.text = this.text.slice(0, from) + text + this.text.slice(to); - this.stateAfter = null; - if (mk) { - var diff = text.length - (to - from); - for (var i = 0; i < mk.length; ++i) { - var mark = mk[i]; - mark.clipTo(from == null, from || 0, to_ == null, to, diff); - if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);} - } + return nw; + } + + function markedSpansAfter(old, startCh, endCh) { + if (old) for (var i = 0, nw; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || marker.type == "bookmark" && span.from == endCh && span.from != startCh) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh); + (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh, + to: span.to == null ? null : span.to - endCh, + marker: marker}); } - }, - // Split a part off a line, keeping styles and markers intact. - split: function(pos, textBefore) { - var st = [textBefore, null], mk = this.marked; - copyStyles(pos, this.text.length, this.styles, st); - var taken = new Line(textBefore + this.text.slice(pos), st); - if (mk) { - for (var i = 0; i < mk.length; ++i) { - var mark = mk[i]; - var newmark = mark.split(pos, textBefore.length); - if (newmark) { - if (!taken.marked) taken.marked = []; - taken.marked.push(newmark); newmark.attach(taken); - if (newmark == mark) mk.splice(i--, 1); - } + } + return nw; + } + + function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) { + if (!oldFirst && !oldLast) return newText; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh); + var last = markedSpansAfter(oldLast, startCh, endCh); + + // Next, merge those two ends + var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) span.to = startCh; + else if (sameLine) span.to = found.to == null ? null : found.to + offset; } } - return taken; - }, - append: function(line) { - var mylen = this.text.length, mk = line.marked, mymk = this.marked; - this.text += line.text; - copyStyles(0, line.text.length, line.styles, this.styles); - if (mymk) { - for (var i = 0; i < mymk.length; ++i) - if (mymk[i].to == null) mymk[i].to = mylen; - } - if (mk && mk.length) { - if (!mymk) this.marked = mymk = []; - outer: for (var i = 0; i < mk.length; ++i) { - var mark = mk[i]; - if (!mark.from) { - for (var j = 0; j < mymk.length; ++j) { - var mymark = mymk[j]; - if (mymark.to == mylen && mymark.sameSet(mark)) { - mymark.to = mark.to == null ? null : mark.to + mylen; - if (mymark.isDead()) { - mymark.detach(this); - mk.splice(i--, 1); - } - continue outer; - } - } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i = 0; i < last.length; ++i) { + var span = last[i]; + if (span.to != null) span.to += offset; + if (span.from == null) { + var found = getMarkedSpanFor(first, span.marker); + if (!found) { + span.from = offset; + if (sameLine) (first || (first = [])).push(span); } - mymk.push(mark); - mark.attach(this); - mark.from += mylen; - if (mark.to != null) mark.to += mylen; + } else { + span.from += offset; + if (sameLine) (first || (first = [])).push(span); } } - }, - fixMarkEnds: function(other) { - var mk = this.marked, omk = other.marked; - if (!mk) return; - outer: for (var i = 0; i < mk.length; ++i) { - var mark = mk[i], close = mark.to == null; - if (close && omk) { - for (var j = 0; j < omk.length; ++j) { - var om = omk[j]; - if (!om.sameSet(mark) || om.from != null) continue; - if (mark.from == this.text.length && om.to == 0) { - omk.splice(j, 1); - mk.splice(i--, 1); - continue outer; - } else { - close = false; break; - } - } - } - if (close) mark.to = this.text.length; + } + + var newMarkers = [newHL(newText[0], first)]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = newText.length - 2, gapMarkers; + if (gap > 0 && first) + for (var i = 0; i < first.length; ++i) + if (first[i].to == null) + (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker}); + for (var i = 0; i < gap; ++i) + newMarkers.push(newHL(newText[i+1], gapMarkers)); + newMarkers.push(newHL(lst(newText), last)); + } + return newMarkers; + } + + function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function(line) { + if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + (markers || (markers = [])).push(mark); } - }, - fixMarkStarts: function() { - var mk = this.marked; - if (!mk) return; - for (var i = 0; i < mk.length; ++i) - if (mk[i].from == null) mk[i].from = 0; - }, - addMark: function(mark) { - mark.attach(this); - if (this.marked == null) this.marked = []; - this.marked.push(mark); - this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);}); - }, - // Run the given mode's parser over a line, update the styles - // array, which contains alternating fragments of text and CSS - // classes. - highlight: function(mode, state, tabSize) { - var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0; - var changed = false, curWord = st[0], prevWord; - if (this.text == "" && mode.blankLine) mode.blankLine(state); - while (!stream.eol()) { - var style = mode.token(stream, state); - var substr = this.text.slice(stream.start, stream.pos); - stream.start = stream.pos; - if (pos && st[pos-1] == style) - st[pos-2] += substr; - else if (substr) { - if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true; - st[pos++] = substr; st[pos++] = style; - prevWord = curWord; curWord = st[pos]; - } - // Give up when line is ridiculously long - if (stream.pos > 5000) { - st[pos++] = this.text.slice(stream.pos); st[pos++] = null; - break; + }); + if (!markers) return null; + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var m = markers[i].find(); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (!posLess(m.from, p.to) || posLess(m.to, p.from)) continue; + var newParts = [j, 1]; + if (posLess(p.from, m.from)) newParts.push({from: p.from, to: m.from}); + if (posLess(m.to, p.to)) newParts.push({from: m.to, to: p.to}); + parts.splice.apply(parts, newParts); + j += newParts.length - 1; + } + } + return parts; + } + + function collapsedSpanAt(line, ch) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) for (var sp, i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) continue; + if ((sp.from == null || sp.from < ch) && + (sp.to == null || sp.to > ch) && + (!found || found.width < sp.marker.width)) + found = sp.marker; + } + return found; + } + function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); } + function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); } + + function lineIsHidden(line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) for (var sp, i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) continue; + if (sp.from == null) return true; + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(line, sp)) + return true; + } + } + window.lineIsHidden = lineIsHidden; + function lineIsHiddenInner(line, span) { + if (span.to == null || span.marker.inclusiveRight && span.to == line.text.length) + return true; + for (var sp, i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && sp.from == span.to && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(line, sp)) return true; + } + } + + // hl stands for history-line, a data structure that can be either a + // string (line without markers) or a {text, markedSpans} object. + function hlText(val) { return typeof val == "string" ? val : val.text; } + function hlSpans(val) { + if (typeof val == "string") return null; + var spans = val.markedSpans, out = null; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); } + else if (out) out.push(spans[i]); + } + return !out ? spans : out.length ? out : null; + } + function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; } + + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) return; + for (var i = 0; i < spans.length; ++i) { + var lines = spans[i].marker.lines; + var ix = indexOf(lines, line); + lines.splice(ix, 1); + } + line.markedSpans = null; + } + + function attachMarkedSpans(line, spans) { + if (!spans) return; + for (var i = 0; i < spans.length; ++i) + var marker = spans[i].marker.lines.push(line); + line.markedSpans = spans; + } + + // LINE DATA STRUCTURE + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + function makeLine(text, markedSpans, height) { + var line = {text: text, height: height}; + attachMarkedSpans(line, markedSpans); + if (markedSpans && collapsedSpanAtStart(line)) line.height = 0; + return line; + } + + function updateLine(cm, line, text, markedSpans) { + line.text = text; + line.stateAfter = line.styles = null; + if (line.order != null) line.order = null; + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var hidden = collapsedSpanAtStart(line); + if (hidden) line.height = 0; + else if (!line.height) line.height = textHeight(cm.display); + signalLater(cm, line, "change"); + } + + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + // Run the given mode's parser over a line, update the styles + // array, which contains alternating fragments of text and CSS + // classes. + function highlightLine(cm, line, state) { + var mode = cm.view.mode, flattenSpans = cm.options.flattenSpans; + var changed = !line.styles, pos = 0, curText = "", curStyle = null; + var stream = new StringStream(line.text, cm.options.tabSize), st = line.styles || (line.styles = []); + if (line.text == "" && mode.blankLine) mode.blankLine(state); + while (!stream.eol()) { + var style = mode.token(stream, state), substr = stream.current(); + stream.start = stream.pos; + if (!flattenSpans || curStyle != style) { + if (curText) { + changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1]; + st[pos++] = curText; st[pos++] = curStyle; } + curText = substr; curStyle = style; + } else curText = curText + substr; + // Give up when line is ridiculously long + if (stream.pos > 5000) break; + } + if (curText) { + changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1]; + st[pos++] = curText; st[pos++] = curStyle; + } + if (stream.pos > 5000) { st[pos++] = line.text.slice(stream.pos); st[pos++] = null; } + if (pos != st.length) { st.length = pos; changed = true; } + return changed; + } + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. + function processLine(cm, line, state) { + var mode = cm.view.mode; + var stream = new StringStream(line.text, cm.options.tabSize); + if (line.text == "" && mode.blankLine) mode.blankLine(state); + while (!stream.eol() && stream.pos <= 5000) { + mode.token(stream, state); + stream.start = stream.pos; + } + } + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + function getTokenAt(cm, line, state, ch) { + var mode = cm.view.mode; + var txt = line.text, stream = new StringStream(txt, cm.options.tabSize); + while (stream.pos < ch && !stream.eol()) { + stream.start = stream.pos; + var style = mode.token(stream, state); + } + return {start: stream.start, + end: stream.pos, + string: stream.current(), + className: style || null, + state: state}; + } + + var styleToClassCache = {}; + function styleToClass(style) { + if (!style) return null; + return styleToClassCache[style] || + (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-")); + } + + function lineContent(cm, realLine, measure) { + var merged, line = realLine, lineBefore, sawBefore; + while (merged = collapsedSpanAtStart(line)) { + line = getLine(cm.view.doc, merged.find().from.line); + if (!lineBefore) lineBefore = line; + } + + var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure, + measure: null, addedOne: false, cm: cm}; + if (line.textClass) builder.pre.className = line.textClass; + + do { + if (!line.styles) + highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line))); + builder.measure = line == realLine && measure; + builder.pos = 0; + builder.addToken = builder.measure ? buildTokenMeasure : buildToken; + if (measure && sawBefore && line != realLine && !builder.addedOne) { + measure[0] = builder.pre.appendChild(elt("span", "\u200b")); + builder.addedOne = true; } - if (st.length != pos) {st.length = pos; changed = true;} - if (pos && st[pos-2] != prevWord) changed = true; - // Short lines with simple highlights return null, and are - // counted as changed by the driver because they are likely to - // highlight the same way in various contexts. - return changed || (st.length < 5 && this.text.length < 10 ? null : false); - }, - // Fetch the parser token for a given character. Useful for hacks - // that want to inspect the mode state (say, for completion). - getTokenAt: function(mode, state, tabSize, ch) { - var txt = this.text, stream = new StringStream(txt, tabSize); - while (stream.pos < ch && !stream.eol()) { - stream.start = stream.pos; - var style = mode.token(stream, state); - } - return {start: stream.start, - end: stream.pos, - string: stream.current(), - className: style || null, - state: state}; - }, - indentation: function(tabSize) {return countColumn(this.text, null, tabSize);}, - // Produces an HTML fragment for the line, taking selection, - // marking, and highlighting into account. - getElement: function(makeTab, wrapAt, wrapWBR) { - var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g; - var pre = elt("pre"); - function span_(html, text, style) { - if (!text) return; - // Work around a bug where, in some compat modes, IE ignores leading spaces - if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1); - first = false; - if (!specials.test(text)) { - col += text.length; - var content = document.createTextNode(text); + var next = insertLineContent(line, builder); + sawBefore = line == lineBefore; + if (next) line = getLine(cm.view.doc, next.to.line); + } while (next); + + if (measure && !builder.addedOne) + measure[0] = builder.pre.appendChild(elt("span", "\u200b")); + if (!builder.pre.firstChild && !lineIsHidden(realLine)) + builder.pre.appendChild(document.createTextNode("\u00a0")); + + return builder.pre; + } + + var tokenSpecialChars = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g; + function buildToken(builder, text, style, startStyle, endStyle) { + if (!text) return; + if (!tokenSpecialChars.test(text)) { + builder.col += text.length; + var content = document.createTextNode(text); + } else { + var content = document.createDocumentFragment(), pos = 0; + while (true) { + tokenSpecialChars.lastIndex = pos; + var m = tokenSpecialChars.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + content.appendChild(document.createTextNode(text.slice(pos, pos + skipped))); + builder.col += skipped; + } + if (!m) break; + pos += skipped + 1; + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + builder.col += tabWidth; } else { - var content = document.createDocumentFragment(), pos = 0; - while (true) { - specials.lastIndex = pos; - var m = specials.exec(text); - var skipped = m ? m.index - pos : text.length - pos; - if (skipped) { - content.appendChild(document.createTextNode(text.slice(pos, pos + skipped))); - col += skipped; - } - if (!m) break; - pos += skipped + 1; - if (m[0] == "\t") { - var tab = makeTab(col); - content.appendChild(tab.element.cloneNode(true)); - col += tab.width; - } else { - var token = elt("span", "\u2022", "cm-invalidchar"); - token.title = "\\u" + m[0].charCodeAt(0).toString(16); - content.appendChild(token); - col += 1; - } - } + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + m[0].charCodeAt(0).toString(16); + content.appendChild(token); + builder.col += 1; } - if (style) html.appendChild(elt("span", [content], style)); - else html.appendChild(content); - } - var span = span_; - if (wrapAt != null) { - var outPos = 0, anchor = pre.anchor = elt("span"); - span = function(html, text, style) { - var l = text.length; - if (wrapAt >= outPos && wrapAt < outPos + l) { - if (wrapAt > outPos) { - span_(html, text.slice(0, wrapAt - outPos), style); - // See comment at the definition of spanAffectsWrapping - if (wrapWBR) html.appendChild(elt("wbr")); - } - html.appendChild(anchor); - var cut = wrapAt - outPos; - span_(anchor, opera ? text.slice(cut, cut + 1) : text.slice(cut), style); - if (opera) span_(html, text.slice(cut + 1), style); - wrapAt--; - outPos += l; - } else { - outPos += l; - span_(html, text, style); - if (outPos == wrapAt && outPos == len) { - setTextContent(anchor, eolSpanContent); - html.appendChild(anchor); - } - // Stop outputting HTML when gone sufficiently far beyond measure - else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){}; - } - }; } + } + if (style || startStyle || endStyle || builder.measure) { + var fullStyle = style || ""; + if (startStyle) fullStyle += startStyle; + if (endStyle) fullStyle += endStyle; + return builder.pre.appendChild(elt("span", [content], fullStyle)); + } + builder.pre.appendChild(content); + } - var st = this.styles, allText = this.text, marked = this.marked; - var len = allText.length; - function styleToClass(style) { - if (!style) return null; - return "cm-" + style.replace(/ +/g, " cm-"); - } - if (!allText && wrapAt == null) { - span(pre, " "); - } else if (!marked || !marked.length) { - for (var i = 0, ch = 0; ch < len; i+=2) { - var str = st[i], style = st[i+1], l = str.length; - if (ch + l > len) str = str.slice(0, len - ch); - ch += l; - span(pre, str, styleToClass(style)); - } - } else { - var pos = 0, i = 0, text = "", style, sg = 0; - var nextChange = marked[0].from || 0, marks = [], markpos = 0; - var advanceMarks = function() { - var m; - while (markpos < marked.length && - ((m = marked[markpos]).from == pos || m.from == null)) { - if (m.style != null) marks.push(m); - ++markpos; - } - nextChange = markpos < marked.length ? marked[markpos].from : Infinity; - for (var i = 0; i < marks.length; ++i) { - var to = marks[i].to; - if (to == null) to = Infinity; - if (to == pos) marks.splice(i--, 1); - else nextChange = Math.min(to, nextChange); - } - }; - var m = 0; - while (pos < len) { - if (nextChange == pos) advanceMarks(); - var upto = Math.min(len, nextChange); - while (true) { - if (text) { - var end = pos + text.length; - var appliedStyle = style; - for (var j = 0; j < marks.length; ++j) - appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style; - span(pre, end > upto ? text.slice(0, upto - pos) : text, appliedStyle); - if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} - pos = end; - } - text = st[i++]; style = styleToClass(st[i++]); + function buildTokenMeasure(builder, text, style, startStyle, endStyle) { + for (var i = 0; i < text.length; ++i) { + if (i && i < text.length - 1 && + builder.cm.options.lineWrapping && + spanAffectsWrapping.test(text.slice(i - 1, i + 1))) + builder.pre.appendChild(elt("wbr")); + builder.measure[builder.pos++] = + buildToken(builder, text.charAt(i), style, + i == 0 && startStyle, i == text.length - 1 && endStyle); + } + if (text.length) builder.addedOne = true; + } + + function buildCollapsedSpan(builder, size, widget) { + if (widget) { + if (!builder.display) widget = widget.cloneNode(true); + builder.pre.appendChild(widget); + if (builder.measure && size) { + builder.measure[builder.pos] = widget; + builder.addedOne = true; + } + } + builder.pos += size; + } + + // Outputs a number of spans to make up a line, taking highlighting + // and marked text into account. + function insertLineContent(line, builder) { + var st = line.styles, spans = line.markedSpans; + if (!spans) { + for (var i = 0; i < st.length; i+=2) + builder.addToken(builder, st[i], styleToClass(st[i+1])); + return; + } + + var allText = line.text, len = allText.length; + var pos = 0, i = 0, text = "", style; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = ""; + collapsed = null; nextChange = Infinity; + var foundBookmark = null; + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (sp.from <= pos && (sp.to == null || sp.to > pos)) { + if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; } + if (m.className) spanStyle += " " + m.className; + if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle; + if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle; + if (m.collapsed && (!collapsed || collapsed.marker.width < m.width)) + collapsed = sp; + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; } + if (m.type == "bookmark" && sp.from == pos && m.replacedWith) + foundBookmark = m.replacedWith; } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos, + collapsed.from != null && collapsed.marker.replacedWith); + if (collapsed.to == null) return collapsed.marker.find(); + } + if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark); } - return pre; - }, - cleanUp: function() { - this.parent = null; - if (this.marked) - for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this); - } - }; - // Utility used by replace and split above - function copyStyles(from, to, source, dest) { - for (var i = 0, pos = 0, state = 0; pos < to; i+=2) { - var part = source[i], end = pos + part.length; - if (state == 0) { - if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]); - if (end >= from) state = 1; - } else if (state == 1) { - if (end > to) dest.push(part.slice(0, to - pos), source[i+1]); - else dest.push(part, source[i+1]); + if (pos >= len) break; + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style + spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : ""); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} + pos = end; + spanStartStyle = ""; + } + text = st[i++]; style = styleToClass(st[i++]); } - pos = end; } } - // Data structure that holds the sequence of lines. + // DOCUMENT DATA STRUCTURE + function LeafChunk(lines) { this.lines = lines; this.parent = null; @@ -2764,15 +3633,15 @@ var CodeMirror = (function() { } this.height = height; } + LeafChunk.prototype = { chunkSize: function() { return this.lines.length; }, - remove: function(at, n, callbacks) { + remove: function(at, n, cm) { for (var i = at, e = at + n; i < e; ++i) { var line = this.lines[i]; this.height -= line.height; - line.cleanUp(); - if (line.handlers) - for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]); + cleanUpLine(line); + signalLater(cm, line, "delete"); } this.lines.splice(at, n); }, @@ -2789,6 +3658,7 @@ var CodeMirror = (function() { if (op(this.lines[at])) return true; } }; + function BranchChunk(children) { this.children = children; var size = 0, height = 0; @@ -2801,6 +3671,7 @@ var CodeMirror = (function() { this.height = height; this.parent = null; } + BranchChunk.prototype = { chunkSize: function() { return this.size; }, remove: function(at, n, callbacks) { @@ -2888,7 +3759,14 @@ var CodeMirror = (function() { } }; - function getLineAt(chunk, n) { + // LINE UTILITIES + + function lineDoc(line) { + for (var d = line.parent; d && d.parent; d = d.parent) {} + return d; + } + + function getLine(chunk, n) { while (!chunk.lines) { for (var i = 0;; ++i) { var child = chunk.children[i], sz = child.chunkSize(); @@ -2898,6 +3776,12 @@ var CodeMirror = (function() { } return chunk.lines[n]; } + + function updateLineHeight(line, height) { + var diff = height - line.height; + for (var n = line; n; n = n.parent) n.height += diff; + } + function lineNo(line) { if (line.parent == null) return null; var cur = line.parent, no = indexOf(cur.lines, line); @@ -2909,6 +3793,7 @@ var CodeMirror = (function() { } return no; } + function lineAtHeight(chunk, h) { var n = 0; outer: do { @@ -2927,58 +3812,84 @@ var CodeMirror = (function() { } return n + i; } - function heightAtLine(chunk, n) { - var h = 0; - outer: do { - for (var i = 0, e = chunk.children.length; i < e; ++i) { - var child = chunk.children[i], sz = child.chunkSize(); - if (n < sz) { chunk = child; continue outer; } - n -= sz; - h += child.height; + + function heightAtLine(cm, lineObj) { + var merged; + while (merged = collapsedSpanAtStart(lineObj)) + lineObj = getLine(cm.view.doc, merged.find().from.line); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) break; + else h += line.height; + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i = 0; i < p.children.length; ++i) { + var cur = p.children[i]; + if (cur == chunk) break; + else h += cur.height; } - return h; - } while (!chunk.lines); - for (var i = 0; i < n; ++i) h += chunk.lines[i].height; + } return h; } + function getOrder(line) { + var order = line.order; + if (order == null) order = line.order = bidiOrdering(line.text); + return order; + } + + // HISTORY + // The history object 'chunks' changes that are made close together // and at almost the same time into bigger undoable units. - function History() { - this.time = 0; - this.done = []; this.undone = []; - this.compound = 0; - this.closed = false; - } - History.prototype = { - addChange: function(start, added, old) { - this.undone.length = 0; - var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1]; - var dtime = time - this.time; - - if (this.compound && cur && !this.closed) { - cur.push({start: start, added: added, old: old}); - } else if (dtime > 400 || !last || this.closed || - last.start > start + old.length || last.start + last.added < start) { - this.done.push([{start: start, added: added, old: old}]); - this.closed = false; - } else { - var startBefore = Math.max(0, last.start - start), - endAfter = Math.max(0, (start + old.length) - (last.start + last.added)); - for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]); - for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]); - if (startBefore) last.start = start; - last.added += added - (old.length - startBefore - endAfter); - } - this.time = time; - }, - startCompound: function() { - if (!this.compound++) this.closed = true; - }, - endCompound: function() { - if (!--this.compound) this.closed = true; + function makeHistory() { + return {time: 0, done: [], undone: [], + compound: 0, closed: false, dirtyCounter: 0}; + } + + function addChange(history, start, added, old) { + history.undone.length = 0; + var time = +new Date, cur = lst(history.done), last = cur && lst(cur); + var dtime = time - history.time; + + function updateDirty() { + if (history.dirtyCounter < 0) { + // The user has made a change after undoing past the last clean state. + // We can never get back to a clean state now until markClean() is called. + history.dirtyCounter = NaN; + } + history.dirtyCounter++; } - }; + + if (cur && !history.closed && history.compound) { + updateDirty(); + cur.push({start: start, added: added, old: old}); + } else if (dtime > 400 || !last || history.closed || + last.start > start + old.length || last.start + last.added < start) { + updateDirty(); + history.done.push([{start: start, added: added, old: old}]); + history.closed = false; + } else { + var startBefore = Math.max(0, last.start - start), + endAfter = Math.max(0, (start + old.length) - (last.start + last.added)); + for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]); + for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]); + if (startBefore) last.start = start; + last.added += added - (old.length - startBefore - endAfter); + } + history.time = time; + } + + function compoundChange(cm, f) { + var hist = cm.view.history; + if (!hist.compound++) hist.closed = true; + try { return f(); } + finally { if (!--hist.compound) hist.closed = true; } + } + + // EVENT OPERATORS function stopMethod() {e_stop(this);} // Ensure an event has a stop method. @@ -3019,53 +3930,68 @@ var CodeMirror = (function() { return overridden ? e.override[prop] : e[prop]; } - // Event handler registration. If disconnect is true, it'll return a - // function that unregisters the handler. - function connect(node, type, handler, disconnect) { - if (typeof node.addEventListener == "function") { - node.addEventListener(type, handler, false); - if (disconnect) return function() {node.removeEventListener(type, handler, false);}; - } else { - var wrapHandler = function(event) {handler(event || window.event);}; - node.attachEvent("on" + type, wrapHandler); - if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);}; + // EVENT HANDLING + + function on(emitter, type, f) { + if (emitter.addEventListener) + emitter.addEventListener(type, f, false); + else if (emitter.attachEvent) + emitter.attachEvent("on" + type, f); + else { + var map = emitter._handlers || (emitter._handlers = {}); + var arr = map[type] || (map[type] = []); + arr.push(f); } } - CodeMirror.connect = connect; - function Delayed() {this.id = null;} - Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}}; + function off(emitter, type, f) { + if (emitter.removeEventListener) + emitter.removeEventListener(type, f, false); + else if (emitter.detachEvent) + emitter.detachEvent("on" + type, f); + else { + var arr = emitter._handlers && emitter._handlers[type]; + if (!arr) return; + for (var i = 0; i < arr.length; ++i) + if (arr[i] == f) { arr.splice(i, 1); break; } + } + } - var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}}; + function signal(emitter, type /*, values...*/) { + var arr = emitter._handlers && emitter._handlers[type]; + if (!arr) return; + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args); + } - // Detect drag-and-drop - var dragAndDrop = function() { - // There is *some* kind of drag-and-drop support in IE6-8, but I - // couldn't get it to work yet. - if (ie_lt9) return false; - var div = elt('div'); - return "draggable" in div || "dragDrop" in div; - }(); + function signalLater(cm, emitter, type /*, values...*/) { + var arr = emitter._handlers && emitter._handlers[type]; + if (!arr) return; + var args = Array.prototype.slice.call(arguments, 3), flist = cm.curOp && cm.curOp.delayedCallbacks; + function bnd(f) {return function(){f.apply(null, args);};}; + for (var i = 0; i < arr.length; ++i) + if (flist) flist.push(bnd(arr[i])); + else arr[i].apply(null, args); + } - // Feature-detect whether newlines in textareas are converted to \r\n - var lineSep = function () { - var te = elt("textarea"); - te.value = "foo\nbar"; - if (te.value.indexOf("\r") > -1) return "\r\n"; - return "\n"; - }(); + function hasHandler(emitter, type) { + var arr = emitter._handlers && emitter._handlers[type]; + return arr && arr.length > 0; + } - // For a reason I have yet to figure out, some browsers disallow - // word wrapping between certain characters *only* if a new inline - // element is started between them. This makes it hard to reliably - // measure the position of things, since that requires inserting an - // extra span. This terribly fragile set of regexps matches the - // character combinations that suffer from this phenomenon on the - // various browsers. - var spanAffectsWrapping = /^$/; // Won't match any two-character string - if (gecko) spanAffectsWrapping = /$'/; - else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/; - else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/; + CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal; + + // MISC UTILITIES + + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerCutOff = 30; + + // Returned or thrown by various protocols to signal 'I'm not + // handling this'. + var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}}; + + function Delayed() {this.id = null;} + Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}}; // Counts the column offset in a string, taking tabs into account. // Used mostly to find indentation. @@ -3080,34 +4006,17 @@ var CodeMirror = (function() { } return n; } + CodeMirror.countColumn = countColumn; - function computedStyle(elt) { - if (elt.currentStyle) return elt.currentStyle; - return window.getComputedStyle(elt, null); + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + spaceStrs.push(lst(spaceStrs) + " "); + return spaceStrs[n]; } - function eltOffset(node, screen) { - // Take the parts of bounding client rect that we are interested in so we are able to edit if need be, - // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page) - try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; } - catch(e) { box = {top: 0, left: 0}; } - if (!screen) { - // Get the toplevel scroll, working around browser differences. - if (window.pageYOffset == null) { - var t = document.documentElement || document.body.parentNode; - if (t.scrollTop == null) t = document.body; - box.top += t.scrollTop; box.left += t.scrollLeft; - } else { - box.top += window.pageYOffset; box.left += window.pageXOffset; - } - } - return box; - } + function lst(arr) { return arr[arr.length-1]; } - // Get a node's text content. - function eltText(node) { - return node.textContent || node.innerText || node.nodeValue || ""; - } function selectInput(node) { if (ios) { // Mobile Safari apparently has a bug where select() is broken. node.selectionStart = 0; @@ -3115,10 +4024,48 @@ var CodeMirror = (function() { } else node.select(); } - // Operations on {line, ch} objects. - function posEq(a, b) {return a.line == b.line && a.ch == b.ch;} - function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} - function copyPos(x) {return {line: x.line, ch: x.ch};} + // Used to position the cursor after an undo/redo by finding the + // last edited character. + function editEnd(from, to) { + if (!to) return 0; + if (!from) return to.length; + for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j) + if (from.charAt(i) != to.charAt(j)) break; + return j + 1; + } + + function indexOf(collection, elt) { + if (collection.indexOf) return collection.indexOf(elt); + for (var i = 0, e = collection.length; i < e; ++i) + if (collection[i] == elt) return i; + return -1; + } + + function emptyArray(size) { + for (var a = [], i = 0; i < size; ++i) a.push(undefined); + return a; + } + + function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args);}; + } + + var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/; + function isWordChar(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)); + } + + function isEmpty(obj) { + var c = 0; + for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c; + return !c; + } + + var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/; + + // DOM UTILITIES function elt(tag, content, className, style) { var e = document.createElement(tag); @@ -3128,39 +4075,62 @@ var CodeMirror = (function() { else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]); return e; } + function removeChildren(e) { e.innerHTML = ""; return e; } + function removeChildrenAndAdd(parent, e) { - removeChildren(parent).appendChild(e); + return removeChildren(parent).appendChild(e); } + function setTextContent(e, str) { if (ie_lt9) { e.innerHTML = ""; e.appendChild(document.createTextNode(str)); } else e.textContent = str; } - CodeMirror.setTextContent = setTextContent; - // Used to position the cursor after an undo/redo by finding the - // last edited character. - function editEnd(from, to) { - if (!to) return 0; - if (!from) return to.length; - for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j) - if (from.charAt(i) != to.charAt(j)) break; - return j + 1; - } + // FEATURE DETECTION - function indexOf(collection, elt) { - if (collection.indexOf) return collection.indexOf(elt); - for (var i = 0, e = collection.length; i < e; ++i) - if (collection[i] == elt) return i; - return -1; - } - function isWordChar(ch) { - return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase(); + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie_lt9) return false; + var div = elt('div'); + return "draggable" in div || "dragDrop" in div; + }(); + + // Feature-detect whether newlines in textareas are converted to \r\n + var lineSep = function () { + var te = elt("textarea"); + te.value = "foo\nbar"; + if (te.value.indexOf("\r") > -1) return "\r\n"; + return "\n"; + }(); + + // For a reason I have yet to figure out, some browsers disallow + // word wrapping between certain characters *only* if a new inline + // element is started between them. This makes it hard to reliably + // measure the position of things, since that requires inserting an + // extra span. This terribly fragile set of regexps matches the + // character combinations that suffer from this phenomenon on the + // various browsers. + var spanAffectsWrapping = /^$/; // Won't match any two-character string + if (gecko) spanAffectsWrapping = /$'/; + else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/; + else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/; + + var knownScrollbarWidth; + function scrollbarWidth(measure) { + if (knownScrollbarWidth != null) return knownScrollbarWidth; + var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll"); + removeChildrenAndAdd(measure, test); + if (test.offsetWidth) + knownScrollbarWidth = test.offsetHeight - test.clientHeight; + return knownScrollbarWidth || 0; } // See if "".split is the broken IE version, if so, provide an @@ -3194,10 +4164,14 @@ var CodeMirror = (function() { return range.compareEndPoints("StartToEnd", range) != 0; }; - CodeMirror.defineMode("null", function() { - return {token: function(stream) {stream.skipToEnd();}}; - }); - CodeMirror.defineMIME("text/plain", "null"); + var hasCopyEvent = (function() { + var e = elt("div"); + if ("oncopy" in e) return true; + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == 'function'; + })(); + + // KEY NAMING var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", @@ -3216,5 +4190,261 @@ var CodeMirror = (function() { for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; })(); + // BIDI HELPERS + + function iterateBidiSections(order, from, to, f) { + if (!order) return f(from, to, "ltr"); + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from) + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr"); + } + } + + function bidiLeft(part) { return part.level % 2 ? part.to : part.from; } + function bidiRight(part) { return part.level % 2 ? part.from : part.to; } + + function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; } + function lineRight(line) { + var order = getOrder(line); + if (!order) return line.text.length; + return bidiRight(lst(order)); + } + + function lineStart(cm, lineNo) { + var merged, line; + while (merged = collapsedSpanAtStart(line = getLine(cm.view.doc, lineNo))) + lineNo = merged.find().from.line; + var order = getOrder(line); + var ch = !order ? 0 : order[0].level % 2 ? lineRight(line) : lineLeft(line); + return {line: lineNo, ch: ch}; + } + function lineEnd(cm, lineNo) { + var merged, line; + while (merged = collapsedSpanAtEnd(line = getLine(cm.view.doc, lineNo))) + lineNo = merged.find().to.line; + var order = getOrder(line); + var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line); + return {line: lineNo, ch: ch}; + } + + // This is somewhat involved. It is needed in order to move + // 'visually' through bi-directional text -- i.e., pressing left + // should make the cursor go left, even when in RTL text. The + // tricky part is the 'jumps', where RTL and LTR text touch each + // other. This often requires the cursor offset to move more than + // one unit, in order to visually move one unit. + function moveVisually(line, start, dir, byUnit) { + var bidi = getOrder(line); + if (!bidi) return moveLogically(line, start, dir, byUnit); + var moveOneUnit = byUnit ? function(pos, dir) { + do pos += dir; + while (pos > 0 && isExtendingChar.test(line.text.charAt(pos))); + return pos; + } : function(pos, dir) { return pos + dir; }; + var linedir = bidi[0].level; + for (var i = 0; i < bidi.length; ++i) { + var part = bidi[i], sticky = part.level % 2 == linedir; + if ((part.from < start && part.to > start) || + (sticky && (part.from == start || part.to == start))) break; + } + var target = moveOneUnit(start, part.level % 2 ? -dir : dir); + + while (target != null) { + if (part.level % 2 == linedir) { + if (target < part.from || target > part.to) { + part = bidi[i += dir]; + target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1)); + } else break; + } else { + if (target == bidiLeft(part)) { + part = bidi[--i]; + target = part && bidiRight(part); + } else if (target == bidiRight(part)) { + part = bidi[++i]; + target = part && bidiLeft(part); + } else break; + } + } + + return target < 0 || target > line.text.length ? null : target; + } + + function moveLogically(line, start, dir, byUnit) { + var target = start + dir; + if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir; + return target < 0 || target > line.text.length ? null : target; + } + + // Bidirectional ordering algorithm + // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm + // that this (partially) implements. + + // One-char codes used for character types: + // L (L): Left-to-Right + // R (R): Right-to-Left + // r (AL): Right-to-Left Arabic + // 1 (EN): European Number + // + (ES): European Number Separator + // % (ET): European Number Terminator + // n (AN): Arabic Number + // , (CS): Common Number Separator + // m (NSM): Non-Spacing Mark + // b (BN): Boundary Neutral + // s (B): Paragraph Separator + // t (S): Segment Separator + // w (WS): Whitespace + // N (ON): Other Neutrals + + // Returns null if characters are ordered as they appear + // (left-to-right), or an array of sections ({from, to, level} + // objects) in the order in which they occur visually. + var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL"; + // Character types for codepoints 0x600 to 0x6ff + var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr"; + function charType(code) { + var type = "L"; + if (code <= 0xff) return lowTypes.charAt(code); + else if (0x590 <= code && code <= 0x5f4) return "R"; + else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600); + else if (0x700 <= code && code <= 0x8ac) return "r"; + else return "L"; + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + + return function charOrdering(str) { + if (!bidiRE.test(str)) return false; + var len = str.length, types = [], startType = null; + for (var i = 0, type; i < len; ++i) { + types.push(type = charType(str.charCodeAt(i))); + if (startType == null) { + if (type == "L") startType = "L"; + else if (type == "R" || type == "r") startType = "R"; + } + } + if (startType == null) startType = "L"; + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i = 0, prev = startType; i < len; ++i) { + var type = types[i]; + if (type == "m") types[i] = prev; + else prev = type; + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i = 0, cur = startType; i < len; ++i) { + var type = types[i]; + if (type == "1" && cur == "r") types[i] = "n"; + else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i = 1, prev = types[0]; i < len - 1; ++i) { + var type = types[i]; + if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1"; + else if (type == "," && prev == types[i+1] && + (prev == "1" || prev == "n")) types[i] = prev; + prev = type; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i = 0; i < len; ++i) { + var type = types[i]; + if (type == ",") types[i] = "N"; + else if (type == "%") { + for (var end = i + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N"; + for (var j = i; j < end; ++j) types[j] = replace; + i = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i = 0, cur = startType; i < len; ++i) { + var type = types[i]; + if (cur == "L" && type == "1") types[i] = "L"; + else if (isStrong.test(type)) cur = type; + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i = 0; i < len; ++i) { + if (isNeutral.test(types[i])) { + for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {} + var before = (i ? types[i-1] : startType) == "L"; + var after = (end < len - 1 ? types[end] : startType) == "L"; + var replace = before || after ? "L" : "R"; + for (var j = i; j < end; ++j) types[j] = replace; + i = end - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i = 0; i < len;) { + if (countsAsLeft.test(types[i])) { + var start = i; + for (++i; i < len && countsAsLeft.test(types[i]); ++i) {} + order.push({from: start, to: i, level: 0}); + } else { + var pos = i, at = order.length; + for (++i; i < len && types[i] != "L"; ++i) {} + for (var j = pos; j < i;) { + if (countsAsNum.test(types[j])) { + if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1}); + var nstart = j; + for (++j; j < i && countsAsNum.test(types[j]); ++j) {} + order.splice(at, 0, {from: nstart, to: j, level: 2}); + pos = j; + } else ++j; + } + if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1}); + } + } + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift({from: 0, to: m[0].length, level: 0}); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push({from: len - m[0].length, to: len, level: 0}); + } + if (order[0].level != lst(order).level) + order.push({from: len, to: len, level: order[0].level}); + + return order; + }; + })(); + + // THE END + + CodeMirror.version = "3.0 B2"; + return CodeMirror; })(); diff --git a/Source/WebCore/inspector/front-end/elementsPanel.css b/Source/WebCore/inspector/front-end/elementsPanel.css index dc2344762..9dd1d7822 100644 --- a/Source/WebCore/inspector/front-end/elementsPanel.css +++ b/Source/WebCore/inspector/front-end/elementsPanel.css @@ -178,6 +178,10 @@ margin-left: 0px; } +.styles-section.computed-style .properties li.not-parsed-ok { + margin-left: -6px; +} + .styles-section .properties li.not-parsed-ok img.exclamation-mark { content: url(Images/warningIcon.png); opacity: 0.75; @@ -194,6 +198,10 @@ z-index: 1; } +.styles-section.computed-style .properties li.not-parsed-ok img.exclamation-mark { + left: 0; +} + .styles-section .header { white-space: nowrap; -webkit-background-origin: padding; @@ -388,7 +396,10 @@ margin-left: 16px; } -.styles-section .properties .overloaded, .styles-section .properties .inactive, .styles-section .properties .disabled { +.styles-section .properties .overloaded, +.styles-section .properties .inactive, +.styles-section .properties .disabled, +.styles-section .properties .not-parsed-ok { text-decoration: line-through; } @@ -505,118 +516,3 @@ -webkit-user-select: text; -webkit-user-drag: auto; } - -/* https://github.com/bgrins/spectrum */ -.spectrum-container { - position: absolute; - top: 0; - left: 0; - display: inline-block; - background: rgba(230, 230, 230, 1) !important; - border: 1px solid #646464; - padding: 10px; - width: 220px; - z-index: 10; - -webkit-user-select: none; -} - -.spectrum-top { - position: relative; - width: 100%; - display: inline-block; -} - -.spectrum-color { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 40px; -} - -.spectrum-display-value { - -webkit-user-select: text; - position: relative; - left: 2px; - top: -6px; -} - -.spectrum-hue { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 170px; - -webkit-box-reflect: right -28px; -} - -.spectrum-fill { - /* Same as spectrum-color width to force aspect ratio. */ - margin-top: 150px; -} - -.spectrum-range-container { - position: relative; - padding-bottom: 5px; -} - -.spectrum-range-container * { - font-size: 11px; - vertical-align: middle; -} - -.spectrum-range-container label { - display: inline-block; - padding-right: 4px; -} - -.spectrum-range-container input { - position: absolute; - left: 15px; - right: 40px; - margin: 3px 0 0 0; -} - -.swatch, .spectrum-dragger, .spectrum-slider { - -webkit-user-select: none; -} - -.spectrum-sat { - background-image: -webkit-linear-gradient(left, white, rgba(204, 154, 129, 0)); -} - -.spectrum-val { - background-image: -webkit-linear-gradient(bottom, black, rgba(204, 154, 129, 0)); -} - -.spectrum-hue { - background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); -} - -.spectrum-dragger { - border-radius: 5px; - height: 5px; - width: 5px; - border: 1px solid white; - cursor: pointer; - position: absolute; - top: 0; - left: 0; - background: black; -} - -.spectrum-slider { - position: absolute; - top: 0; - cursor: pointer; - border: 1px solid black; - height: 4px; - left: -1px; - right: -1px; -} - -.spectrum-container .swatch { - width: 20px; - height:20px; - margin: 0; -} diff --git a/Source/WebCore/inspector/front-end/inspector.css b/Source/WebCore/inspector/front-end/inspector.css index 0dd2b7a7b..89da79cf7 100644 --- a/Source/WebCore/inspector/front-end/inspector.css +++ b/Source/WebCore/inspector/front-end/inspector.css @@ -96,11 +96,18 @@ body.compact.inactive #toolbar { .toolbar-item.toggleable { padding-top: 4px; + padding-bottom: 4px; +} + +body.compact .toolbar-item.toggleable { + margin: 1px 0; + padding-bottom: 2px; } .toolbar-item.toggleable.toggled-on { border-width: 0 2px 0 2px; - padding: 4px 4px 0 4px; + padding-left: 4px; + padding-right: 4px; -webkit-border-image: url(Images/toolbarItemSelected.png) 0 2 0 2; } @@ -109,6 +116,7 @@ body.compact.inactive #toolbar { width: 32px; height: 32px; background-image: url(Images/toolbarIcons.png); + vertical-align: top; } body.compact .toolbar-icon { @@ -131,9 +139,12 @@ body.compact .toolbar-item:active .toolbar-icon { } .toolbar-label { + line-height: 15px; font-size: 11px; font-family: Lucida Grande, sans-serif; text-shadow: rgba(255, 255, 255, 0.5) 0 1px 0; + position: relative; + top: 1px; } .toolbar-item.toggleable:active .toolbar-label { @@ -143,8 +154,7 @@ body.compact .toolbar-item:active .toolbar-icon { body.compact .toolbar-label { display: inline-block; margin-left: 3px; - position: relative; - top: 2px; + top: 0; } body.compact #search-toolbar-label { @@ -159,17 +169,19 @@ body.compact #search-toolbar-label { } #toolbar-dropdown-arrow { - font-size: 16px; + font-size: 14px; font-weight: bold; border: 0; background-color: transparent; -webkit-border-radius: 5px; text-shadow: none; -} + margin: 0; + position: relative; + top: 1px; -body.compact #toolbar-dropdown-arrow { - font-size: 14px; - padding-bottom: 4px; + /* A line height of 0 allows precise text positioning using padding. */ + line-height: 0; + padding: 9px 6px 11px; } #toolbar-dropdown-arrow.dropdown-visible { @@ -233,6 +245,11 @@ body.undocked.platform-mac-snowleopard #toolbar-dropdown { margin-right: 0.5em; } +#toolbar-dropdown .toolbar-label { + line-height: 32px; + top: 0; +} + .scrollable-content { position: static; height: 100%; @@ -454,7 +471,7 @@ body.compact .toolbar-item.console .toolbar-icon { background-position: 0 0; background-color: transparent; border: 0 none transparent; - margin-top: 4px; + padding: 0; } #close-button-left:hover, #close-button-right:hover { @@ -490,7 +507,9 @@ body:not(.platform-mac) .toolbar-item.close-left { } .toolbar-item.close-left { - margin-top: 5px; + display: -webkit-box; + -webkit-box-align: center; + height: 100%; } #main { @@ -769,7 +788,7 @@ body.port-qt .dock-status-bar-item { } #error-warning-count { - padding: 6px 2px 6px 0px; + padding: 5px 2px 6px 0px; font-size: 10px; height: 19px; cursor: pointer; @@ -866,7 +885,9 @@ body.platform-linux .monospace, body.platform-linux .source-code { } #console-prompt { + clear: right; position: relative; + border-top: 1px solid rgb(240, 240, 240); padding: 1px 22px 1px 0px; margin-left: 24px; min-height: 16px; @@ -883,13 +904,18 @@ body.platform-linux .monospace, body.platform-linux .source-code { } .console-message, .console-user-command { + clear: right; position: relative; - border-bottom: 1px solid rgb(240, 240, 240); + border-top: 1px solid rgb(240, 240, 240); padding: 1px 22px 1px 0px; margin-left: 24px; min-height: 16px; } +.console-mesage:first-child { + border-top: none; +} + .console-adjacent-user-command-result { border-bottom: none; } @@ -902,11 +928,11 @@ body.platform-linux .monospace, body.platform-linux .source-code { position: absolute; display: block; content: ""; - left: -14px; + left: -17px; top: 0.8em; width: 10px; height: 10px; - margin-top: -5px; + margin-top: -6px; -webkit-user-select: none; } @@ -917,7 +943,7 @@ body.platform-linux .monospace, body.platform-linux .source-code { vertical-align: middle; white-space: nowrap; padding: 1px 4px; - margin-top: -2px; + margin-top: -1px; margin-right: 4px; margin-left: -18px; text-align: left; @@ -938,6 +964,15 @@ body.platform-linux .monospace, body.platform-linux .source-code { visibility: hidden; } +.repeated-message .outline-disclosure, .repeated-message > .console-message-text { + -webkit-flex: 1; +} + +.console-info { + color: rgb(128, 128, 128); + font-style: italic; +} + .console-group .console-group > .console-group-messages { margin-left: 16px; } @@ -1000,6 +1035,7 @@ body.platform-linux .monospace, body.platform-linux .source-code { .console-warning-level::before { background-image: url(Images/warningIcon.png); + margin-top: -7px; } .console-user-command .console-message { @@ -1025,13 +1061,13 @@ body.platform-linux .monospace, body.platform-linux .source-code { color: rgb(15%, 15%, 15%); } -ol.watch-expressions > li.hovered, -.console-message:hover { +ol.watch-expressions > li.hovered { background-color: #F0F0F0; } .console-message-url { float: right; + max-width: 100%; margin-left: 4px; } @@ -1262,7 +1298,6 @@ iframe.panel.extension { .outline-disclosure li { padding: 0 0 0 14px; margin-top: 1px; - margin-bottom: 1px; margin-left: -2px; word-wrap: break-word; } @@ -1300,7 +1335,6 @@ iframe.panel.extension { float: left; width: 8px; height: 8px; - margin-top: 1px; padding-right: 2px; } @@ -1671,19 +1705,18 @@ li.editing .swatch, li.editing .enabled-button, li.editing-sub-part .delete-but margin-bottom: -1px; width: 1em; height: 1em; - border: 1px solid rgba(128, 128, 128, 0.6); - background-image: url(Images/checker.png); display: inline-block; + background-image: url(Images/checker.png); } .swatch-inner { width: 100%; height: 100%; display: inline-block; - margin-bottom: 1px; + border: 1px solid rgba(128, 128, 128, 0.6); } -.swatch:hover { +.swatch-inner:hover { border: 1px solid rgba(64, 64, 64, 0.8); } @@ -1981,6 +2014,12 @@ button.enable-toggle-status-bar-item.toggled-on .glyph { display: block; } +.filter-all .console-warning-level.repeated-message, .filter-warnings .console-warning-level.repeated-message, +.filter-all .console-error-level.repeated-message, .filter-errors .console-error-level.repeated-message, +.filter-all .console-log-level.repeated-message, .filter-logs .console-log-level.repeated-message { + display: -webkit-flex; +} + .console-user-command-result { display: block; } diff --git a/Source/WebCore/inspector/front-end/inspector.html b/Source/WebCore/inspector/front-end/inspector.html index eda8ddf0c..6ea0af7e9 100644 --- a/Source/WebCore/inspector/front-end/inspector.html +++ b/Source/WebCore/inspector/front-end/inspector.html @@ -29,7 +29,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> - <meta http-equiv="X-WebKit-CSP" content="object-src 'none'; script-src 'self' 'unsafe-eval'"> + <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' 'unsafe-eval'"> <link rel="stylesheet" type="text/css" href="dialog.css"> <link rel="stylesheet" type="text/css" href="inspector.css"> <link rel="stylesheet" type="text/css" href="inspectorCommon.css"> @@ -71,6 +71,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="ContentProvider.js"></script> <script type="text/javascript" src="Resource.js"></script> <script type="text/javascript" src="NetworkRequest.js"></script> + <script type="text/javascript" src="UISourceCode.js"></script> <script type="text/javascript" src="CSSStyleModel.js"></script> <script type="text/javascript" src="NetworkManager.js"></script> <script type="text/javascript" src="NetworkLog.js"></script> @@ -131,11 +132,11 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="FileUtils.js"></script> <script type="text/javascript" src="DebuggerModel.js"></script> <script type="text/javascript" src="SourceMapping.js"></script> - <script type="text/javascript" src="UISourceCode.js"></script> <script type="text/javascript" src="Script.js"></script> <script type="text/javascript" src="Linkifier.js"></script> <script type="text/javascript" src="DebuggerScriptMapping.js"></script> <script type="text/javascript" src="PresentationConsoleMessageHelper.js"></script> + <script type="text/javascript" src="NetworkWorkspaceProvider.js"></script> <script type="text/javascript" src="Workspace.js"></script> <script type="text/javascript" src="BreakpointManager.js"></script> <script type="text/javascript" src="ContentProviders.js"></script> @@ -172,7 +173,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <div id="toolbar"> <div class="toolbar-item close-left"><button id="close-button-left"></button></div> <div id="toolbar-controls"> - <div class="toolbar-item"><button id="toolbar-dropdown-arrow" class="toolbar-label">»</button></div> + <div class="toolbar-item"><button id="toolbar-dropdown-arrow">»</button></div> <div class="toolbar-item close-right"><button id="close-button-right"></button></div> </div> </div> diff --git a/Source/WebCore/inspector/front-end/inspector.js b/Source/WebCore/inspector/front-end/inspector.js index d2a4d3a2d..d67fb0cb1 100644 --- a/Source/WebCore/inspector/front-end/inspector.js +++ b/Source/WebCore/inspector/front-end/inspector.js @@ -336,7 +336,8 @@ WebInspector.loaded = function() WebInspector.doLoadedDone(); } WebInspector.socket.onclose = function() { - (new WebInspector.RemoteDebuggingTerminatedScreen()).showModal(); + if (!WebInspector.socket._detachReason) + (new WebInspector.RemoteDebuggingTerminatedScreen("websocket_closed")).showModal(); } return; } @@ -369,6 +370,7 @@ WebInspector.doLoadedDone = function() ProfilerAgent.isSampling(WebInspector._initializeCapability.bind(WebInspector, "samplingCPUProfiler", null)); ProfilerAgent.hasHeapProfiler(WebInspector._initializeCapability.bind(WebInspector, "heapProfilerPresent", null)); TimelineAgent.supportsFrameInstrumentation(WebInspector._initializeCapability.bind(WebInspector, "timelineSupportsFrameInstrumentation", null)); + TimelineAgent.canMonitorMainThread(WebInspector._initializeCapability.bind(WebInspector, "timelineCanMonitorMainThread", null)); PageAgent.canOverrideDeviceMetrics(WebInspector._initializeCapability.bind(WebInspector, "canOverrideDeviceMetrics", null)); PageAgent.canOverrideGeolocation(WebInspector._initializeCapability.bind(WebInspector, "canOverrideGeolocation", null)); PageAgent.canOverrideDeviceOrientation(WebInspector._initializeCapability.bind(WebInspector, "canOverrideDeviceOrientation", WebInspector._doLoadedDoneWithCapabilities.bind(WebInspector))); @@ -423,18 +425,20 @@ WebInspector._doLoadedDoneWithCapabilities = function() this.openAnchorLocationRegistry = new WebInspector.HandlerRegistry(openAnchorLocationSetting); this.openAnchorLocationRegistry.registerHandler(autoselectPanel, function() { return false; }); + this.networkWorkspaceProvider = new WebInspector.NetworkWorkspaceProvider(); this.workspace = new WebInspector.Workspace(); + this.workspace.addProject("network", this.networkWorkspaceProvider); this.workspaceController = new WebInspector.WorkspaceController(this.workspace); this.breakpointManager = new WebInspector.BreakpointManager(WebInspector.settings.breakpoints, this.debuggerModel, this.workspace); - this.scriptSnippetModel = new WebInspector.ScriptSnippetModel(this.workspace); - new WebInspector.DebuggerScriptMapping(this.workspace); + this.scriptSnippetModel = new WebInspector.ScriptSnippetModel(this.workspace, this.networkWorkspaceProvider); + new WebInspector.DebuggerScriptMapping(this.workspace, this.networkWorkspaceProvider); this.styleContentBinding = new WebInspector.StyleContentBinding(this.cssModel); - new WebInspector.NetworkUISourceCodeProvider(this.workspace); + new WebInspector.NetworkUISourceCodeProvider(this.workspace, this.networkWorkspaceProvider); new WebInspector.StylesSourceMapping(this.workspace); if (WebInspector.experimentsSettings.sass.isEnabled()) - new WebInspector.SASSSourceMapping(this.workspace); + new WebInspector.SASSSourceMapping(this.workspace, this.networkWorkspaceProvider); new WebInspector.PresentationConsoleMessageHelper(this.workspace); @@ -885,6 +889,7 @@ WebInspector.showErrorMessage = function(error) WebInspector.log(error, WebInspector.ConsoleMessage.MessageLevel.Error, true); } +// Inspector.inspect protocol event WebInspector.inspect = function(payload, hints) { var object = WebInspector.RemoteObject.fromPayload(payload); @@ -906,6 +911,13 @@ WebInspector.inspect = function(payload, hints) object.release(); } +// Inspector.detached protocol event +WebInspector.detached = function(reason) +{ + WebInspector.socket._detachReason = reason; + (new WebInspector.RemoteDebuggingTerminatedScreen(reason)).showModal(); +} + WebInspector._updateFocusedNode = function(nodeId) { if (WebInspector._nodeSearchButton.toggled) { diff --git a/Source/WebCore/inspector/front-end/nativeMemoryProfiler.css b/Source/WebCore/inspector/front-end/nativeMemoryProfiler.css index 4c42afd9f..815c71a89 100644 --- a/Source/WebCore/inspector/front-end/nativeMemoryProfiler.css +++ b/Source/WebCore/inspector/front-end/nativeMemoryProfiler.css @@ -56,12 +56,13 @@ } .memory-bar-chart-bar { - border: 1px solid #bbb; + border: 0px; border-radius: 2px; float: left; - height: 20px; + height: 18px; overflow: hidden; position: relative; + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255, 255, 255, 0.5)), to(rgba(192, 192, 192, 0))); } .memory-bar-chart-unused { @@ -84,3 +85,62 @@ .memory-bar-chart-total { font-weight: bold; } + +.native-snapshot-view { + display: none; + overflow: hidden; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.native-snapshot-view.visible { + display: block; +} + +.native-snapshot-view .data-grid { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border: none; +} + +.native-snapshot-view .data-grid table { + font-size: 11px; +} + +.native-snapshot-view .data-grid td { + height: 14px; + line-height: 14px; + padding: 1px 4px; +} + +.native-snapshot-view .data-grid div.size-text { + float: left; + text-align: right; + width: 70px; + padding-right: 5px; +} + +.native-snapshot-view .data-grid .expanded div.size-bar { + background-image: -webkit-gradient(linear, left top, left bottom, from(hsla(0, 100%, 100%, 0.85)), to(hsla(0, 100%, 100%, 0.65))); + color: hsla(0, 0%, 0%, 0.5); +} + +.native-snapshot-view .data-grid div.size-bar { + text-align: center; + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255, 255, 255, 0.5)), to(rgba(192, 192, 192, 0))); + height: 14px; + border-radius: 2px; + color: black; + white-space: normal; + min-width: 1px; +} + +.native-snapshot-view .data-grid div.percent-text { + display: inline-block; +} diff --git a/Source/WebCore/inspector/front-end/spectrum.css b/Source/WebCore/inspector/front-end/spectrum.css new file mode 100644 index 000000000..7a9ea28b9 --- /dev/null +++ b/Source/WebCore/inspector/front-end/spectrum.css @@ -0,0 +1,114 @@ +/* https://github.com/bgrins/spectrum */ +.spectrum-container { + position: absolute; + top: 0; + left: 0; + display: inline-block; + background: rgba(230, 230, 230, 1) !important; + border: 1px solid #646464; + padding: 10px; + width: 220px; + z-index: 10; + -webkit-user-select: none; +} + +.spectrum-top { + position: relative; + width: 100%; + display: inline-block; +} + +.spectrum-color { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 40px; +} + +.spectrum-display-value { + -webkit-user-select: text; + position: relative; + left: 2px; + top: -6px; +} + +.spectrum-hue { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 170px; + -webkit-box-reflect: right -28px; +} + +.spectrum-fill { + /* Same as spectrum-color width to force aspect ratio. */ + margin-top: 150px; +} + +.spectrum-range-container { + position: relative; + padding-bottom: 5px; +} + +.spectrum-range-container * { + font-size: 11px; + vertical-align: middle; +} + +.spectrum-range-container label { + display: inline-block; + padding-right: 4px; +} + +.spectrum-range-container input { + position: absolute; + left: 15px; + right: 40px; + margin: 3px 0 0 0; +} + +.spectrum-dragger, .spectrum-slider { + -webkit-user-select: none; +} + +.spectrum-sat { + background-image: -webkit-linear-gradient(left, white, rgba(204, 154, 129, 0)); +} + +.spectrum-val { + background-image: -webkit-linear-gradient(bottom, black, rgba(204, 154, 129, 0)); +} + +.spectrum-hue { + background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); +} + +.spectrum-dragger { + border-radius: 5px; + height: 5px; + width: 5px; + border: 1px solid white; + cursor: pointer; + position: absolute; + top: 0; + left: 0; + background: black; +} + +.spectrum-slider { + position: absolute; + top: 0; + cursor: pointer; + border: 1px solid black; + height: 4px; + left: -1px; + right: -1px; +} + +.spectrum-container .swatch { + width: 20px; + height:20px; + margin: 0; +} diff --git a/Source/WebCore/inspector/front-end/timelinePanel.css b/Source/WebCore/inspector/front-end/timelinePanel.css index c9dfabe2b..64a1adc7d 100644 --- a/Source/WebCore/inspector/front-end/timelinePanel.css +++ b/Source/WebCore/inspector/front-end/timelinePanel.css @@ -653,24 +653,19 @@ .timeline-cpu-bars { position: absolute; - top: 19px; - height: 18px; + top: 0; + height: 19px; z-index: 350; width: 100%; overflow: hidden; } -.timeline-cpu-bars-label { - font-weight: bold; - font-family: monospace; - font-size: 9px; - line-height: 7px; - position: absolute; - top: 5px; - left: 4px; - color: rgb(51, 51, 51); - background-color: rgba(255, 255, 255, 0.75); - z-index: 350; +.timeline-cpu-bars .timeline-graph-bar { + border-color: rgb(192, 192, 192); + background-color: rgba(0, 0, 0, 0.15); + top: 2px; + bottom: 2px; + height: auto; } .image-preview-container { |