1<!-- 2 Copyright (C) 2012 Google 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 8 1. Redistributions of source code must retain the above copyright 9 notice, this list of conditions and the following disclaimer. 10 2. Redistributions in binary form must reproduce the above copyright 11 notice, this list of conditions and the following disclaimer in the 12 documentation and/or other materials provided with the distribution. 13 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 its contributors may be used to endorse or promote products derived 15 from this software without specific prior written permission. 16 17 THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27--> 28<!DOCTYPE html> 29<html> 30<head> 31<style> 32body { 33 margin: 0; 34 padding: 0; 35} 36 37body.platform-mac { 38 font-size: 11px; 39 font-family: Menlo, Monaco; 40} 41 42body.platform-windows { 43 font-size: 12px; 44 font-family: Consolas, Lucida Console; 45} 46 47body.platform-linux { 48 font-size: 11px; 49 font-family: dejavu sans mono; 50} 51 52.fill { 53 position: absolute; 54 top: 0; 55 right: 0; 56 bottom: 0; 57 left: 0; 58} 59 60.dimmed { 61 background-color: rgba(0, 0, 0, 0.31); 62} 63 64.message-line { 65 margin: 10px 0; 66 text-align: center; 67} 68 69.message-box { 70 background-color: rgb(255, 255, 194); 71 border: 1px solid rgb(128, 128, 128); 72 display: inline-block; 73 padding: 2px 4px; 74} 75 76.px { 77 color: rgb(128, 128, 128); 78} 79 80#element-title { 81 position: absolute; 82 z-index: 10; 83} 84 85#tag-name { 86 /* Keep this in sync with view-source.css (.webkit-html-tag-name) */ 87 color: rgb(136, 18, 128); 88} 89 90#node-id { 91 /* Keep this in sync with view-source.css (.webkit-html-attribute-value) */ 92 color: rgb(26, 26, 166); 93} 94 95#class-name { 96 /* Keep this in sync with view-source.css (.webkit-html-attribute-name) */ 97 color: rgb(153, 69, 0); 98} 99 100#right-gutter { 101 display: none; 102 right: 0; 103 top: 0; 104 bottom: 0; 105 position: absolute; 106 background-color: darkgray; 107} 108 109#bottom-gutter { 110 display: none; 111 left: 0; 112 right: 0; 113 bottom: 0; 114 position: absolute; 115 background-color: darkgray; 116} 117</style> 118<script> 119const lightGridColor = "rgba(0,0,0,0.2)"; 120const darkGridColor = "rgba(0,0,0,0.5)"; 121const transparentColor = "rgba(0, 0, 0, 0)"; 122const gridBackgroundColor = "rgba(255, 255, 255, 0.6)"; 123 124function drawPausedInDebuggerMessage(message) 125{ 126 var pausedInDebugger = document.getElementById("paused-in-debugger"); 127 pausedInDebugger.textContent = message; 128 pausedInDebugger.style.visibility = "visible"; 129 document.body.classList.add("dimmed"); 130} 131 132function _drawGrid(highlight, rulerAtRight, rulerAtBottom) 133{ 134 if (!highlight.showRulers) 135 return; 136 context.save(); 137 138 var width = canvas.width; 139 var height = canvas.height; 140 141 const gridSubStep = 5; 142 const gridStep = 50; 143 144 { 145 // Draw X grid background 146 context.save(); 147 context.fillStyle = gridBackgroundColor; 148 if (rulerAtBottom) 149 context.fillRect(0, height - 15, width, height); 150 else 151 context.fillRect(0, 0, width, 15); 152 153 // Clip out backgrounds intersection 154 context.globalCompositeOperation = "destination-out"; 155 context.fillStyle = "red"; 156 if (rulerAtRight) 157 context.fillRect(width - 15, 0, width, height); 158 else 159 context.fillRect(0, 0, 15, height); 160 context.restore(); 161 162 // Draw Y grid background 163 context.fillStyle = gridBackgroundColor; 164 if (rulerAtRight) 165 context.fillRect(width - 15, 0, width, height); 166 else 167 context.fillRect(0, 0, 15, height); 168 } 169 170 context.lineWidth = 1; 171 context.strokeStyle = darkGridColor; 172 context.fillStyle = darkGridColor; 173 { 174 // Draw labels. 175 context.save(); 176 context.translate(-highlight.scrollX, 0.5 - highlight.scrollY); 177 for (var y = 2 * gridStep; y < height + highlight.scrollY; y += 2 * gridStep) { 178 context.save(); 179 context.translate(highlight.scrollX, y); 180 context.rotate(-Math.PI / 2); 181 context.fillText(y, 2, rulerAtRight ? width - 7 : 13); 182 context.restore(); 183 } 184 context.translate(0.5, -0.5); 185 for (var x = 2 * gridStep; x < width + highlight.scrollX; x += 2 * gridStep) { 186 context.save(); 187 context.fillText(x, x + 2, rulerAtBottom ? highlight.scrollY + height - 7 : highlight.scrollY + 13); 188 context.restore(); 189 } 190 context.restore(); 191 } 192 193 { 194 // Draw vertical grid 195 context.save(); 196 if (rulerAtRight) { 197 context.translate(width, 0); 198 context.scale(-1, 1); 199 } 200 context.translate(-highlight.scrollX, 0.5 - highlight.scrollY); 201 for (var y = gridStep; y < height + highlight.scrollY; y += gridStep) { 202 context.beginPath(); 203 context.moveTo(highlight.scrollX, y); 204 var markLength = (y % (gridStep * 2)) ? 5 : 8; 205 context.lineTo(highlight.scrollX + markLength, y); 206 context.stroke(); 207 } 208 context.strokeStyle = lightGridColor; 209 for (var y = gridSubStep; y < highlight.scrollY + height; y += gridSubStep) { 210 if (!(y % gridStep)) 211 continue; 212 context.beginPath(); 213 context.moveTo(highlight.scrollX, y); 214 context.lineTo(highlight.scrollX + gridSubStep, y); 215 context.stroke(); 216 } 217 context.restore(); 218 } 219 220 { 221 // Draw horizontal grid 222 context.save(); 223 if (rulerAtBottom) { 224 context.translate(0, height); 225 context.scale(1, -1); 226 } 227 context.translate(0.5 - highlight.scrollX, -highlight.scrollY); 228 for (var x = gridStep; x < width + highlight.scrollX; x += gridStep) { 229 context.beginPath(); 230 context.moveTo(x, highlight.scrollY); 231 var markLength = (x % (gridStep * 2)) ? 5 : 8; 232 context.lineTo(x, highlight.scrollY + markLength); 233 context.stroke(); 234 } 235 context.strokeStyle = lightGridColor; 236 for (var x = gridSubStep; x < highlight.scrollX + width; x += gridSubStep) { 237 if (!(x % gridStep)) 238 continue; 239 context.beginPath(); 240 context.moveTo(x, highlight.scrollY); 241 context.lineTo(x, highlight.scrollY + gridSubStep); 242 context.stroke(); 243 } 244 context.restore(); 245 } 246 247 context.restore(); 248} 249 250function quadToPath(quad) 251{ 252 context.beginPath(); 253 context.moveTo(quad[0].x, quad[0].y); 254 context.lineTo(quad[1].x, quad[1].y); 255 context.lineTo(quad[2].x, quad[2].y); 256 context.lineTo(quad[3].x, quad[3].y); 257 context.closePath(); 258 return context; 259} 260 261function drawOutlinedQuad(quad, fillColor, outlineColor) 262{ 263 context.save(); 264 context.lineWidth = 2; 265 quadToPath(quad).clip(); 266 context.fillStyle = fillColor; 267 context.fill(); 268 if (outlineColor) { 269 context.strokeStyle = outlineColor; 270 context.stroke(); 271 } 272 context.restore(); 273} 274 275function drawOutlinedQuadWithClip(quad, clipQuad, fillColor) 276{ 277 var canvas = document.getElementById("canvas"); 278 context.fillStyle = fillColor; 279 context.save(); 280 context.lineWidth = 0; 281 quadToPath(quad).fill(); 282 context.globalCompositeOperation = "destination-out"; 283 context.fillStyle = "red"; 284 quadToPath(clipQuad).fill(); 285 context.restore(); 286} 287 288function quadEquals(quad1, quad2) 289{ 290 return quad1[0].x === quad2[0].x && quad1[0].y === quad2[0].y && 291 quad1[1].x === quad2[1].x && quad1[1].y === quad2[1].y && 292 quad1[2].x === quad2[2].x && quad1[2].y === quad2[2].y && 293 quad1[3].x === quad2[3].x && quad1[3].y === quad2[3].y; 294} 295 296function drawGutter() 297{ 298 var frameWidth = frameViewFullSize.width; 299 var frameHeight = frameViewFullSize.height; 300 301 if (!frameWidth || document.body.offsetWidth <= frameWidth) 302 rightGutter.style.removeProperty("display"); 303 else { 304 rightGutter.style.display = "block"; 305 rightGutter.style.left = frameWidth + "px"; 306 } 307 308 if (!frameHeight || document.body.offsetHeight <= frameHeight) 309 bottomGutter.style.removeProperty("display"); 310 else { 311 bottomGutter.style.display = "block"; 312 bottomGutter.style.top = frameHeight + "px"; 313 } 314} 315 316function reset(resetData) 317{ 318 var deviceScaleFactor = resetData.deviceScaleFactor; 319 var viewportSize = resetData.viewportSize; 320 window.frameViewFullSize = resetData.frameViewFullSize; 321 322 window.canvas = document.getElementById("canvas"); 323 window.context = canvas.getContext("2d"); 324 window.rightGutter = document.getElementById("right-gutter"); 325 window.bottomGutter = document.getElementById("bottom-gutter"); 326 327 canvas.width = deviceScaleFactor * viewportSize.width; 328 canvas.height = deviceScaleFactor * viewportSize.height; 329 canvas.style.width = viewportSize.width + "px"; 330 canvas.style.height = viewportSize.height + "px"; 331 context.scale(deviceScaleFactor, deviceScaleFactor); 332 333 document.getElementById("paused-in-debugger").style.visibility = "hidden"; 334 document.getElementById("element-title").style.visibility = "hidden"; 335 document.body.classList.remove("dimmed"); 336} 337 338function _drawElementTitle(highlight) 339{ 340 var elementInfo = highlight.elementInfo; 341 if (!highlight.elementInfo) 342 return; 343 344 document.getElementById("tag-name").textContent = elementInfo.tagName; 345 document.getElementById("node-id").textContent = elementInfo.idValue ? "#" + elementInfo.idValue : ""; 346 var className = elementInfo.className; 347 if (className && className.length > 50) 348 className = className.substring(0, 50) + "\u2026"; 349 document.getElementById("class-name").textContent = className || ""; 350 document.getElementById("node-width").textContent = elementInfo.nodeWidth; 351 document.getElementById("node-height").textContent = elementInfo.nodeHeight; 352 var elementTitle = document.getElementById("element-title"); 353 354 var marginQuad = highlight.quads[0]; 355 356 var titleWidth = elementTitle.offsetWidth + 6; 357 var titleHeight = elementTitle.offsetHeight + 4; 358 359 var anchorTop = marginQuad[0].y; 360 var anchorBottom = marginQuad[3].y 361 362 const arrowHeight = 7; 363 var renderArrowUp = false; 364 var renderArrowDown = false; 365 366 var boxX = Math.max(2, marginQuad[0].x); 367 if (boxX + titleWidth > canvas.width) 368 boxX = canvas.width - titleWidth - 2; 369 370 var boxY; 371 if (anchorTop > canvas.height) { 372 boxY = canvas.height - titleHeight - arrowHeight; 373 renderArrowDown = true; 374 } else if (anchorBottom < 0) { 375 boxY = arrowHeight; 376 renderArrowUp = true; 377 } else if (anchorBottom + titleHeight + arrowHeight < canvas.height) { 378 boxY = anchorBottom + arrowHeight - 4; 379 renderArrowUp = true; 380 } else if (anchorTop - titleHeight - arrowHeight > 0) { 381 boxY = anchorTop - titleHeight - arrowHeight + 3; 382 renderArrowDown = true; 383 } else 384 boxY = arrowHeight; 385 386 context.save(); 387 context.translate(0.5, 0.5); 388 context.beginPath(); 389 context.moveTo(boxX, boxY); 390 if (renderArrowUp) { 391 context.lineTo(boxX + 2 * arrowHeight, boxY); 392 context.lineTo(boxX + 3 * arrowHeight, boxY - arrowHeight); 393 context.lineTo(boxX + 4 * arrowHeight, boxY); 394 } 395 context.lineTo(boxX + titleWidth, boxY); 396 context.lineTo(boxX + titleWidth, boxY + titleHeight); 397 if (renderArrowDown) { 398 context.lineTo(boxX + 4 * arrowHeight, boxY + titleHeight); 399 context.lineTo(boxX + 3 * arrowHeight, boxY + titleHeight + arrowHeight); 400 context.lineTo(boxX + 2 * arrowHeight, boxY + titleHeight); 401 } 402 context.lineTo(boxX, boxY + titleHeight); 403 context.closePath(); 404 context.fillStyle = "rgb(255, 255, 194)"; 405 context.fill(); 406 context.strokeStyle = "rgb(128, 128, 128)"; 407 context.stroke(); 408 409 context.restore(); 410 411 elementTitle.style.visibility = "visible"; 412 elementTitle.style.top = (boxY + 3) + "px"; 413 elementTitle.style.left = (boxX + 3) + "px"; 414} 415 416function _drawRulers(highlight, rulerAtRight, rulerAtBottom) 417{ 418 if (!highlight.showRulers) 419 return; 420 context.save(); 421 var width = canvas.width; 422 var height = canvas.height; 423 context.strokeStyle = "rgba(128, 128, 128, 0.3)"; 424 context.lineWidth = 1; 425 context.translate(0.5, 0.5); 426 var leftmostXForY = {}; 427 var rightmostXForY = {}; 428 var topmostYForX = {}; 429 var bottommostYForX = {}; 430 431 for (var i = 0; i < highlight.quads.length; ++i) { 432 var quad = highlight.quads[i]; 433 for (var j = 0; j < quad.length; ++j) { 434 var x = quad[j].x; 435 var y = quad[j].y; 436 leftmostXForY[Math.round(y)] = Math.min(leftmostXForY[y] || Number.MAX_VALUE, Math.round(quad[j].x)); 437 rightmostXForY[Math.round(y)] = Math.max(rightmostXForY[y] || Number.MIN_VALUE, Math.round(quad[j].x)); 438 topmostYForX[Math.round(x)] = Math.min(topmostYForX[x] || Number.MAX_VALUE, Math.round(quad[j].y)); 439 bottommostYForX[Math.round(x)] = Math.max(bottommostYForX[x] || Number.MIN_VALUE, Math.round(quad[j].y)); 440 } 441 } 442 443 if (rulerAtRight) { 444 for (var y in rightmostXForY) { 445 context.beginPath(); 446 context.moveTo(width, y); 447 context.lineTo(rightmostXForY[y], y); 448 context.stroke(); 449 } 450 } else { 451 for (var y in leftmostXForY) { 452 context.beginPath(); 453 context.moveTo(0, y); 454 context.lineTo(leftmostXForY[y], y); 455 context.stroke(); 456 } 457 } 458 459 if (rulerAtBottom) { 460 for (var x in bottommostYForX) { 461 context.beginPath(); 462 context.moveTo(x, height); 463 context.lineTo(x, topmostYForX[x]); 464 context.stroke(); 465 } 466 } else { 467 for (var x in topmostYForX) { 468 context.beginPath(); 469 context.moveTo(x, 0); 470 context.lineTo(x, topmostYForX[x]); 471 context.stroke(); 472 } 473 } 474 475 context.restore(); 476} 477 478function drawNodeHighlight(highlight) 479{ 480 if (!highlight.quads.length) { 481 _drawGrid(highlight, false, false); 482 return; 483 } 484 485 context.save(); 486 487 for (var i = 0; i < highlight.quads.length; ++i) { 488 var quad = highlight.quads[i]; 489 for (var j = 0; j < quad.length; ++j) { 490 quad[j].x -= highlight.scrollX; 491 quad[j].y -= highlight.scrollY; 492 } 493 } 494 495 var quads = highlight.quads.slice(); 496 var contentQuad = quads.pop(); 497 var paddingQuad = quads.pop(); 498 var borderQuad = quads.pop(); 499 var marginQuad = quads.pop(); 500 501 var hasContent = contentQuad && highlight.contentColor !== transparentColor || highlight.contentOutlineColor !== transparentColor; 502 var hasPadding = paddingQuad && highlight.paddingColor !== transparentColor; 503 var hasBorder = borderQuad && highlight.borderColor !== transparentColor; 504 var hasMargin = marginQuad && highlight.marginColor !== transparentColor; 505 506 var clipQuad; 507 if (hasMargin && (!hasBorder || !quadEquals(marginQuad, borderQuad))) { 508 drawOutlinedQuadWithClip(marginQuad, borderQuad, highlight.marginColor); 509 clipQuad = borderQuad; 510 } 511 if (hasBorder && (!hasPadding || !quadEquals(borderQuad, paddingQuad))) { 512 drawOutlinedQuadWithClip(borderQuad, paddingQuad, highlight.borderColor); 513 clipQuad = paddingQuad; 514 } 515 if (hasPadding && (!hasContent || !quadEquals(paddingQuad, contentQuad))) { 516 drawOutlinedQuadWithClip(paddingQuad, contentQuad, highlight.paddingColor); 517 clipQuad = contentQuad; 518 } 519 if (hasContent) 520 drawOutlinedQuad(contentQuad, highlight.contentColor, highlight.contentOutlineColor); 521 522 var width = canvas.width; 523 var height = canvas.height; 524 var minX = Number.MAX_VALUE, minY = Number.MAX_VALUE, maxX = Number.MIN_VALUE; maxY = Number.MIN_VALUE; 525 for (var i = 0; i < highlight.quads.length; ++i) { 526 var quad = highlight.quads[i]; 527 for (var j = 0; j < quad.length; ++j) { 528 minX = Math.min(minX, quad[j].x); 529 maxX = Math.max(maxX, quad[j].x); 530 minY = Math.min(minY, quad[j].y); 531 maxY = Math.max(maxY, quad[j].y); 532 } 533 } 534 535 var rulerAtRight = minX < 20 && maxX + 20 < width; 536 var rulerAtBottom = minY < 20 && maxY + 20 < height; 537 538 _drawGrid(highlight, rulerAtRight, rulerAtBottom); 539 _drawRulers(highlight, rulerAtRight, rulerAtBottom); 540 _drawElementTitle(highlight); 541 context.restore(); 542} 543 544function drawQuadHighlight(highlight) 545{ 546 context.save(); 547 drawOutlinedQuad(highlight.quads[0], highlight.contentColor, highlight.contentOutlineColor); 548 context.restore(); 549} 550 551function setPlatform(platform) 552{ 553 document.body.classList.add("platform-" + platform); 554} 555 556function dispatch(message) 557{ 558 var functionName = message.shift(); 559 window[functionName].apply(null, message); 560} 561 562function log(text) 563{ 564 var logEntry = document.createElement("div"); 565 logEntry.textContent = text; 566 document.getElementById("log").appendChild(logEntry); 567} 568 569</script> 570</head> 571<body class="fill"> 572<div class="message-line"><span class="message-box" id="paused-in-debugger"></span></div> 573</body> 574<canvas id="canvas" class="fill"></canvas> 575<div id="element-title"> 576 <span id="tag-name"></span><span id="node-id"></span><span id="class-name"></span> 577 <span id="node-width"></span><span class="px">px</span><span class="px"> × </span><span id="node-height"></span><span class="px">px</span> 578</div> 579<div id="right-gutter"></div> 580<div id="bottom-gutter"></div> 581<div id="log"></div> 582</html> 583