diff options
author | Steve Block <steveblock@google.com> | 2009-10-08 17:19:54 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2009-10-20 00:41:58 +0100 |
commit | 231d4e3152a9c27a73b6ac7badbe6be673aa3ddf (patch) | |
tree | a6c7e2d6cd7bfa7011cc39abbb436142d7a4a7c8 /WebCore/inspector/front-end/SourceFrame.js | |
parent | e196732677050bd463301566a68a643b6d14b907 (diff) | |
download | external_webkit-231d4e3152a9c27a73b6ac7badbe6be673aa3ddf.zip external_webkit-231d4e3152a9c27a73b6ac7badbe6be673aa3ddf.tar.gz external_webkit-231d4e3152a9c27a73b6ac7badbe6be673aa3ddf.tar.bz2 |
Merge webkit.org at R49305 : Automatic merge by git.
Change-Id: I8968561bc1bfd72b8923b7118d3728579c6dbcc7
Diffstat (limited to 'WebCore/inspector/front-end/SourceFrame.js')
-rw-r--r-- | WebCore/inspector/front-end/SourceFrame.js | 679 |
1 files changed, 510 insertions, 169 deletions
diff --git a/WebCore/inspector/front-end/SourceFrame.js b/WebCore/inspector/front-end/SourceFrame.js index 930eb16..790055a 100644 --- a/WebCore/inspector/front-end/SourceFrame.js +++ b/WebCore/inspector/front-end/SourceFrame.js @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Joseph Pecoraro * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -115,7 +116,7 @@ WebInspector.SourceFrame.prototype = { this._lineNumberToReveal = lineNumber; return; } - + var row = this.sourceRow(lineNumber); if (row) row.scrollIntoViewIfNeeded(true); @@ -199,6 +200,7 @@ WebInspector.SourceFrame.prototype = { _loaded: function() { WebInspector.addMainEventListeners(this.element.contentDocument); + this.element.contentDocument.addEventListener("contextmenu", this._documentContextMenu.bind(this), true); this.element.contentDocument.addEventListener("mousedown", this._documentMouseDown.bind(this), true); this.element.contentDocument.addEventListener("keydown", this._documentKeyDown.bind(this), true); this.element.contentDocument.addEventListener("keyup", WebInspector.documentKeyUp.bind(WebInspector), true); @@ -215,27 +217,45 @@ WebInspector.SourceFrame.prototype = { headElement = this.element.contentDocument.createElement("head"); this.element.contentDocument.documentElement.insertBefore(headElement, this.element.contentDocument.documentElement.firstChild); } + + var linkElement = this.element.contentDocument.createElement("link"); + linkElement.type = "text/css"; + linkElement.rel = "stylesheet"; + linkElement.href = "inspectorSyntaxHighlight.css"; + headElement.appendChild(linkElement); var styleElement = this.element.contentDocument.createElement("style"); headElement.appendChild(styleElement); // Add these style rules here since they are specific to the Inspector. They also behave oddly and not - // all properties apply if added to view-source.css (becuase it is a user agent sheet.) + // all properties apply if added to view-source.css (because it is a user agent sheet.) var styleText = ".webkit-line-number { background-repeat: no-repeat; background-position: right 1px; }\n"; + styleText += ".webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(program-counter); }\n"; + styleText += ".webkit-breakpoint .webkit-line-number { color: white; background-image: -webkit-canvas(breakpoint); }\n"; styleText += ".webkit-breakpoint-disabled .webkit-line-number { color: white; background-image: -webkit-canvas(breakpoint-disabled); }\n"; - styleText += ".webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(program-counter); }\n"; styleText += ".webkit-breakpoint.webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(breakpoint-program-counter); }\n"; styleText += ".webkit-breakpoint-disabled.webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(breakpoint-disabled-program-counter); }\n"; + + styleText += ".webkit-breakpoint.webkit-breakpoint-conditional .webkit-line-number { color: white; background-image: -webkit-canvas(breakpoint-conditional); }\n"; + styleText += ".webkit-breakpoint-disabled.webkit-breakpoint-conditional .webkit-line-number { color: white; background-image: -webkit-canvas(breakpoint-disabled-conditional); }\n"; + styleText += ".webkit-breakpoint.webkit-breakpoint-conditional.webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(breakpoint-conditional-program-counter); }\n"; + styleText += ".webkit-breakpoint-disabled.webkit-breakpoint-conditional.webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(breakpoint-disabled-conditional-program-counter); }\n"; + styleText += ".webkit-execution-line .webkit-line-content { background-color: rgb(171, 191, 254); outline: 1px solid rgb(64, 115, 244); }\n"; styleText += ".webkit-height-sized-to-fit { overflow-y: hidden }\n"; styleText += ".webkit-line-content { background-color: white; }\n"; styleText += "@-webkit-keyframes fadeout {from {background-color: rgb(255, 255, 120);} to { background-color: white;}}\n"; styleText += ".webkit-highlighted-line .webkit-line-content { background-color: rgb(255, 255, 120); -webkit-animation: 'fadeout' 2s 500ms}\n"; - styleText += ".webkit-javascript-comment { color: rgb(0, 116, 0); }\n"; - styleText += ".webkit-javascript-keyword { color: rgb(170, 13, 145); }\n"; - styleText += ".webkit-javascript-number { color: rgb(28, 0, 207); }\n"; - styleText += ".webkit-javascript-string, .webkit-javascript-regexp { color: rgb(196, 26, 22); }\n"; + + // TODO: Move these styles into inspector.css once https://bugs.webkit.org/show_bug.cgi?id=28913 is fixed and popup moved into the top frame. + styleText += ".popup-content { position: absolute; z-index: 10000; padding: 4px; background-color: rgb(203, 226, 255); -webkit-border-radius: 7px; border: 2px solid rgb(169, 172, 203); }"; + styleText += ".popup-glasspane { position: absolute; top: 0; left: 0; height: 100%; width: 100%; opacity: 0; z-index: 9900; }"; + styleText += ".popup-message { background-color: transparent; font-family: Lucida Grande, sans-serif; font-weight: normal; font-size: 11px; text-align: left; text-shadow: none; color: rgb(85, 85, 85); cursor: default; margin: 0 0 2px 0; }"; + styleText += ".popup-content.breakpoint-condition { width: 90%; }"; + styleText += ".popup-content input#bp-condition { font-family: monospace; margin: 0; border: 1px inset rgb(190, 190, 190) !important; width: 100%; box-shadow: none !important; outline: none !important; -webkit-user-modify: read-write; }"; + // This class is already in inspector.css + styleText += ".hidden { display: none !important; }"; styleElement.textContent = styleText; @@ -244,8 +264,12 @@ WebInspector.SourceFrame.prototype = { this.element.contentWindow.Element.prototype.addStyleClass = Element.prototype.addStyleClass; this.element.contentWindow.Element.prototype.removeStyleClass = Element.prototype.removeStyleClass; + this.element.contentWindow.Element.prototype.positionAt = Element.prototype.positionAt; this.element.contentWindow.Element.prototype.removeMatchingStyleClasses = Element.prototype.removeMatchingStyleClasses; this.element.contentWindow.Element.prototype.hasStyleClass = Element.prototype.hasStyleClass; + this.element.contentWindow.Element.prototype.pageOffsetRelativeToWindow = Element.prototype.pageOffsetRelativeToWindow; + this.element.contentWindow.Element.prototype.__defineGetter__("totalOffsetLeft", Element.prototype.__lookupGetter__("totalOffsetLeft")); + this.element.contentWindow.Element.prototype.__defineGetter__("totalOffsetTop", Element.prototype.__lookupGetter__("totalOffsetTop")); this.element.contentWindow.Node.prototype.enclosingNodeOrSelfWithNodeName = Node.prototype.enclosingNodeOrSelfWithNodeName; this.element.contentWindow.Node.prototype.enclosingNodeOrSelfWithNodeNameInArray = Node.prototype.enclosingNodeOrSelfWithNodeNameInArray; @@ -257,20 +281,20 @@ WebInspector.SourceFrame.prototype = { if (this.autoSizesToFitContentHeight) this.sizeToFitContentHeight(); - + if (this._lineNumberToReveal) { this.revealLine(this._lineNumberToReveal); delete this._lineNumberToReveal; } - + if (this._lineNumberToHighlight) { this.highlightLine(this._lineNumberToHighlight); delete this._lineNumberToHighlight; } - + this.dispatchEventToListeners("content loaded"); }, - + _isContentLoaded: function() { var doc = this.element.contentDocument; return doc && doc.getElementsByTagName("table")[0]; @@ -283,18 +307,102 @@ WebInspector.SourceFrame.prototype = { this.sizeToFitContentHeight(); }, - _documentMouseDown: function(event) + _documentContextMenu: function(event) { if (!event.target.hasStyleClass("webkit-line-number")) return; + var sourceRow = event.target.enclosingNodeOrSelfWithNodeName("tr"); + if (!sourceRow._breakpointObject && this.addBreakpointDelegate) + this.addBreakpointDelegate(this.lineNumberForSourceRow(sourceRow)); + + var breakpoint = sourceRow._breakpointObject; + if (!breakpoint) + return; + this._editBreakpointCondition(event.target, sourceRow, breakpoint); + event.preventDefault(); + }, + + _documentMouseDown: function(event) + { + if (!event.target.hasStyleClass("webkit-line-number")) + return; + if (event.button != 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) + return; var sourceRow = event.target.enclosingNodeOrSelfWithNodeName("tr"); - if (sourceRow._breakpointObject) - sourceRow._breakpointObject.enabled = !sourceRow._breakpointObject.enabled; + if (sourceRow._breakpointObject && sourceRow._breakpointObject.enabled) + sourceRow._breakpointObject.enabled = false; + else if (sourceRow._breakpointObject) + WebInspector.panels.scripts.removeBreakpoint(sourceRow._breakpointObject); else if (this.addBreakpointDelegate) this.addBreakpointDelegate(this.lineNumberForSourceRow(sourceRow)); }, + _editBreakpointCondition: function(eventTarget, sourceRow, breakpoint) + { + // TODO: Migrate the popup to the top-level document and remove the blur listener from conditionElement once https://bugs.webkit.org/show_bug.cgi?id=28913 is fixed. + var popupDocument = this.element.contentDocument; + this._showBreakpointConditionPopup(eventTarget, breakpoint.line, popupDocument); + + function committed(element, newText) + { + breakpoint.condition = newText; + if (breakpoint.condition) + sourceRow.addStyleClass("webkit-breakpoint-conditional"); + else + sourceRow.removeStyleClass("webkit-breakpoint-conditional"); + dismissed.call(this); + } + + function dismissed() + { + this._popup.hide(); + delete this._conditionEditorElement; + } + + var dismissedHandler = dismissed.bind(this); + this._conditionEditorElement.addEventListener("blur", dismissedHandler, false); + + WebInspector.startEditing(this._conditionEditorElement, committed.bind(this), dismissedHandler); + this._conditionEditorElement.value = breakpoint.condition; + this._conditionEditorElement.select(); + }, + + _showBreakpointConditionPopup: function(clickedElement, lineNumber, popupDocument) + { + var popupContentElement = this._createPopupElement(lineNumber, popupDocument); + var lineElement = clickedElement.enclosingNodeOrSelfWithNodeName("td").nextSibling; + if (this._popup) { + this._popup.hide(); + this._popup.element = popupContentElement; + } else { + this._popup = new WebInspector.Popup(popupContentElement); + this._popup.autoHide = true; + } + this._popup.anchor = lineElement; + this._popup.show(); + }, + + _createPopupElement: function(lineNumber, popupDocument) + { + var popupContentElement = popupDocument.createElement("div"); + popupContentElement.className = "popup-content breakpoint-condition"; + + var labelElement = document.createElement("label"); + labelElement.className = "popup-message"; + labelElement.htmlFor = "bp-condition"; + labelElement.appendChild(document.createTextNode(WebInspector.UIString("The breakpoint on line %d will stop only if this expression is true:", lineNumber))); + popupContentElement.appendChild(labelElement); + + var editorElement = document.createElement("input"); + editorElement.id = "bp-condition"; + editorElement.type = "text" + popupContentElement.appendChild(editorElement); + this._conditionEditorElement = editorElement; + + return popupContentElement; + }, + _documentKeyDown: function(event) { var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event); @@ -380,6 +488,8 @@ WebInspector.SourceFrame.prototype = { sourceRow.addStyleClass("webkit-breakpoint"); if (!breakpoint.enabled) sourceRow.addStyleClass("webkit-breakpoint-disabled"); + if (breakpoint.condition) + sourceRow.addStyleClass("webkit-breakpoint-conditional"); }, _removeBreakpointFromSource: function(breakpoint) @@ -392,8 +502,9 @@ WebInspector.SourceFrame.prototype = { sourceRow.removeStyleClass("webkit-breakpoint"); sourceRow.removeStyleClass("webkit-breakpoint-disabled"); + sourceRow.removeStyleClass("webkit-breakpoint-conditional"); }, - + _incrementMessageRepeatCount: function(msg, repeatDelta) { if (!msg._resourceMessageLineElement) @@ -518,12 +629,12 @@ WebInspector.SourceFrame.prototype = { delete this._needsProgramCounterImage; }, - _drawBreakpointImagesIfNeeded: function() + _drawBreakpointImagesIfNeeded: function(conditional) { if (!this._needsBreakpointImages || !this.element.contentDocument) return; - function drawBreakpoint(ctx, disabled) + function drawBreakpoint(ctx, disabled, conditional) { ctx.beginPath(); ctx.moveTo(0, 2); @@ -534,8 +645,8 @@ WebInspector.SourceFrame.prototype = { ctx.lineTo(2, 11); ctx.lineTo(0, 9); ctx.closePath(); - ctx.fillStyle = "rgb(1, 142, 217)"; - ctx.strokeStyle = "rgb(0, 103, 205)"; + ctx.fillStyle = conditional ? "rgb(217, 142, 1)" : "rgb(1, 142, 217)"; + ctx.strokeStyle = conditional ? "rgb(205, 103, 0)" : "rgb(0, 103, 205)"; ctx.lineWidth = 3; ctx.fill(); ctx.save(); @@ -553,6 +664,9 @@ WebInspector.SourceFrame.prototype = { ctx.restore(); } + + // Unconditional breakpoints. + var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint", 26, 11); ctx.clearRect(0, 0, 26, 11); drawBreakpoint(ctx); @@ -573,6 +687,29 @@ WebInspector.SourceFrame.prototype = { ctx.clearRect(20, 0, 6, 11); this._drawProgramCounterInContext(ctx, true); + + // Conditional breakpoints. + + var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-conditional", 26, 11); + ctx.clearRect(0, 0, 26, 11); + drawBreakpoint(ctx, false, true); + + var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-conditional-program-counter", 26, 11); + ctx.clearRect(0, 0, 26, 11); + drawBreakpoint(ctx, false, true); + ctx.clearRect(20, 0, 6, 11); + this._drawProgramCounterInContext(ctx, true); + + var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-disabled-conditional", 26, 11); + ctx.clearRect(0, 0, 26, 11); + drawBreakpoint(ctx, true, true); + + var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-disabled-conditional-program-counter", 26, 11); + ctx.clearRect(0, 0, 26, 11); + drawBreakpoint(ctx, true, true); + ctx.clearRect(20, 0, 6, 11); + this._drawProgramCounterInContext(ctx, true); + delete this._needsBreakpointImages; }, @@ -582,190 +719,394 @@ WebInspector.SourceFrame.prototype = { if (!table) return; - function deleteContinueFlags(cell) - { - if (!cell) - return; - delete cell._commentContinues; - delete cell._singleQuoteStringContinues; - delete cell._doubleQuoteStringContinues; - delete cell._regexpContinues; - } + var jsSyntaxHighlighter = new WebInspector.JavaScriptSourceSyntaxHighlighter(table, this); + jsSyntaxHighlighter.process(); + }, - function createSpan(content, className) - { - var span = document.createElement("span"); - span.className = className; - span.appendChild(document.createTextNode(content)); - return span; - } + syntaxHighlightCSS: function() + { + var table = this.element.contentDocument.getElementsByTagName("table")[0]; + if (!table) + return; - function generateFinder(regex, matchNumber, className) - { - return function(str) { - var match = regex.exec(str); - if (!match) - return null; - previousMatchLength = match[matchNumber].length; - return createSpan(match[matchNumber], className); - }; - } + var cssSyntaxHighlighter = new WebInspector.CSSSourceSyntaxHighligher(table, this); + cssSyntaxHighlighter.process(); + } +} - var findNumber = generateFinder(/^(-?(\d+\.?\d*([eE][+-]\d+)?|0[xX]\h+|Infinity)|NaN)(?:\W|$)/, 1, "webkit-javascript-number"); - var findKeyword = generateFinder(/^(null|true|false|break|case|catch|const|default|finally|for|instanceof|new|var|continue|function|return|void|delete|if|this|do|while|else|in|switch|throw|try|typeof|with|debugger|class|enum|export|extends|import|super|get|set)(?:\W|$)/, 1, "webkit-javascript-keyword"); - var findSingleLineString = generateFinder(/^"(?:[^"\\]|\\.)*"|^'([^'\\]|\\.)*'/, 0, "webkit-javascript-string"); // " this quote keeps Xcode happy - var findMultilineCommentStart = generateFinder(/^\/\*.*$/, 0, "webkit-javascript-comment"); - var findMultilineCommentEnd = generateFinder(/^.*?\*\//, 0, "webkit-javascript-comment"); - var findMultilineSingleQuoteStringStart = generateFinder(/^'(?:[^'\\]|\\.)*\\$/, 0, "webkit-javascript-string"); - var findMultilineSingleQuoteStringEnd = generateFinder(/^(?:[^'\\]|\\.)*?'/, 0, "webkit-javascript-string"); - var findMultilineDoubleQuoteStringStart = generateFinder(/^"(?:[^"\\]|\\.)*\\$/, 0, "webkit-javascript-string"); - var findMultilineDoubleQuoteStringEnd = generateFinder(/^(?:[^"\\]|\\.)*?"/, 0, "webkit-javascript-string"); - var findMultilineRegExpEnd = generateFinder(/^(?:[^\/\\]|\\.)*?\/([gim]{0,3})/, 0, "webkit-javascript-regexp"); - var findSingleLineComment = generateFinder(/^\/\/.*|^\/\*.*?\*\//, 0, "webkit-javascript-comment"); - - function findMultilineRegExpStart(str) - { - var match = /^\/(?:[^\/\\]|\\.)*\\$/.exec(str); - if (!match || !/\\|\$|\.[\?\*\+]|[^\|]\|[^\|]/.test(match[0])) +WebInspector.SourceFrame.prototype.__proto__ = WebInspector.Object.prototype; + +WebInspector.SourceSyntaxHighligher = function(table, sourceFrame) +{ + this.table = table; + this.sourceFrame = sourceFrame; +} + +WebInspector.SourceSyntaxHighligher.prototype = { + createSpan: function(content, className) + { + var span = document.createElement("span"); + span.className = className; + span.appendChild(document.createTextNode(content)); + return span; + }, + + generateFinder: function(regex, matchNumber, className) + { + return function(str) { + var match = regex.exec(str); + if (!match) return null; - var node = createSpan(match[0], "webkit-javascript-regexp"); - previousMatchLength = match[0].length; - return node; - } + this.previousMatchLength = match[matchNumber].length; + return this.createSpan(match[matchNumber], className); + }; + }, + + process: function() + { + // Split up the work into chunks so we don't block the + // UI thread while processing. + + var i = 0; + var rows = this.table.rows; + var rowsLength = rows.length; + var previousCell = null; + const linesPerChunk = 10; - function findSingleLineRegExp(str) + function processChunk() { - var match = /^(\/(?:[^\/\\]|\\.)*\/([gim]{0,3}))(.?)/.exec(str); - if (!match || !(match[2].length > 0 || /\\|\$|\.[\?\*\+]|[^\|]\|[^\|]/.test(match[1]) || /\.|;|,/.test(match[3]))) - return null; - var node = createSpan(match[1], "webkit-javascript-regexp"); - previousMatchLength = match[1].length; - return node; + for (var end = Math.min(i + linesPerChunk, rowsLength); i < end; ++i) { + var row = rows[i]; + if (!row) + continue; + var cell = row.cells[1]; + if (!cell) + continue; + this.syntaxHighlightLine(cell, previousCell); + if (i < (end - 1)) + this.deleteContinueFlags(previousCell); + previousCell = cell; + } + + if (i >= rowsLength && processChunkInterval) { + this.deleteContinueFlags(previousCell); + delete this.previousMatchLength; + clearInterval(processChunkInterval); + + this.sourceFrame.dispatchEventToListeners("syntax highlighting complete"); + } } - function syntaxHighlightJavascriptLine(line, prevLine) - { - var messageBubble = line.lastChild; - if (messageBubble && messageBubble.nodeType === Node.ELEMENT_NODE && messageBubble.hasStyleClass("webkit-html-message-bubble")) - line.removeChild(messageBubble); - else - messageBubble = null; + var boundProcessChunk = processChunk.bind(this); + var processChunkInterval = setInterval(boundProcessChunk, 25); + boundProcessChunk(); + } +} + +WebInspector.CSSSourceSyntaxHighligher = function(table, sourceFrame) { + WebInspector.SourceSyntaxHighligher.call(this, table, sourceFrame); + + this.findNumber = this.generateFinder(/^((-?(\d+|\d*\.\d+))|^(#[a-fA-F0-9]{3,6}))(?:\D|$)/, 1, "webkit-css-number"); + this.findUnits = this.generateFinder(/^(px|em|pt|in|cm|mm|pc|ex)(?:\W|$)/, 1, "webkit-css-unit"); + this.findKeyword = this.generateFinder(/^(rgba?|hsla?|var)(?:\W|$)/, 1, "webkit-css-keyword"); + this.findSingleLineString = this.generateFinder(/^"(?:[^"\\]|\\.)*"|^'([^'\\]|\\.)*'/, 0, "webkit-css-string"); // " this quote keeps Xcode happy + this.findSingleLineComment = this.generateFinder(/^\/\*.*?\*\//, 0, "webkit-css-comment"); + this.findMultilineCommentStart = this.generateFinder(/^\/\*.*$/, 0, "webkit-css-comment"); + this.findMultilineCommentEnd = this.generateFinder(/^.*?\*\//, 0, "webkit-css-comment"); + this.findSelector = this.generateFinder(/^([#\.]?[_a-zA-Z].*?)(?:\W|$)/, 1, "webkit-css-selector"); + this.findProperty = this.generateFinder(/^(-?[_a-z0-9][_a-z0-9-]*\s*)(?:\:)/, 1, "webkit-css-property"); + this.findGenericIdent = this.generateFinder(/^([@-]?[_a-z0-9][_a-z0-9-]*)(?:\W|$)/, 1, "webkit-css-string"); +} + +WebInspector.CSSSourceSyntaxHighligher.prototype = { + deleteContinueFlags: function(cell) + { + if (!cell) + return; + delete cell._commentContinues; + delete cell._inSelector; + }, + + findPseudoClass: function(str) + { + var match = /^(::?)([_a-z0-9][_a-z0-9-]*)/.exec(str); + if (!match) + return null; + this.previousMatchLength = match[0].length; + var span = document.createElement("span"); + span.appendChild(document.createTextNode(match[1])); + span.appendChild(this.createSpan(match[2], "webkit-css-pseudo-class")); + return span; + }, + + findURL: function(str) + { + var match = /^(?:local|url)\(([^\)]*?)\)/.exec(str); + if (!match) + return null; + this.previousMatchLength = match[0].length; + var innerUrlSpan = this.createSpan(match[1], "webkit-css-url"); + var outerSpan = document.createElement("span"); + outerSpan.appendChild(this.createSpan("url", "webkit-css-keyword")); + outerSpan.appendChild(document.createTextNode("(")); + outerSpan.appendChild(innerUrlSpan); + outerSpan.appendChild(document.createTextNode(")")); + return outerSpan; + }, + + findAtRule: function(str) + { + var match = /^@[_a-z0-9][_a-z0-9-]*(?:\W|$)/.exec(str); + if (!match) + return null; + this.previousMatchLength = match[0].length; + return this.createSpan(match[0], "webkit-css-at-rule"); + }, - var code = line.textContent; + syntaxHighlightLine: function(line, prevLine) + { + var code = line.textContent; + while (line.firstChild) + line.removeChild(line.firstChild); - while (line.firstChild) - line.removeChild(line.firstChild); + var token; + var tmp = 0; + var i = 0; + this.previousMatchLength = 0; - var token; - var tmp = 0; - var i = 0; - previousMatchLength = 0; + if (prevLine) { + if (prevLine._commentContinues) { + if (!(token = this.findMultilineCommentEnd(code))) { + token = this.createSpan(code, "webkit-javascript-comment"); + line._commentContinues = true; + } + } + if (token) { + i += this.previousMatchLength ? this.previousMatchLength : code.length; + tmp = i; + line.appendChild(token); + } + } - if (prevLine) { - if (prevLine._commentContinues) { - if (!(token = findMultilineCommentEnd(code))) { - token = createSpan(code, "webkit-javascript-comment"); - line._commentContinues = true; + var inSelector = (prevLine && prevLine._inSelector); // inside a selector, we can now parse properties and values + var inAtRuleBlock = (prevLine && prevLine._inAtRuleBlock); // inside an @rule block, but not necessarily inside a selector yet + var atRuleStarted = (prevLine && prevLine._atRuleStarted); // we received an @rule, we may stop the @rule at a semicolon or open a block and become inAtRuleBlock + var atRuleIsSelector = (prevLine && prevLine._atRuleIsSelector); // when this @rule opens a block it immediately goes into parsing properties and values instead of selectors + + for ( ; i < code.length; ++i) { + var codeFragment = code.substr(i); + var prevChar = code[i - 1]; + var currChar = codeFragment[0]; + token = this.findSingleLineComment(codeFragment); + if (!token) { + if ((token = this.findMultilineCommentStart(codeFragment))) + line._commentContinues = true; + else if (currChar === ";" && !inAtRuleBlock) + atRuleStarted = false; + else if (currChar === "}") { + if (inSelector && inAtRuleBlock && atRuleIsSelector) { + inSelector = false; + inAtRuleBlock = false; + atRuleStarted = false; + } else if (inSelector) { + inSelector = false; + } else if (inAtRuleBlock) { + inAtRuleBlock = false; + atRuleStarted = false; } - } else if (prevLine._singleQuoteStringContinues) { - if (!(token = findMultilineSingleQuoteStringEnd(code))) { - token = createSpan(code, "webkit-javascript-string"); - line._singleQuoteStringContinues = true; + } else if (currChar === "{") { + if (!atRuleStarted || inAtRuleBlock) { + inSelector = true; + } else if (!inAtRuleBlock && atRuleIsSelector) { + inAtRuleBlock = true; + inSelector = true; + } else if (!inAtRuleBlock) { + inAtRuleBlock = true; + inSelector = false; } - } else if (prevLine._doubleQuoteStringContinues) { - if (!(token = findMultilineDoubleQuoteStringEnd(code))) { - token = createSpan(code, "webkit-javascript-string"); - line._doubleQuoteStringContinues = true; + } else if (inSelector) { + if (!prevChar || /^\d/.test(prevChar)) { + token = this.findUnits(codeFragment); + } else if (!prevChar || /^\W/.test(prevChar)) { + token = this.findNumber(codeFragment) || + this.findKeyword(codeFragment) || + this.findURL(codeFragment) || + this.findProperty(codeFragment) || + this.findAtRule(codeFragment) || + this.findGenericIdent(codeFragment) || + this.findSingleLineString(codeFragment); } - } else if (prevLine._regexpContinues) { - if (!(token = findMultilineRegExpEnd(code))) { - token = createSpan(code, "webkit-javascript-regexp"); - line._regexpContinues = true; + } else if (!inSelector) { + if (atRuleStarted && !inAtRuleBlock) + token = this.findURL(codeFragment); // for @import + if (!token) { + token = this.findSelector(codeFragment) || + this.findPseudoClass(codeFragment) || + this.findAtRule(codeFragment); } } - if (token) { - i += previousMatchLength ? previousMatchLength : code.length; - tmp = i; - line.appendChild(token); - } } - for ( ; i < code.length; ++i) { - var codeFragment = code.substr(i); - var prevChar = code[i - 1]; - token = findSingleLineComment(codeFragment); - if (!token) { - if ((token = findMultilineCommentStart(codeFragment))) - line._commentContinues = true; - else if (!prevChar || /^\W/.test(prevChar)) { - token = findNumber(codeFragment, code[i - 1]) || - findKeyword(codeFragment, code[i - 1]) || - findSingleLineString(codeFragment) || - findSingleLineRegExp(codeFragment); - if (!token) { - if (token = findMultilineSingleQuoteStringStart(codeFragment)) - line._singleQuoteStringContinues = true; - else if (token = findMultilineDoubleQuoteStringStart(codeFragment)) - line._doubleQuoteStringContinues = true; - else if (token = findMultilineRegExpStart(codeFragment)) - line._regexpContinues = true; - } - } - } + if (token) { + if (currChar === "@") { + atRuleStarted = true; - if (token) { - if (tmp !== i) - line.appendChild(document.createTextNode(code.substring(tmp, i))); - line.appendChild(token); - i += previousMatchLength - 1; - tmp = i + 1; + // The @font-face, @page, and @variables at-rules do not contain selectors like other at-rules + // instead it acts as a selector and contains properties and values. + var text = token.textContent; + atRuleIsSelector = /font-face/.test(text) || /page/.test(text) || /variables/.test(text); } + + if (tmp !== i) + line.appendChild(document.createTextNode(code.substring(tmp, i))); + line.appendChild(token); + i += this.previousMatchLength - 1; + tmp = i + 1; } + } - if (tmp < code.length) - line.appendChild(document.createTextNode(code.substring(tmp, i))); + line._inSelector = inSelector; + line._inAtRuleBlock = inAtRuleBlock; + line._atRuleStarted = atRuleStarted; + line._atRuleIsSelector = atRuleIsSelector; - if (messageBubble) - line.appendChild(messageBubble); - } + if (tmp < code.length) + line.appendChild(document.createTextNode(code.substring(tmp, i))); + } +} - var i = 0; - var rows = table.rows; - var rowsLength = rows.length; - var previousCell = null; - var previousMatchLength = 0; - var sourceFrame = this; +WebInspector.CSSSourceSyntaxHighligher.prototype.__proto__ = WebInspector.SourceSyntaxHighligher.prototype; + +WebInspector.JavaScriptSourceSyntaxHighlighter = function(table, sourceFrame) { + WebInspector.SourceSyntaxHighligher.call(this, table, sourceFrame); + + this.findNumber = this.generateFinder(/^(-?(\d+\.?\d*([eE][+-]\d+)?|0[xX]\h+|Infinity)|NaN)(?:\W|$)/, 1, "webkit-javascript-number"); + this.findKeyword = this.generateFinder(/^(null|true|false|break|case|catch|const|default|finally|for|instanceof|new|var|continue|function|return|void|delete|if|this|do|while|else|in|switch|throw|try|typeof|with|debugger|class|enum|export|extends|import|super|get|set)(?:\W|$)/, 1, "webkit-javascript-keyword"); + this.findSingleLineString = this.generateFinder(/^"(?:[^"\\]|\\.)*"|^'([^'\\]|\\.)*'/, 0, "webkit-javascript-string"); // " this quote keeps Xcode happy + this.findMultilineCommentStart = this.generateFinder(/^\/\*.*$/, 0, "webkit-javascript-comment"); + this.findMultilineCommentEnd = this.generateFinder(/^.*?\*\//, 0, "webkit-javascript-comment"); + this.findMultilineSingleQuoteStringStart = this.generateFinder(/^'(?:[^'\\]|\\.)*\\$/, 0, "webkit-javascript-string"); + this.findMultilineSingleQuoteStringEnd = this.generateFinder(/^(?:[^'\\]|\\.)*?'/, 0, "webkit-javascript-string"); + this.findMultilineDoubleQuoteStringStart = this.generateFinder(/^"(?:[^"\\]|\\.)*\\$/, 0, "webkit-javascript-string"); + this.findMultilineDoubleQuoteStringEnd = this.generateFinder(/^(?:[^"\\]|\\.)*?"/, 0, "webkit-javascript-string"); + this.findMultilineRegExpEnd = this.generateFinder(/^(?:[^\/\\]|\\.)*?\/([gim]{0,3})/, 0, "webkit-javascript-regexp"); + this.findSingleLineComment = this.generateFinder(/^\/\/.*|^\/\*.*?\*\//, 0, "webkit-javascript-comment"); +} - // Split up the work into chunks so we don't block the - // UI thread while processing. +WebInspector.JavaScriptSourceSyntaxHighlighter.prototype = { + deleteContinueFlags: function(cell) + { + if (!cell) + return; + delete cell._commentContinues; + delete cell._singleQuoteStringContinues; + delete cell._doubleQuoteStringContinues; + delete cell._regexpContinues; + }, - function processChunk() - { - for (var end = Math.min(i + 10, rowsLength); i < end; ++i) { - var row = rows[i]; - if (!row) - continue; - var cell = row.cells[1]; - if (!cell) - continue; - syntaxHighlightJavascriptLine(cell, previousCell); - if (i < (end - 1)) - deleteContinueFlags(previousCell); - previousCell = cell; + findMultilineRegExpStart: function(str) + { + var match = /^\/(?:[^\/\\]|\\.)*\\$/.exec(str); + if (!match || !/\\|\$|\.[\?\*\+]|[^\|]\|[^\|]/.test(match[0])) + return null; + this.previousMatchLength = match[0].length; + return this.createSpan(match[0], "webkit-javascript-regexp"); + }, + + findSingleLineRegExp: function(str) + { + var match = /^(\/(?:[^\/\\]|\\.)*\/([gim]{0,3}))(.?)/.exec(str); + if (!match || !(match[2].length > 0 || /\\|\$|\.[\?\*\+]|[^\|]\|[^\|]/.test(match[1]) || /\.|;|,/.test(match[3]))) + return null; + this.previousMatchLength = match[1].length; + return this.createSpan(match[1], "webkit-javascript-regexp"); + }, + + syntaxHighlightLine: function(line, prevLine) + { + var messageBubble = line.lastChild; + if (messageBubble && messageBubble.nodeType === Node.ELEMENT_NODE && messageBubble.hasStyleClass("webkit-html-message-bubble")) + line.removeChild(messageBubble); + else + messageBubble = null; + + var code = line.textContent; + + while (line.firstChild) + line.removeChild(line.firstChild); + + var token; + var tmp = 0; + var i = 0; + this.previousMatchLength = 0; + + if (prevLine) { + if (prevLine._commentContinues) { + if (!(token = this.findMultilineCommentEnd(code))) { + token = this.createSpan(code, "webkit-javascript-comment"); + line._commentContinues = true; + } + } else if (prevLine._singleQuoteStringContinues) { + if (!(token = this.findMultilineSingleQuoteStringEnd(code))) { + token = this.createSpan(code, "webkit-javascript-string"); + line._singleQuoteStringContinues = true; + } + } else if (prevLine._doubleQuoteStringContinues) { + if (!(token = this.findMultilineDoubleQuoteStringEnd(code))) { + token = this.createSpan(code, "webkit-javascript-string"); + line._doubleQuoteStringContinues = true; + } + } else if (prevLine._regexpContinues) { + if (!(token = this.findMultilineRegExpEnd(code))) { + token = this.createSpan(code, "webkit-javascript-regexp"); + line._regexpContinues = true; + } + } + if (token) { + i += this.previousMatchLength ? this.previousMatchLength : code.length; + tmp = i; + line.appendChild(token); } + } - if (i >= rowsLength && processChunkInterval) { - deleteContinueFlags(previousCell); - clearInterval(processChunkInterval); + for ( ; i < code.length; ++i) { + var codeFragment = code.substr(i); + var prevChar = code[i - 1]; + token = this.findSingleLineComment(codeFragment); + if (!token) { + if ((token = this.findMultilineCommentStart(codeFragment))) + line._commentContinues = true; + else if (!prevChar || /^\W/.test(prevChar)) { + token = this.findNumber(codeFragment) || + this.findKeyword(codeFragment) || + this.findSingleLineString(codeFragment) || + this.findSingleLineRegExp(codeFragment); + if (!token) { + if (token = this.findMultilineSingleQuoteStringStart(codeFragment)) + line._singleQuoteStringContinues = true; + else if (token = this.findMultilineDoubleQuoteStringStart(codeFragment)) + line._doubleQuoteStringContinues = true; + else if (token = this.findMultilineRegExpStart(codeFragment)) + line._regexpContinues = true; + } + } + } - sourceFrame.dispatchEventToListeners("syntax highlighting complete"); + if (token) { + if (tmp !== i) + line.appendChild(document.createTextNode(code.substring(tmp, i))); + line.appendChild(token); + i += this.previousMatchLength - 1; + tmp = i + 1; } } - processChunk(); + if (tmp < code.length) + line.appendChild(document.createTextNode(code.substring(tmp, i))); - var processChunkInterval = setInterval(processChunk, 25); + if (messageBubble) + line.appendChild(messageBubble); } } -WebInspector.SourceFrame.prototype.__proto__ = WebInspector.Object.prototype; +WebInspector.JavaScriptSourceSyntaxHighlighter.prototype.__proto__ = WebInspector.SourceSyntaxHighligher.prototype; |