1/* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26WebInspector.SourceFrame = function(element, addBreakpointDelegate) 27{ 28 this.messages = []; 29 this.breakpoints = []; 30 this._shortcuts = {}; 31 32 this.addBreakpointDelegate = addBreakpointDelegate; 33 34 this.element = element || document.createElement("iframe"); 35 this.element.addStyleClass("source-view-frame"); 36 this.element.setAttribute("viewsource", "true"); 37 38 this.element.addEventListener("load", this._loaded.bind(this), false); 39} 40 41WebInspector.SourceFrame.prototype = { 42 get executionLine() 43 { 44 return this._executionLine; 45 }, 46 47 set executionLine(x) 48 { 49 if (this._executionLine === x) 50 return; 51 52 var previousLine = this._executionLine; 53 this._executionLine = x; 54 55 this._updateExecutionLine(previousLine); 56 }, 57 58 get autoSizesToFitContentHeight() 59 { 60 return this._autoSizesToFitContentHeight; 61 }, 62 63 set autoSizesToFitContentHeight(x) 64 { 65 if (this._autoSizesToFitContentHeight === x) 66 return; 67 68 this._autoSizesToFitContentHeight = x; 69 70 if (this._autoSizesToFitContentHeight) { 71 this._windowResizeListener = this._windowResized.bind(this); 72 window.addEventListener("resize", this._windowResizeListener, false); 73 this.sizeToFitContentHeight(); 74 } else { 75 this.element.style.removeProperty("height"); 76 if (this.element.contentDocument) 77 this.element.contentDocument.body.removeStyleClass("webkit-height-sized-to-fit"); 78 window.removeEventListener("resize", this._windowResizeListener, false); 79 delete this._windowResizeListener; 80 } 81 }, 82 83 sourceRow: function(lineNumber) 84 { 85 if (!lineNumber || !this.element.contentDocument) 86 return; 87 88 var table = this.element.contentDocument.getElementsByTagName("table")[0]; 89 if (!table) 90 return; 91 92 var rows = table.rows; 93 94 // Line numbers are a 1-based index, but the rows collection is 0-based. 95 --lineNumber; 96 97 return rows[lineNumber]; 98 }, 99 100 lineNumberForSourceRow: function(sourceRow) 101 { 102 // Line numbers are a 1-based index, but the rows collection is 0-based. 103 var lineNumber = 0; 104 while (sourceRow) { 105 ++lineNumber; 106 sourceRow = sourceRow.previousSibling; 107 } 108 109 return lineNumber; 110 }, 111 112 revealLine: function(lineNumber) 113 { 114 if (!this._isContentLoaded()) { 115 this._lineNumberToReveal = lineNumber; 116 return; 117 } 118 119 var row = this.sourceRow(lineNumber); 120 if (row) 121 row.scrollIntoViewIfNeeded(true); 122 }, 123 124 addBreakpoint: function(breakpoint) 125 { 126 this.breakpoints.push(breakpoint); 127 breakpoint.addEventListener("enabled", this._breakpointEnableChanged, this); 128 breakpoint.addEventListener("disabled", this._breakpointEnableChanged, this); 129 this._addBreakpointToSource(breakpoint); 130 }, 131 132 removeBreakpoint: function(breakpoint) 133 { 134 this.breakpoints.remove(breakpoint); 135 breakpoint.removeEventListener("enabled", null, this); 136 breakpoint.removeEventListener("disabled", null, this); 137 this._removeBreakpointFromSource(breakpoint); 138 }, 139 140 addMessage: function(msg) 141 { 142 // Don't add the message if there is no message or valid line or if the msg isn't an error or warning. 143 if (!msg.message || msg.line <= 0 || !msg.isErrorOrWarning()) 144 return; 145 this.messages.push(msg); 146 this._addMessageToSource(msg); 147 }, 148 149 clearMessages: function() 150 { 151 this.messages = []; 152 153 if (!this.element.contentDocument) 154 return; 155 156 var bubbles = this.element.contentDocument.querySelectorAll(".webkit-html-message-bubble"); 157 if (!bubbles) 158 return; 159 160 for (var i = 0; i < bubbles.length; ++i) { 161 var bubble = bubbles[i]; 162 bubble.parentNode.removeChild(bubble); 163 } 164 }, 165 166 sizeToFitContentHeight: function() 167 { 168 if (this.element.contentDocument) { 169 this.element.style.setProperty("height", this.element.contentDocument.body.offsetHeight + "px"); 170 this.element.contentDocument.body.addStyleClass("webkit-height-sized-to-fit"); 171 } 172 }, 173 174 _highlightLineEnds: function(event) 175 { 176 event.target.parentNode.removeStyleClass("webkit-highlighted-line"); 177 }, 178 179 highlightLine: function(lineNumber) 180 { 181 if (!this._isContentLoaded()) { 182 this._lineNumberToHighlight = lineNumber; 183 return; 184 } 185 186 var sourceRow = this.sourceRow(lineNumber); 187 if (!sourceRow) 188 return; 189 var line = sourceRow.getElementsByClassName('webkit-line-content')[0]; 190 // Trick to reset the animation if the user clicks on the same link 191 // Using a timeout to avoid coalesced style updates 192 line.style.setProperty("-webkit-animation-name", "none"); 193 setTimeout(function () { 194 line.style.removeProperty("-webkit-animation-name"); 195 sourceRow.addStyleClass("webkit-highlighted-line"); 196 }, 0); 197 }, 198 199 _loaded: function() 200 { 201 WebInspector.addMainEventListeners(this.element.contentDocument); 202 this.element.contentDocument.addEventListener("mousedown", this._documentMouseDown.bind(this), true); 203 this.element.contentDocument.addEventListener("keydown", this._documentKeyDown.bind(this), true); 204 this.element.contentDocument.addEventListener("keyup", WebInspector.documentKeyUp.bind(WebInspector), true); 205 this.element.contentDocument.addEventListener("webkitAnimationEnd", this._highlightLineEnds.bind(this), false); 206 207 // Register 'eval' shortcut. 208 var isMac = InspectorController.platform().indexOf("mac-") === 0; 209 var platformSpecificModifier = isMac ? WebInspector.KeyboardShortcut.Modifiers.Meta : WebInspector.KeyboardShortcut.Modifiers.Ctrl; 210 var shortcut = WebInspector.KeyboardShortcut.makeKey(69 /* 'E' */, platformSpecificModifier | WebInspector.KeyboardShortcut.Modifiers.Shift); 211 this._shortcuts[shortcut] = this._evalSelectionInCallFrame.bind(this); 212 213 var headElement = this.element.contentDocument.getElementsByTagName("head")[0]; 214 if (!headElement) { 215 headElement = this.element.contentDocument.createElement("head"); 216 this.element.contentDocument.documentElement.insertBefore(headElement, this.element.contentDocument.documentElement.firstChild); 217 } 218 219 var styleElement = this.element.contentDocument.createElement("style"); 220 headElement.appendChild(styleElement); 221 222 // Add these style rules here since they are specific to the Inspector. They also behave oddly and not 223 // all properties apply if added to view-source.css (becuase it is a user agent sheet.) 224 var styleText = ".webkit-line-number { background-repeat: no-repeat; background-position: right 1px; }\n"; 225 styleText += ".webkit-breakpoint .webkit-line-number { color: white; background-image: -webkit-canvas(breakpoint); }\n"; 226 styleText += ".webkit-breakpoint-disabled .webkit-line-number { color: white; background-image: -webkit-canvas(breakpoint-disabled); }\n"; 227 styleText += ".webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(program-counter); }\n"; 228 styleText += ".webkit-breakpoint.webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(breakpoint-program-counter); }\n"; 229 styleText += ".webkit-breakpoint-disabled.webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(breakpoint-disabled-program-counter); }\n"; 230 styleText += ".webkit-execution-line .webkit-line-content { background-color: rgb(171, 191, 254); outline: 1px solid rgb(64, 115, 244); }\n"; 231 styleText += ".webkit-height-sized-to-fit { overflow-y: hidden }\n"; 232 styleText += ".webkit-line-content { background-color: white; }\n"; 233 styleText += "@-webkit-keyframes fadeout {from {background-color: rgb(255, 255, 120);} to { background-color: white;}}\n"; 234 styleText += ".webkit-highlighted-line .webkit-line-content { background-color: rgb(255, 255, 120); -webkit-animation: 'fadeout' 2s 500ms}\n"; 235 styleText += ".webkit-javascript-comment { color: rgb(0, 116, 0); }\n"; 236 styleText += ".webkit-javascript-keyword { color: rgb(170, 13, 145); }\n"; 237 styleText += ".webkit-javascript-number { color: rgb(28, 0, 207); }\n"; 238 styleText += ".webkit-javascript-string, .webkit-javascript-regexp { color: rgb(196, 26, 22); }\n"; 239 240 styleElement.textContent = styleText; 241 242 this._needsProgramCounterImage = true; 243 this._needsBreakpointImages = true; 244 245 this.element.contentWindow.Element.prototype.addStyleClass = Element.prototype.addStyleClass; 246 this.element.contentWindow.Element.prototype.removeStyleClass = Element.prototype.removeStyleClass; 247 this.element.contentWindow.Element.prototype.removeMatchingStyleClasses = Element.prototype.removeMatchingStyleClasses; 248 this.element.contentWindow.Element.prototype.hasStyleClass = Element.prototype.hasStyleClass; 249 this.element.contentWindow.Node.prototype.enclosingNodeOrSelfWithNodeName = Node.prototype.enclosingNodeOrSelfWithNodeName; 250 this.element.contentWindow.Node.prototype.enclosingNodeOrSelfWithNodeNameInArray = Node.prototype.enclosingNodeOrSelfWithNodeNameInArray; 251 252 this._addExistingMessagesToSource(); 253 this._addExistingBreakpointsToSource(); 254 this._updateExecutionLine(); 255 if (this._executionLine) 256 this.revealLine(this._executionLine); 257 258 if (this.autoSizesToFitContentHeight) 259 this.sizeToFitContentHeight(); 260 261 if (this._lineNumberToReveal) { 262 this.revealLine(this._lineNumberToReveal); 263 delete this._lineNumberToReveal; 264 } 265 266 if (this._lineNumberToHighlight) { 267 this.highlightLine(this._lineNumberToHighlight); 268 delete this._lineNumberToHighlight; 269 } 270 271 this.dispatchEventToListeners("content loaded"); 272 }, 273 274 _isContentLoaded: function() { 275 var doc = this.element.contentDocument; 276 return doc && doc.getElementsByTagName("table")[0]; 277 }, 278 279 _windowResized: function(event) 280 { 281 if (!this._autoSizesToFitContentHeight) 282 return; 283 this.sizeToFitContentHeight(); 284 }, 285 286 _documentMouseDown: function(event) 287 { 288 if (!event.target.hasStyleClass("webkit-line-number")) 289 return; 290 291 var sourceRow = event.target.enclosingNodeOrSelfWithNodeName("tr"); 292 if (sourceRow._breakpointObject) 293 sourceRow._breakpointObject.enabled = !sourceRow._breakpointObject.enabled; 294 else if (this.addBreakpointDelegate) 295 this.addBreakpointDelegate(this.lineNumberForSourceRow(sourceRow)); 296 }, 297 298 _documentKeyDown: function(event) 299 { 300 var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event); 301 var handler = this._shortcuts[shortcut]; 302 if (handler) { 303 handler(event); 304 event.preventDefault(); 305 } else { 306 WebInspector.documentKeyDown(event); 307 } 308 }, 309 310 _evalSelectionInCallFrame: function(event) 311 { 312 if (!WebInspector.panels.scripts || !WebInspector.panels.scripts.paused) 313 return; 314 315 var selection = this.element.contentWindow.getSelection(); 316 if (!selection.rangeCount) 317 return; 318 319 var expression = selection.getRangeAt(0).toString().trimWhitespace(); 320 WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression, false, function(result, exception) { 321 WebInspector.showConsole(); 322 var commandMessage = new WebInspector.ConsoleCommand(expression); 323 WebInspector.console.addMessage(commandMessage); 324 WebInspector.console.addMessage(new WebInspector.ConsoleCommandResult(result, exception, commandMessage)); 325 }); 326 }, 327 328 _breakpointEnableChanged: function(event) 329 { 330 var breakpoint = event.target; 331 var sourceRow = this.sourceRow(breakpoint.line); 332 if (!sourceRow) 333 return; 334 335 sourceRow.addStyleClass("webkit-breakpoint"); 336 337 if (breakpoint.enabled) 338 sourceRow.removeStyleClass("webkit-breakpoint-disabled"); 339 else 340 sourceRow.addStyleClass("webkit-breakpoint-disabled"); 341 }, 342 343 _updateExecutionLine: function(previousLine) 344 { 345 if (previousLine) { 346 var sourceRow = this.sourceRow(previousLine); 347 if (sourceRow) 348 sourceRow.removeStyleClass("webkit-execution-line"); 349 } 350 351 if (!this._executionLine) 352 return; 353 354 this._drawProgramCounterImageIfNeeded(); 355 356 var sourceRow = this.sourceRow(this._executionLine); 357 if (sourceRow) 358 sourceRow.addStyleClass("webkit-execution-line"); 359 }, 360 361 _addExistingBreakpointsToSource: function() 362 { 363 var length = this.breakpoints.length; 364 for (var i = 0; i < length; ++i) 365 this._addBreakpointToSource(this.breakpoints[i]); 366 }, 367 368 _addBreakpointToSource: function(breakpoint) 369 { 370 var sourceRow = this.sourceRow(breakpoint.line); 371 if (!sourceRow) 372 return; 373 374 breakpoint.sourceText = sourceRow.getElementsByClassName('webkit-line-content')[0].textContent; 375 376 this._drawBreakpointImagesIfNeeded(); 377 378 sourceRow._breakpointObject = breakpoint; 379 380 sourceRow.addStyleClass("webkit-breakpoint"); 381 if (!breakpoint.enabled) 382 sourceRow.addStyleClass("webkit-breakpoint-disabled"); 383 }, 384 385 _removeBreakpointFromSource: function(breakpoint) 386 { 387 var sourceRow = this.sourceRow(breakpoint.line); 388 if (!sourceRow) 389 return; 390 391 delete sourceRow._breakpointObject; 392 393 sourceRow.removeStyleClass("webkit-breakpoint"); 394 sourceRow.removeStyleClass("webkit-breakpoint-disabled"); 395 }, 396 397 _incrementMessageRepeatCount: function(msg, repeatDelta) 398 { 399 if (!msg._resourceMessageLineElement) 400 return; 401 402 if (!msg._resourceMessageRepeatCountElement) { 403 var repeatedElement = document.createElement("span"); 404 msg._resourceMessageLineElement.appendChild(repeatedElement); 405 msg._resourceMessageRepeatCountElement = repeatedElement; 406 } 407 408 msg.repeatCount += repeatDelta; 409 msg._resourceMessageRepeatCountElement.textContent = WebInspector.UIString(" (repeated %d times)", msg.repeatCount); 410 }, 411 412 _addExistingMessagesToSource: function() 413 { 414 var length = this.messages.length; 415 for (var i = 0; i < length; ++i) 416 this._addMessageToSource(this.messages[i]); 417 }, 418 419 _addMessageToSource: function(msg) 420 { 421 var row = this.sourceRow(msg.line); 422 if (!row) 423 return; 424 425 var cell = row.cells[1]; 426 if (!cell) 427 return; 428 429 var messageBubbleElement = cell.lastChild; 430 if (!messageBubbleElement || messageBubbleElement.nodeType !== Node.ELEMENT_NODE || !messageBubbleElement.hasStyleClass("webkit-html-message-bubble")) { 431 messageBubbleElement = this.element.contentDocument.createElement("div"); 432 messageBubbleElement.className = "webkit-html-message-bubble"; 433 cell.appendChild(messageBubbleElement); 434 } 435 436 if (!row.messages) 437 row.messages = []; 438 439 for (var i = 0; i < row.messages.length; ++i) { 440 if (row.messages[i].isEqual(msg, true)) { 441 this._incrementMessageRepeatCount(row.messages[i], msg.repeatDelta); 442 return; 443 } 444 } 445 446 row.messages.push(msg); 447 448 var imageURL; 449 switch (msg.level) { 450 case WebInspector.ConsoleMessage.MessageLevel.Error: 451 messageBubbleElement.addStyleClass("webkit-html-error-message"); 452 imageURL = "Images/errorIcon.png"; 453 break; 454 case WebInspector.ConsoleMessage.MessageLevel.Warning: 455 messageBubbleElement.addStyleClass("webkit-html-warning-message"); 456 imageURL = "Images/warningIcon.png"; 457 break; 458 } 459 460 var messageLineElement = this.element.contentDocument.createElement("div"); 461 messageLineElement.className = "webkit-html-message-line"; 462 messageBubbleElement.appendChild(messageLineElement); 463 464 // Create the image element in the Inspector's document so we can use relative image URLs. 465 var image = document.createElement("img"); 466 image.src = imageURL; 467 image.className = "webkit-html-message-icon"; 468 469 // Adopt the image element since it wasn't created in element's contentDocument. 470 image = this.element.contentDocument.adoptNode(image); 471 messageLineElement.appendChild(image); 472 messageLineElement.appendChild(this.element.contentDocument.createTextNode(msg.message)); 473 474 msg._resourceMessageLineElement = messageLineElement; 475 }, 476 477 _drawProgramCounterInContext: function(ctx, glow) 478 { 479 if (glow) 480 ctx.save(); 481 482 ctx.beginPath(); 483 ctx.moveTo(17, 2); 484 ctx.lineTo(19, 2); 485 ctx.lineTo(19, 0); 486 ctx.lineTo(21, 0); 487 ctx.lineTo(26, 5.5); 488 ctx.lineTo(21, 11); 489 ctx.lineTo(19, 11); 490 ctx.lineTo(19, 9); 491 ctx.lineTo(17, 9); 492 ctx.closePath(); 493 ctx.fillStyle = "rgb(142, 5, 4)"; 494 495 if (glow) { 496 ctx.shadowBlur = 4; 497 ctx.shadowColor = "rgb(255, 255, 255)"; 498 ctx.shadowOffsetX = -1; 499 ctx.shadowOffsetY = 0; 500 } 501 502 ctx.fill(); 503 ctx.fill(); // Fill twice to get a good shadow and darker anti-aliased pixels. 504 505 if (glow) 506 ctx.restore(); 507 }, 508 509 _drawProgramCounterImageIfNeeded: function() 510 { 511 if (!this._needsProgramCounterImage || !this.element.contentDocument) 512 return; 513 514 var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "program-counter", 26, 11); 515 ctx.clearRect(0, 0, 26, 11); 516 this._drawProgramCounterInContext(ctx, true); 517 518 delete this._needsProgramCounterImage; 519 }, 520 521 _drawBreakpointImagesIfNeeded: function() 522 { 523 if (!this._needsBreakpointImages || !this.element.contentDocument) 524 return; 525 526 function drawBreakpoint(ctx, disabled) 527 { 528 ctx.beginPath(); 529 ctx.moveTo(0, 2); 530 ctx.lineTo(2, 0); 531 ctx.lineTo(21, 0); 532 ctx.lineTo(26, 5.5); 533 ctx.lineTo(21, 11); 534 ctx.lineTo(2, 11); 535 ctx.lineTo(0, 9); 536 ctx.closePath(); 537 ctx.fillStyle = "rgb(1, 142, 217)"; 538 ctx.strokeStyle = "rgb(0, 103, 205)"; 539 ctx.lineWidth = 3; 540 ctx.fill(); 541 ctx.save(); 542 ctx.clip(); 543 ctx.stroke(); 544 ctx.restore(); 545 546 if (!disabled) 547 return; 548 549 ctx.save(); 550 ctx.globalCompositeOperation = "destination-out"; 551 ctx.fillStyle = "rgba(0, 0, 0, 0.5)"; 552 ctx.fillRect(0, 0, 26, 11); 553 ctx.restore(); 554 } 555 556 var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint", 26, 11); 557 ctx.clearRect(0, 0, 26, 11); 558 drawBreakpoint(ctx); 559 560 var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-program-counter", 26, 11); 561 ctx.clearRect(0, 0, 26, 11); 562 drawBreakpoint(ctx); 563 ctx.clearRect(20, 0, 6, 11); 564 this._drawProgramCounterInContext(ctx, true); 565 566 var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-disabled", 26, 11); 567 ctx.clearRect(0, 0, 26, 11); 568 drawBreakpoint(ctx, true); 569 570 var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-disabled-program-counter", 26, 11); 571 ctx.clearRect(0, 0, 26, 11); 572 drawBreakpoint(ctx, true); 573 ctx.clearRect(20, 0, 6, 11); 574 this._drawProgramCounterInContext(ctx, true); 575 576 delete this._needsBreakpointImages; 577 }, 578 579 syntaxHighlightJavascript: function() 580 { 581 var table = this.element.contentDocument.getElementsByTagName("table")[0]; 582 if (!table) 583 return; 584 585 function deleteContinueFlags(cell) 586 { 587 if (!cell) 588 return; 589 delete cell._commentContinues; 590 delete cell._singleQuoteStringContinues; 591 delete cell._doubleQuoteStringContinues; 592 delete cell._regexpContinues; 593 } 594 595 function createSpan(content, className) 596 { 597 var span = document.createElement("span"); 598 span.className = className; 599 span.appendChild(document.createTextNode(content)); 600 return span; 601 } 602 603 function generateFinder(regex, matchNumber, className) 604 { 605 return function(str) { 606 var match = regex.exec(str); 607 if (!match) 608 return null; 609 previousMatchLength = match[matchNumber].length; 610 return createSpan(match[matchNumber], className); 611 }; 612 } 613 614 var findNumber = generateFinder(/^(-?(\d+\.?\d*([eE][+-]\d+)?|0[xX]\h+|Infinity)|NaN)(?:\W|$)/, 1, "webkit-javascript-number"); 615 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"); 616 var findSingleLineString = generateFinder(/^"(?:[^"\\]|\\.)*"|^'([^'\\]|\\.)*'/, 0, "webkit-javascript-string"); // " this quote keeps Xcode happy 617 var findMultilineCommentStart = generateFinder(/^\/\*.*$/, 0, "webkit-javascript-comment"); 618 var findMultilineCommentEnd = generateFinder(/^.*?\*\//, 0, "webkit-javascript-comment"); 619 var findMultilineSingleQuoteStringStart = generateFinder(/^'(?:[^'\\]|\\.)*\\$/, 0, "webkit-javascript-string"); 620 var findMultilineSingleQuoteStringEnd = generateFinder(/^(?:[^'\\]|\\.)*?'/, 0, "webkit-javascript-string"); 621 var findMultilineDoubleQuoteStringStart = generateFinder(/^"(?:[^"\\]|\\.)*\\$/, 0, "webkit-javascript-string"); 622 var findMultilineDoubleQuoteStringEnd = generateFinder(/^(?:[^"\\]|\\.)*?"/, 0, "webkit-javascript-string"); 623 var findMultilineRegExpEnd = generateFinder(/^(?:[^\/\\]|\\.)*?\/([gim]{0,3})/, 0, "webkit-javascript-regexp"); 624 var findSingleLineComment = generateFinder(/^\/\/.*|^\/\*.*?\*\//, 0, "webkit-javascript-comment"); 625 626 function findMultilineRegExpStart(str) 627 { 628 var match = /^\/(?:[^\/\\]|\\.)*\\$/.exec(str); 629 if (!match || !/\\|\$|\.[\?\*\+]|[^\|]\|[^\|]/.test(match[0])) 630 return null; 631 var node = createSpan(match[0], "webkit-javascript-regexp"); 632 previousMatchLength = match[0].length; 633 return node; 634 } 635 636 function findSingleLineRegExp(str) 637 { 638 var match = /^(\/(?:[^\/\\]|\\.)*\/([gim]{0,3}))(.?)/.exec(str); 639 if (!match || !(match[2].length > 0 || /\\|\$|\.[\?\*\+]|[^\|]\|[^\|]/.test(match[1]) || /\.|;|,/.test(match[3]))) 640 return null; 641 var node = createSpan(match[1], "webkit-javascript-regexp"); 642 previousMatchLength = match[1].length; 643 return node; 644 } 645 646 function syntaxHighlightJavascriptLine(line, prevLine) 647 { 648 var messageBubble = line.lastChild; 649 if (messageBubble && messageBubble.nodeType === Node.ELEMENT_NODE && messageBubble.hasStyleClass("webkit-html-message-bubble")) 650 line.removeChild(messageBubble); 651 else 652 messageBubble = null; 653 654 var code = line.textContent; 655 656 while (line.firstChild) 657 line.removeChild(line.firstChild); 658 659 var token; 660 var tmp = 0; 661 var i = 0; 662 previousMatchLength = 0; 663 664 if (prevLine) { 665 if (prevLine._commentContinues) { 666 if (!(token = findMultilineCommentEnd(code))) { 667 token = createSpan(code, "webkit-javascript-comment"); 668 line._commentContinues = true; 669 } 670 } else if (prevLine._singleQuoteStringContinues) { 671 if (!(token = findMultilineSingleQuoteStringEnd(code))) { 672 token = createSpan(code, "webkit-javascript-string"); 673 line._singleQuoteStringContinues = true; 674 } 675 } else if (prevLine._doubleQuoteStringContinues) { 676 if (!(token = findMultilineDoubleQuoteStringEnd(code))) { 677 token = createSpan(code, "webkit-javascript-string"); 678 line._doubleQuoteStringContinues = true; 679 } 680 } else if (prevLine._regexpContinues) { 681 if (!(token = findMultilineRegExpEnd(code))) { 682 token = createSpan(code, "webkit-javascript-regexp"); 683 line._regexpContinues = true; 684 } 685 } 686 if (token) { 687 i += previousMatchLength ? previousMatchLength : code.length; 688 tmp = i; 689 line.appendChild(token); 690 } 691 } 692 693 for ( ; i < code.length; ++i) { 694 var codeFragment = code.substr(i); 695 var prevChar = code[i - 1]; 696 token = findSingleLineComment(codeFragment); 697 if (!token) { 698 if ((token = findMultilineCommentStart(codeFragment))) 699 line._commentContinues = true; 700 else if (!prevChar || /^\W/.test(prevChar)) { 701 token = findNumber(codeFragment, code[i - 1]) || 702 findKeyword(codeFragment, code[i - 1]) || 703 findSingleLineString(codeFragment) || 704 findSingleLineRegExp(codeFragment); 705 if (!token) { 706 if (token = findMultilineSingleQuoteStringStart(codeFragment)) 707 line._singleQuoteStringContinues = true; 708 else if (token = findMultilineDoubleQuoteStringStart(codeFragment)) 709 line._doubleQuoteStringContinues = true; 710 else if (token = findMultilineRegExpStart(codeFragment)) 711 line._regexpContinues = true; 712 } 713 } 714 } 715 716 if (token) { 717 if (tmp !== i) 718 line.appendChild(document.createTextNode(code.substring(tmp, i))); 719 line.appendChild(token); 720 i += previousMatchLength - 1; 721 tmp = i + 1; 722 } 723 } 724 725 if (tmp < code.length) 726 line.appendChild(document.createTextNode(code.substring(tmp, i))); 727 728 if (messageBubble) 729 line.appendChild(messageBubble); 730 } 731 732 var i = 0; 733 var rows = table.rows; 734 var rowsLength = rows.length; 735 var previousCell = null; 736 var previousMatchLength = 0; 737 var sourceFrame = this; 738 739 // Split up the work into chunks so we don't block the 740 // UI thread while processing. 741 742 function processChunk() 743 { 744 for (var end = Math.min(i + 10, rowsLength); i < end; ++i) { 745 var row = rows[i]; 746 if (!row) 747 continue; 748 var cell = row.cells[1]; 749 if (!cell) 750 continue; 751 syntaxHighlightJavascriptLine(cell, previousCell); 752 if (i < (end - 1)) 753 deleteContinueFlags(previousCell); 754 previousCell = cell; 755 } 756 757 if (i >= rowsLength && processChunkInterval) { 758 deleteContinueFlags(previousCell); 759 clearInterval(processChunkInterval); 760 761 sourceFrame.dispatchEventToListeners("syntax highlighting complete"); 762 } 763 } 764 765 processChunk(); 766 767 var processChunkInterval = setInterval(processChunk, 25); 768 } 769} 770 771WebInspector.SourceFrame.prototype.__proto__ = WebInspector.Object.prototype; 772