1 /* 2 * Copyright (C) 2009 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 COMPUTER, 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 26 #include "config.h" 27 28 #include "core/platform/graphics/GraphicsLayer.h" 29 30 #include "SkImageFilter.h" 31 #include "SkMatrix44.h" 32 #include "core/platform/ScrollableArea.h" 33 #include "core/platform/graphics/FloatPoint.h" 34 #include "core/platform/graphics/FloatRect.h" 35 #include "core/platform/graphics/GraphicsContext.h" 36 #include "core/platform/graphics/GraphicsLayerFactory.h" 37 #include "core/platform/graphics/LayoutRect.h" 38 #include "core/platform/graphics/chromium/AnimationTranslationUtil.h" 39 #include "core/platform/graphics/chromium/TransformSkMatrix44Conversions.h" 40 #include "core/platform/graphics/filters/SkiaImageFilterBuilder.h" 41 #include "core/platform/graphics/skia/NativeImageSkia.h" 42 #include "core/platform/graphics/transforms/RotateTransformOperation.h" 43 #include "core/platform/text/TextStream.h" 44 45 #include "wtf/CurrentTime.h" 46 #include "wtf/HashMap.h" 47 #include "wtf/HashSet.h" 48 #include "wtf/text/CString.h" 49 #include "wtf/text/StringBuilder.h" 50 #include "wtf/text/StringHash.h" 51 #include "wtf/text/WTFString.h" 52 53 #include "public/platform/Platform.h" 54 #include "public/platform/WebAnimation.h" 55 #include "public/platform/WebCompositorSupport.h" 56 #include "public/platform/WebFilterOperations.h" 57 #include "public/platform/WebFloatPoint.h" 58 #include "public/platform/WebFloatRect.h" 59 #include "public/platform/WebLayer.h" 60 #include "public/platform/WebPoint.h" 61 #include "public/platform/WebSize.h" 62 63 #ifndef NDEBUG 64 #include <stdio.h> 65 #endif 66 67 using WebKit::Platform; 68 using WebKit::WebAnimation; 69 using WebKit::WebFilterOperations; 70 using WebKit::WebLayer; 71 using WebKit::WebPoint; 72 73 namespace WebCore { 74 75 typedef HashMap<const GraphicsLayer*, Vector<FloatRect> > RepaintMap; 76 static RepaintMap& repaintRectMap() 77 { 78 DEFINE_STATIC_LOCAL(RepaintMap, map, ()); 79 return map; 80 } 81 82 void KeyframeValueList::insert(PassOwnPtr<const AnimationValue> value) 83 { 84 for (size_t i = 0; i < m_values.size(); ++i) { 85 const AnimationValue* curValue = m_values[i].get(); 86 if (curValue->keyTime() == value->keyTime()) { 87 ASSERT_NOT_REACHED(); 88 // insert after 89 m_values.insert(i + 1, value); 90 return; 91 } 92 if (curValue->keyTime() > value->keyTime()) { 93 // insert before 94 m_values.insert(i, value); 95 return; 96 } 97 } 98 99 m_values.append(value); 100 } 101 102 PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerFactory* factory, GraphicsLayerClient* client) 103 { 104 return factory->createGraphicsLayer(client); 105 } 106 107 GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client) 108 : m_client(client) 109 , m_anchorPoint(0.5f, 0.5f, 0) 110 , m_opacity(1) 111 , m_zPosition(0) 112 , m_contentsOpaque(false) 113 , m_preserves3D(false) 114 , m_backfaceVisibility(true) 115 , m_masksToBounds(false) 116 , m_drawsContent(false) 117 , m_contentsVisible(true) 118 , m_showRepaintCounter(false) 119 , m_paintingPhase(GraphicsLayerPaintAllWithOverflowClip) 120 , m_contentsOrientation(CompositingCoordinatesTopDown) 121 , m_parent(0) 122 , m_maskLayer(0) 123 , m_replicaLayer(0) 124 , m_replicatedLayer(0) 125 , m_repaintCount(0) 126 , m_contentsLayer(0) 127 , m_contentsLayerId(0) 128 , m_linkHighlight(0) 129 , m_contentsLayerPurpose(NoContentsLayer) 130 , m_scrollableArea(0) 131 { 132 #ifndef NDEBUG 133 if (m_client) 134 m_client->verifyNotPainting(); 135 #endif 136 137 m_opaqueRectTrackingContentLayerDelegate = adoptPtr(new OpaqueRectTrackingContentLayerDelegate(this)); 138 m_layer = adoptPtr(Platform::current()->compositorSupport()->createContentLayer(m_opaqueRectTrackingContentLayerDelegate.get())); 139 m_layer->layer()->setDrawsContent(m_drawsContent && m_contentsVisible); 140 m_layer->setAutomaticallyComputeRasterScale(true); 141 } 142 143 GraphicsLayer::~GraphicsLayer() 144 { 145 if (m_linkHighlight) { 146 m_linkHighlight->clearCurrentGraphicsLayer(); 147 m_linkHighlight = 0; 148 } 149 150 #ifndef NDEBUG 151 if (m_client) 152 m_client->verifyNotPainting(); 153 #endif 154 155 if (m_replicaLayer) 156 m_replicaLayer->setReplicatedLayer(0); 157 158 if (m_replicatedLayer) 159 m_replicatedLayer->setReplicatedByLayer(0); 160 161 removeAllChildren(); 162 removeFromParent(); 163 164 resetTrackedRepaints(); 165 ASSERT(!m_parent); 166 } 167 168 void GraphicsLayer::setParent(GraphicsLayer* layer) 169 { 170 ASSERT(!layer || !layer->hasAncestor(this)); 171 m_parent = layer; 172 } 173 174 bool GraphicsLayer::hasAncestor(GraphicsLayer* ancestor) const 175 { 176 for (GraphicsLayer* curr = parent(); curr; curr = curr->parent()) { 177 if (curr == ancestor) 178 return true; 179 } 180 181 return false; 182 } 183 184 bool GraphicsLayer::setChildren(const Vector<GraphicsLayer*>& newChildren) 185 { 186 // If the contents of the arrays are the same, nothing to do. 187 if (newChildren == m_children) 188 return false; 189 190 removeAllChildren(); 191 192 size_t listSize = newChildren.size(); 193 for (size_t i = 0; i < listSize; ++i) 194 addChildInternal(newChildren[i]); 195 196 updateChildList(); 197 198 return true; 199 } 200 201 void GraphicsLayer::addChildInternal(GraphicsLayer* childLayer) 202 { 203 ASSERT(childLayer != this); 204 205 if (childLayer->parent()) 206 childLayer->removeFromParent(); 207 208 childLayer->setParent(this); 209 m_children.append(childLayer); 210 211 // Don't call updateChildList here, this function is used in cases where it 212 // should not be called until all children are processed. 213 } 214 215 void GraphicsLayer::addChild(GraphicsLayer* childLayer) 216 { 217 addChildInternal(childLayer); 218 updateChildList(); 219 } 220 221 void GraphicsLayer::addChildAtIndex(GraphicsLayer* childLayer, int index) 222 { 223 ASSERT(childLayer != this); 224 225 if (childLayer->parent()) 226 childLayer->removeFromParent(); 227 228 childLayer->setParent(this); 229 m_children.insert(index, childLayer); 230 231 updateChildList(); 232 } 233 234 void GraphicsLayer::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) 235 { 236 ASSERT(childLayer != this); 237 childLayer->removeFromParent(); 238 239 bool found = false; 240 for (unsigned i = 0; i < m_children.size(); i++) { 241 if (sibling == m_children[i]) { 242 m_children.insert(i, childLayer); 243 found = true; 244 break; 245 } 246 } 247 248 childLayer->setParent(this); 249 250 if (!found) 251 m_children.append(childLayer); 252 253 updateChildList(); 254 } 255 256 void GraphicsLayer::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) 257 { 258 childLayer->removeFromParent(); 259 ASSERT(childLayer != this); 260 261 bool found = false; 262 for (unsigned i = 0; i < m_children.size(); i++) { 263 if (sibling == m_children[i]) { 264 m_children.insert(i+1, childLayer); 265 found = true; 266 break; 267 } 268 } 269 270 childLayer->setParent(this); 271 272 if (!found) 273 m_children.append(childLayer); 274 275 updateChildList(); 276 } 277 278 bool GraphicsLayer::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) 279 { 280 ASSERT(!newChild->parent()); 281 bool found = false; 282 for (unsigned i = 0; i < m_children.size(); i++) { 283 if (oldChild == m_children[i]) { 284 m_children[i] = newChild; 285 found = true; 286 break; 287 } 288 } 289 290 if (found) { 291 oldChild->setParent(0); 292 293 newChild->removeFromParent(); 294 newChild->setParent(this); 295 296 updateChildList(); 297 return true; 298 } 299 300 return false; 301 } 302 303 void GraphicsLayer::removeAllChildren() 304 { 305 while (m_children.size()) { 306 GraphicsLayer* curLayer = m_children[0]; 307 ASSERT(curLayer->parent()); 308 curLayer->removeFromParent(); 309 } 310 } 311 312 void GraphicsLayer::removeFromParent() 313 { 314 if (m_parent) { 315 unsigned i; 316 for (i = 0; i < m_parent->m_children.size(); i++) { 317 if (this == m_parent->m_children[i]) { 318 m_parent->m_children.remove(i); 319 break; 320 } 321 } 322 323 setParent(0); 324 } 325 326 platformLayer()->removeFromParent(); 327 } 328 329 void GraphicsLayer::setReplicatedByLayer(GraphicsLayer* layer) 330 { 331 // FIXME: this could probably be a full early exit. 332 if (m_replicaLayer != layer) { 333 if (m_replicaLayer) 334 m_replicaLayer->setReplicatedLayer(0); 335 336 if (layer) 337 layer->setReplicatedLayer(this); 338 339 m_replicaLayer = layer; 340 } 341 342 WebLayer* webReplicaLayer = layer ? layer->platformLayer() : 0; 343 platformLayer()->setReplicaLayer(webReplicaLayer); 344 } 345 346 void GraphicsLayer::setOffsetFromRenderer(const IntSize& offset, ShouldSetNeedsDisplay shouldSetNeedsDisplay) 347 { 348 if (offset == m_offsetFromRenderer) 349 return; 350 351 m_offsetFromRenderer = offset; 352 353 // If the compositing layer offset changes, we need to repaint. 354 if (shouldSetNeedsDisplay == SetNeedsDisplay) 355 setNeedsDisplay(); 356 } 357 358 void GraphicsLayer::paintGraphicsLayerContents(GraphicsContext& context, const IntRect& clip) 359 { 360 if (m_client) 361 m_client->paintContents(this, context, m_paintingPhase, clip); 362 } 363 364 String GraphicsLayer::animationNameForTransition(AnimatedPropertyID property) 365 { 366 // | is not a valid identifier character in CSS, so this can never conflict with a keyframe identifier. 367 StringBuilder id; 368 id.appendLiteral("-|transition"); 369 id.appendNumber(static_cast<int>(property)); 370 id.append('-'); 371 return id.toString(); 372 } 373 374 void GraphicsLayer::setZPosition(float position) 375 { 376 m_zPosition = position; 377 } 378 379 float GraphicsLayer::accumulatedOpacity() const 380 { 381 if (!preserves3D()) 382 return 1; 383 384 return m_opacity * (parent() ? parent()->accumulatedOpacity() : 1); 385 } 386 387 void GraphicsLayer::distributeOpacity(float accumulatedOpacity) 388 { 389 // If this is a transform layer we need to distribute our opacity to all our children 390 391 // Incoming accumulatedOpacity is the contribution from our parent(s). We mutiply this by our own 392 // opacity to get the total contribution 393 accumulatedOpacity *= m_opacity; 394 395 if (preserves3D()) { 396 size_t numChildren = children().size(); 397 for (size_t i = 0; i < numChildren; ++i) 398 children()[i]->distributeOpacity(accumulatedOpacity); 399 } 400 } 401 402 static inline const FilterOperations* filterOperationsAt(const KeyframeValueList& valueList, size_t index) 403 { 404 return static_cast<const FilterAnimationValue*>(valueList.at(index))->value(); 405 } 406 407 int GraphicsLayer::validateFilterOperations(const KeyframeValueList& valueList) 408 { 409 ASSERT(valueList.property() == AnimatedPropertyWebkitFilter); 410 411 if (valueList.size() < 2) 412 return -1; 413 414 // Empty filters match anything, so find the first non-empty entry as the reference 415 size_t firstIndex = 0; 416 for ( ; firstIndex < valueList.size(); ++firstIndex) { 417 if (filterOperationsAt(valueList, firstIndex)->operations().size() > 0) 418 break; 419 } 420 421 if (firstIndex >= valueList.size()) 422 return -1; 423 424 const FilterOperations* firstVal = filterOperationsAt(valueList, firstIndex); 425 426 for (size_t i = firstIndex + 1; i < valueList.size(); ++i) { 427 const FilterOperations* val = filterOperationsAt(valueList, i); 428 429 // An emtpy filter list matches anything. 430 if (val->operations().isEmpty()) 431 continue; 432 433 if (!firstVal->operationsMatch(*val)) 434 return -1; 435 } 436 437 return firstIndex; 438 } 439 440 // An "invalid" list is one whose functions don't match, and therefore has to be animated as a Matrix 441 // The hasBigRotation flag will always return false if isValid is false. Otherwise hasBigRotation is 442 // true if the rotation between any two keyframes is >= 180 degrees. 443 444 static inline const TransformOperations* operationsAt(const KeyframeValueList& valueList, size_t index) 445 { 446 return static_cast<const TransformAnimationValue*>(valueList.at(index))->value(); 447 } 448 449 int GraphicsLayer::validateTransformOperations(const KeyframeValueList& valueList, bool& hasBigRotation) 450 { 451 ASSERT(valueList.property() == AnimatedPropertyWebkitTransform); 452 453 hasBigRotation = false; 454 455 if (valueList.size() < 2) 456 return -1; 457 458 // Empty transforms match anything, so find the first non-empty entry as the reference. 459 size_t firstIndex = 0; 460 for ( ; firstIndex < valueList.size(); ++firstIndex) { 461 if (operationsAt(valueList, firstIndex)->operations().size() > 0) 462 break; 463 } 464 465 if (firstIndex >= valueList.size()) 466 return -1; 467 468 const TransformOperations* firstVal = operationsAt(valueList, firstIndex); 469 470 // See if the keyframes are valid. 471 for (size_t i = firstIndex + 1; i < valueList.size(); ++i) { 472 const TransformOperations* val = operationsAt(valueList, i); 473 474 // An emtpy transform list matches anything. 475 if (val->operations().isEmpty()) 476 continue; 477 478 if (!firstVal->operationsMatch(*val)) 479 return -1; 480 } 481 482 // Keyframes are valid, check for big rotations. 483 double lastRotAngle = 0.0; 484 double maxRotAngle = -1.0; 485 486 for (size_t j = 0; j < firstVal->operations().size(); ++j) { 487 TransformOperation::OperationType type = firstVal->operations().at(j)->getOperationType(); 488 489 // if this is a rotation entry, we need to see if any angle differences are >= 180 deg 490 if (type == TransformOperation::RotateX 491 || type == TransformOperation::RotateY 492 || type == TransformOperation::RotateZ 493 || type == TransformOperation::Rotate3D) { 494 lastRotAngle = static_cast<RotateTransformOperation*>(firstVal->operations().at(j).get())->angle(); 495 496 if (maxRotAngle < 0) 497 maxRotAngle = fabs(lastRotAngle); 498 499 for (size_t i = firstIndex + 1; i < valueList.size(); ++i) { 500 const TransformOperations* val = operationsAt(valueList, i); 501 double rotAngle = val->operations().isEmpty() ? 0 : (static_cast<RotateTransformOperation*>(val->operations().at(j).get())->angle()); 502 double diffAngle = fabs(rotAngle - lastRotAngle); 503 if (diffAngle > maxRotAngle) 504 maxRotAngle = diffAngle; 505 lastRotAngle = rotAngle; 506 } 507 } 508 } 509 510 hasBigRotation = maxRotAngle >= 180.0; 511 512 return firstIndex; 513 } 514 515 void GraphicsLayer::updateNames() 516 { 517 String debugName = "Layer for " + m_nameBase; 518 m_layer->layer()->setDebugName(debugName); 519 520 if (WebLayer* contentsLayer = contentsLayerIfRegistered()) { 521 String debugName = "ContentsLayer for " + m_nameBase; 522 contentsLayer->setDebugName(debugName); 523 } 524 if (m_linkHighlight) { 525 String debugName = "LinkHighlight for " + m_nameBase; 526 m_linkHighlight->layer()->setDebugName(debugName); 527 } 528 } 529 530 void GraphicsLayer::updateChildList() 531 { 532 WebLayer* childHost = m_layer->layer(); 533 childHost->removeAllChildren(); 534 535 clearContentsLayerIfUnregistered(); 536 537 if (m_contentsLayer) { 538 // FIXME: add the contents layer in the correct order with negative z-order children. 539 // This does not cause visible rendering issues because currently contents layers are only used 540 // for replaced elements that don't have children. 541 childHost->addChild(m_contentsLayer); 542 } 543 544 const Vector<GraphicsLayer*>& childLayers = children(); 545 size_t numChildren = childLayers.size(); 546 for (size_t i = 0; i < numChildren; ++i) { 547 GraphicsLayer* curChild = childLayers[i]; 548 549 childHost->addChild(curChild->platformLayer()); 550 } 551 552 if (m_linkHighlight) 553 childHost->addChild(m_linkHighlight->layer()); 554 } 555 556 void GraphicsLayer::updateLayerIsDrawable() 557 { 558 // For the rest of the accelerated compositor code, there is no reason to make a 559 // distinction between drawsContent and contentsVisible. So, for m_layer->layer(), these two 560 // flags are combined here. m_contentsLayer shouldn't receive the drawsContent flag 561 // so it is only given contentsVisible. 562 563 m_layer->layer()->setDrawsContent(m_drawsContent && m_contentsVisible); 564 if (WebLayer* contentsLayer = contentsLayerIfRegistered()) 565 contentsLayer->setDrawsContent(m_contentsVisible); 566 567 if (m_drawsContent) { 568 m_layer->layer()->invalidate(); 569 if (m_linkHighlight) 570 m_linkHighlight->invalidate(); 571 } 572 } 573 574 void GraphicsLayer::updateContentsRect() 575 { 576 WebLayer* contentsLayer = contentsLayerIfRegistered(); 577 if (!contentsLayer) 578 return; 579 580 contentsLayer->setPosition(FloatPoint(m_contentsRect.x(), m_contentsRect.y())); 581 contentsLayer->setBounds(IntSize(m_contentsRect.width(), m_contentsRect.height())); 582 } 583 584 static HashSet<int>* s_registeredLayerSet; 585 586 void GraphicsLayer::registerContentsLayer(WebLayer* layer) 587 { 588 if (!s_registeredLayerSet) 589 s_registeredLayerSet = new HashSet<int>; 590 if (s_registeredLayerSet->contains(layer->id())) 591 CRASH(); 592 s_registeredLayerSet->add(layer->id()); 593 } 594 595 void GraphicsLayer::unregisterContentsLayer(WebLayer* layer) 596 { 597 ASSERT(s_registeredLayerSet); 598 if (!s_registeredLayerSet->contains(layer->id())) 599 CRASH(); 600 s_registeredLayerSet->remove(layer->id()); 601 } 602 603 void GraphicsLayer::setContentsTo(ContentsLayerPurpose purpose, WebLayer* layer) 604 { 605 bool childrenChanged = false; 606 if (layer) { 607 ASSERT(s_registeredLayerSet); 608 if (!s_registeredLayerSet->contains(layer->id())) 609 CRASH(); 610 if (m_contentsLayerId != layer->id()) { 611 setupContentsLayer(layer); 612 m_contentsLayerPurpose = purpose; 613 childrenChanged = true; 614 } 615 updateContentsRect(); 616 } else { 617 if (m_contentsLayer) { 618 childrenChanged = true; 619 620 // The old contents layer will be removed via updateChildList. 621 m_contentsLayer = 0; 622 } 623 } 624 625 if (childrenChanged) 626 updateChildList(); 627 } 628 629 void GraphicsLayer::setupContentsLayer(WebLayer* contentsLayer) 630 { 631 m_contentsLayer = contentsLayer; 632 m_contentsLayerId = m_contentsLayer->id(); 633 634 if (m_contentsLayer) { 635 m_contentsLayer->setAnchorPoint(FloatPoint(0, 0)); 636 m_contentsLayer->setUseParentBackfaceVisibility(true); 637 638 // It is necessary to call setDrawsContent as soon as we receive the new contentsLayer, for 639 // the correctness of early exit conditions in setDrawsContent() and setContentsVisible(). 640 m_contentsLayer->setDrawsContent(m_contentsVisible); 641 642 // Insert the content layer first. Video elements require this, because they have 643 // shadow content that must display in front of the video. 644 m_layer->layer()->insertChild(m_contentsLayer, 0); 645 } 646 updateNames(); 647 } 648 649 void GraphicsLayer::clearContentsLayerIfUnregistered() 650 { 651 if (!m_contentsLayerId || s_registeredLayerSet->contains(m_contentsLayerId)) 652 return; 653 654 m_contentsLayer = 0; 655 m_contentsLayerId = 0; 656 } 657 658 WebLayer* GraphicsLayer::contentsLayerIfRegistered() 659 { 660 clearContentsLayerIfUnregistered(); 661 return m_contentsLayer; 662 } 663 664 double GraphicsLayer::backingStoreMemoryEstimate() const 665 { 666 if (!drawsContent()) 667 return 0; 668 669 // Effects of page and device scale are ignored; subclasses should override to take these into account. 670 return static_cast<double>(4 * size().width()) * size().height(); 671 } 672 673 void GraphicsLayer::resetTrackedRepaints() 674 { 675 repaintRectMap().remove(this); 676 } 677 678 void GraphicsLayer::addRepaintRect(const FloatRect& repaintRect) 679 { 680 if (m_client->isTrackingRepaints()) { 681 FloatRect largestRepaintRect(FloatPoint(), m_size); 682 largestRepaintRect.intersect(repaintRect); 683 RepaintMap::iterator repaintIt = repaintRectMap().find(this); 684 if (repaintIt == repaintRectMap().end()) { 685 Vector<FloatRect> repaintRects; 686 repaintRects.append(largestRepaintRect); 687 repaintRectMap().set(this, repaintRects); 688 } else { 689 Vector<FloatRect>& repaintRects = repaintIt->value; 690 repaintRects.append(largestRepaintRect); 691 } 692 } 693 } 694 695 void GraphicsLayer::writeIndent(TextStream& ts, int indent) 696 { 697 for (int i = 0; i != indent; ++i) 698 ts << " "; 699 } 700 701 void GraphicsLayer::dumpLayer(TextStream& ts, int indent, LayerTreeFlags flags) const 702 { 703 writeIndent(ts, indent); 704 ts << "(" << "GraphicsLayer"; 705 706 if (flags & LayerTreeIncludesDebugInfo) { 707 ts << " " << static_cast<void*>(const_cast<GraphicsLayer*>(this)); 708 ts << " \"" << m_name << "\""; 709 } 710 711 ts << "\n"; 712 dumpProperties(ts, indent, flags); 713 writeIndent(ts, indent); 714 ts << ")\n"; 715 } 716 717 void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeFlags flags) const 718 { 719 if (m_position != FloatPoint()) { 720 writeIndent(ts, indent + 1); 721 ts << "(position " << m_position.x() << " " << m_position.y() << ")\n"; 722 } 723 724 if (m_boundsOrigin != FloatPoint()) { 725 writeIndent(ts, indent + 1); 726 ts << "(bounds origin " << m_boundsOrigin.x() << " " << m_boundsOrigin.y() << ")\n"; 727 } 728 729 if (m_anchorPoint != FloatPoint3D(0.5f, 0.5f, 0)) { 730 writeIndent(ts, indent + 1); 731 ts << "(anchor " << m_anchorPoint.x() << " " << m_anchorPoint.y() << ")\n"; 732 } 733 734 if (m_size != IntSize()) { 735 writeIndent(ts, indent + 1); 736 ts << "(bounds " << m_size.width() << " " << m_size.height() << ")\n"; 737 } 738 739 if (m_opacity != 1) { 740 writeIndent(ts, indent + 1); 741 ts << "(opacity " << m_opacity << ")\n"; 742 } 743 744 if (m_contentsOpaque) { 745 writeIndent(ts, indent + 1); 746 ts << "(contentsOpaque " << m_contentsOpaque << ")\n"; 747 } 748 749 if (m_preserves3D) { 750 writeIndent(ts, indent + 1); 751 ts << "(preserves3D " << m_preserves3D << ")\n"; 752 } 753 754 if (m_drawsContent) { 755 writeIndent(ts, indent + 1); 756 ts << "(drawsContent " << m_drawsContent << ")\n"; 757 } 758 759 if (!m_contentsVisible) { 760 writeIndent(ts, indent + 1); 761 ts << "(contentsVisible " << m_contentsVisible << ")\n"; 762 } 763 764 if (!m_backfaceVisibility) { 765 writeIndent(ts, indent + 1); 766 ts << "(backfaceVisibility " << (m_backfaceVisibility ? "visible" : "hidden") << ")\n"; 767 } 768 769 if (flags & LayerTreeIncludesDebugInfo) { 770 writeIndent(ts, indent + 1); 771 ts << "("; 772 if (m_client) 773 ts << "client " << static_cast<void*>(m_client); 774 else 775 ts << "no client"; 776 ts << ")\n"; 777 } 778 779 if (m_backgroundColor != Color::transparent) { 780 writeIndent(ts, indent + 1); 781 ts << "(backgroundColor " << m_backgroundColor.nameForRenderTreeAsText() << ")\n"; 782 } 783 784 if (!m_transform.isIdentity()) { 785 writeIndent(ts, indent + 1); 786 ts << "(transform "; 787 ts << "[" << m_transform.m11() << " " << m_transform.m12() << " " << m_transform.m13() << " " << m_transform.m14() << "] "; 788 ts << "[" << m_transform.m21() << " " << m_transform.m22() << " " << m_transform.m23() << " " << m_transform.m24() << "] "; 789 ts << "[" << m_transform.m31() << " " << m_transform.m32() << " " << m_transform.m33() << " " << m_transform.m34() << "] "; 790 ts << "[" << m_transform.m41() << " " << m_transform.m42() << " " << m_transform.m43() << " " << m_transform.m44() << "])\n"; 791 } 792 793 // Avoid dumping the sublayer transform on the root layer, because it's used for geometry flipping, whose behavior 794 // differs between platforms. 795 if (parent() && !m_childrenTransform.isIdentity()) { 796 writeIndent(ts, indent + 1); 797 ts << "(childrenTransform "; 798 ts << "[" << m_childrenTransform.m11() << " " << m_childrenTransform.m12() << " " << m_childrenTransform.m13() << " " << m_childrenTransform.m14() << "] "; 799 ts << "[" << m_childrenTransform.m21() << " " << m_childrenTransform.m22() << " " << m_childrenTransform.m23() << " " << m_childrenTransform.m24() << "] "; 800 ts << "[" << m_childrenTransform.m31() << " " << m_childrenTransform.m32() << " " << m_childrenTransform.m33() << " " << m_childrenTransform.m34() << "] "; 801 ts << "[" << m_childrenTransform.m41() << " " << m_childrenTransform.m42() << " " << m_childrenTransform.m43() << " " << m_childrenTransform.m44() << "])\n"; 802 } 803 804 if (m_replicaLayer) { 805 writeIndent(ts, indent + 1); 806 ts << "(replica layer"; 807 if (flags & LayerTreeIncludesDebugInfo) 808 ts << " " << m_replicaLayer; 809 ts << ")\n"; 810 m_replicaLayer->dumpLayer(ts, indent + 2, flags); 811 } 812 813 if (m_replicatedLayer) { 814 writeIndent(ts, indent + 1); 815 ts << "(replicated layer"; 816 if (flags & LayerTreeIncludesDebugInfo) 817 ts << " " << m_replicatedLayer; 818 ts << ")\n"; 819 } 820 821 if ((flags & LayerTreeIncludesRepaintRects) && repaintRectMap().contains(this) && !repaintRectMap().get(this).isEmpty()) { 822 writeIndent(ts, indent + 1); 823 ts << "(repaint rects\n"; 824 for (size_t i = 0; i < repaintRectMap().get(this).size(); ++i) { 825 if (repaintRectMap().get(this)[i].isEmpty()) 826 continue; 827 writeIndent(ts, indent + 2); 828 ts << "(rect "; 829 ts << repaintRectMap().get(this)[i].x() << " "; 830 ts << repaintRectMap().get(this)[i].y() << " "; 831 ts << repaintRectMap().get(this)[i].width() << " "; 832 ts << repaintRectMap().get(this)[i].height(); 833 ts << ")\n"; 834 } 835 writeIndent(ts, indent + 1); 836 ts << ")\n"; 837 } 838 839 if ((flags & LayerTreeIncludesPaintingPhases) && paintingPhase()) { 840 writeIndent(ts, indent + 1); 841 ts << "(paintingPhases\n"; 842 if (paintingPhase() & GraphicsLayerPaintBackground) { 843 writeIndent(ts, indent + 2); 844 ts << "GraphicsLayerPaintBackground\n"; 845 } 846 if (paintingPhase() & GraphicsLayerPaintForeground) { 847 writeIndent(ts, indent + 2); 848 ts << "GraphicsLayerPaintForeground\n"; 849 } 850 if (paintingPhase() & GraphicsLayerPaintMask) { 851 writeIndent(ts, indent + 2); 852 ts << "GraphicsLayerPaintMask\n"; 853 } 854 if (paintingPhase() & GraphicsLayerPaintOverflowContents) { 855 writeIndent(ts, indent + 2); 856 ts << "GraphicsLayerPaintOverflowContents\n"; 857 } 858 if (paintingPhase() & GraphicsLayerPaintCompositedScroll) { 859 writeIndent(ts, indent + 2); 860 ts << "GraphicsLayerPaintCompositedScroll\n"; 861 } 862 writeIndent(ts, indent + 1); 863 ts << ")\n"; 864 } 865 866 dumpAdditionalProperties(ts, indent, flags); 867 868 if (m_children.size()) { 869 writeIndent(ts, indent + 1); 870 ts << "(children " << m_children.size() << "\n"; 871 872 unsigned i; 873 for (i = 0; i < m_children.size(); i++) 874 m_children[i]->dumpLayer(ts, indent + 2, flags); 875 writeIndent(ts, indent + 1); 876 ts << ")\n"; 877 } 878 } 879 880 String GraphicsLayer::layerTreeAsText(LayerTreeFlags flags) const 881 { 882 TextStream ts; 883 884 dumpLayer(ts, 0, flags); 885 return ts.release(); 886 } 887 888 void GraphicsLayer::setName(const String& name) 889 { 890 m_nameBase = name; 891 m_name = String::format("GraphicsLayer(%p) ", this) + name; 892 updateNames(); 893 } 894 895 void GraphicsLayer::setCompositingReasons(WebKit::WebCompositingReasons reasons) 896 { 897 m_layer->layer()->setCompositingReasons(reasons); 898 } 899 900 void GraphicsLayer::setPosition(const FloatPoint& point) 901 { 902 m_position = point; 903 platformLayer()->setPosition(m_position); 904 } 905 906 void GraphicsLayer::setAnchorPoint(const FloatPoint3D& point) 907 { 908 m_anchorPoint = point; 909 platformLayer()->setAnchorPoint(FloatPoint(m_anchorPoint.x(), m_anchorPoint.y())); 910 platformLayer()->setAnchorPointZ(m_anchorPoint.z()); 911 } 912 913 void GraphicsLayer::setSize(const FloatSize& size) 914 { 915 // We are receiving negative sizes here that cause assertions to fail in the compositor. Clamp them to 0 to 916 // avoid those assertions. 917 // FIXME: This should be an ASSERT instead, as negative sizes should not exist in WebCore. 918 FloatSize clampedSize = size; 919 if (clampedSize.width() < 0 || clampedSize.height() < 0) 920 clampedSize = FloatSize(); 921 922 if (clampedSize == m_size) 923 return; 924 925 m_size = clampedSize; 926 927 m_layer->layer()->setBounds(flooredIntSize(m_size)); 928 // Note that we don't resize m_contentsLayer. It's up the caller to do that. 929 } 930 931 void GraphicsLayer::setTransform(const TransformationMatrix& transform) 932 { 933 m_transform = transform; 934 platformLayer()->setTransform(TransformSkMatrix44Conversions::convert(m_transform)); 935 } 936 937 void GraphicsLayer::setChildrenTransform(const TransformationMatrix& transform) 938 { 939 m_childrenTransform = transform; 940 platformLayer()->setSublayerTransform(TransformSkMatrix44Conversions::convert(m_childrenTransform)); 941 } 942 943 void GraphicsLayer::setPreserves3D(bool preserves3D) 944 { 945 if (preserves3D == m_preserves3D) 946 return; 947 948 m_preserves3D = preserves3D; 949 m_layer->layer()->setPreserves3D(m_preserves3D); 950 } 951 952 void GraphicsLayer::setMasksToBounds(bool masksToBounds) 953 { 954 m_masksToBounds = masksToBounds; 955 m_layer->layer()->setMasksToBounds(m_masksToBounds); 956 } 957 958 void GraphicsLayer::setDrawsContent(bool drawsContent) 959 { 960 // Note carefully this early-exit is only correct because we also properly call 961 // WebLayer::setDrawsContent whenever m_contentsLayer is set to a new layer in setupContentsLayer(). 962 if (drawsContent == m_drawsContent) 963 return; 964 965 m_drawsContent = drawsContent; 966 updateLayerIsDrawable(); 967 } 968 969 void GraphicsLayer::setContentsVisible(bool contentsVisible) 970 { 971 // Note carefully this early-exit is only correct because we also properly call 972 // WebLayer::setDrawsContent whenever m_contentsLayer is set to a new layer in setupContentsLayer(). 973 if (contentsVisible == m_contentsVisible) 974 return; 975 976 m_contentsVisible = contentsVisible; 977 updateLayerIsDrawable(); 978 } 979 980 void GraphicsLayer::setBackgroundColor(const Color& color) 981 { 982 if (color == m_backgroundColor) 983 return; 984 985 m_backgroundColor = color; 986 m_layer->layer()->setBackgroundColor(m_backgroundColor.rgb()); 987 } 988 989 void GraphicsLayer::setContentsOpaque(bool opaque) 990 { 991 m_contentsOpaque = opaque; 992 m_layer->layer()->setOpaque(m_contentsOpaque); 993 m_opaqueRectTrackingContentLayerDelegate->setOpaque(m_contentsOpaque); 994 } 995 996 void GraphicsLayer::setMaskLayer(GraphicsLayer* maskLayer) 997 { 998 if (maskLayer == m_maskLayer) 999 return; 1000 1001 m_maskLayer = maskLayer; 1002 WebLayer* maskWebLayer = m_maskLayer ? m_maskLayer->platformLayer() : 0; 1003 m_layer->layer()->setMaskLayer(maskWebLayer); 1004 } 1005 1006 void GraphicsLayer::setBackfaceVisibility(bool visible) 1007 { 1008 m_backfaceVisibility = visible; 1009 m_layer->setDoubleSided(m_backfaceVisibility); 1010 } 1011 1012 void GraphicsLayer::setOpacity(float opacity) 1013 { 1014 float clampedOpacity = std::max(std::min(opacity, 1.0f), 0.0f); 1015 m_opacity = clampedOpacity; 1016 platformLayer()->setOpacity(opacity); 1017 } 1018 1019 void GraphicsLayer::setContentsNeedsDisplay() 1020 { 1021 if (WebLayer* contentsLayer = contentsLayerIfRegistered()) { 1022 contentsLayer->invalidate(); 1023 addRepaintRect(contentsRect()); 1024 } 1025 } 1026 1027 void GraphicsLayer::setNeedsDisplay() 1028 { 1029 if (drawsContent()) { 1030 m_layer->layer()->invalidate(); 1031 addRepaintRect(FloatRect(FloatPoint(), m_size)); 1032 if (m_linkHighlight) 1033 m_linkHighlight->invalidate(); 1034 } 1035 } 1036 1037 void GraphicsLayer::setNeedsDisplayInRect(const FloatRect& rect) 1038 { 1039 if (drawsContent()) { 1040 m_layer->layer()->invalidateRect(rect); 1041 addRepaintRect(rect); 1042 if (m_linkHighlight) 1043 m_linkHighlight->invalidate(); 1044 } 1045 } 1046 1047 void GraphicsLayer::setContentsRect(const IntRect& rect) 1048 { 1049 if (rect == m_contentsRect) 1050 return; 1051 1052 m_contentsRect = rect; 1053 updateContentsRect(); 1054 } 1055 1056 void GraphicsLayer::setContentsToImage(Image* image) 1057 { 1058 bool childrenChanged = false; 1059 RefPtr<NativeImageSkia> nativeImage = image ? image->nativeImageForCurrentFrame() : 0; 1060 if (nativeImage) { 1061 if (m_contentsLayerPurpose != ContentsLayerForImage) { 1062 m_imageLayer = adoptPtr(Platform::current()->compositorSupport()->createImageLayer()); 1063 registerContentsLayer(m_imageLayer->layer()); 1064 1065 setupContentsLayer(m_imageLayer->layer()); 1066 m_contentsLayerPurpose = ContentsLayerForImage; 1067 childrenChanged = true; 1068 } 1069 m_imageLayer->setBitmap(nativeImage->bitmap()); 1070 m_imageLayer->layer()->setOpaque(image->currentFrameKnownToBeOpaque()); 1071 updateContentsRect(); 1072 } else { 1073 if (m_imageLayer) { 1074 childrenChanged = true; 1075 1076 unregisterContentsLayer(m_imageLayer->layer()); 1077 m_imageLayer.clear(); 1078 } 1079 // The old contents layer will be removed via updateChildList. 1080 m_contentsLayer = 0; 1081 } 1082 1083 if (childrenChanged) 1084 updateChildList(); 1085 } 1086 1087 void GraphicsLayer::setContentsToCanvas(WebLayer* layer) 1088 { 1089 setContentsTo(ContentsLayerForCanvas, layer); 1090 } 1091 1092 void GraphicsLayer::setContentsToMedia(WebLayer* layer) 1093 { 1094 setContentsTo(ContentsLayerForVideo, layer); 1095 } 1096 1097 bool GraphicsLayer::addAnimation(const KeyframeValueList& values, const IntSize& boxSize, const CSSAnimationData* animation, const String& animationName, double timeOffset) 1098 { 1099 platformLayer()->setAnimationDelegate(this); 1100 1101 int animationId = 0; 1102 1103 if (m_animationIdMap.contains(animationName)) 1104 animationId = m_animationIdMap.get(animationName); 1105 1106 OwnPtr<WebAnimation> toAdd(createWebAnimation(values, animation, animationId, timeOffset, boxSize)); 1107 1108 if (toAdd) { 1109 animationId = toAdd->id(); 1110 m_animationIdMap.set(animationName, animationId); 1111 1112 // Remove any existing animations with the same animation id and target property. 1113 platformLayer()->removeAnimation(animationId, toAdd->targetProperty()); 1114 return platformLayer()->addAnimation(toAdd.get()); 1115 } 1116 1117 return false; 1118 } 1119 1120 void GraphicsLayer::pauseAnimation(const String& animationName, double timeOffset) 1121 { 1122 if (m_animationIdMap.contains(animationName)) 1123 platformLayer()->pauseAnimation(m_animationIdMap.get(animationName), timeOffset); 1124 } 1125 1126 void GraphicsLayer::removeAnimation(const String& animationName) 1127 { 1128 if (m_animationIdMap.contains(animationName)) 1129 platformLayer()->removeAnimation(m_animationIdMap.get(animationName)); 1130 } 1131 1132 void GraphicsLayer::suspendAnimations(double wallClockTime) 1133 { 1134 // |wallClockTime| is in the wrong time base. Need to convert here. 1135 // FIXME: find a more reliable way to do this. 1136 double monotonicTime = wallClockTime + monotonicallyIncreasingTime() - currentTime(); 1137 platformLayer()->suspendAnimations(monotonicTime); 1138 } 1139 1140 void GraphicsLayer::resumeAnimations() 1141 { 1142 platformLayer()->resumeAnimations(monotonicallyIncreasingTime()); 1143 } 1144 1145 WebLayer* GraphicsLayer::platformLayer() const 1146 { 1147 return m_layer->layer(); 1148 } 1149 1150 static bool copyWebCoreFilterOperationsToWebFilterOperations(const FilterOperations& filters, WebFilterOperations& webFilters) 1151 { 1152 for (size_t i = 0; i < filters.size(); ++i) { 1153 const FilterOperation& op = *filters.at(i); 1154 switch (op.getOperationType()) { 1155 case FilterOperation::REFERENCE: 1156 return false; // Not supported. 1157 case FilterOperation::GRAYSCALE: 1158 case FilterOperation::SEPIA: 1159 case FilterOperation::SATURATE: 1160 case FilterOperation::HUE_ROTATE: { 1161 float amount = static_cast<const BasicColorMatrixFilterOperation*>(&op)->amount(); 1162 switch (op.getOperationType()) { 1163 case FilterOperation::GRAYSCALE: 1164 webFilters.appendGrayscaleFilter(amount); 1165 break; 1166 case FilterOperation::SEPIA: 1167 webFilters.appendSepiaFilter(amount); 1168 break; 1169 case FilterOperation::SATURATE: 1170 webFilters.appendSaturateFilter(amount); 1171 break; 1172 case FilterOperation::HUE_ROTATE: 1173 webFilters.appendHueRotateFilter(amount); 1174 break; 1175 default: 1176 ASSERT_NOT_REACHED(); 1177 } 1178 break; 1179 } 1180 case FilterOperation::INVERT: 1181 case FilterOperation::OPACITY: 1182 case FilterOperation::BRIGHTNESS: 1183 case FilterOperation::CONTRAST: { 1184 float amount = static_cast<const BasicComponentTransferFilterOperation*>(&op)->amount(); 1185 switch (op.getOperationType()) { 1186 case FilterOperation::INVERT: 1187 webFilters.appendInvertFilter(amount); 1188 break; 1189 case FilterOperation::OPACITY: 1190 webFilters.appendOpacityFilter(amount); 1191 break; 1192 case FilterOperation::BRIGHTNESS: 1193 webFilters.appendBrightnessFilter(amount); 1194 break; 1195 case FilterOperation::CONTRAST: 1196 webFilters.appendContrastFilter(amount); 1197 break; 1198 default: 1199 ASSERT_NOT_REACHED(); 1200 } 1201 break; 1202 } 1203 case FilterOperation::BLUR: { 1204 float pixelRadius = static_cast<const BlurFilterOperation*>(&op)->stdDeviation().getFloatValue(); 1205 webFilters.appendBlurFilter(pixelRadius); 1206 break; 1207 } 1208 case FilterOperation::DROP_SHADOW: { 1209 const DropShadowFilterOperation& dropShadowOp = *static_cast<const DropShadowFilterOperation*>(&op); 1210 webFilters.appendDropShadowFilter(WebPoint(dropShadowOp.x(), dropShadowOp.y()), dropShadowOp.stdDeviation(), dropShadowOp.color().rgb()); 1211 break; 1212 } 1213 case FilterOperation::CUSTOM: 1214 case FilterOperation::VALIDATED_CUSTOM: 1215 return false; // Not supported. 1216 case FilterOperation::PASSTHROUGH: 1217 case FilterOperation::NONE: 1218 break; 1219 } 1220 } 1221 return true; 1222 } 1223 1224 bool GraphicsLayer::setFilters(const FilterOperations& filters) 1225 { 1226 // FIXME: For now, we only use SkImageFilters if there is a reference 1227 // filter in the chain. Once all issues have been ironed out, we should 1228 // switch all filtering over to this path, and remove setFilters() and 1229 // WebFilterOperations altogether. 1230 if (filters.hasReferenceFilter()) { 1231 if (filters.hasCustomFilter()) { 1232 // Make sure the filters are removed from the platform layer, as they are 1233 // going to fallback to software mode. 1234 m_layer->layer()->setFilter(0); 1235 m_filters = FilterOperations(); 1236 return false; 1237 } 1238 SkiaImageFilterBuilder builder; 1239 RefPtr<SkImageFilter> imageFilter = builder.build(filters); 1240 m_layer->layer()->setFilter(imageFilter.get()); 1241 } else { 1242 OwnPtr<WebFilterOperations> webFilters = adoptPtr(Platform::current()->compositorSupport()->createFilterOperations()); 1243 if (!copyWebCoreFilterOperationsToWebFilterOperations(filters, *webFilters)) { 1244 // Make sure the filters are removed from the platform layer, as they are 1245 // going to fallback to software mode. 1246 webFilters->clear(); 1247 m_layer->layer()->setFilters(*webFilters); 1248 m_filters = FilterOperations(); 1249 return false; 1250 } 1251 m_layer->layer()->setFilters(*webFilters); 1252 } 1253 1254 m_filters = filters; 1255 return true; 1256 } 1257 1258 void GraphicsLayer::setBackgroundFilters(const FilterOperations& filters) 1259 { 1260 OwnPtr<WebFilterOperations> webFilters = adoptPtr(Platform::current()->compositorSupport()->createFilterOperations()); 1261 if (!copyWebCoreFilterOperationsToWebFilterOperations(filters, *webFilters)) 1262 return; 1263 m_layer->layer()->setBackgroundFilters(*webFilters); 1264 } 1265 1266 void GraphicsLayer::setLinkHighlight(LinkHighlightClient* linkHighlight) 1267 { 1268 m_linkHighlight = linkHighlight; 1269 updateChildList(); 1270 } 1271 1272 void GraphicsLayer::setScrollableArea(ScrollableArea* scrollableArea, bool isMainFrame) 1273 { 1274 if (m_scrollableArea == scrollableArea) 1275 return; 1276 1277 m_scrollableArea = scrollableArea; 1278 1279 // Main frame scrolling may involve pinch zoom and gets routed through 1280 // WebViewImpl explicitly rather than via GraphicsLayer::didScroll. 1281 if (isMainFrame) 1282 m_layer->layer()->setScrollClient(0); 1283 else 1284 m_layer->layer()->setScrollClient(this); 1285 } 1286 1287 void GraphicsLayer::paint(GraphicsContext& context, const IntRect& clip) 1288 { 1289 paintGraphicsLayerContents(context, clip); 1290 } 1291 1292 1293 void GraphicsLayer::notifyAnimationStarted(double startTime) 1294 { 1295 if (m_client) 1296 m_client->notifyAnimationStarted(this, startTime); 1297 } 1298 1299 void GraphicsLayer::notifyAnimationFinished(double) 1300 { 1301 // Do nothing. 1302 } 1303 1304 void GraphicsLayer::didScroll() 1305 { 1306 if (m_scrollableArea) 1307 m_scrollableArea->scrollToOffsetWithoutAnimation(m_scrollableArea->minimumScrollPosition() + toIntSize(m_layer->layer()->scrollPosition())); 1308 } 1309 1310 } // namespace WebCore 1311 1312 #ifndef NDEBUG 1313 void showGraphicsLayerTree(const WebCore::GraphicsLayer* layer) 1314 { 1315 if (!layer) 1316 return; 1317 1318 String output = layer->layerTreeAsText(WebCore::LayerTreeIncludesDebugInfo); 1319 fprintf(stderr, "%s\n", output.utf8().data()); 1320 } 1321 #endif 1322