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:
PathBuilder()71 PathBuilder() : m_path(TypeBuilder::Array<JSONValue>::create()) { }
~PathBuilder()72 virtual ~PathBuilder() { }
73
path() const74 PassRefPtr<TypeBuilder::Array<JSONValue> > path() const { return m_path; }
appendPath(const Path & path)75 void appendPath(const Path& path)
76 {
77 path.apply(this, &PathBuilder::appendPathElement);
78 }
79
80 protected:
translatePoint(const FloatPoint & point)81 virtual FloatPoint translatePoint(const FloatPoint& point) { return point; }
82
83 private:
appendPathElement(void * pathBuilder,const PathElement * pathElement)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:
ShapePathBuilder(FrameView & view,RenderObject & renderer,const ShapeOutsideInfo & shapeOutsideInfo)97 ShapePathBuilder(FrameView& view, RenderObject& renderer, const ShapeOutsideInfo& shapeOutsideInfo)
98 : m_view(view)
99 , m_renderer(renderer)
100 , m_shapeOutsideInfo(shapeOutsideInfo) { }
101
buildPath(FrameView & view,RenderObject & renderer,const ShapeOutsideInfo & shapeOutsideInfo,const Path & path)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:
translatePoint(const FloatPoint & point)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:
InspectorOverlayChromeClient(ChromeClient & client,InspectorOverlay * overlay)124 InspectorOverlayChromeClient(ChromeClient& client, InspectorOverlay* overlay)
125 : m_client(client)
126 , m_overlay(overlay)
127 { }
128
setCursor(const Cursor & cursor)129 virtual void setCursor(const Cursor& cursor) OVERRIDE
130 {
131 m_client.setCursor(cursor);
132 }
133
setToolTip(const String & tooltip,TextDirection direction)134 virtual void setToolTip(const String& tooltip, TextDirection direction) OVERRIDE
135 {
136 m_client.setToolTip(tooltip, direction);
137 }
138
invalidateContentsAndRootView(const IntRect &)139 virtual void invalidateContentsAndRootView(const IntRect&) OVERRIDE
140 {
141 m_overlay->invalidate();
142 }
143
invalidateContentsForSlowScroll(const IntRect &)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
quadToPath(const FloatQuad & quad)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
drawOutlinedQuad(GraphicsContext * context,const FloatQuad & quad,const Color & fillColor,const Color & outlineColor)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:
Highlight()191 Highlight()
192 : m_showRulers(false)
193 , m_showExtensionLines(false)
194 , m_highlightPaths(JSONArray::create())
195 { }
196
setDataFromConfig(const HighlightConfig & highlightConfig)197 void setDataFromConfig(const HighlightConfig& highlightConfig)
198 {
199 m_showRulers = highlightConfig.showRulers;
200 m_showExtensionLines = highlightConfig.showExtensionLines;
201 }
202
setElementInfo(PassRefPtr<JSONObject> elementInfo)203 void setElementInfo(PassRefPtr<JSONObject> elementInfo)
204 {
205 m_elementInfo = elementInfo;
206 }
207
appendQuad(const FloatQuad & quad,const Color & fillColor,const Color & outlineColor=Color::transparent)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
appendPath(PassRefPtr<JSONArrayBase> path,const Color & fillColor,const Color & outlineColor)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
asJSONObject() const226 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
contentsQuadToScreen(const FrameView * view,FloatQuad & quad)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
buildNodeQuads(RenderObject * renderer,FloatQuad * content,FloatQuad * padding,FloatQuad * border,FloatQuad * margin)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
buildNodeHighlight(Node & node,const HighlightConfig & highlightConfig,Highlight * highlight)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
InspectorOverlay(Page * page,InspectorClient * client)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
~InspectorOverlay()347 InspectorOverlay::~InspectorOverlay()
348 {
349 ASSERT(!m_overlayPage);
350 }
351
paint(GraphicsContext & context)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
invalidate()362 void InspectorOverlay::invalidate()
363 {
364 m_client->highlight();
365 }
366
handleGestureEvent(const PlatformGestureEvent & event)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
handleMouseEvent(const PlatformMouseEvent & event)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
handleTouchEvent(const PlatformTouchEvent & event)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
handleKeyboardEvent(const PlatformKeyboardEvent & event)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
drawOutline(GraphicsContext * context,const LayoutRect & rect,const Color & color)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
setPausedInDebuggerMessage(const String * message)415 void InspectorOverlay::setPausedInDebuggerMessage(const String* message)
416 {
417 m_pausedInDebuggerMessage = message ? *message : String();
418 update();
419 }
420
setInspectModeEnabled(bool enabled)421 void InspectorOverlay::setInspectModeEnabled(bool enabled)
422 {
423 m_inspectModeEnabled = enabled;
424 update();
425 }
426
hideHighlight()427 void InspectorOverlay::hideHighlight()
428 {
429 m_highlightNode.clear();
430 m_eventTargetNode.clear();
431 m_highlightQuad.clear();
432 update();
433 }
434
highlightNode(Node * node,Node * eventTarget,const HighlightConfig & highlightConfig,bool omitTooltip)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
highlightQuad(PassOwnPtr<FloatQuad> quad,const HighlightConfig & highlightConfig)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
showAndHideViewSize(bool showGrid)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
highlightedNode() const460 Node* InspectorOverlay::highlightedNode() const
461 {
462 return m_highlightNode.get();
463 }
464
isEmpty()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
update()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
hide()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
buildObjectForSize(const IntSize & size)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
appendPathCommandAndPoints(const char * command,const FloatPoint points[],size_t length)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
appendPathElement(const PathElement * pathElement)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
buildArrayForQuad(const FloatQuad & quad)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
shapeOutsideInfoForNode(Node * node,Shape::DisplayPaths * paths,FloatQuad * bounds)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
appendPathsForShapeOutside(Highlight & highlight,const HighlightConfig & config,Node * node)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
buildElementInfo(Element * element)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
drawNodeHighlight()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
drawQuadHighlight()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
drawPausedInDebuggerMessage()690 void InspectorOverlay::drawPausedInDebuggerMessage()
691 {
692 if (!m_pausedInDebuggerMessage.isNull())
693 evaluateInOverlay("drawPausedInDebuggerMessage", m_pausedInDebuggerMessage);
694 }
695
drawViewSize()696 void InspectorOverlay::drawViewSize()
697 {
698 if (m_drawViewSize)
699 evaluateInOverlay("drawViewSize", m_drawViewSizeWithGrid ? "true" : "false");
700 }
701
overlayPage()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
reset(const IntSize & viewportSize,int scrollX,int scrollY)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
evaluateInOverlay(const String & method,const String & argument)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
evaluateInOverlay(const String & method,PassRefPtr<JSONValue> argument)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
onTimer(Timer<InspectorOverlay> *)795 void InspectorOverlay::onTimer(Timer<InspectorOverlay>*)
796 {
797 m_drawViewSize = false;
798 update();
799 }
800
getBoxModel(Node * node,RefPtr<TypeBuilder::DOM::BoxModel> & model)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
freePage()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
startedRecordingProfile()849 void InspectorOverlay::startedRecordingProfile()
850 {
851 if (!m_activeProfilerCount++)
852 freePage();
853 }
854
855 } // namespace blink
856