1 /* 2 * Copyright (C) 2011 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 29 #include "config.h" 30 #include "core/inspector/InspectorOverlay.h" 31 32 #include "bindings/core/v8/ScriptController.h" 33 #include "bindings/core/v8/ScriptSourceCode.h" 34 #include "bindings/core/v8/V8InspectorOverlayHost.h" 35 #include "core/dom/Element.h" 36 #include "core/dom/Node.h" 37 #include "core/dom/PseudoElement.h" 38 #include "core/frame/FrameView.h" 39 #include "core/frame/LocalFrame.h" 40 #include "core/frame/Settings.h" 41 #include "core/inspector/InspectorClient.h" 42 #include "core/inspector/InspectorOverlayHost.h" 43 #include "core/loader/EmptyClients.h" 44 #include "core/loader/FrameLoadRequest.h" 45 #include "core/page/Chrome.h" 46 #include "core/page/EventHandler.h" 47 #include "core/page/Page.h" 48 #include "core/rendering/RenderBox.h" 49 #include "core/rendering/RenderBoxModelObject.h" 50 #include "core/rendering/RenderInline.h" 51 #include "core/rendering/RenderObject.h" 52 #include "core/rendering/shapes/ShapeOutsideInfo.h" 53 #include "core/rendering/style/RenderStyleConstants.h" 54 #include "platform/JSONValues.h" 55 #include "platform/PlatformMouseEvent.h" 56 #include "platform/ScriptForbiddenScope.h" 57 #include "platform/graphics/GraphicsContextStateSaver.h" 58 #include "public/platform/Platform.h" 59 #include "public/platform/WebData.h" 60 #include "wtf/Vector.h" 61 #include "wtf/text/StringBuilder.h" 62 #include <v8.h> 63 64 namespace blink { 65 66 namespace { 67 68 class PathBuilder { 69 WTF_MAKE_NONCOPYABLE(PathBuilder); 70 public: 71 PathBuilder() : m_path(TypeBuilder::Array<JSONValue>::create()) { } 72 virtual ~PathBuilder() { } 73 74 PassRefPtr<TypeBuilder::Array<JSONValue> > path() const { return m_path; } 75 void appendPath(const Path& path) 76 { 77 path.apply(this, &PathBuilder::appendPathElement); 78 } 79 80 protected: 81 virtual FloatPoint translatePoint(const FloatPoint& point) { return point; } 82 83 private: 84 static void appendPathElement(void* pathBuilder, const PathElement* pathElement) 85 { 86 static_cast<PathBuilder*>(pathBuilder)->appendPathElement(pathElement); 87 } 88 89 void appendPathElement(const PathElement*); 90 void appendPathCommandAndPoints(const char* command, const FloatPoint points[], size_t length); 91 92 RefPtr<TypeBuilder::Array<JSONValue> > m_path; 93 }; 94 95 class ShapePathBuilder : public PathBuilder { 96 public: 97 ShapePathBuilder(FrameView& view, RenderObject& renderer, const ShapeOutsideInfo& shapeOutsideInfo) 98 : m_view(view) 99 , m_renderer(renderer) 100 , m_shapeOutsideInfo(shapeOutsideInfo) { } 101 102 static PassRefPtr<TypeBuilder::Array<JSONValue> > buildPath(FrameView& view, RenderObject& renderer, const ShapeOutsideInfo& shapeOutsideInfo, const Path& path) 103 { 104 ShapePathBuilder builder(view, renderer, shapeOutsideInfo); 105 builder.appendPath(path); 106 return builder.path(); 107 } 108 109 protected: 110 virtual FloatPoint translatePoint(const FloatPoint& point) 111 { 112 FloatPoint rendererPoint = m_shapeOutsideInfo.shapeToRendererPoint(point); 113 return m_view.contentsToRootView(roundedIntPoint(m_renderer.localToAbsolute(rendererPoint))); 114 } 115 116 private: 117 FrameView& m_view; 118 RenderObject& m_renderer; 119 const ShapeOutsideInfo& m_shapeOutsideInfo; 120 }; 121 122 class InspectorOverlayChromeClient FINAL: public EmptyChromeClient { 123 public: 124 InspectorOverlayChromeClient(ChromeClient& client, InspectorOverlay* overlay) 125 : m_client(client) 126 , m_overlay(overlay) 127 { } 128 129 virtual void setCursor(const Cursor& cursor) OVERRIDE 130 { 131 m_client.setCursor(cursor); 132 } 133 134 virtual void setToolTip(const String& tooltip, TextDirection direction) OVERRIDE 135 { 136 m_client.setToolTip(tooltip, direction); 137 } 138 139 virtual void invalidateContentsAndRootView(const IntRect&) OVERRIDE 140 { 141 m_overlay->invalidate(); 142 } 143 144 virtual void invalidateContentsForSlowScroll(const IntRect&) OVERRIDE 145 { 146 m_overlay->invalidate(); 147 } 148 149 private: 150 ChromeClient& m_client; 151 InspectorOverlay* m_overlay; 152 }; 153 154 static Path quadToPath(const FloatQuad& quad) 155 { 156 Path quadPath; 157 quadPath.moveTo(quad.p1()); 158 quadPath.addLineTo(quad.p2()); 159 quadPath.addLineTo(quad.p3()); 160 quadPath.addLineTo(quad.p4()); 161 quadPath.closeSubpath(); 162 return quadPath; 163 } 164 165 void drawOutlinedQuad(GraphicsContext* context, const FloatQuad& quad, const Color& fillColor, const Color& outlineColor) 166 { 167 static const int outlineThickness = 2; 168 169 Path quadPath = quadToPath(quad); 170 171 // Clip out the quad, then draw with a 2px stroke to get a pixel 172 // of outline (because inflating a quad is hard) 173 { 174 context->save(); 175 context->clipOut(quadPath); 176 177 context->setStrokeThickness(outlineThickness); 178 context->setStrokeColor(outlineColor); 179 context->strokePath(quadPath); 180 181 context->restore(); 182 } 183 184 // Now do the fill 185 context->setFillColor(fillColor); 186 context->fillPath(quadPath); 187 } 188 189 class Highlight { 190 public: 191 Highlight() 192 : m_showRulers(false) 193 , m_showExtensionLines(false) 194 , m_highlightPaths(JSONArray::create()) 195 { } 196 197 void setDataFromConfig(const HighlightConfig& highlightConfig) 198 { 199 m_showRulers = highlightConfig.showRulers; 200 m_showExtensionLines = highlightConfig.showExtensionLines; 201 } 202 203 void setElementInfo(PassRefPtr<JSONObject> elementInfo) 204 { 205 m_elementInfo = elementInfo; 206 } 207 208 void appendQuad(const FloatQuad& quad, const Color& fillColor, const Color& outlineColor = Color::transparent) 209 { 210 Path path = quadToPath(quad); 211 PathBuilder builder; 212 builder.appendPath(path); 213 appendPath(builder.path(), fillColor, outlineColor); 214 } 215 216 void appendPath(PassRefPtr<JSONArrayBase> path, const Color& fillColor, const Color& outlineColor) 217 { 218 RefPtr<JSONObject> object = JSONObject::create(); 219 object->setValue("path", path); 220 object->setString("fillColor", fillColor.serialized()); 221 if (outlineColor != Color::transparent) 222 object->setString("outlineColor", outlineColor.serialized()); 223 m_highlightPaths->pushObject(object.release()); 224 } 225 226 PassRefPtr<JSONObject> asJSONObject() const 227 { 228 RefPtr<JSONObject> object = JSONObject::create(); 229 object->setArray("paths", m_highlightPaths); 230 object->setBoolean("showRulers", m_showRulers); 231 object->setBoolean("showExtensionLines", m_showExtensionLines); 232 if (m_elementInfo) 233 object->setObject("elementInfo", m_elementInfo); 234 return object.release(); 235 } 236 237 private: 238 bool m_showRulers; 239 bool m_showExtensionLines; 240 RefPtr<JSONObject> m_elementInfo; 241 RefPtr<JSONArray> m_highlightPaths; 242 }; 243 244 static void contentsQuadToScreen(const FrameView* view, FloatQuad& quad) 245 { 246 quad.setP1(view->contentsToRootView(roundedIntPoint(quad.p1()))); 247 quad.setP2(view->contentsToRootView(roundedIntPoint(quad.p2()))); 248 quad.setP3(view->contentsToRootView(roundedIntPoint(quad.p3()))); 249 quad.setP4(view->contentsToRootView(roundedIntPoint(quad.p4()))); 250 } 251 252 static bool buildNodeQuads(RenderObject* renderer, FloatQuad* content, FloatQuad* padding, FloatQuad* border, FloatQuad* margin) 253 { 254 FrameView* containingView = renderer->frameView(); 255 if (!containingView) 256 return false; 257 if (!renderer->isBox() && !renderer->isRenderInline()) 258 return false; 259 260 LayoutRect contentBox; 261 LayoutRect paddingBox; 262 LayoutRect borderBox; 263 LayoutRect marginBox; 264 265 if (renderer->isBox()) { 266 RenderBox* renderBox = toRenderBox(renderer); 267 268 // RenderBox returns the "pure" content area box, exclusive of the scrollbars (if present), which also count towards the content area in CSS. 269 contentBox = renderBox->contentBoxRect(); 270 contentBox.setWidth(contentBox.width() + renderBox->verticalScrollbarWidth()); 271 contentBox.setHeight(contentBox.height() + renderBox->horizontalScrollbarHeight()); 272 273 paddingBox = LayoutRect(contentBox.x() - renderBox->paddingLeft(), contentBox.y() - renderBox->paddingTop(), 274 contentBox.width() + renderBox->paddingLeft() + renderBox->paddingRight(), contentBox.height() + renderBox->paddingTop() + renderBox->paddingBottom()); 275 borderBox = LayoutRect(paddingBox.x() - renderBox->borderLeft(), paddingBox.y() - renderBox->borderTop(), 276 paddingBox.width() + renderBox->borderLeft() + renderBox->borderRight(), paddingBox.height() + renderBox->borderTop() + renderBox->borderBottom()); 277 marginBox = LayoutRect(borderBox.x() - renderBox->marginLeft(), borderBox.y() - renderBox->marginTop(), 278 borderBox.width() + renderBox->marginWidth(), borderBox.height() + renderBox->marginHeight()); 279 } else { 280 RenderInline* renderInline = toRenderInline(renderer); 281 282 // RenderInline's bounding box includes paddings and borders, excludes margins. 283 borderBox = renderInline->linesBoundingBox(); 284 paddingBox = LayoutRect(borderBox.x() + renderInline->borderLeft(), borderBox.y() + renderInline->borderTop(), 285 borderBox.width() - renderInline->borderLeft() - renderInline->borderRight(), borderBox.height() - renderInline->borderTop() - renderInline->borderBottom()); 286 contentBox = LayoutRect(paddingBox.x() + renderInline->paddingLeft(), paddingBox.y() + renderInline->paddingTop(), 287 paddingBox.width() - renderInline->paddingLeft() - renderInline->paddingRight(), paddingBox.height() - renderInline->paddingTop() - renderInline->paddingBottom()); 288 // Ignore marginTop and marginBottom for inlines. 289 marginBox = LayoutRect(borderBox.x() - renderInline->marginLeft(), borderBox.y(), 290 borderBox.width() + renderInline->marginWidth(), borderBox.height()); 291 } 292 293 *content = renderer->localToAbsoluteQuad(FloatRect(contentBox)); 294 *padding = renderer->localToAbsoluteQuad(FloatRect(paddingBox)); 295 *border = renderer->localToAbsoluteQuad(FloatRect(borderBox)); 296 *margin = renderer->localToAbsoluteQuad(FloatRect(marginBox)); 297 298 contentsQuadToScreen(containingView, *content); 299 contentsQuadToScreen(containingView, *padding); 300 contentsQuadToScreen(containingView, *border); 301 contentsQuadToScreen(containingView, *margin); 302 303 return true; 304 } 305 306 static void buildNodeHighlight(Node& node, const HighlightConfig& highlightConfig, Highlight* highlight) 307 { 308 RenderObject* renderer = node.renderer(); 309 if (!renderer) 310 return; 311 312 highlight->setDataFromConfig(highlightConfig); 313 314 // RenderSVGRoot should be highlighted through the isBox() code path, all other SVG elements should just dump their absoluteQuads(). 315 if (renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGRoot()) { 316 Vector<FloatQuad> quads; 317 renderer->absoluteQuads(quads); 318 for (size_t i = 0; i < quads.size(); ++i) 319 highlight->appendQuad(quads[i], highlightConfig.content, highlightConfig.contentOutline); 320 return; 321 } 322 323 FloatQuad content, padding, border, margin; 324 if (!buildNodeQuads(renderer, &content, &padding, &border, &margin)) 325 return; 326 highlight->appendQuad(content, highlightConfig.content, highlightConfig.contentOutline); 327 highlight->appendQuad(padding, highlightConfig.padding); 328 highlight->appendQuad(border, highlightConfig.border); 329 highlight->appendQuad(margin, highlightConfig.margin); 330 } 331 332 } // anonymous namespace 333 334 InspectorOverlay::InspectorOverlay(Page* page, InspectorClient* client) 335 : m_page(page) 336 , m_client(client) 337 , m_inspectModeEnabled(false) 338 , m_overlayHost(InspectorOverlayHost::create()) 339 , m_drawViewSize(false) 340 , m_drawViewSizeWithGrid(false) 341 , m_omitTooltip(false) 342 , m_timer(this, &InspectorOverlay::onTimer) 343 , m_activeProfilerCount(0) 344 { 345 } 346 347 InspectorOverlay::~InspectorOverlay() 348 { 349 ASSERT(!m_overlayPage); 350 } 351 352 void InspectorOverlay::paint(GraphicsContext& context) 353 { 354 if (isEmpty()) 355 return; 356 GraphicsContextStateSaver stateSaver(context); 357 FrameView* view = toLocalFrame(overlayPage()->mainFrame())->view(); 358 ASSERT(!view->needsLayout()); 359 view->paint(&context, IntRect(0, 0, view->width(), view->height())); 360 } 361 362 void InspectorOverlay::invalidate() 363 { 364 m_client->highlight(); 365 } 366 367 bool InspectorOverlay::handleGestureEvent(const PlatformGestureEvent& event) 368 { 369 if (isEmpty()) 370 return false; 371 372 return toLocalFrame(overlayPage()->mainFrame())->eventHandler().handleGestureEvent(event); 373 } 374 375 bool InspectorOverlay::handleMouseEvent(const PlatformMouseEvent& event) 376 { 377 if (isEmpty()) 378 return false; 379 380 EventHandler& eventHandler = toLocalFrame(overlayPage()->mainFrame())->eventHandler(); 381 switch (event.type()) { 382 case PlatformEvent::MouseMoved: 383 return eventHandler.handleMouseMoveEvent(event); 384 case PlatformEvent::MousePressed: 385 return eventHandler.handleMousePressEvent(event); 386 case PlatformEvent::MouseReleased: 387 return eventHandler.handleMouseReleaseEvent(event); 388 default: 389 return false; 390 } 391 } 392 393 bool InspectorOverlay::handleTouchEvent(const PlatformTouchEvent& event) 394 { 395 if (isEmpty()) 396 return false; 397 398 return toLocalFrame(overlayPage()->mainFrame())->eventHandler().handleTouchEvent(event); 399 } 400 401 bool InspectorOverlay::handleKeyboardEvent(const PlatformKeyboardEvent& event) 402 { 403 if (isEmpty()) 404 return false; 405 406 return toLocalFrame(overlayPage()->mainFrame())->eventHandler().keyEvent(event); 407 } 408 409 void InspectorOverlay::drawOutline(GraphicsContext* context, const LayoutRect& rect, const Color& color) 410 { 411 FloatRect outlineRect = rect; 412 drawOutlinedQuad(context, outlineRect, Color(), color); 413 } 414 415 void InspectorOverlay::setPausedInDebuggerMessage(const String* message) 416 { 417 m_pausedInDebuggerMessage = message ? *message : String(); 418 update(); 419 } 420 421 void InspectorOverlay::setInspectModeEnabled(bool enabled) 422 { 423 m_inspectModeEnabled = enabled; 424 update(); 425 } 426 427 void InspectorOverlay::hideHighlight() 428 { 429 m_highlightNode.clear(); 430 m_eventTargetNode.clear(); 431 m_highlightQuad.clear(); 432 update(); 433 } 434 435 void InspectorOverlay::highlightNode(Node* node, Node* eventTarget, const HighlightConfig& highlightConfig, bool omitTooltip) 436 { 437 m_nodeHighlightConfig = highlightConfig; 438 m_highlightNode = node; 439 m_eventTargetNode = eventTarget; 440 m_omitTooltip = omitTooltip; 441 update(); 442 } 443 444 void InspectorOverlay::highlightQuad(PassOwnPtr<FloatQuad> quad, const HighlightConfig& highlightConfig) 445 { 446 m_quadHighlightConfig = highlightConfig; 447 m_highlightQuad = quad; 448 m_omitTooltip = false; 449 update(); 450 } 451 452 void InspectorOverlay::showAndHideViewSize(bool showGrid) 453 { 454 m_drawViewSize = true; 455 m_drawViewSizeWithGrid = showGrid; 456 update(); 457 m_timer.startOneShot(1, FROM_HERE); 458 } 459 460 Node* InspectorOverlay::highlightedNode() const 461 { 462 return m_highlightNode.get(); 463 } 464 465 bool InspectorOverlay::isEmpty() 466 { 467 if (m_activeProfilerCount) 468 return true; 469 bool hasAlwaysVisibleElements = m_highlightNode || m_eventTargetNode || m_highlightQuad || m_drawViewSize; 470 bool hasInvisibleInInspectModeElements = !m_pausedInDebuggerMessage.isNull(); 471 return !(hasAlwaysVisibleElements || (hasInvisibleInInspectModeElements && !m_inspectModeEnabled)); 472 } 473 474 void InspectorOverlay::update() 475 { 476 if (isEmpty()) { 477 m_client->hideHighlight(); 478 return; 479 } 480 481 FrameView* view = m_page->deprecatedLocalMainFrame()->view(); 482 if (!view) 483 return; 484 485 // Include scrollbars to avoid masking them by the gutter. 486 IntSize size = view->unscaledVisibleContentSize(IncludeScrollbars); 487 toLocalFrame(overlayPage()->mainFrame())->view()->resize(size); 488 489 // Clear canvas and paint things. 490 IntRect viewRect = view->visibleContentRect(); 491 reset(size, viewRect.x(), viewRect.y()); 492 493 drawNodeHighlight(); 494 drawQuadHighlight(); 495 if (!m_inspectModeEnabled) 496 drawPausedInDebuggerMessage(); 497 drawViewSize(); 498 499 toLocalFrame(overlayPage()->mainFrame())->view()->updateLayoutAndStyleForPainting(); 500 501 m_client->highlight(); 502 } 503 504 void InspectorOverlay::hide() 505 { 506 m_timer.stop(); 507 m_highlightNode.clear(); 508 m_eventTargetNode.clear(); 509 m_highlightQuad.clear(); 510 m_pausedInDebuggerMessage = String(); 511 m_drawViewSize = false; 512 m_drawViewSizeWithGrid = false; 513 update(); 514 } 515 516 static PassRefPtr<JSONObject> buildObjectForSize(const IntSize& size) 517 { 518 RefPtr<JSONObject> result = JSONObject::create(); 519 result->setNumber("width", size.width()); 520 result->setNumber("height", size.height()); 521 return result.release(); 522 } 523 524 void PathBuilder::appendPathCommandAndPoints(const char* command, const FloatPoint points[], size_t length) 525 { 526 m_path->addItem(JSONString::create(command)); 527 for (size_t i = 0; i < length; i++) { 528 FloatPoint point = translatePoint(points[i]); 529 m_path->addItem(JSONBasicValue::create(point.x())); 530 m_path->addItem(JSONBasicValue::create(point.y())); 531 } 532 } 533 534 void PathBuilder::appendPathElement(const PathElement* pathElement) 535 { 536 switch (pathElement->type) { 537 // The points member will contain 1 value. 538 case PathElementMoveToPoint: 539 appendPathCommandAndPoints("M", pathElement->points, 1); 540 break; 541 // The points member will contain 1 value. 542 case PathElementAddLineToPoint: 543 appendPathCommandAndPoints("L", pathElement->points, 1); 544 break; 545 // The points member will contain 3 values. 546 case PathElementAddCurveToPoint: 547 appendPathCommandAndPoints("C", pathElement->points, 3); 548 break; 549 // The points member will contain 2 values. 550 case PathElementAddQuadCurveToPoint: 551 appendPathCommandAndPoints("Q", pathElement->points, 2); 552 break; 553 // The points member will contain no values. 554 case PathElementCloseSubpath: 555 appendPathCommandAndPoints("Z", 0, 0); 556 break; 557 } 558 } 559 560 static RefPtr<TypeBuilder::Array<double> > buildArrayForQuad(const FloatQuad& quad) 561 { 562 RefPtr<TypeBuilder::Array<double> > array = TypeBuilder::Array<double>::create(); 563 array->addItem(quad.p1().x()); 564 array->addItem(quad.p1().y()); 565 array->addItem(quad.p2().x()); 566 array->addItem(quad.p2().y()); 567 array->addItem(quad.p3().x()); 568 array->addItem(quad.p3().y()); 569 array->addItem(quad.p4().x()); 570 array->addItem(quad.p4().y()); 571 return array.release(); 572 } 573 574 static const ShapeOutsideInfo* shapeOutsideInfoForNode(Node* node, Shape::DisplayPaths* paths, FloatQuad* bounds) 575 { 576 RenderObject* renderer = node->renderer(); 577 if (!renderer || !renderer->isBox() || !toRenderBox(renderer)->shapeOutsideInfo()) 578 return 0; 579 580 FrameView* containingView = node->document().view(); 581 RenderBox* renderBox = toRenderBox(renderer); 582 const ShapeOutsideInfo* shapeOutsideInfo = renderBox->shapeOutsideInfo(); 583 584 shapeOutsideInfo->computedShape().buildDisplayPaths(*paths); 585 586 LayoutRect shapeBounds = shapeOutsideInfo->computedShapePhysicalBoundingBox(); 587 *bounds = renderBox->localToAbsoluteQuad(FloatRect(shapeBounds)); 588 contentsQuadToScreen(containingView, *bounds); 589 590 return shapeOutsideInfo; 591 } 592 593 static void appendPathsForShapeOutside(Highlight& highlight, const HighlightConfig& config, Node* node) 594 { 595 Shape::DisplayPaths paths; 596 FloatQuad boundsQuad; 597 598 const ShapeOutsideInfo* shapeOutsideInfo = shapeOutsideInfoForNode(node, &paths, &boundsQuad); 599 if (!shapeOutsideInfo) 600 return; 601 602 if (!paths.shape.length()) { 603 highlight.appendQuad(boundsQuad, config.shape); 604 return; 605 } 606 607 highlight.appendPath(ShapePathBuilder::buildPath(*node->document().view(), *node->renderer(), *shapeOutsideInfo, paths.shape), config.shape, Color::transparent); 608 if (paths.marginShape.length()) 609 highlight.appendPath(ShapePathBuilder::buildPath(*node->document().view(), *node->renderer(), *shapeOutsideInfo, paths.marginShape), config.shapeMargin, Color::transparent); 610 } 611 612 PassRefPtr<JSONObject> buildElementInfo(Element* element) 613 { 614 RefPtr<JSONObject> elementInfo = JSONObject::create(); 615 Element* realElement = element; 616 PseudoElement* pseudoElement = 0; 617 if (element->isPseudoElement()) { 618 pseudoElement = toPseudoElement(element); 619 realElement = element->parentOrShadowHostElement(); 620 } 621 bool isXHTML = realElement->document().isXHTMLDocument(); 622 elementInfo->setString("tagName", isXHTML ? realElement->nodeName() : realElement->nodeName().lower()); 623 elementInfo->setString("idValue", realElement->getIdAttribute()); 624 StringBuilder classNames; 625 if (realElement->hasClass() && realElement->isStyledElement()) { 626 HashSet<AtomicString> usedClassNames; 627 const SpaceSplitString& classNamesString = realElement->classNames(); 628 size_t classNameCount = classNamesString.size(); 629 for (size_t i = 0; i < classNameCount; ++i) { 630 const AtomicString& className = classNamesString[i]; 631 if (!usedClassNames.add(className).isNewEntry) 632 continue; 633 classNames.append('.'); 634 classNames.append(className); 635 } 636 } 637 if (pseudoElement) { 638 if (pseudoElement->pseudoId() == BEFORE) 639 classNames.appendLiteral("::before"); 640 else if (pseudoElement->pseudoId() == AFTER) 641 classNames.appendLiteral("::after"); 642 } 643 if (!classNames.isEmpty()) 644 elementInfo->setString("className", classNames.toString()); 645 646 RenderObject* renderer = element->renderer(); 647 FrameView* containingView = element->document().view(); 648 if (!renderer || !containingView) 649 return elementInfo; 650 651 IntRect boundingBox = pixelSnappedIntRect(containingView->contentsToRootView(renderer->absoluteBoundingBoxRect())); 652 RenderBoxModelObject* modelObject = renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : 0; 653 elementInfo->setString("nodeWidth", String::number(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), modelObject) : boundingBox.width())); 654 elementInfo->setString("nodeHeight", String::number(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), modelObject) : boundingBox.height())); 655 656 return elementInfo; 657 } 658 659 void InspectorOverlay::drawNodeHighlight() 660 { 661 if (!m_highlightNode) 662 return; 663 664 Highlight highlight; 665 appendPathsForShapeOutside(highlight, m_nodeHighlightConfig, m_highlightNode.get()); 666 buildNodeHighlight(*m_highlightNode, m_nodeHighlightConfig, &highlight); 667 668 if (m_eventTargetNode && m_eventTargetNode->renderer()) { 669 FloatQuad border, unused; 670 if (buildNodeQuads(m_eventTargetNode->renderer(), &unused, &unused, &border, &unused)) 671 highlight.appendQuad(border, m_nodeHighlightConfig.eventTarget); 672 } 673 674 if (m_highlightNode->isElementNode() && !m_omitTooltip && m_nodeHighlightConfig.showInfo && m_highlightNode->renderer() && m_highlightNode->document().frame()) 675 highlight.setElementInfo(buildElementInfo(toElement(m_highlightNode.get()))); 676 677 evaluateInOverlay("drawHighlight", highlight.asJSONObject()); 678 } 679 680 void InspectorOverlay::drawQuadHighlight() 681 { 682 if (!m_highlightQuad) 683 return; 684 685 Highlight highlight; 686 highlight.appendQuad(*m_highlightQuad, m_quadHighlightConfig.content, m_quadHighlightConfig.contentOutline); 687 evaluateInOverlay("drawHighlight", highlight.asJSONObject()); 688 } 689 690 void InspectorOverlay::drawPausedInDebuggerMessage() 691 { 692 if (!m_pausedInDebuggerMessage.isNull()) 693 evaluateInOverlay("drawPausedInDebuggerMessage", m_pausedInDebuggerMessage); 694 } 695 696 void InspectorOverlay::drawViewSize() 697 { 698 if (m_drawViewSize) 699 evaluateInOverlay("drawViewSize", m_drawViewSizeWithGrid ? "true" : "false"); 700 } 701 702 Page* InspectorOverlay::overlayPage() 703 { 704 if (m_overlayPage) 705 return m_overlayPage.get(); 706 707 ScriptForbiddenScope::AllowUserAgentScript allowScript; 708 709 static FrameLoaderClient* dummyFrameLoaderClient = new EmptyFrameLoaderClient; 710 Page::PageClients pageClients; 711 fillWithEmptyClients(pageClients); 712 ASSERT(!m_overlayChromeClient); 713 m_overlayChromeClient = adoptPtr(new InspectorOverlayChromeClient(m_page->chrome().client(), this)); 714 pageClients.chromeClient = m_overlayChromeClient.get(); 715 m_overlayPage = adoptPtrWillBeNoop(new Page(pageClients)); 716 717 Settings& settings = m_page->settings(); 718 Settings& overlaySettings = m_overlayPage->settings(); 719 720 overlaySettings.genericFontFamilySettings().updateStandard(settings.genericFontFamilySettings().standard()); 721 overlaySettings.genericFontFamilySettings().updateSerif(settings.genericFontFamilySettings().serif()); 722 overlaySettings.genericFontFamilySettings().updateSansSerif(settings.genericFontFamilySettings().sansSerif()); 723 overlaySettings.genericFontFamilySettings().updateCursive(settings.genericFontFamilySettings().cursive()); 724 overlaySettings.genericFontFamilySettings().updateFantasy(settings.genericFontFamilySettings().fantasy()); 725 overlaySettings.genericFontFamilySettings().updatePictograph(settings.genericFontFamilySettings().pictograph()); 726 overlaySettings.setMinimumFontSize(settings.minimumFontSize()); 727 overlaySettings.setMinimumLogicalFontSize(settings.minimumLogicalFontSize()); 728 overlaySettings.setScriptEnabled(true); 729 overlaySettings.setPluginsEnabled(false); 730 overlaySettings.setLoadsImagesAutomatically(true); 731 // FIXME: http://crbug.com/363843. Inspector should probably create its 732 // own graphics layers and attach them to the tree rather than going 733 // through some non-composited paint function. 734 overlaySettings.setAcceleratedCompositingEnabled(false); 735 736 RefPtrWillBeRawPtr<LocalFrame> frame = LocalFrame::create(dummyFrameLoaderClient, &m_overlayPage->frameHost(), 0); 737 frame->setView(FrameView::create(frame.get())); 738 frame->init(); 739 FrameLoader& loader = frame->loader(); 740 frame->view()->setCanHaveScrollbars(false); 741 frame->view()->setTransparent(true); 742 743 const blink::WebData& overlayPageHTMLResource = blink::Platform::current()->loadResource("InspectorOverlayPage.html"); 744 RefPtr<SharedBuffer> data = SharedBuffer::create(overlayPageHTMLResource.data(), overlayPageHTMLResource.size()); 745 loader.load(FrameLoadRequest(0, blankURL(), SubstituteData(data, "text/html", "UTF-8", KURL(), ForceSynchronousLoad))); 746 v8::Isolate* isolate = toIsolate(frame.get()); 747 ScriptState* scriptState = ScriptState::forMainWorld(frame.get()); 748 ASSERT(!scriptState->contextIsValid()); 749 ScriptState::Scope scope(scriptState); 750 v8::Handle<v8::Object> global = scriptState->context()->Global(); 751 v8::Handle<v8::Value> overlayHostObj = toV8(m_overlayHost.get(), global, isolate); 752 global->Set(v8::String::NewFromUtf8(isolate, "InspectorOverlayHost"), overlayHostObj); 753 754 #if OS(WIN) 755 evaluateInOverlay("setPlatform", "windows"); 756 #elif OS(MACOSX) 757 evaluateInOverlay("setPlatform", "mac"); 758 #elif OS(POSIX) 759 evaluateInOverlay("setPlatform", "linux"); 760 #endif 761 762 return m_overlayPage.get(); 763 } 764 765 void InspectorOverlay::reset(const IntSize& viewportSize, int scrollX, int scrollY) 766 { 767 RefPtr<JSONObject> resetData = JSONObject::create(); 768 resetData->setNumber("pageScaleFactor", m_page->settings().pinchVirtualViewportEnabled() ? 1 : m_page->pageScaleFactor()); 769 resetData->setNumber("deviceScaleFactor", m_page->deviceScaleFactor()); 770 resetData->setObject("viewportSize", buildObjectForSize(viewportSize)); 771 resetData->setNumber("pageZoomFactor", m_page->deprecatedLocalMainFrame()->pageZoomFactor()); 772 resetData->setNumber("scrollX", scrollX); 773 resetData->setNumber("scrollY", scrollY); 774 evaluateInOverlay("reset", resetData.release()); 775 } 776 777 void InspectorOverlay::evaluateInOverlay(const String& method, const String& argument) 778 { 779 ScriptForbiddenScope::AllowUserAgentScript allowScript; 780 RefPtr<JSONArray> command = JSONArray::create(); 781 command->pushString(method); 782 command->pushString(argument); 783 toLocalFrame(overlayPage()->mainFrame())->script().executeScriptInMainWorld("dispatch(" + command->toJSONString() + ")", ScriptController::ExecuteScriptWhenScriptsDisabled); 784 } 785 786 void InspectorOverlay::evaluateInOverlay(const String& method, PassRefPtr<JSONValue> argument) 787 { 788 ScriptForbiddenScope::AllowUserAgentScript allowScript; 789 RefPtr<JSONArray> command = JSONArray::create(); 790 command->pushString(method); 791 command->pushValue(argument); 792 toLocalFrame(overlayPage()->mainFrame())->script().executeScriptInMainWorld("dispatch(" + command->toJSONString() + ")", ScriptController::ExecuteScriptWhenScriptsDisabled); 793 } 794 795 void InspectorOverlay::onTimer(Timer<InspectorOverlay>*) 796 { 797 m_drawViewSize = false; 798 update(); 799 } 800 801 bool InspectorOverlay::getBoxModel(Node* node, RefPtr<TypeBuilder::DOM::BoxModel>& model) 802 { 803 RenderObject* renderer = node->renderer(); 804 FrameView* view = node->document().view(); 805 if (!renderer || !view) 806 return false; 807 808 FloatQuad content, padding, border, margin; 809 if (!buildNodeQuads(node->renderer(), &content, &padding, &border, &margin)) 810 return false; 811 812 IntRect boundingBox = pixelSnappedIntRect(view->contentsToRootView(renderer->absoluteBoundingBoxRect())); 813 RenderBoxModelObject* modelObject = renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : 0; 814 815 model = TypeBuilder::DOM::BoxModel::create() 816 .setContent(buildArrayForQuad(content)) 817 .setPadding(buildArrayForQuad(padding)) 818 .setBorder(buildArrayForQuad(border)) 819 .setMargin(buildArrayForQuad(margin)) 820 .setWidth(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), modelObject) : boundingBox.width()) 821 .setHeight(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), modelObject) : boundingBox.height()); 822 823 Shape::DisplayPaths paths; 824 FloatQuad boundsQuad; 825 if (const ShapeOutsideInfo* shapeOutsideInfo = shapeOutsideInfoForNode(node, &paths, &boundsQuad)) { 826 RefPtr<TypeBuilder::DOM::ShapeOutsideInfo> shapeTypeBuilder = TypeBuilder::DOM::ShapeOutsideInfo::create() 827 .setBounds(buildArrayForQuad(boundsQuad)) 828 .setShape(ShapePathBuilder::buildPath(*view, *renderer, *shapeOutsideInfo, paths.shape)) 829 .setMarginShape(ShapePathBuilder::buildPath(*view, *renderer, *shapeOutsideInfo, paths.marginShape)); 830 model->setShapeOutside(shapeTypeBuilder); 831 } 832 833 return true; 834 } 835 836 void InspectorOverlay::freePage() 837 { 838 if (m_overlayPage) { 839 m_overlayPage->willBeDestroyed(); 840 m_overlayPage.clear(); 841 } 842 m_overlayChromeClient.clear(); 843 m_timer.stop(); 844 845 // This will clear internal structures and issue update to the client. Safe to call last. 846 hideHighlight(); 847 } 848 849 void InspectorOverlay::startedRecordingProfile() 850 { 851 if (!m_activeProfilerCount++) 852 freePage(); 853 } 854 855 } // namespace blink 856