From 6b70adc33054f8aee8c54d0f460458a9df11b8a5 Mon Sep 17 00:00:00 2001 From: Russell Brenner Date: Thu, 18 Nov 2010 17:33:13 -0800 Subject: Merge WebKit at r72274: Initial merge by git. Change-Id: Ie51f0b4a16da82942bd516dce59cfb79ebbe25fb --- .../graphics/mac/ComplexTextController.cpp | 4 +- .../graphics/mac/ComplexTextControllerATSUI.cpp | 2 +- .../graphics/mac/ComplexTextControllerCoreText.cpp | 37 +- WebCore/platform/graphics/mac/FontCacheMac.mm | 4 +- WebCore/platform/graphics/mac/FontMac.mm | 2 +- .../platform/graphics/mac/GlyphPageTreeNodeMac.cpp | 2 +- .../platform/graphics/mac/GraphicsContext3DMac.mm | 11 - WebCore/platform/graphics/mac/GraphicsLayerCA.h | 388 --- WebCore/platform/graphics/mac/GraphicsLayerCA.mm | 2514 -------------------- WebCore/platform/graphics/mac/GraphicsLayerMac.h | 388 +++ WebCore/platform/graphics/mac/GraphicsLayerMac.mm | 2514 ++++++++++++++++++++ .../graphics/mac/MediaPlayerPrivateQTKit.h | 12 +- .../graphics/mac/MediaPlayerPrivateQTKit.mm | 196 +- .../graphics/mac/SimpleFontDataCoreText.cpp | 6 +- WebCore/platform/graphics/mac/SimpleFontDataMac.mm | 31 +- 15 files changed, 3089 insertions(+), 3022 deletions(-) delete mode 100644 WebCore/platform/graphics/mac/GraphicsLayerCA.h delete mode 100644 WebCore/platform/graphics/mac/GraphicsLayerCA.mm create mode 100644 WebCore/platform/graphics/mac/GraphicsLayerMac.h create mode 100644 WebCore/platform/graphics/mac/GraphicsLayerMac.mm (limited to 'WebCore/platform/graphics/mac') diff --git a/WebCore/platform/graphics/mac/ComplexTextController.cpp b/WebCore/platform/graphics/mac/ComplexTextController.cpp index a2733f8..e6a7bef 100644 --- a/WebCore/platform/graphics/mac/ComplexTextController.cpp +++ b/WebCore/platform/graphics/mac/ComplexTextController.cpp @@ -487,7 +487,7 @@ void ComplexTextController::adjustGlyphsAndAdvances() advance.width += m_font.letterSpacing(); // Handle justification and word-spacing. - if (glyph == fontData->spaceGlyph()) { + if (treatAsSpace) { // Account for padding. WebCore uses space padding to justify text. // We distribute the specified padding over the available spaces in the run. if (m_padding) { @@ -503,7 +503,7 @@ void ComplexTextController::adjustGlyphsAndAdvances() } // Account for word-spacing. - if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing()) + if (characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing()) advance.width += m_font.wordSpacing(); } } diff --git a/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp b/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp index 1656854..7ff316c 100644 --- a/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp +++ b/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp @@ -262,7 +262,7 @@ static void disableLigatures(const SimpleFontData* fontData, ATSUStyle atsuStyle // Don't be too aggressive: if the font doesn't contain 'a', then assume that any ligatures it contains are // in characters that always go through ATSUI, and therefore allow them. Geeza Pro is an example. // See bugzilla 5166. - if ((typesettingFeatures & Ligatures) || fontData->platformData().allowsLigatures()) + if ((typesettingFeatures & Ligatures) || (fontData->orientation() == Horizontal && fontData->platformData().allowsLigatures())) return; ATSUFontFeatureType featureTypes[] = { kLigaturesType }; diff --git a/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp b/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp index 744e6d4..cbb7610 100644 --- a/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp +++ b/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp @@ -24,6 +24,7 @@ #include "config.h" #include "ComplexTextController.h" +#include "WebCoreSystemInterface.h" #if USE(CORE_TEXT) @@ -101,6 +102,23 @@ void ComplexTextController::ComplexTextRun::createTextRunFromFontDataCoreText(bo m_advances = m_advancesVector.data(); } +struct ProviderInfo { + const UChar* cp; + unsigned length; + CFDictionaryRef attributes; +}; + +static const UniChar* provideStringAndAttributes(CFIndex stringIndex, CFIndex* charCount, CFDictionaryRef* attributes, void* refCon) +{ + ProviderInfo* info = static_cast(refCon); + if (stringIndex < 0 || static_cast(stringIndex) >= info->length) + return 0; + + *charCount = info->length - stringIndex; + *attributes = info->attributes; + return info->cp + stringIndex; +} + void ComplexTextController::collectComplexTextRunsForCharactersCoreText(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) { if (!fontData) { @@ -112,10 +130,6 @@ void ComplexTextController::collectComplexTextRunsForCharactersCoreText(const UC if (m_fallbackFonts && fontData != m_font.primaryFont()) m_fallbackFonts->add(fontData); - RetainPtr string(AdoptCF, CFStringCreateWithCharactersNoCopy(NULL, cp, length, kCFAllocatorNull)); - - RetainPtr attributedString(AdoptCF, CFAttributedStringCreate(NULL, string.get(), fontData->getCFStringAttributes(m_font.typesettingFeatures()))); - RetainPtr line; if (!m_mayUseNaturalWritingDirection || m_run.directionalOverride()) { @@ -126,11 +140,22 @@ void ComplexTextController::collectComplexTextRunsForCharactersCoreText(const UC static const void* rtlOptionValues[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &rtlForcedEmbeddingLevelValue) }; static CFDictionaryRef ltrTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, ltrOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); static CFDictionaryRef rtlTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + ProviderInfo info = { cp, length, fontData->getCFStringAttributes(m_font.typesettingFeatures()) }; + RetainPtr typesetter(AdoptCF, wkCreateCTTypesetterWithUniCharProviderAndOptions(&provideStringAndAttributes, 0, &info, m_run.ltr() ? ltrTypesetterOptions : rtlTypesetterOptions)); +#else + RetainPtr string(AdoptCF, CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, cp, length, kCFAllocatorNull)); + RetainPtr attributedString(AdoptCF, CFAttributedStringCreate(kCFAllocatorDefault, string.get(), fontData->getCFStringAttributes(m_font.typesettingFeatures()))); RetainPtr typesetter(AdoptCF, CTTypesetterCreateWithAttributedStringAndOptions(attributedString.get(), m_run.ltr() ? ltrTypesetterOptions : rtlTypesetterOptions)); +#endif line.adoptCF(CTTypesetterCreateLine(typesetter.get(), CFRangeMake(0, 0))); - } else - line.adoptCF(CTLineCreateWithAttributedString(attributedString.get())); + } else { + ProviderInfo info = { cp, length, fontData->getCFStringAttributes(m_font.typesettingFeatures()) }; + + line.adoptCF(wkCreateCTLineWithUniCharProvider(&provideStringAndAttributes, 0, &info)); + } CFArrayRef runArray = CTLineGetGlyphRuns(line.get()); diff --git a/WebCore/platform/graphics/mac/FontCacheMac.mm b/WebCore/platform/graphics/mac/FontCacheMac.mm index 313db1c..068bd8e 100644 --- a/WebCore/platform/graphics/mac/FontCacheMac.mm +++ b/WebCore/platform/graphics/mac/FontCacheMac.mm @@ -140,7 +140,7 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, cons NSFontTraitMask substituteFontTraits = [fontManager traitsOfFont:substituteFont]; NSInteger substituteFontWeight = [fontManager weightOfFont:substituteFont]; - FontPlatformData alternateFont(substituteFont, + FontPlatformData alternateFont(substituteFont, platformData.size(), !font.isPlatformFont() && isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(substituteFontWeight), !font.isPlatformFont() && (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait), platformData.m_orientation); @@ -211,7 +211,7 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD bool syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight); bool syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait); - return new FontPlatformData(platformFont, syntheticBold, syntheticOblique, fontDescription.orientation()); + return new FontPlatformData(platformFont, size, syntheticBold, syntheticOblique, fontDescription.orientation()); } } // namespace WebCore diff --git a/WebCore/platform/graphics/mac/FontMac.mm b/WebCore/platform/graphics/mac/FontMac.mm index d760f5a..8dc741b 100644 --- a/WebCore/platform/graphics/mac/FontMac.mm +++ b/WebCore/platform/graphics/mac/FontMac.mm @@ -52,7 +52,7 @@ static void showGlyphsWithAdvances(const SimpleFontData* font, CGContextRef cont const FontPlatformData& platformData = font->platformData(); if (!platformData.isColorBitmapFont()) { CGAffineTransform savedMatrix; - bool isVertical = platformData.orientation() == Vertical; + bool isVertical = font->orientation() == Vertical; if (isVertical) { CGAffineTransform rotateLeftTransform = CGAffineTransformMake(0, -1, 1, 0, 0, 0); diff --git a/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp b/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp index 48ad1c0..9524cd2 100644 --- a/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp +++ b/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp @@ -40,7 +40,7 @@ bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned b bool haveGlyphs = false; #ifndef BUILDING_ON_TIGER - if (fontData->platformData().orientation() == Horizontal) { + if (fontData->orientation() == Horizontal || fontData->isBrokenIdeographFont()) { Vector glyphs(bufferLength); wkGetGlyphsForCharacters(fontData->platformData().cgFont(), buffer, glyphs.data(), bufferLength); for (unsigned i = 0; i < length; ++i) { diff --git a/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm b/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm index ab6993f..321d0ef 100644 --- a/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm +++ b/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm @@ -251,17 +251,6 @@ bool GraphicsContext3D::isGLES2Compliant() const return false; } -bool GraphicsContext3D::isGLES2NPOTStrict() const -{ - return false; -} - -bool GraphicsContext3D::isErrorGeneratedOnOutOfBoundsAccesses() const -{ - return false; -} - - } #endif // ENABLE(3D_CANVAS) diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.h b/WebCore/platform/graphics/mac/GraphicsLayerCA.h deleted file mode 100644 index 17a67ac..0000000 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.h +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef GraphicsLayerCA_h -#define GraphicsLayerCA_h - -#if USE(ACCELERATED_COMPOSITING) - -#include "GraphicsLayer.h" -#include "WebLayer.h" -#include -#include -#include -#include - -@class CABasicAnimation; -@class CAKeyframeAnimation; -@class CAMediaTimingFunction; -@class CAPropertyAnimation; -@class WebAnimationDelegate; - -namespace WebCore { - -class GraphicsLayerCA : public GraphicsLayer { -public: - - GraphicsLayerCA(GraphicsLayerClient*); - virtual ~GraphicsLayerCA(); - - virtual void setName(const String&); - - // for hosting this GraphicsLayer in a native layer hierarchy - virtual NativeLayer nativeLayer() const; - - virtual bool setChildren(const Vector&); - virtual void addChild(GraphicsLayer*); - virtual void addChildAtIndex(GraphicsLayer*, int index); - virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling); - virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling); - virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild); - - virtual void removeFromParent(); - - virtual void setMaskLayer(GraphicsLayer*); - virtual void setReplicatedLayer(GraphicsLayer*); - - virtual void setPosition(const FloatPoint&); - virtual void setAnchorPoint(const FloatPoint3D&); - virtual void setSize(const FloatSize&); - - virtual void setTransform(const TransformationMatrix&); - - virtual void setChildrenTransform(const TransformationMatrix&); - - virtual void setPreserves3D(bool); - virtual void setMasksToBounds(bool); - virtual void setDrawsContent(bool); - - virtual void setBackgroundColor(const Color&); - virtual void clearBackgroundColor(); - - virtual void setContentsOpaque(bool); - virtual void setBackfaceVisibility(bool); - - // return true if we started an animation - virtual void setOpacity(float); - - virtual void setNeedsDisplay(); - virtual void setNeedsDisplayInRect(const FloatRect&); - virtual void setContentsNeedsDisplay(); - - virtual void setContentsRect(const IntRect&); - - virtual void suspendAnimations(double time); - virtual void resumeAnimations(); - - virtual bool addAnimation(const KeyframeValueList&, const IntSize& boxSize, const Animation*, const String& animationName, double timeOffset); - virtual void pauseAnimation(const String& animationName, double timeOffset); - virtual void removeAnimation(const String& animationName); - - virtual void setContentsToImage(Image*); - virtual void setContentsToMedia(PlatformLayer*); - virtual void setContentsToCanvas(PlatformLayer*); - - virtual bool hasContentsLayer() const { return m_contentsLayer; } - - virtual PlatformLayer* platformLayer() const; - - virtual void setDebugBackgroundColor(const Color&); - virtual void setDebugBorder(const Color&, float borderWidth); - - virtual void didDisplay(PlatformLayer*); - - void recursiveCommitChanges(); - - virtual void syncCompositingState(); - virtual void syncCompositingStateForThisLayerOnly(); - - // Should only be called by animationDidStart: callback - void animationDidStart(CAAnimation*); - -protected: - virtual void setOpacityInternal(float); - -private: - void updateOpacityOnLayer(); - - CALayer* primaryLayer() const { return m_structuralLayer.get() ? m_structuralLayer.get() : m_layer.get(); } - CALayer* hostLayerForSublayers() const; - CALayer* layerForSuperlayer() const; - CALayer* animatedLayer(AnimatedPropertyID) const; - - typedef String CloneID; // Identifier for a given clone, based on original/replica branching down the tree. - static bool isReplicatedRootClone(const CloneID& cloneID) { return cloneID[0U] & 1; } - - typedef HashMap > LayerMap; - LayerMap* primaryLayerClones() const { return m_structuralLayer.get() ? m_structuralLayerClones.get() : m_layerClones.get(); } - LayerMap* animatedLayerClones(AnimatedPropertyID) const; - - bool createAnimationFromKeyframes(const KeyframeValueList&, const Animation*, const String& animationName, double timeOffset); - bool createTransformAnimationsFromKeyframes(const KeyframeValueList&, const Animation*, const String& animationName, double timeOffset, const IntSize& boxSize); - - // Return autoreleased animation (use RetainPtr?) - CABasicAnimation* createBasicAnimation(const Animation*, AnimatedPropertyID, bool additive); - CAKeyframeAnimation* createKeyframeAnimation(const Animation*, AnimatedPropertyID, bool additive); - void setupAnimation(CAPropertyAnimation*, const Animation*, bool additive); - - CAMediaTimingFunction* timingFunctionForAnimationValue(const AnimationValue*, const Animation*); - - bool setAnimationEndpoints(const KeyframeValueList&, const Animation*, CABasicAnimation*); - bool setAnimationKeyframes(const KeyframeValueList&, const Animation*, CAKeyframeAnimation*); - - bool setTransformAnimationEndpoints(const KeyframeValueList&, const Animation*, CABasicAnimation*, int functionIndex, TransformOperation::OperationType, bool isMatrixAnimation, const IntSize& boxSize); - bool setTransformAnimationKeyframes(const KeyframeValueList&, const Animation*, CAKeyframeAnimation*, int functionIndex, TransformOperation::OperationType, bool isMatrixAnimation, const IntSize& boxSize); - - bool animationIsRunning(const String& animationName) const - { - return m_runningAnimations.find(animationName) != m_runningAnimations.end(); - } - - void commitLayerChangesBeforeSublayers(); - void commitLayerChangesAfterSublayers(); - - FloatSize constrainedSize() const; - - bool requiresTiledLayer(const FloatSize&) const; - void swapFromOrToTiledLayer(bool useTiledLayer); - - CompositingCoordinatesOrientation defaultContentsOrientation() const; - void updateContentsTransform(); - - void setupContentsLayer(CALayer*); - CALayer* contentsLayer() const { return m_contentsLayer.get(); } - - virtual void setReplicatedByLayer(GraphicsLayer*); - - // Used to track the path down the tree for replica layers. - struct ReplicaState { - static const size_t maxReplicaDepth = 16; - enum ReplicaBranchType { ChildBranch = 0, ReplicaBranch = 1 }; - ReplicaState(ReplicaBranchType firstBranch) - : m_replicaDepth(0) - { - push(firstBranch); - } - - // Called as we walk down the tree to build replicas. - void push(ReplicaBranchType branchType) - { - m_replicaBranches.append(branchType); - if (branchType == ReplicaBranch) - ++m_replicaDepth; - } - - void setBranchType(ReplicaBranchType branchType) - { - ASSERT(!m_replicaBranches.isEmpty()); - - if (m_replicaBranches.last() != branchType) { - if (branchType == ReplicaBranch) - ++m_replicaDepth; - else - --m_replicaDepth; - } - - m_replicaBranches.last() = branchType; - } - - void pop() - { - if (m_replicaBranches.last() == ReplicaBranch) - --m_replicaDepth; - m_replicaBranches.removeLast(); - } - - size_t depth() const { return m_replicaBranches.size(); } - size_t replicaDepth() const { return m_replicaDepth; } - - CloneID cloneID() const; - - private: - Vector m_replicaBranches; - size_t m_replicaDepth; - }; - CALayer *replicatedLayerRoot(ReplicaState&); - - enum CloneLevel { RootCloneLevel, IntermediateCloneLevel }; - CALayer *fetchCloneLayers(GraphicsLayer* replicaRoot, ReplicaState&, CloneLevel); - - CALayer *cloneLayer(CALayer *, CloneLevel); - CALayer *findOrMakeClone(CloneID, CALayer *, LayerMap*, CloneLevel); - - void ensureCloneLayers(CloneID index, CALayer *& primaryLayer, CALayer *& structuralLayer, CALayer *& contentsLayer, CloneLevel); - - bool hasCloneLayers() const { return m_layerClones; } - void removeCloneLayers(); - FloatPoint positionForCloneRootLayer() const; - - void propagateLayerChangeToReplicas(); - - // All these "update" methods will be called inside a BEGIN_BLOCK_OBJC_EXCEPTIONS/END_BLOCK_OBJC_EXCEPTIONS block. - void updateLayerNames(); - void updateSublayerList(); - void updateLayerPosition(); - void updateLayerSize(); - void updateAnchorPoint(); - void updateTransform(); - void updateChildrenTransform(); - void updateMasksToBounds(); - void updateContentsOpaque(); - void updateBackfaceVisibility(); - void updateStructuralLayer(); - void updateLayerDrawsContent(); - void updateLayerBackgroundColor(); - - void updateContentsImage(); - void updateContentsMediaLayer(); - void updateContentsCanvasLayer(); - void updateContentsRect(); - void updateMaskLayer(); - void updateReplicatedLayers(); - - void updateLayerAnimations(); - void updateContentsNeedsDisplay(); - - enum StructuralLayerPurpose { - NoStructuralLayer = 0, - StructuralLayerForPreserves3D, - StructuralLayerForReplicaFlattening - }; - void ensureStructuralLayer(StructuralLayerPurpose); - StructuralLayerPurpose structuralLayerPurpose() const; - - void setCAAnimationOnLayer(CAPropertyAnimation*, AnimatedPropertyID, const String& animationName, int index, double timeOffset); - bool removeCAAnimationFromLayer(AnimatedPropertyID, const String& animationName, int index); - void pauseCAAnimationOnLayer(AnimatedPropertyID, const String& animationName, int index, double timeOffset); - - enum MoveOrCopy { Move, Copy }; - static void moveOrCopyLayerAnimation(MoveOrCopy, const String& animationIdentifier, CALayer *fromLayer, CALayer *toLayer); - void moveOrCopyAnimationsForProperty(MoveOrCopy, AnimatedPropertyID, CALayer * fromLayer, CALayer * toLayer); - - enum LayerChange { - NoChange = 0, - NameChanged = 1 << 1, - ChildrenChanged = 1 << 2, // also used for content layer, and preserves-3d, and size if tiling changes? - PositionChanged = 1 << 3, - AnchorPointChanged = 1 << 4, - SizeChanged = 1 << 5, - TransformChanged = 1 << 6, - ChildrenTransformChanged = 1 << 7, - Preserves3DChanged = 1 << 8, - MasksToBoundsChanged = 1 << 9, - DrawsContentChanged = 1 << 10, // need this? - BackgroundColorChanged = 1 << 11, - ContentsOpaqueChanged = 1 << 12, - BackfaceVisibilityChanged = 1 << 13, - OpacityChanged = 1 << 14, - AnimationChanged = 1 << 15, - DirtyRectsChanged = 1 << 16, - ContentsImageChanged = 1 << 17, - ContentsMediaLayerChanged = 1 << 18, - ContentsCanvasLayerChanged = 1 << 19, - ContentsRectChanged = 1 << 20, - MaskLayerChanged = 1 << 21, - ReplicatedLayerChanged = 1 << 22, - ContentsNeedsDisplay = 1 << 23 - }; - typedef unsigned LayerChangeFlags; - void noteLayerPropertyChanged(LayerChangeFlags flags); - void noteSublayersChanged(); - - void repaintLayerDirtyRects(); - - RetainPtr m_layer; // The main layer - RetainPtr m_structuralLayer; // A layer used for structural reasons, like preserves-3d or replica-flattening. Is the parent of m_layer. - RetainPtr m_contentsLayer; // A layer used for inner content, like image and video - - // References to clones of our layers, for replicated layers. - OwnPtr m_layerClones; - OwnPtr m_structuralLayerClones; - OwnPtr m_contentsLayerClones; - - enum ContentsLayerPurpose { - NoContentsLayer = 0, - ContentsLayerForImage, - ContentsLayerForMedia, - ContentsLayerForCanvas - }; - - ContentsLayerPurpose m_contentsLayerPurpose; - bool m_contentsLayerHasBackgroundColor : 1; - - RetainPtr m_animationDelegate; - - RetainPtr m_uncorrectedContentsImage; - RetainPtr m_pendingContentsImage; - - // This represents the animation of a single property. There may be multiple transform animations for - // a single transition or keyframe animation, so index is used to distinguish these. - struct LayerPropertyAnimation { - LayerPropertyAnimation(CAPropertyAnimation* caAnimation, const String& animationName, AnimatedPropertyID property, int index, double timeOffset) - : m_animation(caAnimation) - , m_name(animationName) - , m_property(property) - , m_index(index) - , m_timeOffset(timeOffset) - { } - - RetainPtr m_animation; - String m_name; - AnimatedPropertyID m_property; - int m_index; - double m_timeOffset; - }; - - // Uncommitted transitions and animations. - Vector m_uncomittedAnimations; - - enum Action { Remove, Pause }; - struct AnimationProcessingAction { - AnimationProcessingAction(Action action = Remove, double timeOffset = 0) - : action(action) - , timeOffset(timeOffset) - { - } - Action action; - double timeOffset; // only used for pause - }; - typedef HashMap AnimationsToProcessMap; - AnimationsToProcessMap m_animationsToProcess; - - // Map of animation names to their associated lists of property animations, so we can remove/pause them. - typedef HashMap > AnimationsMap; - AnimationsMap m_runningAnimations; - - Vector m_dirtyRects; - - LayerChangeFlags m_uncommittedChanges; -}; - -} // namespace WebCore - - -#endif // USE(ACCELERATED_COMPOSITING) - -#endif // GraphicsLayerCA_h diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm deleted file mode 100644 index c4128ef..0000000 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm +++ /dev/null @@ -1,2514 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "config.h" - -#if USE(ACCELERATED_COMPOSITING) - -#import "GraphicsLayerCA.h" - -#import "Animation.h" -#import "BlockExceptions.h" -#import "FloatConversion.h" -#import "FloatRect.h" -#import "Image.h" -#import "PlatformString.h" -#import -#import "RotateTransformOperation.h" -#import "ScaleTransformOperation.h" -#import "SystemTime.h" -#import "TranslateTransformOperation.h" -#import "WebLayer.h" -#import "WebTiledLayer.h" -#import -#import -#import -#import -#import -#import - -using namespace std; - -#define HAVE_MODERN_QUARTZCORE (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)) - -namespace WebCore { - -static NSString * const WebKitAnimationBeginTimeSetKey = @"WebKitAnimationBeginTimeSet"; - -// The threshold width or height above which a tiled layer will be used. This should be -// large enough to avoid tiled layers for most GraphicsLayers, but less than the OpenGL -// texture size limit on all supported hardware. -static const int cMaxPixelDimension = 2000; - -// The width and height of a single tile in a tiled layer. Should be large enough to -// avoid lots of small tiles (and therefore lots of drawing callbacks), but small enough -// to keep the overall tile cost low. -static const int cTiledLayerTileSize = 512; - -// If we send a duration of 0 to CA, then it will use the default duration -// of 250ms. So send a very small value instead. -static const float cAnimationAlmostZeroDuration = 1e-3f; - -// CACurrentMediaTime() is a time since boot. These methods convert between that and -// WebCore time, which is system time (UTC). -static CFTimeInterval currentTimeToMediaTime(double t) -{ - return CACurrentMediaTime() + t - WTF::currentTime(); -} - -static double mediaTimeToCurrentTime(CFTimeInterval t) -{ - return WTF::currentTime() + t - CACurrentMediaTime(); -} - -} // namespace WebCore - -@interface CALayer(Private) -- (void)setContentsChanged; -@end - -@interface WebAnimationDelegate : NSObject { - WebCore::GraphicsLayerCA* m_graphicsLayer; -} - -- (void)animationDidStart:(CAAnimation *)anim; -- (WebCore::GraphicsLayerCA*)graphicsLayer; -- (void)setLayer:(WebCore::GraphicsLayerCA*)graphicsLayer; - -@end - -@implementation WebAnimationDelegate - -- (void)animationDidStart:(CAAnimation *)animation -{ - if (m_graphicsLayer) - m_graphicsLayer->animationDidStart(animation); -} - -- (WebCore::GraphicsLayerCA*)graphicsLayer -{ - return m_graphicsLayer; -} - -- (void)setLayer:(WebCore::GraphicsLayerCA*)graphicsLayer -{ - m_graphicsLayer = graphicsLayer; -} - -@end - -namespace WebCore { - -static inline void copyTransform(CATransform3D& toT3D, const TransformationMatrix& t) -{ - toT3D.m11 = narrowPrecisionToFloat(t.m11()); - toT3D.m12 = narrowPrecisionToFloat(t.m12()); - toT3D.m13 = narrowPrecisionToFloat(t.m13()); - toT3D.m14 = narrowPrecisionToFloat(t.m14()); - toT3D.m21 = narrowPrecisionToFloat(t.m21()); - toT3D.m22 = narrowPrecisionToFloat(t.m22()); - toT3D.m23 = narrowPrecisionToFloat(t.m23()); - toT3D.m24 = narrowPrecisionToFloat(t.m24()); - toT3D.m31 = narrowPrecisionToFloat(t.m31()); - toT3D.m32 = narrowPrecisionToFloat(t.m32()); - toT3D.m33 = narrowPrecisionToFloat(t.m33()); - toT3D.m34 = narrowPrecisionToFloat(t.m34()); - toT3D.m41 = narrowPrecisionToFloat(t.m41()); - toT3D.m42 = narrowPrecisionToFloat(t.m42()); - toT3D.m43 = narrowPrecisionToFloat(t.m43()); - toT3D.m44 = narrowPrecisionToFloat(t.m44()); -} - -static NSValue* getTransformFunctionValue(const TransformOperation* transformOp, TransformOperation::OperationType transformType, const IntSize& size) -{ - switch (transformType) { - case TransformOperation::ROTATE: - case TransformOperation::ROTATE_X: - case TransformOperation::ROTATE_Y: - return [NSNumber numberWithDouble:transformOp ? deg2rad(static_cast(transformOp)->angle()) : 0]; - case TransformOperation::SCALE_X: - return [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->x() : 1]; - case TransformOperation::SCALE_Y: - return [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->y() : 1]; - case TransformOperation::SCALE_Z: - return [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->z() : 1]; - case TransformOperation::TRANSLATE_X: - return [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->x(size) : 0]; - case TransformOperation::TRANSLATE_Y: - return [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->y(size) : 0]; - case TransformOperation::TRANSLATE_Z: - return [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->z(size) : 0]; - case TransformOperation::SCALE: - case TransformOperation::SCALE_3D: - return [NSArray arrayWithObjects: - [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->x() : 1], - [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->y() : 1], - [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->z() : 1], - nil]; - case TransformOperation::TRANSLATE: - case TransformOperation::TRANSLATE_3D: - return [NSArray arrayWithObjects: - [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->x(size) : 0], - [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->y(size) : 0], - [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->z(size) : 0], - nil]; - case TransformOperation::SKEW_X: - case TransformOperation::SKEW_Y: - case TransformOperation::SKEW: - case TransformOperation::MATRIX: - case TransformOperation::ROTATE_3D: - case TransformOperation::MATRIX_3D: - case TransformOperation::PERSPECTIVE: - case TransformOperation::IDENTITY: - case TransformOperation::NONE: { - TransformationMatrix transform; - if (transformOp) - transformOp->apply(transform, size); - CATransform3D caTransform; - copyTransform(caTransform, transform); - return [NSValue valueWithCATransform3D:caTransform]; - } - } - - return 0; -} - -#if HAVE_MODERN_QUARTZCORE -static NSString *getValueFunctionNameForTransformOperation(TransformOperation::OperationType transformType) -{ - // Use literal strings to avoid link-time dependency on those symbols. - switch (transformType) { - case TransformOperation::ROTATE_X: - return @"rotateX"; // kCAValueFunctionRotateX; - case TransformOperation::ROTATE_Y: - return @"rotateY"; // kCAValueFunctionRotateY; - case TransformOperation::ROTATE: - return @"rotateZ"; // kCAValueFunctionRotateZ; - case TransformOperation::SCALE_X: - return @"scaleX"; // kCAValueFunctionScaleX; - case TransformOperation::SCALE_Y: - return @"scaleY"; // kCAValueFunctionScaleY; - case TransformOperation::SCALE_Z: - return @"scaleZ"; // kCAValueFunctionScaleZ; - case TransformOperation::TRANSLATE_X: - return @"translateX"; // kCAValueFunctionTranslateX; - case TransformOperation::TRANSLATE_Y: - return @"translateY"; // kCAValueFunctionTranslateY; - case TransformOperation::TRANSLATE_Z: - return @"translateZ"; // kCAValueFunctionTranslateZ; - case TransformOperation::SCALE: - case TransformOperation::SCALE_3D: - return @"scale"; // kCAValueFunctionScale; - case TransformOperation::TRANSLATE: - case TransformOperation::TRANSLATE_3D: - return @"translate"; // kCAValueFunctionTranslate; - default: - return nil; - } -} -#endif - -static String propertyIdToString(AnimatedPropertyID property) -{ - switch (property) { - case AnimatedPropertyWebkitTransform: - return "transform"; - case AnimatedPropertyOpacity: - return "opacity"; - case AnimatedPropertyBackgroundColor: - return "backgroundColor"; - case AnimatedPropertyInvalid: - ASSERT_NOT_REACHED(); - } - ASSERT_NOT_REACHED(); - return ""; -} - -static String animationIdentifier(const String& animationName, AnimatedPropertyID property, int index) -{ - return makeString(animationName, '_', String::number(property), '_', String::number(index)); -} - -static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction* timingFunction) -{ - // By this point, timing functions can only be linear or cubic, not steps. - ASSERT(!timingFunction->isStepsTimingFunction()); - if (timingFunction->isCubicBezierTimingFunction()) { - const CubicBezierTimingFunction* ctf = static_cast(timingFunction); - return [CAMediaTimingFunction functionWithControlPoints:static_cast(ctf->x1()) :static_cast(ctf->y1()) - :static_cast(ctf->x2()) :static_cast(ctf->y2())]; - } else - return [CAMediaTimingFunction functionWithName:@"linear"]; -} - -static void setLayerBorderColor(PlatformLayer* layer, const Color& color) -{ - [layer setBorderColor:cachedCGColor(color, ColorSpaceDeviceRGB)]; -} - -static void clearBorderColor(PlatformLayer* layer) -{ - [layer setBorderColor:nil]; -} - -static void setLayerBackgroundColor(PlatformLayer* layer, const Color& color) -{ - [layer setBackgroundColor:cachedCGColor(color, ColorSpaceDeviceRGB)]; -} - -static void clearLayerBackgroundColor(PlatformLayer* layer) -{ - [layer setBackgroundColor:0]; -} - -static void safeSetSublayers(CALayer* layer, NSArray* sublayers) -{ - // Workaround for : -[CALayer setSublayers:] crashes if sublayers is an empty array, or nil, under GC. - if (objc_collectingEnabled() && ![sublayers count]) { - while ([[layer sublayers] count]) - [[[layer sublayers] objectAtIndex:0] removeFromSuperlayer]; - return; - } - - [layer setSublayers:sublayers]; -} - -static bool caValueFunctionSupported() -{ - static bool sHaveValueFunction = [CAPropertyAnimation instancesRespondToSelector:@selector(setValueFunction:)]; - return sHaveValueFunction; -} - -static bool forceSoftwareAnimation() -{ - static bool forceSoftwareAnimation = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreForceSoftwareAnimation"]; - return forceSoftwareAnimation; -} - -static NSDictionary* nullActionsDictionary() -{ - NSNull* nullValue = [NSNull null]; - NSDictionary* actions = [NSDictionary dictionaryWithObjectsAndKeys: - nullValue, @"anchorPoint", - nullValue, @"bounds", - nullValue, @"contents", - nullValue, @"contentsRect", - nullValue, @"opacity", - nullValue, @"position", - nullValue, @"shadowColor", - nullValue, @"sublayerTransform", - nullValue, @"sublayers", - nullValue, @"transform", - nullValue, @"zPosition", - nil]; - return actions; -} - -static bool animationHasStepsTimingFunction(const KeyframeValueList& valueList, const Animation* anim) -{ - if (anim->timingFunction()->isStepsTimingFunction()) - return true; - - for (unsigned i = 0; i < valueList.size(); ++i) { - const TimingFunction* timingFunction = valueList.at(i)->timingFunction(); - if (timingFunction && timingFunction->isStepsTimingFunction()) - return true; - } - - return false; -} - -PassOwnPtr GraphicsLayer::create(GraphicsLayerClient* client) -{ - return new GraphicsLayerCA(client); -} - -GraphicsLayerCA::GraphicsLayerCA(GraphicsLayerClient* client) - : GraphicsLayer(client) - , m_contentsLayerPurpose(NoContentsLayer) - , m_contentsLayerHasBackgroundColor(false) - , m_uncommittedChanges(NoChange) -{ - BEGIN_BLOCK_OBJC_EXCEPTIONS - m_layer.adoptNS([[WebLayer alloc] init]); - [m_layer.get() setLayerOwner:this]; - -#if !HAVE_MODERN_QUARTZCORE - setContentsOrientation(defaultContentsOrientation()); -#endif - - updateDebugIndicators(); - - m_animationDelegate.adoptNS([[WebAnimationDelegate alloc] init]); - [m_animationDelegate.get() setLayer:this]; - - END_BLOCK_OBJC_EXCEPTIONS -} - -GraphicsLayerCA::~GraphicsLayerCA() -{ - // We release our references to the CALayers here, but do not actively unparent them, - // since that will cause a commit and break our batched commit model. The layers will - // get released when the rootmost modified GraphicsLayerCA rebuilds its child layers. - - BEGIN_BLOCK_OBJC_EXCEPTIONS - - // Clean up the WK layer. - if (m_layer) { - WebLayer* layer = m_layer.get(); - [layer setLayerOwner:nil]; - } - - if (m_contentsLayer) { - if ([m_contentsLayer.get() respondsToSelector:@selector(setLayerOwner:)]) - [(id)m_contentsLayer.get() setLayerOwner:nil]; - } - - // animationDidStart: can fire after this, so we need to clear out the layer on the delegate. - [m_animationDelegate.get() setLayer:0]; - - // Release the clone layers inside the exception-handling block. - removeCloneLayers(); - - END_BLOCK_OBJC_EXCEPTIONS -} - -void GraphicsLayerCA::setName(const String& name) -{ - String longName = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + name; - GraphicsLayer::setName(longName); - noteLayerPropertyChanged(NameChanged); -} - -NativeLayer GraphicsLayerCA::nativeLayer() const -{ - return m_layer.get(); -} - -bool GraphicsLayerCA::setChildren(const Vector& children) -{ - bool childrenChanged = GraphicsLayer::setChildren(children); - if (childrenChanged) - noteSublayersChanged(); - - return childrenChanged; -} - -void GraphicsLayerCA::addChild(GraphicsLayer* childLayer) -{ - GraphicsLayer::addChild(childLayer); - noteSublayersChanged(); -} - -void GraphicsLayerCA::addChildAtIndex(GraphicsLayer* childLayer, int index) -{ - GraphicsLayer::addChildAtIndex(childLayer, index); - noteSublayersChanged(); -} - -void GraphicsLayerCA::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) -{ - GraphicsLayer::addChildBelow(childLayer, sibling); - noteSublayersChanged(); -} - -void GraphicsLayerCA::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) -{ - GraphicsLayer::addChildAbove(childLayer, sibling); - noteSublayersChanged(); -} - -bool GraphicsLayerCA::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) -{ - if (GraphicsLayer::replaceChild(oldChild, newChild)) { - noteSublayersChanged(); - return true; - } - return false; -} - -void GraphicsLayerCA::removeFromParent() -{ - if (m_parent) - static_cast(m_parent)->noteSublayersChanged(); - GraphicsLayer::removeFromParent(); -} - -void GraphicsLayerCA::setMaskLayer(GraphicsLayer* layer) -{ - if (layer == m_maskLayer) - return; - - GraphicsLayer::setMaskLayer(layer); - noteLayerPropertyChanged(MaskLayerChanged); - - propagateLayerChangeToReplicas(); - - if (m_replicatedLayer) - static_cast(m_replicatedLayer)->propagateLayerChangeToReplicas(); -} - -void GraphicsLayerCA::setReplicatedLayer(GraphicsLayer* layer) -{ - if (layer == m_replicatedLayer) - return; - - GraphicsLayer::setReplicatedLayer(layer); - noteLayerPropertyChanged(ReplicatedLayerChanged); -} - -void GraphicsLayerCA::setReplicatedByLayer(GraphicsLayer* layer) -{ - if (layer == m_replicaLayer) - return; - - GraphicsLayer::setReplicatedByLayer(layer); - noteSublayersChanged(); - noteLayerPropertyChanged(ReplicatedLayerChanged); -} - -void GraphicsLayerCA::setPosition(const FloatPoint& point) -{ - if (point == m_position) - return; - - GraphicsLayer::setPosition(point); - noteLayerPropertyChanged(PositionChanged); -} - -void GraphicsLayerCA::setAnchorPoint(const FloatPoint3D& point) -{ - if (point == m_anchorPoint) - return; - - GraphicsLayer::setAnchorPoint(point); - noteLayerPropertyChanged(AnchorPointChanged); -} - -void GraphicsLayerCA::setSize(const FloatSize& size) -{ - if (size == m_size) - return; - - GraphicsLayer::setSize(size); - noteLayerPropertyChanged(SizeChanged); -} - -void GraphicsLayerCA::setTransform(const TransformationMatrix& t) -{ - if (t == m_transform) - return; - - GraphicsLayer::setTransform(t); - noteLayerPropertyChanged(TransformChanged); -} - -void GraphicsLayerCA::setChildrenTransform(const TransformationMatrix& t) -{ - if (t == m_childrenTransform) - return; - - GraphicsLayer::setChildrenTransform(t); - noteLayerPropertyChanged(ChildrenTransformChanged); -} - -void GraphicsLayerCA::moveOrCopyLayerAnimation(MoveOrCopy operation, const String& animationIdentifier, CALayer *fromLayer, CALayer *toLayer) -{ - NSString *animationID = animationIdentifier; - CAAnimation *anim = [fromLayer animationForKey:animationID]; - if (!anim) - return; - - switch (operation) { - case Move: - [anim retain]; - [fromLayer removeAnimationForKey:animationID]; - [toLayer addAnimation:anim forKey:animationID]; - [anim release]; - break; - - case Copy: - [toLayer addAnimation:anim forKey:animationID]; - break; - } -} - -void GraphicsLayerCA::moveOrCopyAnimationsForProperty(MoveOrCopy operation, AnimatedPropertyID property, CALayer *fromLayer, CALayer *toLayer) -{ - // Look for running animations affecting this property. - AnimationsMap::const_iterator end = m_runningAnimations.end(); - for (AnimationsMap::const_iterator it = m_runningAnimations.begin(); it != end; ++it) { - const Vector& propertyAnimations = it->second; - size_t numAnimations = propertyAnimations.size(); - for (size_t i = 0; i < numAnimations; ++i) { - const LayerPropertyAnimation& currAnimation = propertyAnimations[i]; - if (currAnimation.m_property == property) - moveOrCopyLayerAnimation(operation, animationIdentifier(currAnimation.m_name, currAnimation.m_property, currAnimation.m_index), fromLayer, toLayer); - } - } -} - -void GraphicsLayerCA::setPreserves3D(bool preserves3D) -{ - if (preserves3D == m_preserves3D) - return; - - GraphicsLayer::setPreserves3D(preserves3D); - noteLayerPropertyChanged(Preserves3DChanged); -} - -void GraphicsLayerCA::setMasksToBounds(bool masksToBounds) -{ - if (masksToBounds == m_masksToBounds) - return; - - GraphicsLayer::setMasksToBounds(masksToBounds); - noteLayerPropertyChanged(MasksToBoundsChanged); -} - -void GraphicsLayerCA::setDrawsContent(bool drawsContent) -{ - if (drawsContent == m_drawsContent) - return; - - GraphicsLayer::setDrawsContent(drawsContent); - noteLayerPropertyChanged(DrawsContentChanged); -} - -void GraphicsLayerCA::setBackgroundColor(const Color& color) -{ - if (m_backgroundColorSet && m_backgroundColor == color) - return; - - GraphicsLayer::setBackgroundColor(color); - - m_contentsLayerHasBackgroundColor = true; - noteLayerPropertyChanged(BackgroundColorChanged); -} - -void GraphicsLayerCA::clearBackgroundColor() -{ - if (!m_backgroundColorSet) - return; - - GraphicsLayer::clearBackgroundColor(); - m_contentsLayerHasBackgroundColor = false; - noteLayerPropertyChanged(BackgroundColorChanged); -} - -void GraphicsLayerCA::setContentsOpaque(bool opaque) -{ - if (m_contentsOpaque == opaque) - return; - - GraphicsLayer::setContentsOpaque(opaque); - noteLayerPropertyChanged(ContentsOpaqueChanged); -} - -void GraphicsLayerCA::setBackfaceVisibility(bool visible) -{ - if (m_backfaceVisibility == visible) - return; - - GraphicsLayer::setBackfaceVisibility(visible); - noteLayerPropertyChanged(BackfaceVisibilityChanged); -} - -void GraphicsLayerCA::setOpacity(float opacity) -{ - float clampedOpacity = max(0.0f, min(opacity, 1.0f)); - - if (clampedOpacity == m_opacity) - return; - - GraphicsLayer::setOpacity(clampedOpacity); - noteLayerPropertyChanged(OpacityChanged); -} - -void GraphicsLayerCA::setNeedsDisplay() -{ - FloatRect hugeRect(-numeric_limits::max() / 2, -numeric_limits::max() / 2, - numeric_limits::max(), numeric_limits::max()); - - setNeedsDisplayInRect(hugeRect); -} - -void GraphicsLayerCA::setNeedsDisplayInRect(const FloatRect& rect) -{ - if (!drawsContent()) - return; - - const size_t maxDirtyRects = 32; - - for (size_t i = 0; i < m_dirtyRects.size(); ++i) { - if (m_dirtyRects[i].contains(rect)) - return; - } - - if (m_dirtyRects.size() < maxDirtyRects) - m_dirtyRects.append(rect); - else - m_dirtyRects[0].unite(rect); - - noteLayerPropertyChanged(DirtyRectsChanged); -} - -void GraphicsLayerCA::setContentsNeedsDisplay() -{ - noteLayerPropertyChanged(ContentsNeedsDisplay); -} - -void GraphicsLayerCA::setContentsRect(const IntRect& rect) -{ - if (rect == m_contentsRect) - return; - - GraphicsLayer::setContentsRect(rect); - noteLayerPropertyChanged(ContentsRectChanged); -} - -bool GraphicsLayerCA::addAnimation(const KeyframeValueList& valueList, const IntSize& boxSize, const Animation* anim, const String& animationName, double timeOffset) -{ - ASSERT(!animationName.isEmpty()); - - if (forceSoftwareAnimation() || !anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) - return false; - -#if !HAVE_MODERN_QUARTZCORE - // Older versions of QuartzCore do not handle opacity in transform layers properly, so we will - // always do software animation in that case. - if (valueList.property() == AnimatedPropertyOpacity) - return false; -#endif - - // CoreAnimation does not handle the steps() timing function. Fall back - // to software animation in that case. - if (animationHasStepsTimingFunction(valueList, anim)) - return false; - - bool createdAnimations = false; - if (valueList.property() == AnimatedPropertyWebkitTransform) - createdAnimations = createTransformAnimationsFromKeyframes(valueList, anim, animationName, timeOffset, boxSize); - else - createdAnimations = createAnimationFromKeyframes(valueList, anim, animationName, timeOffset); - - if (createdAnimations) - noteLayerPropertyChanged(AnimationChanged); - - return createdAnimations; -} - -void GraphicsLayerCA::pauseAnimation(const String& animationName, double timeOffset) -{ - if (!animationIsRunning(animationName)) - return; - - AnimationsToProcessMap::iterator it = m_animationsToProcess.find(animationName); - if (it != m_animationsToProcess.end()) { - AnimationProcessingAction& processingInfo = it->second; - // If an animation is scheduled to be removed, don't change the remove to a pause. - if (processingInfo.action != Remove) - processingInfo.action = Pause; - } else - m_animationsToProcess.add(animationName, AnimationProcessingAction(Pause, timeOffset)); - - noteLayerPropertyChanged(AnimationChanged); -} - -void GraphicsLayerCA::removeAnimation(const String& animationName) -{ - if (!animationIsRunning(animationName)) - return; - - m_animationsToProcess.add(animationName, AnimationProcessingAction(Remove)); - noteLayerPropertyChanged(AnimationChanged); -} - -void GraphicsLayerCA::animationDidStart(CAAnimation* caAnimation) -{ - bool hadNonZeroBeginTime = [[caAnimation valueForKey:WebKitAnimationBeginTimeSetKey] boolValue]; - - double startTime; - if (hadNonZeroBeginTime) { - // We don't know what time CA used to commit the animation, so just use the current time - // (even though this will be slightly off). - startTime = WebCore::mediaTimeToCurrentTime(CACurrentMediaTime()); - } else - startTime = WebCore::mediaTimeToCurrentTime([caAnimation beginTime]); - - if (m_client) - m_client->notifyAnimationStarted(this, startTime); -} - -void GraphicsLayerCA::setContentsToImage(Image* image) -{ - if (image) { - CGImageRef newImage = image->nativeImageForCurrentFrame(); - if (!newImage) - return; - - // Check to see if the image changed; we have to do this because the call to - // CGImageCreateCopyWithColorSpace() below can create a new image every time. - if (m_uncorrectedContentsImage && m_uncorrectedContentsImage.get() == newImage) - return; - - m_uncorrectedContentsImage = newImage; - m_pendingContentsImage = newImage; - CGColorSpaceRef colorSpace = CGImageGetColorSpace(m_pendingContentsImage.get()); - - static CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); - if (colorSpace && CFEqual(colorSpace, deviceRGB)) { - // CoreGraphics renders images tagged with DeviceRGB using the color space of the main display. When we hand such - // images to CA we need to tag them similarly so CA rendering matches CG rendering. - static CGColorSpaceRef genericRGB = CGDisplayCopyColorSpace(kCGDirectMainDisplay); - m_pendingContentsImage.adoptCF(CGImageCreateCopyWithColorSpace(m_pendingContentsImage.get(), genericRGB)); - } - m_contentsLayerPurpose = ContentsLayerForImage; - if (!m_contentsLayer) - noteSublayersChanged(); - } else { - m_uncorrectedContentsImage = 0; - m_pendingContentsImage = 0; - m_contentsLayerPurpose = NoContentsLayer; - if (m_contentsLayer) - noteSublayersChanged(); - } - - noteLayerPropertyChanged(ContentsImageChanged); -} - -void GraphicsLayerCA::setContentsToMedia(PlatformLayer* mediaLayer) -{ - if (mediaLayer == m_contentsLayer) - return; - - m_contentsLayer = mediaLayer; - m_contentsLayerPurpose = mediaLayer ? ContentsLayerForMedia : NoContentsLayer; - - noteSublayersChanged(); - noteLayerPropertyChanged(ContentsMediaLayerChanged); -} - -void GraphicsLayerCA::didDisplay(PlatformLayer* layer) -{ - CALayer* sourceLayer; - LayerMap* layerCloneMap; - - if (layer == m_layer) { - sourceLayer = m_layer.get(); - layerCloneMap = m_layerClones.get(); - } else if (layer == m_contentsLayer) { - sourceLayer = m_contentsLayer.get(); - layerCloneMap = m_contentsLayerClones.get(); - } else - return; - - if (layerCloneMap) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - CALayer *currClone = it->second.get(); - if (!currClone) - continue; - - if ([currClone contents] != [sourceLayer contents]) - [currClone setContents:[sourceLayer contents]]; - else - [currClone setContentsChanged]; - } - } -} - -void GraphicsLayerCA::syncCompositingState() -{ - recursiveCommitChanges(); -} - -void GraphicsLayerCA::syncCompositingStateForThisLayerOnly() -{ - commitLayerChangesBeforeSublayers(); - commitLayerChangesAfterSublayers(); -} - -void GraphicsLayerCA::recursiveCommitChanges() -{ - commitLayerChangesBeforeSublayers(); - - if (m_maskLayer) - static_cast(m_maskLayer)->commitLayerChangesBeforeSublayers(); - - const Vector& childLayers = children(); - size_t numChildren = childLayers.size(); - for (size_t i = 0; i < numChildren; ++i) { - GraphicsLayerCA* curChild = static_cast(childLayers[i]); - curChild->recursiveCommitChanges(); - } - - if (m_replicaLayer) - static_cast(m_replicaLayer)->recursiveCommitChanges(); - - if (m_maskLayer) - static_cast(m_maskLayer)->commitLayerChangesAfterSublayers(); - - commitLayerChangesAfterSublayers(); -} - -void GraphicsLayerCA::commitLayerChangesBeforeSublayers() -{ - if (!m_uncommittedChanges) - return; - - BEGIN_BLOCK_OBJC_EXCEPTIONS - - // Need to handle Preserves3DChanged first, because it affects which layers subsequent properties are applied to - if (m_uncommittedChanges & (Preserves3DChanged | ReplicatedLayerChanged)) - updateStructuralLayer(); - - if (m_uncommittedChanges & NameChanged) - updateLayerNames(); - - if (m_uncommittedChanges & ContentsImageChanged) // Needs to happen before ChildrenChanged - updateContentsImage(); - - if (m_uncommittedChanges & ContentsMediaLayerChanged) // Needs to happen before ChildrenChanged - updateContentsMediaLayer(); - - if (m_uncommittedChanges & ContentsCanvasLayerChanged) // Needs to happen before ChildrenChanged - updateContentsCanvasLayer(); - - if (m_uncommittedChanges & BackgroundColorChanged) // Needs to happen before ChildrenChanged, and after updating image or video - updateLayerBackgroundColor(); - - if (m_uncommittedChanges & ChildrenChanged) - updateSublayerList(); - - if (m_uncommittedChanges & PositionChanged) - updateLayerPosition(); - - if (m_uncommittedChanges & AnchorPointChanged) - updateAnchorPoint(); - - if (m_uncommittedChanges & SizeChanged) - updateLayerSize(); - - if (m_uncommittedChanges & TransformChanged) - updateTransform(); - - if (m_uncommittedChanges & ChildrenTransformChanged) - updateChildrenTransform(); - - if (m_uncommittedChanges & MasksToBoundsChanged) - updateMasksToBounds(); - - if (m_uncommittedChanges & DrawsContentChanged) - updateLayerDrawsContent(); - - if (m_uncommittedChanges & ContentsOpaqueChanged) - updateContentsOpaque(); - - if (m_uncommittedChanges & BackfaceVisibilityChanged) - updateBackfaceVisibility(); - - if (m_uncommittedChanges & OpacityChanged) - updateOpacityOnLayer(); - - if (m_uncommittedChanges & AnimationChanged) - updateLayerAnimations(); - - if (m_uncommittedChanges & DirtyRectsChanged) - repaintLayerDirtyRects(); - - if (m_uncommittedChanges & ContentsRectChanged) - updateContentsRect(); - - if (m_uncommittedChanges & MaskLayerChanged) - updateMaskLayer(); - - if (m_uncommittedChanges & ContentsNeedsDisplay) - updateContentsNeedsDisplay(); - - END_BLOCK_OBJC_EXCEPTIONS -} - -void GraphicsLayerCA::commitLayerChangesAfterSublayers() -{ - if (!m_uncommittedChanges) - return; - - BEGIN_BLOCK_OBJC_EXCEPTIONS - - if (m_uncommittedChanges & ReplicatedLayerChanged) - updateReplicatedLayers(); - - m_uncommittedChanges = NoChange; - END_BLOCK_OBJC_EXCEPTIONS -} - -void GraphicsLayerCA::updateLayerNames() -{ - switch (structuralLayerPurpose()) { - case StructuralLayerForPreserves3D: - [m_structuralLayer.get() setName:("Transform layer " + name())]; - break; - case StructuralLayerForReplicaFlattening: - [m_structuralLayer.get() setName:("Replica flattening layer " + name())]; - break; - case NoStructuralLayer: - break; - } - [m_layer.get() setName:name()]; -} - -void GraphicsLayerCA::updateSublayerList() -{ - NSMutableArray* newSublayers = nil; - - const Vector& childLayers = children(); - - if (m_structuralLayer || m_contentsLayer || childLayers.size() > 0) { - newSublayers = [[NSMutableArray alloc] init]; - - if (m_structuralLayer) { - // Add the replica layer first. - if (m_replicaLayer) - [newSublayers addObject:static_cast(m_replicaLayer)->primaryLayer()]; - // Add the primary layer. Even if we have negative z-order children, the primary layer always comes behind. - [newSublayers addObject:m_layer.get()]; - } else if (m_contentsLayer) { - // FIXME: add the contents layer in the correct order with negative z-order children. - // This does not cause visible rendering issues because currently contents layers are only used - // for replaced elements that don't have children. - [newSublayers addObject:m_contentsLayer.get()]; - } - - size_t numChildren = childLayers.size(); - for (size_t i = 0; i < numChildren; ++i) { - GraphicsLayerCA* curChild = static_cast(childLayers[i]); - CALayer *childLayer = curChild->layerForSuperlayer(); - [newSublayers addObject:childLayer]; - } - - [newSublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; - } - - if (m_structuralLayer) { - safeSetSublayers(m_structuralLayer.get(), newSublayers); - - if (m_contentsLayer) { - // If we have a transform layer, then the contents layer is parented in the - // primary layer (which is itself a child of the transform layer). - safeSetSublayers(m_layer.get(), nil); - [m_layer.get() addSublayer:m_contentsLayer.get()]; - } - } else - safeSetSublayers(m_layer.get(), newSublayers); - - [newSublayers release]; -} - -void GraphicsLayerCA::updateLayerPosition() -{ - FloatSize usedSize = m_usingTiledLayer ? constrainedSize() : m_size; - - // Position is offset on the layer by the layer anchor point. - CGPoint posPoint = CGPointMake(m_position.x() + m_anchorPoint.x() * usedSize.width(), - m_position.y() + m_anchorPoint.y() * usedSize.height()); - - [primaryLayer() setPosition:posPoint]; - - if (LayerMap* layerCloneMap = primaryLayerClones()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - CGPoint clonePosition = posPoint; - if (m_replicaLayer && isReplicatedRootClone(it->first)) { - // Maintain the special-case position for the root of a clone subtree, - // which we set up in replicatedLayerRoot(). - clonePosition = positionForCloneRootLayer(); - } - CALayer *currLayer = it->second.get(); - [currLayer setPosition:clonePosition]; - } - } -} - -void GraphicsLayerCA::updateLayerSize() -{ - CGRect rect = CGRectMake(0, 0, m_size.width(), m_size.height()); - if (m_structuralLayer) { - [m_structuralLayer.get() setBounds:rect]; - - if (LayerMap* layerCloneMap = m_structuralLayerClones.get()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) - [it->second.get() setBounds:rect]; - } - - // The anchor of the contents layer is always at 0.5, 0.5, so the position is center-relative. - CGPoint centerPoint = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); - [m_layer.get() setPosition:centerPoint]; - - if (LayerMap* layerCloneMap = m_layerClones.get()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) - [it->second.get() setPosition:centerPoint]; - } - } - - bool needTiledLayer = requiresTiledLayer(m_size); - if (needTiledLayer != m_usingTiledLayer) - swapFromOrToTiledLayer(needTiledLayer); - - if (m_usingTiledLayer) { - FloatSize sizeToUse = constrainedSize(); - rect = CGRectMake(0, 0, sizeToUse.width(), sizeToUse.height()); - } - - [m_layer.get() setBounds:rect]; - if (LayerMap* layerCloneMap = m_layerClones.get()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) - [it->second.get() setBounds:rect]; - } - - // Contents transform may depend on height. - updateContentsTransform(); - - // Note that we don't resize m_contentsLayer. It's up the caller to do that. - - // if we've changed the bounds, we need to recalculate the position - // of the layer, taking anchor point into account. - updateLayerPosition(); -} - -void GraphicsLayerCA::updateAnchorPoint() -{ - [primaryLayer() setAnchorPoint:FloatPoint(m_anchorPoint.x(), m_anchorPoint.y())]; -#if HAVE_MODERN_QUARTZCORE - [primaryLayer() setAnchorPointZ:m_anchorPoint.z()]; -#endif - - if (LayerMap* layerCloneMap = primaryLayerClones()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - CALayer *currLayer = it->second.get(); - [currLayer setAnchorPoint:FloatPoint(m_anchorPoint.x(), m_anchorPoint.y())]; -#if HAVE_MODERN_QUARTZCORE - [currLayer setAnchorPointZ:m_anchorPoint.z()]; -#endif - } - } - - updateLayerPosition(); -} - -void GraphicsLayerCA::updateTransform() -{ - CATransform3D transform; - copyTransform(transform, m_transform); - [primaryLayer() setTransform:transform]; - - if (LayerMap* layerCloneMap = primaryLayerClones()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - CALayer *currLayer = it->second.get(); - if (m_replicaLayer && isReplicatedRootClone(it->first)) { - // Maintain the special-case transform for the root of a clone subtree, - // which we set up in replicatedLayerRoot(). - [currLayer setTransform:CATransform3DIdentity]; - } else - [currLayer setTransform:transform]; - } - } -} - -void GraphicsLayerCA::updateChildrenTransform() -{ - CATransform3D transform; - copyTransform(transform, m_childrenTransform); - [primaryLayer() setSublayerTransform:transform]; - - if (LayerMap* layerCloneMap = primaryLayerClones()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - CALayer *currLayer = it->second.get(); - [currLayer setSublayerTransform:transform]; - } - } -} - -void GraphicsLayerCA::updateMasksToBounds() -{ - [m_layer.get() setMasksToBounds:m_masksToBounds]; - - if (LayerMap* layerCloneMap = m_layerClones.get()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - CALayer *currLayer = it->second.get(); - [currLayer setMasksToBounds:m_masksToBounds]; - } - } - - updateDebugIndicators(); -} - -void GraphicsLayerCA::updateContentsOpaque() -{ - [m_layer.get() setOpaque:m_contentsOpaque]; - - if (LayerMap* layerCloneMap = m_layerClones.get()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - CALayer *currLayer = it->second.get(); - [currLayer setOpaque:m_contentsOpaque]; - } - } -} - -void GraphicsLayerCA::updateBackfaceVisibility() -{ - if (m_structuralLayer && structuralLayerPurpose() == StructuralLayerForReplicaFlattening) { - [m_structuralLayer.get() setDoubleSided:m_backfaceVisibility]; - - if (LayerMap* layerCloneMap = m_structuralLayerClones.get()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - CALayer *currLayer = it->second.get(); - [currLayer setDoubleSided:m_backfaceVisibility]; - } - } - } - - [m_layer.get() setDoubleSided:m_backfaceVisibility]; - - if (LayerMap* layerCloneMap = m_layerClones.get()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - CALayer *currLayer = it->second.get(); - [currLayer setDoubleSided:m_backfaceVisibility]; - } - } -} - -void GraphicsLayerCA::updateStructuralLayer() -{ - ensureStructuralLayer(structuralLayerPurpose()); -} - -void GraphicsLayerCA::ensureStructuralLayer(StructuralLayerPurpose purpose) -{ - if (purpose == NoStructuralLayer) { - if (m_structuralLayer) { - // Replace the transformLayer in the parent with this layer. - [m_layer.get() removeFromSuperlayer]; - [[m_structuralLayer.get() superlayer] replaceSublayer:m_structuralLayer.get() with:m_layer.get()]; - - moveOrCopyAnimationsForProperty(Move, AnimatedPropertyWebkitTransform, m_structuralLayer.get(), m_layer.get()); - moveOrCopyAnimationsForProperty(Move, AnimatedPropertyOpacity, m_structuralLayer.get(), m_layer.get()); - - // Release the structural layer. - m_structuralLayer = 0; - - // Update the properties of m_layer now that we no longer have a structural layer. - updateLayerPosition(); - updateLayerSize(); - updateAnchorPoint(); - updateTransform(); - updateChildrenTransform(); - - updateSublayerList(); - updateOpacityOnLayer(); - } - return; - } - - bool structuralLayerChanged = false; - - if (purpose == StructuralLayerForPreserves3D) { - Class transformLayerClass = NSClassFromString(@"CATransformLayer"); - if (!transformLayerClass) - return; - - if (m_structuralLayer && ![m_structuralLayer.get() isKindOfClass:transformLayerClass]) - m_structuralLayer = 0; - - if (!m_structuralLayer) { - m_structuralLayer.adoptNS([[transformLayerClass alloc] init]); - structuralLayerChanged = true; - } - } else { - if (m_structuralLayer && ![m_structuralLayer.get() isMemberOfClass:[CALayer self]]) - m_structuralLayer = 0; - - if (!m_structuralLayer) { - m_structuralLayer.adoptNS([[CALayer alloc] init]); - structuralLayerChanged = true; - } - } - - if (!structuralLayerChanged) - return; - - // Turn off default animations. - [m_structuralLayer.get() setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; - - updateLayerNames(); - - // Update the properties of the structural layer. - updateLayerPosition(); - updateLayerSize(); - updateAnchorPoint(); - updateTransform(); - updateChildrenTransform(); - updateBackfaceVisibility(); - - // Set properties of m_layer to their default values, since these are expressed on on the structural layer. - CGPoint point = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); - [m_layer.get() setPosition:point]; - [m_layer.get() setAnchorPoint:CGPointMake(0.5f, 0.5f)]; - [m_layer.get() setTransform:CATransform3DIdentity]; - [m_layer.get() setOpacity:1]; - - // Move this layer to be a child of the transform layer. - [[m_layer.get() superlayer] replaceSublayer:m_layer.get() with:m_structuralLayer.get()]; - [m_structuralLayer.get() addSublayer:m_layer.get()]; - - moveOrCopyAnimationsForProperty(Move, AnimatedPropertyWebkitTransform, m_layer.get(), m_structuralLayer.get()); - moveOrCopyAnimationsForProperty(Move, AnimatedPropertyOpacity, m_layer.get(), m_structuralLayer.get()); - - updateSublayerList(); - updateOpacityOnLayer(); -} - -GraphicsLayerCA::StructuralLayerPurpose GraphicsLayerCA::structuralLayerPurpose() const -{ - if (preserves3D()) - return StructuralLayerForPreserves3D; - - if (isReplicated()) - return StructuralLayerForReplicaFlattening; - - return NoStructuralLayer; -} - -void GraphicsLayerCA::updateLayerDrawsContent() -{ - bool needTiledLayer = requiresTiledLayer(m_size); - if (needTiledLayer != m_usingTiledLayer) - swapFromOrToTiledLayer(needTiledLayer); - - if (m_drawsContent) - [m_layer.get() setNeedsDisplay]; - else - [m_layer.get() setContents:nil]; - - updateDebugIndicators(); -} - -void GraphicsLayerCA::updateLayerBackgroundColor() -{ - if (!m_contentsLayer) - return; - - // We never create the contents layer just for background color yet. - if (m_backgroundColorSet) - setLayerBackgroundColor(m_contentsLayer.get(), m_backgroundColor); - else - clearLayerBackgroundColor(m_contentsLayer.get()); -} - -void GraphicsLayerCA::updateContentsImage() -{ - if (m_pendingContentsImage) { - if (!m_contentsLayer.get()) { - WebLayer* imageLayer = [WebLayer layer]; -#ifndef NDEBUG - [imageLayer setName:@"Image Layer"]; -#endif - setupContentsLayer(imageLayer); - m_contentsLayer.adoptNS([imageLayer retain]); - // m_contentsLayer will be parented by updateSublayerList - } - - // FIXME: maybe only do trilinear if the image is being scaled down, - // but then what if the layer size changes? -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) - [m_contentsLayer.get() setMinificationFilter:kCAFilterTrilinear]; -#endif - [m_contentsLayer.get() setContents:(id)m_pendingContentsImage.get()]; - m_pendingContentsImage = 0; - - if (m_contentsLayerClones) { - LayerMap::const_iterator end = m_contentsLayerClones->end(); - for (LayerMap::const_iterator it = m_contentsLayerClones->begin(); it != end; ++it) - [it->second.get() setContents:[m_contentsLayer.get() contents]]; - } - - updateContentsRect(); - } else { - // No image. - // m_contentsLayer will be removed via updateSublayerList. - m_contentsLayer = 0; - } -} - -void GraphicsLayerCA::updateContentsMediaLayer() -{ - // Video layer was set as m_contentsLayer, and will get parented in updateSublayerList(). - if (m_contentsLayer) { - setupContentsLayer(m_contentsLayer.get()); - updateContentsRect(); - } -} - -void GraphicsLayerCA::updateContentsCanvasLayer() -{ - // CanvasLayer was set as m_contentsLayer, and will get parented in updateSublayerList(). - if (m_contentsLayer) { - setupContentsLayer(m_contentsLayer.get()); - [m_contentsLayer.get() setNeedsDisplay]; - updateContentsRect(); - } -} - -void GraphicsLayerCA::updateContentsRect() -{ - if (!m_contentsLayer) - return; - - CGPoint point = CGPointMake(m_contentsRect.x(), - m_contentsRect.y()); - CGRect rect = CGRectMake(0.0f, - 0.0f, - m_contentsRect.width(), - m_contentsRect.height()); - - [m_contentsLayer.get() setPosition:point]; - [m_contentsLayer.get() setBounds:rect]; - - if (m_contentsLayerClones) { - LayerMap::const_iterator end = m_contentsLayerClones->end(); - for (LayerMap::const_iterator it = m_contentsLayerClones->begin(); it != end; ++it) { - CALayer *currLayer = it->second.get(); - [currLayer setPosition:point]; - [currLayer setBounds:rect]; - } - } -} - -void GraphicsLayerCA::updateMaskLayer() -{ - CALayer *maskCALayer = m_maskLayer ? m_maskLayer->platformLayer() : 0; - [m_layer.get() setMask:maskCALayer]; - - LayerMap* maskLayerCloneMap = m_maskLayer ? static_cast(m_maskLayer)->primaryLayerClones() : 0; - - if (LayerMap* layerCloneMap = m_layerClones.get()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - CALayer *currLayer = it->second.get(); - - CALayer *maskClone = maskLayerCloneMap ? maskLayerCloneMap->get(it->first).get() : 0; - [currLayer setMask:maskClone]; - } - } -} - -void GraphicsLayerCA::updateReplicatedLayers() -{ - // Clone the descendants of the replicated layer, and parent under us. - ReplicaState replicaState(ReplicaState::ReplicaBranch); - - CALayer *replicaRoot = replicatedLayerRoot(replicaState); - if (!replicaRoot) - return; - - if (m_structuralLayer) - [m_structuralLayer.get() insertSublayer:replicaRoot atIndex:0]; - else - [m_layer.get() insertSublayer:replicaRoot atIndex:0]; -} - -// For now, this assumes that layers only ever have one replica, so replicaIndices contains only 0 and 1. -GraphicsLayerCA::CloneID GraphicsLayerCA::ReplicaState::cloneID() const -{ - size_t depth = m_replicaBranches.size(); - - const size_t bitsPerUChar = sizeof(UChar) * 8; - size_t vectorSize = (depth + bitsPerUChar - 1) / bitsPerUChar; - - Vector result(vectorSize); - result.fill(0); - - // Create a string from the bit sequence which we can use to identify the clone. - // Note that the string may contain embedded nulls, but that's OK. - for (size_t i = 0; i < depth; ++i) { - UChar& currChar = result[i / bitsPerUChar]; - currChar = (currChar << 1) | m_replicaBranches[i]; - } - - return String::adopt(result); -} - -CALayer *GraphicsLayerCA::replicatedLayerRoot(ReplicaState& replicaState) -{ - // Limit replica nesting, to avoid 2^N explosion of replica layers. - if (!m_replicatedLayer || replicaState.replicaDepth() == ReplicaState::maxReplicaDepth) - return nil; - - GraphicsLayerCA* replicatedLayer = static_cast(m_replicatedLayer); - - CALayer *clonedLayerRoot = replicatedLayer->fetchCloneLayers(this, replicaState, RootCloneLevel); - FloatPoint cloneRootPosition = replicatedLayer->positionForCloneRootLayer(); - - // Replica root has no offset or transform - [clonedLayerRoot setPosition:cloneRootPosition]; - [clonedLayerRoot setTransform:CATransform3DIdentity]; - - return clonedLayerRoot; -} - -void GraphicsLayerCA::updateLayerAnimations() -{ - if (m_animationsToProcess.size()) { - AnimationsToProcessMap::const_iterator end = m_animationsToProcess.end(); - for (AnimationsToProcessMap::const_iterator it = m_animationsToProcess.begin(); it != end; ++it) { - const String& currAnimationName = it->first; - AnimationsMap::iterator animationIt = m_runningAnimations.find(currAnimationName); - if (animationIt == m_runningAnimations.end()) - continue; - - const AnimationProcessingAction& processingInfo = it->second; - const Vector& animations = animationIt->second; - for (size_t i = 0; i < animations.size(); ++i) { - const LayerPropertyAnimation& currAnimation = animations[i]; - switch (processingInfo.action) { - case Remove: - removeCAAnimationFromLayer(currAnimation.m_property, currAnimationName, currAnimation.m_index); - break; - case Pause: - pauseCAAnimationOnLayer(currAnimation.m_property, currAnimationName, currAnimation.m_index, processingInfo.timeOffset); - break; - } - } - - if (processingInfo.action == Remove) - m_runningAnimations.remove(currAnimationName); - } - - m_animationsToProcess.clear(); - } - - size_t numAnimations; - if ((numAnimations = m_uncomittedAnimations.size())) { - for (size_t i = 0; i < numAnimations; ++i) { - const LayerPropertyAnimation& pendingAnimation = m_uncomittedAnimations[i]; - setCAAnimationOnLayer(pendingAnimation.m_animation.get(), pendingAnimation.m_property, pendingAnimation.m_name, pendingAnimation.m_index, pendingAnimation.m_timeOffset); - - AnimationsMap::iterator it = m_runningAnimations.find(pendingAnimation.m_name); - if (it == m_runningAnimations.end()) { - Vector animations; - animations.append(pendingAnimation); - m_runningAnimations.add(pendingAnimation.m_name, animations); - } else { - Vector& animations = it->second; - animations.append(pendingAnimation); - } - } - - m_uncomittedAnimations.clear(); - } -} - -void GraphicsLayerCA::setCAAnimationOnLayer(CAPropertyAnimation* caAnim, AnimatedPropertyID property, const String& animationName, int index, double timeOffset) -{ - PlatformLayer* layer = animatedLayer(property); - - if (timeOffset) { - [caAnim setBeginTime:CACurrentMediaTime() - timeOffset]; - [caAnim setValue:[NSNumber numberWithBool:YES] forKey:WebKitAnimationBeginTimeSetKey]; - } - - NSString *animationID = animationIdentifier(animationName, property, index); - - [layer removeAnimationForKey:animationID]; - [layer addAnimation:caAnim forKey:animationID]; - - if (LayerMap* layerCloneMap = animatedLayerClones(property)) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - // Skip immediate replicas, since they move with the original. - if (m_replicaLayer && isReplicatedRootClone(it->first)) - continue; - CALayer *currLayer = it->second.get(); - [currLayer removeAnimationForKey:animationID]; - [currLayer addAnimation:caAnim forKey:animationID]; - } - } -} - -// Workaround for -static void bug7311367Workaround(CALayer* transformLayer, const TransformationMatrix& transform) -{ - if (!transformLayer) - return; - - CATransform3D caTransform; - copyTransform(caTransform, transform); - caTransform.m41 += 1; - [transformLayer setTransform:caTransform]; - - caTransform.m41 -= 1; - [transformLayer setTransform:caTransform]; -} - -bool GraphicsLayerCA::removeCAAnimationFromLayer(AnimatedPropertyID property, const String& animationName, int index) -{ - PlatformLayer* layer = animatedLayer(property); - - NSString *animationID = animationIdentifier(animationName, property, index); - - if (![layer animationForKey:animationID]) - return false; - - [layer removeAnimationForKey:animationID]; - bug7311367Workaround(m_structuralLayer.get(), m_transform); - - if (LayerMap* layerCloneMap = animatedLayerClones(property)) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - // Skip immediate replicas, since they move with the original. - if (m_replicaLayer && isReplicatedRootClone(it->first)) - continue; - - CALayer *currLayer = it->second.get(); - [currLayer removeAnimationForKey:animationID]; - } - } - return true; -} - -static void copyAnimationProperties(CAPropertyAnimation* from, CAPropertyAnimation* to) -{ - [to setBeginTime:[from beginTime]]; - [to setDuration:[from duration]]; - [to setRepeatCount:[from repeatCount]]; - [to setAutoreverses:[from autoreverses]]; - [to setFillMode:[from fillMode]]; - [to setRemovedOnCompletion:[from isRemovedOnCompletion]]; - [to setAdditive:[from isAdditive]]; - [to setTimingFunction:[from timingFunction]]; - -#if HAVE_MODERN_QUARTZCORE - [to setValueFunction:[from valueFunction]]; -#endif - - if (id object = [from valueForKey:WebKitAnimationBeginTimeSetKey]) - [to setValue:object forKey:WebKitAnimationBeginTimeSetKey]; -} - -void GraphicsLayerCA::pauseCAAnimationOnLayer(AnimatedPropertyID property, const String& animationName, int index, double timeOffset) -{ - PlatformLayer* layer = animatedLayer(property); - - NSString *animationID = animationIdentifier(animationName, property, index); - - CAAnimation *caAnim = [layer animationForKey:animationID]; - if (!caAnim) - return; - - // Animations on the layer are immutable, so we have to clone and modify. - CAPropertyAnimation* pausedAnim = nil; - if ([caAnim isKindOfClass:[CAKeyframeAnimation class]]) { - CAKeyframeAnimation* existingKeyframeAnim = static_cast(caAnim); - CAKeyframeAnimation* newAnim = [CAKeyframeAnimation animationWithKeyPath:[existingKeyframeAnim keyPath]]; - copyAnimationProperties(existingKeyframeAnim, newAnim); - [newAnim setValues:[existingKeyframeAnim values]]; - [newAnim setKeyTimes:[existingKeyframeAnim keyTimes]]; - [newAnim setTimingFunctions:[existingKeyframeAnim timingFunctions]]; - pausedAnim = newAnim; - } else if ([caAnim isKindOfClass:[CABasicAnimation class]]) { - CABasicAnimation* existingPropertyAnim = static_cast(caAnim); - CABasicAnimation* newAnim = [CABasicAnimation animationWithKeyPath:[existingPropertyAnim keyPath]]; - copyAnimationProperties(existingPropertyAnim, newAnim); - [newAnim setFromValue:[existingPropertyAnim fromValue]]; - [newAnim setToValue:[existingPropertyAnim toValue]]; - pausedAnim = newAnim; - } - - // pausedAnim has the beginTime of caAnim already. - [pausedAnim setSpeed:0]; - [pausedAnim setTimeOffset:timeOffset]; - - [layer addAnimation:pausedAnim forKey:animationID]; // This will replace the running animation. - - // Pause the animations on the clones too. - if (LayerMap* layerCloneMap = animatedLayerClones(property)) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - // Skip immediate replicas, since they move with the original. - if (m_replicaLayer && isReplicatedRootClone(it->first)) - continue; - CALayer *currLayer = it->second.get(); - [currLayer addAnimation:pausedAnim forKey:animationID]; - } - } -} - -void GraphicsLayerCA::setContentsToCanvas(PlatformLayer* canvasLayer) -{ - if (canvasLayer == m_contentsLayer) - return; - - m_contentsLayer = canvasLayer; - if (m_contentsLayer && [m_contentsLayer.get() respondsToSelector:@selector(setLayerOwner:)]) - [(id)m_contentsLayer.get() setLayerOwner:this]; - - m_contentsLayerPurpose = canvasLayer ? ContentsLayerForCanvas : NoContentsLayer; - - noteSublayersChanged(); - noteLayerPropertyChanged(ContentsCanvasLayerChanged); -} - -void GraphicsLayerCA::repaintLayerDirtyRects() -{ - if (!m_dirtyRects.size()) - return; - - for (size_t i = 0; i < m_dirtyRects.size(); ++i) - [m_layer.get() setNeedsDisplayInRect:m_dirtyRects[i]]; - - m_dirtyRects.clear(); -} - -void GraphicsLayerCA::updateContentsNeedsDisplay() -{ - if (m_contentsLayer) - [m_contentsLayer.get() setNeedsDisplay]; -} - -bool GraphicsLayerCA::createAnimationFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& animationName, double timeOffset) -{ - ASSERT(valueList.property() != AnimatedPropertyWebkitTransform); - - BEGIN_BLOCK_OBJC_EXCEPTIONS; - - bool isKeyframe = valueList.size() > 2; - bool valuesOK; - - bool additive = false; - int animationIndex = 0; - - CAPropertyAnimation* caAnimation; - if (isKeyframe) { - CAKeyframeAnimation* keyframeAnim = createKeyframeAnimation(animation, valueList.property(), additive); - valuesOK = setAnimationKeyframes(valueList, animation, keyframeAnim); - caAnimation = keyframeAnim; - } else { - CABasicAnimation* basicAnim = createBasicAnimation(animation, valueList.property(), additive); - valuesOK = setAnimationEndpoints(valueList, animation, basicAnim); - caAnimation = basicAnim; - } - - if (!valuesOK) - return false; - - m_uncomittedAnimations.append(LayerPropertyAnimation(caAnimation, animationName, valueList.property(), animationIndex, timeOffset)); - - END_BLOCK_OBJC_EXCEPTIONS; - - return true; -} - -bool GraphicsLayerCA::createTransformAnimationsFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& animationName, double timeOffset, const IntSize& boxSize) -{ - ASSERT(valueList.property() == AnimatedPropertyWebkitTransform); - - TransformOperationList functionList; - bool listsMatch, hasBigRotation; - fetchTransformOperationList(valueList, functionList, listsMatch, hasBigRotation); - - // We need to fall back to software animation if we don't have setValueFunction:, and - // we would need to animate each incoming transform function separately. This is the - // case if we have a rotation >= 180 or we have more than one transform function. - if ((hasBigRotation || functionList.size() > 1) && !caValueFunctionSupported()) - return false; - - bool validMatrices = true; - - BEGIN_BLOCK_OBJC_EXCEPTIONS; - - // If functionLists don't match we do a matrix animation, otherwise we do a component hardware animation. - // Also, we can't do component animation unless we have valueFunction, so we need to do matrix animation - // if that's not true as well. - bool isMatrixAnimation = !listsMatch || !caValueFunctionSupported(); - - size_t numAnimations = isMatrixAnimation ? 1 : functionList.size(); - bool isKeyframe = valueList.size() > 2; - - // Iterate through the transform functions, sending an animation for each one. - for (size_t animationIndex = 0; animationIndex < numAnimations; ++animationIndex) { - TransformOperation::OperationType transformOp = isMatrixAnimation ? TransformOperation::MATRIX_3D : functionList[animationIndex]; - CAPropertyAnimation* caAnimation; - -#if defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_SNOW_LEOPARD) - // CA applies animations in reverse order () so we need the last one we add (per property) - // to be non-additive. - bool additive = animationIndex < (numAnimations - 1); -#else - bool additive = animationIndex > 0; -#endif - if (isKeyframe) { - CAKeyframeAnimation* keyframeAnim = createKeyframeAnimation(animation, valueList.property(), additive); - validMatrices = setTransformAnimationKeyframes(valueList, animation, keyframeAnim, animationIndex, transformOp, isMatrixAnimation, boxSize); - caAnimation = keyframeAnim; - } else { - CABasicAnimation* basicAnim = createBasicAnimation(animation, valueList.property(), additive); - validMatrices = setTransformAnimationEndpoints(valueList, animation, basicAnim, animationIndex, transformOp, isMatrixAnimation, boxSize); - caAnimation = basicAnim; - } - - if (!validMatrices) - break; - - m_uncomittedAnimations.append(LayerPropertyAnimation(caAnimation, animationName, valueList.property(), animationIndex, timeOffset)); - } - - END_BLOCK_OBJC_EXCEPTIONS; - - return validMatrices; -} - -CABasicAnimation* GraphicsLayerCA::createBasicAnimation(const Animation* anim, AnimatedPropertyID property, bool additive) -{ - CABasicAnimation* basicAnim = [CABasicAnimation animationWithKeyPath:propertyIdToString(property)]; - setupAnimation(basicAnim, anim, additive); - return basicAnim; -} - -CAKeyframeAnimation* GraphicsLayerCA::createKeyframeAnimation(const Animation* anim, AnimatedPropertyID property, bool additive) -{ - CAKeyframeAnimation* keyframeAnim = [CAKeyframeAnimation animationWithKeyPath:propertyIdToString(property)]; - setupAnimation(keyframeAnim, anim, additive); - return keyframeAnim; -} - -void GraphicsLayerCA::setupAnimation(CAPropertyAnimation* propertyAnim, const Animation* anim, bool additive) -{ - double duration = anim->duration(); - if (duration <= 0) - duration = cAnimationAlmostZeroDuration; - - float repeatCount = anim->iterationCount(); - if (repeatCount == Animation::IterationCountInfinite) - repeatCount = FLT_MAX; - else if (anim->direction() == Animation::AnimationDirectionAlternate) - repeatCount /= 2; - - NSString *fillMode = 0; - switch (anim->fillMode()) { - case AnimationFillModeNone: - fillMode = kCAFillModeForwards; // Use "forwards" rather than "removed" because the style system will remove the animation when it is finished. This avoids a flash. - break; - case AnimationFillModeBackwards: - fillMode = kCAFillModeBoth; // Use "both" rather than "backwards" because the style system will remove the animation when it is finished. This avoids a flash. - break; - case AnimationFillModeForwards: - fillMode = kCAFillModeForwards; - break; - case AnimationFillModeBoth: - fillMode = kCAFillModeBoth; - break; - } - - [propertyAnim setDuration:duration]; - [propertyAnim setRepeatCount:repeatCount]; - [propertyAnim setAutoreverses:anim->direction()]; - [propertyAnim setRemovedOnCompletion:NO]; - [propertyAnim setAdditive:additive]; - [propertyAnim setFillMode:fillMode]; - - [propertyAnim setDelegate:m_animationDelegate.get()]; -} - -CAMediaTimingFunction* GraphicsLayerCA::timingFunctionForAnimationValue(const AnimationValue* animValue, const Animation* anim) -{ - const TimingFunction* tf = 0; - if (animValue->timingFunction()) - tf = animValue->timingFunction(); - else if (anim->isTimingFunctionSet()) - tf = anim->timingFunction().get(); - - return getCAMediaTimingFunction(tf ? tf : CubicBezierTimingFunction::create().get()); -} - -bool GraphicsLayerCA::setAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, CABasicAnimation* basicAnim) -{ - id fromValue = nil; - id toValue = nil; - - switch (valueList.property()) { - case AnimatedPropertyOpacity: { - const FloatAnimationValue* startVal = static_cast(valueList.at(0)); - const FloatAnimationValue* endVal = static_cast(valueList.at(1)); - fromValue = [NSNumber numberWithFloat:startVal->value()]; - toValue = [NSNumber numberWithFloat:endVal->value()]; - break; - } - default: - ASSERT_NOT_REACHED(); // we don't animate color yet - break; - } - - // This codepath is used for 2-keyframe animations, so we still need to look in the start - // for a timing function. - CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim); - [basicAnim setTimingFunction:timingFunction]; - - [basicAnim setFromValue:fromValue]; - [basicAnim setToValue:toValue]; - - return true; -} - -bool GraphicsLayerCA::setAnimationKeyframes(const KeyframeValueList& valueList, const Animation* anim, CAKeyframeAnimation* keyframeAnim) -{ - RetainPtr keyTimes(AdoptNS, [[NSMutableArray alloc] init]); - RetainPtr values(AdoptNS, [[NSMutableArray alloc] init]); - RetainPtr timingFunctions(AdoptNS, [[NSMutableArray alloc] init]); - - for (unsigned i = 0; i < valueList.size(); ++i) { - const AnimationValue* curValue = valueList.at(i); - [keyTimes.get() addObject:[NSNumber numberWithFloat:curValue->keyTime()]]; - - switch (valueList.property()) { - case AnimatedPropertyOpacity: { - const FloatAnimationValue* floatValue = static_cast(curValue); - [values.get() addObject:[NSNumber numberWithFloat:floatValue->value()]]; - break; - } - default: - ASSERT_NOT_REACHED(); // we don't animate color yet - break; - } - - CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, anim); - [timingFunctions.get() addObject:timingFunction]; - } - - // We toss the last tfArray value because it has to one shorter than the others. - [timingFunctions.get() removeLastObject]; - - [keyframeAnim setKeyTimes:keyTimes.get()]; - [keyframeAnim setValues:values.get()]; - [keyframeAnim setTimingFunctions:timingFunctions.get()]; - - return true; -} - -bool GraphicsLayerCA::setTransformAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, CABasicAnimation* basicAnim, int functionIndex, TransformOperation::OperationType transformOp, bool isMatrixAnimation, const IntSize& boxSize) -{ - id fromValue; - id toValue; - - ASSERT(valueList.size() == 2); - const TransformAnimationValue* startValue = static_cast(valueList.at(0)); - const TransformAnimationValue* endValue = static_cast(valueList.at(1)); - - if (isMatrixAnimation) { - TransformationMatrix fromTransform, toTransform; - startValue->value()->apply(boxSize, fromTransform); - endValue->value()->apply(boxSize, toTransform); - - // If any matrix is singular, CA won't animate it correctly. So fall back to software animation - if (!fromTransform.isInvertible() || !toTransform.isInvertible()) - return false; - - CATransform3D caTransform; - copyTransform(caTransform, fromTransform); - fromValue = [NSValue valueWithCATransform3D:caTransform]; - - copyTransform(caTransform, toTransform); - toValue = [NSValue valueWithCATransform3D:caTransform]; - } else { - fromValue = getTransformFunctionValue(startValue->value()->at(functionIndex), transformOp, boxSize); - toValue = getTransformFunctionValue(endValue->value()->at(functionIndex), transformOp, boxSize); - } - - // This codepath is used for 2-keyframe animations, so we still need to look in the start - // for a timing function. - CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim); - [basicAnim setTimingFunction:timingFunction]; - - [basicAnim setFromValue:fromValue]; - [basicAnim setToValue:toValue]; - -#if HAVE_MODERN_QUARTZCORE - if (NSString *valueFunctionName = getValueFunctionNameForTransformOperation(transformOp)) - [basicAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; -#endif - - return true; -} - -bool GraphicsLayerCA::setTransformAnimationKeyframes(const KeyframeValueList& valueList, const Animation* animation, CAKeyframeAnimation* keyframeAnim, int functionIndex, TransformOperation::OperationType transformOpType, bool isMatrixAnimation, const IntSize& boxSize) -{ - RetainPtr keyTimes(AdoptNS, [[NSMutableArray alloc] init]); - RetainPtr values(AdoptNS, [[NSMutableArray alloc] init]); - RetainPtr timingFunctions(AdoptNS, [[NSMutableArray alloc] init]); - - for (unsigned i = 0; i < valueList.size(); ++i) { - const TransformAnimationValue* curValue = static_cast(valueList.at(i)); - [keyTimes.get() addObject:[NSNumber numberWithFloat:curValue->keyTime()]]; - - if (isMatrixAnimation) { - TransformationMatrix transform; - curValue->value()->apply(boxSize, transform); - - // If any matrix is singular, CA won't animate it correctly. So fall back to software animation - if (!transform.isInvertible()) - return false; - - CATransform3D caTransform; - copyTransform(caTransform, transform); - [values.get() addObject:[NSValue valueWithCATransform3D:caTransform]]; - } else { - const TransformOperation* transformOp = curValue->value()->at(functionIndex); - [values.get() addObject:getTransformFunctionValue(transformOp, transformOpType, boxSize)]; - } - - CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, animation); - [timingFunctions.get() addObject:timingFunction]; - } - - // We toss the last tfArray value because it has to one shorter than the others. - [timingFunctions.get() removeLastObject]; - - [keyframeAnim setKeyTimes:keyTimes.get()]; - [keyframeAnim setValues:values.get()]; - [keyframeAnim setTimingFunctions:timingFunctions.get()]; - -#if HAVE_MODERN_QUARTZCORE - if (NSString *valueFunctionName = getValueFunctionNameForTransformOperation(transformOpType)) - [keyframeAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; -#endif - return true; -} - -void GraphicsLayerCA::suspendAnimations(double time) -{ - double t = currentTimeToMediaTime(time ? time : currentTime()); - [primaryLayer() setSpeed:0]; - [primaryLayer() setTimeOffset:t]; - - // Suspend the animations on the clones too. - if (LayerMap* layerCloneMap = primaryLayerClones()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - CALayer *currLayer = it->second.get(); - [currLayer setSpeed:0]; - [currLayer setTimeOffset:t]; - } - } -} - -void GraphicsLayerCA::resumeAnimations() -{ - [primaryLayer() setSpeed:1]; - [primaryLayer() setTimeOffset:0]; - - // Resume the animations on the clones too. - if (LayerMap* layerCloneMap = primaryLayerClones()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - CALayer *currLayer = it->second.get(); - [currLayer setSpeed:1]; - [currLayer setTimeOffset:0]; - } - } -} - -CALayer* GraphicsLayerCA::hostLayerForSublayers() const -{ - return m_structuralLayer.get() ? m_structuralLayer.get() : m_layer.get(); -} - -CALayer* GraphicsLayerCA::layerForSuperlayer() const -{ - return m_structuralLayer ? m_structuralLayer.get() : m_layer.get(); -} - -CALayer* GraphicsLayerCA::animatedLayer(AnimatedPropertyID property) const -{ - return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayer.get() : primaryLayer(); -} - -GraphicsLayerCA::LayerMap* GraphicsLayerCA::animatedLayerClones(AnimatedPropertyID property) const -{ - return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayerClones.get() : primaryLayerClones(); -} - -PlatformLayer* GraphicsLayerCA::platformLayer() const -{ - return primaryLayer(); -} - -void GraphicsLayerCA::setDebugBackgroundColor(const Color& color) -{ - BEGIN_BLOCK_OBJC_EXCEPTIONS - - if (color.isValid()) - setLayerBackgroundColor(m_layer.get(), color); - else - clearLayerBackgroundColor(m_layer.get()); - - END_BLOCK_OBJC_EXCEPTIONS -} - -void GraphicsLayerCA::setDebugBorder(const Color& color, float borderWidth) -{ - BEGIN_BLOCK_OBJC_EXCEPTIONS - - if (color.isValid()) { - setLayerBorderColor(m_layer.get(), color); - [m_layer.get() setBorderWidth:borderWidth]; - } else { - clearBorderColor(m_layer.get()); - [m_layer.get() setBorderWidth:0]; - } - - END_BLOCK_OBJC_EXCEPTIONS -} - -FloatSize GraphicsLayerCA::constrainedSize() const -{ - float tileColumns = ceilf(m_size.width() / cTiledLayerTileSize); - float tileRows = ceilf(m_size.height() / cTiledLayerTileSize); - double numTiles = tileColumns * tileRows; - - FloatSize constrainedSize = m_size; - const unsigned cMaxTileCount = 512; - while (numTiles > cMaxTileCount) { - // Constrain the wider dimension. - if (constrainedSize.width() >= constrainedSize.height()) { - tileColumns = max(floorf(cMaxTileCount / tileRows), 1.0f); - constrainedSize.setWidth(tileColumns * cTiledLayerTileSize); - } else { - tileRows = max(floorf(cMaxTileCount / tileColumns), 1.0f); - constrainedSize.setHeight(tileRows * cTiledLayerTileSize); - } - numTiles = tileColumns * tileRows; - } - - return constrainedSize; -} - -bool GraphicsLayerCA::requiresTiledLayer(const FloatSize& size) const -{ - if (!m_drawsContent) - return false; - - // FIXME: catch zero-size height or width here (or earlier)? - return size.width() > cMaxPixelDimension || size.height() > cMaxPixelDimension; -} - -void GraphicsLayerCA::swapFromOrToTiledLayer(bool useTiledLayer) -{ - if (useTiledLayer == m_usingTiledLayer) - return; - - CGSize tileSize = CGSizeMake(cTiledLayerTileSize, cTiledLayerTileSize); - - RetainPtr oldLayer = m_layer.get(); - - Class layerClass = useTiledLayer ? [WebTiledLayer self] : [WebLayer self]; - m_layer.adoptNS([[layerClass alloc] init]); - - m_usingTiledLayer = useTiledLayer; - - if (useTiledLayer) { - WebTiledLayer* tiledLayer = (WebTiledLayer*)m_layer.get(); - [tiledLayer setTileSize:tileSize]; - [tiledLayer setLevelsOfDetail:1]; - [tiledLayer setLevelsOfDetailBias:0]; - [tiledLayer setContentsGravity:@"bottomLeft"]; - -#if !HAVE_MODERN_QUARTZCORE - // Tiled layer has issues with flipped coordinates. - setContentsOrientation(CompositingCoordinatesTopDown); -#endif - } else { -#if !HAVE_MODERN_QUARTZCORE - setContentsOrientation(defaultContentsOrientation()); -#endif - } - - [m_layer.get() setLayerOwner:this]; - safeSetSublayers(m_layer.get(), [oldLayer.get() sublayers]); - - [[oldLayer.get() superlayer] replaceSublayer:oldLayer.get() with:m_layer.get()]; - - updateContentsTransform(); - - updateLayerPosition(); - updateLayerSize(); - updateAnchorPoint(); - updateTransform(); - updateChildrenTransform(); - updateMasksToBounds(); - updateContentsOpaque(); - updateBackfaceVisibility(); - updateLayerBackgroundColor(); - - updateOpacityOnLayer(); - -#ifndef NDEBUG - String name = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + m_name; - [m_layer.get() setName:name]; -#endif - - // move over animations - moveOrCopyAnimationsForProperty(Move, AnimatedPropertyWebkitTransform, oldLayer.get(), m_layer.get()); - moveOrCopyAnimationsForProperty(Move, AnimatedPropertyOpacity, oldLayer.get(), m_layer.get()); - moveOrCopyAnimationsForProperty(Move, AnimatedPropertyBackgroundColor, oldLayer.get(), m_layer.get()); - - // need to tell new layer to draw itself - setNeedsDisplay(); - - updateDebugIndicators(); -} - -GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayerCA::defaultContentsOrientation() const -{ -#if !HAVE_MODERN_QUARTZCORE - // Older QuartzCore does not support -geometryFlipped, so we manually flip the root - // layer geometry, and then flip the contents of each layer back so that the CTM for CG - // is unflipped, allowing it to do the correct font auto-hinting. - return CompositingCoordinatesBottomUp; -#else - return CompositingCoordinatesTopDown; -#endif -} - -void GraphicsLayerCA::updateContentsTransform() -{ -#if !HAVE_MODERN_QUARTZCORE - if (contentsOrientation() == CompositingCoordinatesBottomUp) { - CGAffineTransform contentsTransform = CGAffineTransformMakeScale(1, -1); - contentsTransform = CGAffineTransformTranslate(contentsTransform, 0, -[m_layer.get() bounds].size.height); - [m_layer.get() setContentsTransform:contentsTransform]; - } -#endif -} - -void GraphicsLayerCA::setupContentsLayer(CALayer* contentsLayer) -{ - // Turn off implicit animations on the inner layer. - [contentsLayer setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; - [contentsLayer setMasksToBounds:YES]; - - if (defaultContentsOrientation() == CompositingCoordinatesBottomUp) { - CATransform3D flipper = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f}; - [contentsLayer setTransform:flipper]; - [contentsLayer setAnchorPoint:CGPointMake(0.0f, 1.0f)]; - } else - [contentsLayer setAnchorPoint:CGPointZero]; - - if (showDebugBorders()) { - setLayerBorderColor(contentsLayer, Color(0, 0, 128, 180)); - [contentsLayer setBorderWidth:1.0f]; - } -} - -CALayer *GraphicsLayerCA::findOrMakeClone(CloneID cloneID, CALayer *sourceLayer, LayerMap* clones, CloneLevel cloneLevel) -{ - if (!sourceLayer) - return 0; - - CALayer *resultLayer; - - // Add with a dummy value to get an iterator for the insertion position, and a boolean that tells - // us whether there's an item there. This technique avoids two hash lookups. - RetainPtr dummy; - pair addResult = clones->add(cloneID, dummy); - if (!addResult.second) { - // Value was not added, so it exists already. - resultLayer = addResult.first->second.get(); - } else { - resultLayer = cloneLayer(sourceLayer, cloneLevel); -#ifndef NDEBUG - [resultLayer setName:[NSString stringWithFormat:@"Clone %d of layer %@", cloneID[0U], sourceLayer]]; -#endif - addResult.first->second = resultLayer; - } - - return resultLayer; -} - -void GraphicsLayerCA::ensureCloneLayers(CloneID cloneID, CALayer *& primaryLayer, CALayer *& structuralLayer, CALayer *& contentsLayer, CloneLevel cloneLevel) -{ - structuralLayer = nil; - contentsLayer = nil; - - if (!m_layerClones) - m_layerClones = new LayerMap; - - if (!m_structuralLayerClones && m_structuralLayer) - m_structuralLayerClones = new LayerMap; - - if (!m_contentsLayerClones && m_contentsLayer) - m_contentsLayerClones = new LayerMap; - - primaryLayer = findOrMakeClone(cloneID, m_layer.get(), m_layerClones.get(), cloneLevel); - structuralLayer = findOrMakeClone(cloneID, m_structuralLayer.get(), m_structuralLayerClones.get(), cloneLevel); - contentsLayer = findOrMakeClone(cloneID, m_contentsLayer.get(), m_contentsLayerClones.get(), cloneLevel); -} - -void GraphicsLayerCA::removeCloneLayers() -{ - m_layerClones = 0; - m_structuralLayerClones = 0; - m_contentsLayerClones = 0; -} - -FloatPoint GraphicsLayerCA::positionForCloneRootLayer() const -{ - // This can get called during a sync when we've just removed the m_replicaLayer. - if (!m_replicaLayer) - return FloatPoint(); - - FloatPoint replicaPosition = m_replicaLayer->replicatedLayerPosition(); - return FloatPoint(replicaPosition.x() + m_anchorPoint.x() * m_size.width(), - replicaPosition.y() + m_anchorPoint.y() * m_size.height()); -} - -void GraphicsLayerCA::propagateLayerChangeToReplicas() -{ - for (GraphicsLayer* currLayer = this; currLayer; currLayer = currLayer->parent()) { - GraphicsLayerCA* currLayerCA = static_cast(currLayer); - if (!currLayerCA->hasCloneLayers()) - break; - - if (currLayerCA->replicaLayer()) - static_cast(currLayerCA->replicaLayer())->noteLayerPropertyChanged(ReplicatedLayerChanged); - } -} - -CALayer *GraphicsLayerCA::fetchCloneLayers(GraphicsLayer* replicaRoot, ReplicaState& replicaState, CloneLevel cloneLevel) -{ - CALayer *primaryLayer; - CALayer *structuralLayer; - CALayer *contentsLayer; - ensureCloneLayers(replicaState.cloneID(), primaryLayer, structuralLayer, contentsLayer, cloneLevel); - - if (m_maskLayer) { - CALayer *maskClone = static_cast(m_maskLayer)->fetchCloneLayers(replicaRoot, replicaState, IntermediateCloneLevel); - [primaryLayer setMask:maskClone]; - } - - if (m_replicatedLayer) { - // We are a replica being asked for clones of our layers. - CALayer *replicaRoot = replicatedLayerRoot(replicaState); - if (!replicaRoot) - return nil; - - if (structuralLayer) { - [structuralLayer insertSublayer:replicaRoot atIndex:0]; - return structuralLayer; - } - - [primaryLayer insertSublayer:replicaRoot atIndex:0]; - return primaryLayer; - } - - const Vector& childLayers = children(); - NSMutableArray* clonalSublayers = nil; - - CALayer *replicaLayer = nil; - if (m_replicaLayer && m_replicaLayer != replicaRoot) { - // We have nested replicas. Ask the replica layer for a clone of its contents. - replicaState.setBranchType(ReplicaState::ReplicaBranch); - replicaLayer = static_cast(m_replicaLayer)->fetchCloneLayers(replicaRoot, replicaState, RootCloneLevel); - replicaState.setBranchType(ReplicaState::ChildBranch); - } - - if (replicaLayer || structuralLayer || contentsLayer || childLayers.size() > 0) { - clonalSublayers = [[NSMutableArray alloc] init]; - - if (structuralLayer) { - // Replicas render behind the actual layer content. - if (replicaLayer) - [clonalSublayers addObject:replicaLayer]; - - // Add the primary layer next. Even if we have negative z-order children, the primary layer always comes behind. - [clonalSublayers addObject:primaryLayer]; - } else if (contentsLayer) { - // FIXME: add the contents layer in the correct order with negative z-order children. - // This does not cause visible rendering issues because currently contents layers are only used - // for replaced elements that don't have children. - [clonalSublayers addObject:contentsLayer]; - } - - replicaState.push(ReplicaState::ChildBranch); - - size_t numChildren = childLayers.size(); - for (size_t i = 0; i < numChildren; ++i) { - GraphicsLayerCA* curChild = static_cast(childLayers[i]); - - CALayer *childLayer = curChild->fetchCloneLayers(replicaRoot, replicaState, IntermediateCloneLevel); - if (childLayer) - [clonalSublayers addObject:childLayer]; - } - - replicaState.pop(); - - [clonalSublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; - } - - CALayer *result; - if (structuralLayer) { - [structuralLayer setSublayers:clonalSublayers]; - - if (contentsLayer) { - // If we have a transform layer, then the contents layer is parented in the - // primary layer (which is itself a child of the transform layer). - [primaryLayer setSublayers:nil]; - [primaryLayer addSublayer:contentsLayer]; - } - - result = structuralLayer; - } else { - [primaryLayer setSublayers:clonalSublayers]; - result = primaryLayer; - } - - [clonalSublayers release]; - return result; -} - -CALayer *GraphicsLayerCA::cloneLayer(CALayer *layer, CloneLevel cloneLevel) -{ - static Class transformLayerClass = NSClassFromString(@"CATransformLayer"); - CALayer *newLayer = nil; - if ([layer isKindOfClass:transformLayerClass]) - newLayer = [transformLayerClass layer]; - else - newLayer = [CALayer layer]; - - [newLayer setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; - - [newLayer setPosition:[layer position]]; - [newLayer setBounds:[layer bounds]]; - [newLayer setAnchorPoint:[layer anchorPoint]]; -#if HAVE_MODERN_QUARTZCORE - [newLayer setAnchorPointZ:[layer anchorPointZ]]; -#endif - [newLayer setTransform:[layer transform]]; - [newLayer setSublayerTransform:[layer sublayerTransform]]; - [newLayer setContents:[layer contents]]; - [newLayer setMasksToBounds:[layer masksToBounds]]; - [newLayer setDoubleSided:[layer isDoubleSided]]; - [newLayer setOpaque:[layer isOpaque]]; - [newLayer setBackgroundColor:[layer backgroundColor]]; - - if (cloneLevel == IntermediateCloneLevel) { - [newLayer setOpacity:[layer opacity]]; - moveOrCopyAnimationsForProperty(Copy, AnimatedPropertyWebkitTransform, layer, newLayer); - moveOrCopyAnimationsForProperty(Copy, AnimatedPropertyOpacity, layer, newLayer); - } - - if (showDebugBorders()) { - setLayerBorderColor(newLayer, Color(255, 122, 251)); - [newLayer setBorderWidth:2]; - } - - return newLayer; -} - -void GraphicsLayerCA::setOpacityInternal(float accumulatedOpacity) -{ - LayerMap* layerCloneMap = 0; - - if (preserves3D()) { - [m_layer.get() setOpacity:accumulatedOpacity]; - layerCloneMap = m_layerClones.get(); - } else { - [primaryLayer() setOpacity:accumulatedOpacity]; - layerCloneMap = primaryLayerClones(); - } - - if (layerCloneMap) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - if (m_replicaLayer && isReplicatedRootClone(it->first)) - continue; - CALayer *currLayer = it->second.get(); - [currLayer setOpacity:m_opacity]; - } - } -} - -void GraphicsLayerCA::updateOpacityOnLayer() -{ -#if !HAVE_MODERN_QUARTZCORE - // Distribute opacity either to our own layer or to our children. We pass in the - // contribution from our parent(s). - distributeOpacity(parent() ? parent()->accumulatedOpacity() : 1); -#else - [primaryLayer() setOpacity:m_opacity]; - - if (LayerMap* layerCloneMap = primaryLayerClones()) { - LayerMap::const_iterator end = layerCloneMap->end(); - for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { - if (m_replicaLayer && isReplicatedRootClone(it->first)) - continue; - - CALayer *currLayer = it->second.get(); - [currLayer setOpacity:m_opacity]; - } - - } -#endif -} - -void GraphicsLayerCA::noteSublayersChanged() -{ - noteLayerPropertyChanged(ChildrenChanged); - propagateLayerChangeToReplicas(); -} - -void GraphicsLayerCA::noteLayerPropertyChanged(LayerChangeFlags flags) -{ - if (!m_uncommittedChanges && m_client) - m_client->notifySyncRequired(this); - - m_uncommittedChanges |= flags; -} - -} // namespace WebCore - -#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/mac/GraphicsLayerMac.h b/WebCore/platform/graphics/mac/GraphicsLayerMac.h new file mode 100644 index 0000000..5d4dfe2 --- /dev/null +++ b/WebCore/platform/graphics/mac/GraphicsLayerMac.h @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GraphicsLayerMac_h +#define GraphicsLayerMac_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "GraphicsLayer.h" +#include "WebLayer.h" +#include +#include +#include +#include + +@class CABasicAnimation; +@class CAKeyframeAnimation; +@class CAMediaTimingFunction; +@class CAPropertyAnimation; +@class WebAnimationDelegate; + +namespace WebCore { + +class GraphicsLayerMac : public GraphicsLayer { +public: + + GraphicsLayerMac(GraphicsLayerClient*); + virtual ~GraphicsLayerMac(); + + virtual void setName(const String&); + + // for hosting this GraphicsLayer in a native layer hierarchy + virtual NativeLayer nativeLayer() const; + + virtual bool setChildren(const Vector&); + virtual void addChild(GraphicsLayer*); + virtual void addChildAtIndex(GraphicsLayer*, int index); + virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild); + + virtual void removeFromParent(); + + virtual void setMaskLayer(GraphicsLayer*); + virtual void setReplicatedLayer(GraphicsLayer*); + + virtual void setPosition(const FloatPoint&); + virtual void setAnchorPoint(const FloatPoint3D&); + virtual void setSize(const FloatSize&); + + virtual void setTransform(const TransformationMatrix&); + + virtual void setChildrenTransform(const TransformationMatrix&); + + virtual void setPreserves3D(bool); + virtual void setMasksToBounds(bool); + virtual void setDrawsContent(bool); + + virtual void setBackgroundColor(const Color&); + virtual void clearBackgroundColor(); + + virtual void setContentsOpaque(bool); + virtual void setBackfaceVisibility(bool); + + // return true if we started an animation + virtual void setOpacity(float); + + virtual void setNeedsDisplay(); + virtual void setNeedsDisplayInRect(const FloatRect&); + virtual void setContentsNeedsDisplay(); + + virtual void setContentsRect(const IntRect&); + + virtual void suspendAnimations(double time); + virtual void resumeAnimations(); + + virtual bool addAnimation(const KeyframeValueList&, const IntSize& boxSize, const Animation*, const String& animationName, double timeOffset); + virtual void pauseAnimation(const String& animationName, double timeOffset); + virtual void removeAnimation(const String& animationName); + + virtual void setContentsToImage(Image*); + virtual void setContentsToMedia(PlatformLayer*); + virtual void setContentsToCanvas(PlatformLayer*); + + virtual bool hasContentsLayer() const { return m_contentsLayer; } + + virtual PlatformLayer* platformLayer() const; + + virtual void setDebugBackgroundColor(const Color&); + virtual void setDebugBorder(const Color&, float borderWidth); + + virtual void didDisplay(PlatformLayer*); + + void recursiveCommitChanges(); + + virtual void syncCompositingState(); + virtual void syncCompositingStateForThisLayerOnly(); + + // Should only be called by animationDidStart: callback + void animationDidStart(CAAnimation*); + +protected: + virtual void setOpacityInternal(float); + +private: + void updateOpacityOnLayer(); + + CALayer* primaryLayer() const { return m_structuralLayer.get() ? m_structuralLayer.get() : m_layer.get(); } + CALayer* hostLayerForSublayers() const; + CALayer* layerForSuperlayer() const; + CALayer* animatedLayer(AnimatedPropertyID) const; + + typedef String CloneID; // Identifier for a given clone, based on original/replica branching down the tree. + static bool isReplicatedRootClone(const CloneID& cloneID) { return cloneID[0U] & 1; } + + typedef HashMap > LayerMap; + LayerMap* primaryLayerClones() const { return m_structuralLayer.get() ? m_structuralLayerClones.get() : m_layerClones.get(); } + LayerMap* animatedLayerClones(AnimatedPropertyID) const; + + bool createAnimationFromKeyframes(const KeyframeValueList&, const Animation*, const String& animationName, double timeOffset); + bool createTransformAnimationsFromKeyframes(const KeyframeValueList&, const Animation*, const String& animationName, double timeOffset, const IntSize& boxSize); + + // Return autoreleased animation (use RetainPtr?) + CABasicAnimation* createBasicAnimation(const Animation*, AnimatedPropertyID, bool additive); + CAKeyframeAnimation* createKeyframeAnimation(const Animation*, AnimatedPropertyID, bool additive); + void setupAnimation(CAPropertyAnimation*, const Animation*, bool additive); + + CAMediaTimingFunction* timingFunctionForAnimationValue(const AnimationValue*, const Animation*); + + bool setAnimationEndpoints(const KeyframeValueList&, const Animation*, CABasicAnimation*); + bool setAnimationKeyframes(const KeyframeValueList&, const Animation*, CAKeyframeAnimation*); + + bool setTransformAnimationEndpoints(const KeyframeValueList&, const Animation*, CABasicAnimation*, int functionIndex, TransformOperation::OperationType, bool isMatrixAnimation, const IntSize& boxSize); + bool setTransformAnimationKeyframes(const KeyframeValueList&, const Animation*, CAKeyframeAnimation*, int functionIndex, TransformOperation::OperationType, bool isMatrixAnimation, const IntSize& boxSize); + + bool animationIsRunning(const String& animationName) const + { + return m_runningAnimations.find(animationName) != m_runningAnimations.end(); + } + + void commitLayerChangesBeforeSublayers(); + void commitLayerChangesAfterSublayers(); + + FloatSize constrainedSize() const; + + bool requiresTiledLayer(const FloatSize&) const; + void swapFromOrToTiledLayer(bool useTiledLayer); + + CompositingCoordinatesOrientation defaultContentsOrientation() const; + void updateContentsTransform(); + + void setupContentsLayer(CALayer*); + CALayer* contentsLayer() const { return m_contentsLayer.get(); } + + virtual void setReplicatedByLayer(GraphicsLayer*); + + // Used to track the path down the tree for replica layers. + struct ReplicaState { + static const size_t maxReplicaDepth = 16; + enum ReplicaBranchType { ChildBranch = 0, ReplicaBranch = 1 }; + ReplicaState(ReplicaBranchType firstBranch) + : m_replicaDepth(0) + { + push(firstBranch); + } + + // Called as we walk down the tree to build replicas. + void push(ReplicaBranchType branchType) + { + m_replicaBranches.append(branchType); + if (branchType == ReplicaBranch) + ++m_replicaDepth; + } + + void setBranchType(ReplicaBranchType branchType) + { + ASSERT(!m_replicaBranches.isEmpty()); + + if (m_replicaBranches.last() != branchType) { + if (branchType == ReplicaBranch) + ++m_replicaDepth; + else + --m_replicaDepth; + } + + m_replicaBranches.last() = branchType; + } + + void pop() + { + if (m_replicaBranches.last() == ReplicaBranch) + --m_replicaDepth; + m_replicaBranches.removeLast(); + } + + size_t depth() const { return m_replicaBranches.size(); } + size_t replicaDepth() const { return m_replicaDepth; } + + CloneID cloneID() const; + + private: + Vector m_replicaBranches; + size_t m_replicaDepth; + }; + CALayer *replicatedLayerRoot(ReplicaState&); + + enum CloneLevel { RootCloneLevel, IntermediateCloneLevel }; + CALayer *fetchCloneLayers(GraphicsLayer* replicaRoot, ReplicaState&, CloneLevel); + + CALayer *cloneLayer(CALayer *, CloneLevel); + CALayer *findOrMakeClone(CloneID, CALayer *, LayerMap*, CloneLevel); + + void ensureCloneLayers(CloneID index, CALayer *& primaryLayer, CALayer *& structuralLayer, CALayer *& contentsLayer, CloneLevel); + + bool hasCloneLayers() const { return m_layerClones; } + void removeCloneLayers(); + FloatPoint positionForCloneRootLayer() const; + + void propagateLayerChangeToReplicas(); + + // All these "update" methods will be called inside a BEGIN_BLOCK_OBJC_EXCEPTIONS/END_BLOCK_OBJC_EXCEPTIONS block. + void updateLayerNames(); + void updateSublayerList(); + void updateLayerPosition(); + void updateLayerSize(); + void updateAnchorPoint(); + void updateTransform(); + void updateChildrenTransform(); + void updateMasksToBounds(); + void updateContentsOpaque(); + void updateBackfaceVisibility(); + void updateStructuralLayer(); + void updateLayerDrawsContent(); + void updateLayerBackgroundColor(); + + void updateContentsImage(); + void updateContentsMediaLayer(); + void updateContentsCanvasLayer(); + void updateContentsRect(); + void updateMaskLayer(); + void updateReplicatedLayers(); + + void updateLayerAnimations(); + void updateContentsNeedsDisplay(); + + enum StructuralLayerPurpose { + NoStructuralLayer = 0, + StructuralLayerForPreserves3D, + StructuralLayerForReplicaFlattening + }; + void ensureStructuralLayer(StructuralLayerPurpose); + StructuralLayerPurpose structuralLayerPurpose() const; + + void setCAAnimationOnLayer(CAPropertyAnimation*, AnimatedPropertyID, const String& animationName, int index, double timeOffset); + bool removeCAAnimationFromLayer(AnimatedPropertyID, const String& animationName, int index); + void pauseCAAnimationOnLayer(AnimatedPropertyID, const String& animationName, int index, double timeOffset); + + enum MoveOrCopy { Move, Copy }; + static void moveOrCopyLayerAnimation(MoveOrCopy, const String& animationIdentifier, CALayer *fromLayer, CALayer *toLayer); + void moveOrCopyAnimationsForProperty(MoveOrCopy, AnimatedPropertyID, CALayer * fromLayer, CALayer * toLayer); + + enum LayerChange { + NoChange = 0, + NameChanged = 1 << 1, + ChildrenChanged = 1 << 2, // also used for content layer, and preserves-3d, and size if tiling changes? + PositionChanged = 1 << 3, + AnchorPointChanged = 1 << 4, + SizeChanged = 1 << 5, + TransformChanged = 1 << 6, + ChildrenTransformChanged = 1 << 7, + Preserves3DChanged = 1 << 8, + MasksToBoundsChanged = 1 << 9, + DrawsContentChanged = 1 << 10, // need this? + BackgroundColorChanged = 1 << 11, + ContentsOpaqueChanged = 1 << 12, + BackfaceVisibilityChanged = 1 << 13, + OpacityChanged = 1 << 14, + AnimationChanged = 1 << 15, + DirtyRectsChanged = 1 << 16, + ContentsImageChanged = 1 << 17, + ContentsMediaLayerChanged = 1 << 18, + ContentsCanvasLayerChanged = 1 << 19, + ContentsRectChanged = 1 << 20, + MaskLayerChanged = 1 << 21, + ReplicatedLayerChanged = 1 << 22, + ContentsNeedsDisplay = 1 << 23 + }; + typedef unsigned LayerChangeFlags; + void noteLayerPropertyChanged(LayerChangeFlags flags); + void noteSublayersChanged(); + + void repaintLayerDirtyRects(); + + RetainPtr m_layer; // The main layer + RetainPtr m_structuralLayer; // A layer used for structural reasons, like preserves-3d or replica-flattening. Is the parent of m_layer. + RetainPtr m_contentsLayer; // A layer used for inner content, like image and video + + // References to clones of our layers, for replicated layers. + OwnPtr m_layerClones; + OwnPtr m_structuralLayerClones; + OwnPtr m_contentsLayerClones; + + enum ContentsLayerPurpose { + NoContentsLayer = 0, + ContentsLayerForImage, + ContentsLayerForMedia, + ContentsLayerForCanvas + }; + + ContentsLayerPurpose m_contentsLayerPurpose; + bool m_contentsLayerHasBackgroundColor : 1; + + RetainPtr m_animationDelegate; + + RetainPtr m_uncorrectedContentsImage; + RetainPtr m_pendingContentsImage; + + // This represents the animation of a single property. There may be multiple transform animations for + // a single transition or keyframe animation, so index is used to distinguish these. + struct LayerPropertyAnimation { + LayerPropertyAnimation(CAPropertyAnimation* caAnimation, const String& animationName, AnimatedPropertyID property, int index, double timeOffset) + : m_animation(caAnimation) + , m_name(animationName) + , m_property(property) + , m_index(index) + , m_timeOffset(timeOffset) + { } + + RetainPtr m_animation; + String m_name; + AnimatedPropertyID m_property; + int m_index; + double m_timeOffset; + }; + + // Uncommitted transitions and animations. + Vector m_uncomittedAnimations; + + enum Action { Remove, Pause }; + struct AnimationProcessingAction { + AnimationProcessingAction(Action action = Remove, double timeOffset = 0) + : action(action) + , timeOffset(timeOffset) + { + } + Action action; + double timeOffset; // only used for pause + }; + typedef HashMap AnimationsToProcessMap; + AnimationsToProcessMap m_animationsToProcess; + + // Map of animation names to their associated lists of property animations, so we can remove/pause them. + typedef HashMap > AnimationsMap; + AnimationsMap m_runningAnimations; + + Vector m_dirtyRects; + + LayerChangeFlags m_uncommittedChanges; +}; + +} // namespace WebCore + + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // GraphicsLayerMac_h diff --git a/WebCore/platform/graphics/mac/GraphicsLayerMac.mm b/WebCore/platform/graphics/mac/GraphicsLayerMac.mm new file mode 100644 index 0000000..f9b16a0 --- /dev/null +++ b/WebCore/platform/graphics/mac/GraphicsLayerMac.mm @@ -0,0 +1,2514 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#import "GraphicsLayerMac.h" + +#import "Animation.h" +#import "BlockExceptions.h" +#import "FloatConversion.h" +#import "FloatRect.h" +#import "Image.h" +#import "PlatformString.h" +#import +#import "RotateTransformOperation.h" +#import "ScaleTransformOperation.h" +#import "SystemTime.h" +#import "TranslateTransformOperation.h" +#import "WebLayer.h" +#import "WebTiledLayer.h" +#import +#import +#import +#import +#import +#import + +using namespace std; + +#define HAVE_MODERN_QUARTZCORE (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)) + +namespace WebCore { + +static NSString * const WebKitAnimationBeginTimeSetKey = @"WebKitAnimationBeginTimeSet"; + +// The threshold width or height above which a tiled layer will be used. This should be +// large enough to avoid tiled layers for most GraphicsLayers, but less than the OpenGL +// texture size limit on all supported hardware. +static const int cMaxPixelDimension = 2000; + +// The width and height of a single tile in a tiled layer. Should be large enough to +// avoid lots of small tiles (and therefore lots of drawing callbacks), but small enough +// to keep the overall tile cost low. +static const int cTiledLayerTileSize = 512; + +// If we send a duration of 0 to CA, then it will use the default duration +// of 250ms. So send a very small value instead. +static const float cAnimationAlmostZeroDuration = 1e-3f; + +// CACurrentMediaTime() is a time since boot. These methods convert between that and +// WebCore time, which is system time (UTC). +static CFTimeInterval currentTimeToMediaTime(double t) +{ + return CACurrentMediaTime() + t - WTF::currentTime(); +} + +static double mediaTimeToCurrentTime(CFTimeInterval t) +{ + return WTF::currentTime() + t - CACurrentMediaTime(); +} + +} // namespace WebCore + +@interface CALayer(Private) +- (void)setContentsChanged; +@end + +@interface WebAnimationDelegate : NSObject { + WebCore::GraphicsLayerMac* m_graphicsLayer; +} + +- (void)animationDidStart:(CAAnimation *)anim; +- (WebCore::GraphicsLayerMac*)graphicsLayer; +- (void)setLayer:(WebCore::GraphicsLayerMac*)graphicsLayer; + +@end + +@implementation WebAnimationDelegate + +- (void)animationDidStart:(CAAnimation *)animation +{ + if (m_graphicsLayer) + m_graphicsLayer->animationDidStart(animation); +} + +- (WebCore::GraphicsLayerMac*)graphicsLayer +{ + return m_graphicsLayer; +} + +- (void)setLayer:(WebCore::GraphicsLayerMac*)graphicsLayer +{ + m_graphicsLayer = graphicsLayer; +} + +@end + +namespace WebCore { + +static inline void copyTransform(CATransform3D& toT3D, const TransformationMatrix& t) +{ + toT3D.m11 = narrowPrecisionToFloat(t.m11()); + toT3D.m12 = narrowPrecisionToFloat(t.m12()); + toT3D.m13 = narrowPrecisionToFloat(t.m13()); + toT3D.m14 = narrowPrecisionToFloat(t.m14()); + toT3D.m21 = narrowPrecisionToFloat(t.m21()); + toT3D.m22 = narrowPrecisionToFloat(t.m22()); + toT3D.m23 = narrowPrecisionToFloat(t.m23()); + toT3D.m24 = narrowPrecisionToFloat(t.m24()); + toT3D.m31 = narrowPrecisionToFloat(t.m31()); + toT3D.m32 = narrowPrecisionToFloat(t.m32()); + toT3D.m33 = narrowPrecisionToFloat(t.m33()); + toT3D.m34 = narrowPrecisionToFloat(t.m34()); + toT3D.m41 = narrowPrecisionToFloat(t.m41()); + toT3D.m42 = narrowPrecisionToFloat(t.m42()); + toT3D.m43 = narrowPrecisionToFloat(t.m43()); + toT3D.m44 = narrowPrecisionToFloat(t.m44()); +} + +static NSValue* getTransformFunctionValue(const TransformOperation* transformOp, TransformOperation::OperationType transformType, const IntSize& size) +{ + switch (transformType) { + case TransformOperation::ROTATE: + case TransformOperation::ROTATE_X: + case TransformOperation::ROTATE_Y: + return [NSNumber numberWithDouble:transformOp ? deg2rad(static_cast(transformOp)->angle()) : 0]; + case TransformOperation::SCALE_X: + return [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->x() : 1]; + case TransformOperation::SCALE_Y: + return [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->y() : 1]; + case TransformOperation::SCALE_Z: + return [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->z() : 1]; + case TransformOperation::TRANSLATE_X: + return [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->x(size) : 0]; + case TransformOperation::TRANSLATE_Y: + return [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->y(size) : 0]; + case TransformOperation::TRANSLATE_Z: + return [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->z(size) : 0]; + case TransformOperation::SCALE: + case TransformOperation::SCALE_3D: + return [NSArray arrayWithObjects: + [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->x() : 1], + [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->y() : 1], + [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->z() : 1], + nil]; + case TransformOperation::TRANSLATE: + case TransformOperation::TRANSLATE_3D: + return [NSArray arrayWithObjects: + [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->x(size) : 0], + [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->y(size) : 0], + [NSNumber numberWithDouble:transformOp ? static_cast(transformOp)->z(size) : 0], + nil]; + case TransformOperation::SKEW_X: + case TransformOperation::SKEW_Y: + case TransformOperation::SKEW: + case TransformOperation::MATRIX: + case TransformOperation::ROTATE_3D: + case TransformOperation::MATRIX_3D: + case TransformOperation::PERSPECTIVE: + case TransformOperation::IDENTITY: + case TransformOperation::NONE: { + TransformationMatrix transform; + if (transformOp) + transformOp->apply(transform, size); + CATransform3D caTransform; + copyTransform(caTransform, transform); + return [NSValue valueWithCATransform3D:caTransform]; + } + } + + return 0; +} + +#if HAVE_MODERN_QUARTZCORE +static NSString *getValueFunctionNameForTransformOperation(TransformOperation::OperationType transformType) +{ + // Use literal strings to avoid link-time dependency on those symbols. + switch (transformType) { + case TransformOperation::ROTATE_X: + return @"rotateX"; // kCAValueFunctionRotateX; + case TransformOperation::ROTATE_Y: + return @"rotateY"; // kCAValueFunctionRotateY; + case TransformOperation::ROTATE: + return @"rotateZ"; // kCAValueFunctionRotateZ; + case TransformOperation::SCALE_X: + return @"scaleX"; // kCAValueFunctionScaleX; + case TransformOperation::SCALE_Y: + return @"scaleY"; // kCAValueFunctionScaleY; + case TransformOperation::SCALE_Z: + return @"scaleZ"; // kCAValueFunctionScaleZ; + case TransformOperation::TRANSLATE_X: + return @"translateX"; // kCAValueFunctionTranslateX; + case TransformOperation::TRANSLATE_Y: + return @"translateY"; // kCAValueFunctionTranslateY; + case TransformOperation::TRANSLATE_Z: + return @"translateZ"; // kCAValueFunctionTranslateZ; + case TransformOperation::SCALE: + case TransformOperation::SCALE_3D: + return @"scale"; // kCAValueFunctionScale; + case TransformOperation::TRANSLATE: + case TransformOperation::TRANSLATE_3D: + return @"translate"; // kCAValueFunctionTranslate; + default: + return nil; + } +} +#endif + +static String propertyIdToString(AnimatedPropertyID property) +{ + switch (property) { + case AnimatedPropertyWebkitTransform: + return "transform"; + case AnimatedPropertyOpacity: + return "opacity"; + case AnimatedPropertyBackgroundColor: + return "backgroundColor"; + case AnimatedPropertyInvalid: + ASSERT_NOT_REACHED(); + } + ASSERT_NOT_REACHED(); + return ""; +} + +static String animationIdentifier(const String& animationName, AnimatedPropertyID property, int index) +{ + return makeString(animationName, '_', String::number(property), '_', String::number(index)); +} + +static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction* timingFunction) +{ + // By this point, timing functions can only be linear or cubic, not steps. + ASSERT(!timingFunction->isStepsTimingFunction()); + if (timingFunction->isCubicBezierTimingFunction()) { + const CubicBezierTimingFunction* ctf = static_cast(timingFunction); + return [CAMediaTimingFunction functionWithControlPoints:static_cast(ctf->x1()) :static_cast(ctf->y1()) + :static_cast(ctf->x2()) :static_cast(ctf->y2())]; + } else + return [CAMediaTimingFunction functionWithName:@"linear"]; +} + +static void setLayerBorderColor(PlatformLayer* layer, const Color& color) +{ + [layer setBorderColor:cachedCGColor(color, ColorSpaceDeviceRGB)]; +} + +static void clearBorderColor(PlatformLayer* layer) +{ + [layer setBorderColor:nil]; +} + +static void setLayerBackgroundColor(PlatformLayer* layer, const Color& color) +{ + [layer setBackgroundColor:cachedCGColor(color, ColorSpaceDeviceRGB)]; +} + +static void clearLayerBackgroundColor(PlatformLayer* layer) +{ + [layer setBackgroundColor:0]; +} + +static void safeSetSublayers(CALayer* layer, NSArray* sublayers) +{ + // Workaround for : -[CALayer setSublayers:] crashes if sublayers is an empty array, or nil, under GC. + if (objc_collectingEnabled() && ![sublayers count]) { + while ([[layer sublayers] count]) + [[[layer sublayers] objectAtIndex:0] removeFromSuperlayer]; + return; + } + + [layer setSublayers:sublayers]; +} + +static bool caValueFunctionSupported() +{ + static bool sHaveValueFunction = [CAPropertyAnimation instancesRespondToSelector:@selector(setValueFunction:)]; + return sHaveValueFunction; +} + +static bool forceSoftwareAnimation() +{ + static bool forceSoftwareAnimation = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreForceSoftwareAnimation"]; + return forceSoftwareAnimation; +} + +static NSDictionary* nullActionsDictionary() +{ + NSNull* nullValue = [NSNull null]; + NSDictionary* actions = [NSDictionary dictionaryWithObjectsAndKeys: + nullValue, @"anchorPoint", + nullValue, @"bounds", + nullValue, @"contents", + nullValue, @"contentsRect", + nullValue, @"opacity", + nullValue, @"position", + nullValue, @"shadowColor", + nullValue, @"sublayerTransform", + nullValue, @"sublayers", + nullValue, @"transform", + nullValue, @"zPosition", + nil]; + return actions; +} + +static bool animationHasStepsTimingFunction(const KeyframeValueList& valueList, const Animation* anim) +{ + if (anim->timingFunction()->isStepsTimingFunction()) + return true; + + for (unsigned i = 0; i < valueList.size(); ++i) { + const TimingFunction* timingFunction = valueList.at(i)->timingFunction(); + if (timingFunction && timingFunction->isStepsTimingFunction()) + return true; + } + + return false; +} + +PassOwnPtr GraphicsLayer::create(GraphicsLayerClient* client) +{ + return new GraphicsLayerMac(client); +} + +GraphicsLayerMac::GraphicsLayerMac(GraphicsLayerClient* client) + : GraphicsLayer(client) + , m_contentsLayerPurpose(NoContentsLayer) + , m_contentsLayerHasBackgroundColor(false) + , m_uncommittedChanges(NoChange) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + m_layer.adoptNS([[WebLayer alloc] init]); + [m_layer.get() setLayerOwner:this]; + +#if !HAVE_MODERN_QUARTZCORE + setContentsOrientation(defaultContentsOrientation()); +#endif + + updateDebugIndicators(); + + m_animationDelegate.adoptNS([[WebAnimationDelegate alloc] init]); + [m_animationDelegate.get() setLayer:this]; + + END_BLOCK_OBJC_EXCEPTIONS +} + +GraphicsLayerMac::~GraphicsLayerMac() +{ + // We release our references to the CALayers here, but do not actively unparent them, + // since that will cause a commit and break our batched commit model. The layers will + // get released when the rootmost modified GraphicsLayerMac rebuilds its child layers. + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + // Clean up the WK layer. + if (m_layer) { + WebLayer* layer = m_layer.get(); + [layer setLayerOwner:nil]; + } + + if (m_contentsLayer) { + if ([m_contentsLayer.get() respondsToSelector:@selector(setLayerOwner:)]) + [(id)m_contentsLayer.get() setLayerOwner:nil]; + } + + // animationDidStart: can fire after this, so we need to clear out the layer on the delegate. + [m_animationDelegate.get() setLayer:0]; + + // Release the clone layers inside the exception-handling block. + removeCloneLayers(); + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerMac::setName(const String& name) +{ + String longName = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + name; + GraphicsLayer::setName(longName); + noteLayerPropertyChanged(NameChanged); +} + +NativeLayer GraphicsLayerMac::nativeLayer() const +{ + return m_layer.get(); +} + +bool GraphicsLayerMac::setChildren(const Vector& children) +{ + bool childrenChanged = GraphicsLayer::setChildren(children); + if (childrenChanged) + noteSublayersChanged(); + + return childrenChanged; +} + +void GraphicsLayerMac::addChild(GraphicsLayer* childLayer) +{ + GraphicsLayer::addChild(childLayer); + noteSublayersChanged(); +} + +void GraphicsLayerMac::addChildAtIndex(GraphicsLayer* childLayer, int index) +{ + GraphicsLayer::addChildAtIndex(childLayer, index); + noteSublayersChanged(); +} + +void GraphicsLayerMac::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildBelow(childLayer, sibling); + noteSublayersChanged(); +} + +void GraphicsLayerMac::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildAbove(childLayer, sibling); + noteSublayersChanged(); +} + +bool GraphicsLayerMac::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + if (GraphicsLayer::replaceChild(oldChild, newChild)) { + noteSublayersChanged(); + return true; + } + return false; +} + +void GraphicsLayerMac::removeFromParent() +{ + if (m_parent) + static_cast(m_parent)->noteSublayersChanged(); + GraphicsLayer::removeFromParent(); +} + +void GraphicsLayerMac::setMaskLayer(GraphicsLayer* layer) +{ + if (layer == m_maskLayer) + return; + + GraphicsLayer::setMaskLayer(layer); + noteLayerPropertyChanged(MaskLayerChanged); + + propagateLayerChangeToReplicas(); + + if (m_replicatedLayer) + static_cast(m_replicatedLayer)->propagateLayerChangeToReplicas(); +} + +void GraphicsLayerMac::setReplicatedLayer(GraphicsLayer* layer) +{ + if (layer == m_replicatedLayer) + return; + + GraphicsLayer::setReplicatedLayer(layer); + noteLayerPropertyChanged(ReplicatedLayerChanged); +} + +void GraphicsLayerMac::setReplicatedByLayer(GraphicsLayer* layer) +{ + if (layer == m_replicaLayer) + return; + + GraphicsLayer::setReplicatedByLayer(layer); + noteSublayersChanged(); + noteLayerPropertyChanged(ReplicatedLayerChanged); +} + +void GraphicsLayerMac::setPosition(const FloatPoint& point) +{ + if (point == m_position) + return; + + GraphicsLayer::setPosition(point); + noteLayerPropertyChanged(PositionChanged); +} + +void GraphicsLayerMac::setAnchorPoint(const FloatPoint3D& point) +{ + if (point == m_anchorPoint) + return; + + GraphicsLayer::setAnchorPoint(point); + noteLayerPropertyChanged(AnchorPointChanged); +} + +void GraphicsLayerMac::setSize(const FloatSize& size) +{ + if (size == m_size) + return; + + GraphicsLayer::setSize(size); + noteLayerPropertyChanged(SizeChanged); +} + +void GraphicsLayerMac::setTransform(const TransformationMatrix& t) +{ + if (t == m_transform) + return; + + GraphicsLayer::setTransform(t); + noteLayerPropertyChanged(TransformChanged); +} + +void GraphicsLayerMac::setChildrenTransform(const TransformationMatrix& t) +{ + if (t == m_childrenTransform) + return; + + GraphicsLayer::setChildrenTransform(t); + noteLayerPropertyChanged(ChildrenTransformChanged); +} + +void GraphicsLayerMac::moveOrCopyLayerAnimation(MoveOrCopy operation, const String& animationIdentifier, CALayer *fromLayer, CALayer *toLayer) +{ + NSString *animationID = animationIdentifier; + CAAnimation *anim = [fromLayer animationForKey:animationID]; + if (!anim) + return; + + switch (operation) { + case Move: + [anim retain]; + [fromLayer removeAnimationForKey:animationID]; + [toLayer addAnimation:anim forKey:animationID]; + [anim release]; + break; + + case Copy: + [toLayer addAnimation:anim forKey:animationID]; + break; + } +} + +void GraphicsLayerMac::moveOrCopyAnimationsForProperty(MoveOrCopy operation, AnimatedPropertyID property, CALayer *fromLayer, CALayer *toLayer) +{ + // Look for running animations affecting this property. + AnimationsMap::const_iterator end = m_runningAnimations.end(); + for (AnimationsMap::const_iterator it = m_runningAnimations.begin(); it != end; ++it) { + const Vector& propertyAnimations = it->second; + size_t numAnimations = propertyAnimations.size(); + for (size_t i = 0; i < numAnimations; ++i) { + const LayerPropertyAnimation& currAnimation = propertyAnimations[i]; + if (currAnimation.m_property == property) + moveOrCopyLayerAnimation(operation, animationIdentifier(currAnimation.m_name, currAnimation.m_property, currAnimation.m_index), fromLayer, toLayer); + } + } +} + +void GraphicsLayerMac::setPreserves3D(bool preserves3D) +{ + if (preserves3D == m_preserves3D) + return; + + GraphicsLayer::setPreserves3D(preserves3D); + noteLayerPropertyChanged(Preserves3DChanged); +} + +void GraphicsLayerMac::setMasksToBounds(bool masksToBounds) +{ + if (masksToBounds == m_masksToBounds) + return; + + GraphicsLayer::setMasksToBounds(masksToBounds); + noteLayerPropertyChanged(MasksToBoundsChanged); +} + +void GraphicsLayerMac::setDrawsContent(bool drawsContent) +{ + if (drawsContent == m_drawsContent) + return; + + GraphicsLayer::setDrawsContent(drawsContent); + noteLayerPropertyChanged(DrawsContentChanged); +} + +void GraphicsLayerMac::setBackgroundColor(const Color& color) +{ + if (m_backgroundColorSet && m_backgroundColor == color) + return; + + GraphicsLayer::setBackgroundColor(color); + + m_contentsLayerHasBackgroundColor = true; + noteLayerPropertyChanged(BackgroundColorChanged); +} + +void GraphicsLayerMac::clearBackgroundColor() +{ + if (!m_backgroundColorSet) + return; + + GraphicsLayer::clearBackgroundColor(); + m_contentsLayerHasBackgroundColor = false; + noteLayerPropertyChanged(BackgroundColorChanged); +} + +void GraphicsLayerMac::setContentsOpaque(bool opaque) +{ + if (m_contentsOpaque == opaque) + return; + + GraphicsLayer::setContentsOpaque(opaque); + noteLayerPropertyChanged(ContentsOpaqueChanged); +} + +void GraphicsLayerMac::setBackfaceVisibility(bool visible) +{ + if (m_backfaceVisibility == visible) + return; + + GraphicsLayer::setBackfaceVisibility(visible); + noteLayerPropertyChanged(BackfaceVisibilityChanged); +} + +void GraphicsLayerMac::setOpacity(float opacity) +{ + float clampedOpacity = max(0.0f, min(opacity, 1.0f)); + + if (clampedOpacity == m_opacity) + return; + + GraphicsLayer::setOpacity(clampedOpacity); + noteLayerPropertyChanged(OpacityChanged); +} + +void GraphicsLayerMac::setNeedsDisplay() +{ + FloatRect hugeRect(-numeric_limits::max() / 2, -numeric_limits::max() / 2, + numeric_limits::max(), numeric_limits::max()); + + setNeedsDisplayInRect(hugeRect); +} + +void GraphicsLayerMac::setNeedsDisplayInRect(const FloatRect& rect) +{ + if (!drawsContent()) + return; + + const size_t maxDirtyRects = 32; + + for (size_t i = 0; i < m_dirtyRects.size(); ++i) { + if (m_dirtyRects[i].contains(rect)) + return; + } + + if (m_dirtyRects.size() < maxDirtyRects) + m_dirtyRects.append(rect); + else + m_dirtyRects[0].unite(rect); + + noteLayerPropertyChanged(DirtyRectsChanged); +} + +void GraphicsLayerMac::setContentsNeedsDisplay() +{ + noteLayerPropertyChanged(ContentsNeedsDisplay); +} + +void GraphicsLayerMac::setContentsRect(const IntRect& rect) +{ + if (rect == m_contentsRect) + return; + + GraphicsLayer::setContentsRect(rect); + noteLayerPropertyChanged(ContentsRectChanged); +} + +bool GraphicsLayerMac::addAnimation(const KeyframeValueList& valueList, const IntSize& boxSize, const Animation* anim, const String& animationName, double timeOffset) +{ + ASSERT(!animationName.isEmpty()); + + if (forceSoftwareAnimation() || !anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) + return false; + +#if !HAVE_MODERN_QUARTZCORE + // Older versions of QuartzCore do not handle opacity in transform layers properly, so we will + // always do software animation in that case. + if (valueList.property() == AnimatedPropertyOpacity) + return false; +#endif + + // CoreAnimation does not handle the steps() timing function. Fall back + // to software animation in that case. + if (animationHasStepsTimingFunction(valueList, anim)) + return false; + + bool createdAnimations = false; + if (valueList.property() == AnimatedPropertyWebkitTransform) + createdAnimations = createTransformAnimationsFromKeyframes(valueList, anim, animationName, timeOffset, boxSize); + else + createdAnimations = createAnimationFromKeyframes(valueList, anim, animationName, timeOffset); + + if (createdAnimations) + noteLayerPropertyChanged(AnimationChanged); + + return createdAnimations; +} + +void GraphicsLayerMac::pauseAnimation(const String& animationName, double timeOffset) +{ + if (!animationIsRunning(animationName)) + return; + + AnimationsToProcessMap::iterator it = m_animationsToProcess.find(animationName); + if (it != m_animationsToProcess.end()) { + AnimationProcessingAction& processingInfo = it->second; + // If an animation is scheduled to be removed, don't change the remove to a pause. + if (processingInfo.action != Remove) + processingInfo.action = Pause; + } else + m_animationsToProcess.add(animationName, AnimationProcessingAction(Pause, timeOffset)); + + noteLayerPropertyChanged(AnimationChanged); +} + +void GraphicsLayerMac::removeAnimation(const String& animationName) +{ + if (!animationIsRunning(animationName)) + return; + + m_animationsToProcess.add(animationName, AnimationProcessingAction(Remove)); + noteLayerPropertyChanged(AnimationChanged); +} + +void GraphicsLayerMac::animationDidStart(CAAnimation* caAnimation) +{ + bool hadNonZeroBeginTime = [[caAnimation valueForKey:WebKitAnimationBeginTimeSetKey] boolValue]; + + double startTime; + if (hadNonZeroBeginTime) { + // We don't know what time CA used to commit the animation, so just use the current time + // (even though this will be slightly off). + startTime = WebCore::mediaTimeToCurrentTime(CACurrentMediaTime()); + } else + startTime = WebCore::mediaTimeToCurrentTime([caAnimation beginTime]); + + if (m_client) + m_client->notifyAnimationStarted(this, startTime); +} + +void GraphicsLayerMac::setContentsToImage(Image* image) +{ + if (image) { + CGImageRef newImage = image->nativeImageForCurrentFrame(); + if (!newImage) + return; + + // Check to see if the image changed; we have to do this because the call to + // CGImageCreateCopyWithColorSpace() below can create a new image every time. + if (m_uncorrectedContentsImage && m_uncorrectedContentsImage.get() == newImage) + return; + + m_uncorrectedContentsImage = newImage; + m_pendingContentsImage = newImage; + CGColorSpaceRef colorSpace = CGImageGetColorSpace(m_pendingContentsImage.get()); + + static CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); + if (colorSpace && CFEqual(colorSpace, deviceRGB)) { + // CoreGraphics renders images tagged with DeviceRGB using the color space of the main display. When we hand such + // images to CA we need to tag them similarly so CA rendering matches CG rendering. + static CGColorSpaceRef genericRGB = CGDisplayCopyColorSpace(kCGDirectMainDisplay); + m_pendingContentsImage.adoptCF(CGImageCreateCopyWithColorSpace(m_pendingContentsImage.get(), genericRGB)); + } + m_contentsLayerPurpose = ContentsLayerForImage; + if (!m_contentsLayer) + noteSublayersChanged(); + } else { + m_uncorrectedContentsImage = 0; + m_pendingContentsImage = 0; + m_contentsLayerPurpose = NoContentsLayer; + if (m_contentsLayer) + noteSublayersChanged(); + } + + noteLayerPropertyChanged(ContentsImageChanged); +} + +void GraphicsLayerMac::setContentsToMedia(PlatformLayer* mediaLayer) +{ + if (mediaLayer == m_contentsLayer) + return; + + m_contentsLayer = mediaLayer; + m_contentsLayerPurpose = mediaLayer ? ContentsLayerForMedia : NoContentsLayer; + + noteSublayersChanged(); + noteLayerPropertyChanged(ContentsMediaLayerChanged); +} + +void GraphicsLayerMac::didDisplay(PlatformLayer* layer) +{ + CALayer* sourceLayer; + LayerMap* layerCloneMap; + + if (layer == m_layer) { + sourceLayer = m_layer.get(); + layerCloneMap = m_layerClones.get(); + } else if (layer == m_contentsLayer) { + sourceLayer = m_contentsLayer.get(); + layerCloneMap = m_contentsLayerClones.get(); + } else + return; + + if (layerCloneMap) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currClone = it->second.get(); + if (!currClone) + continue; + + if ([currClone contents] != [sourceLayer contents]) + [currClone setContents:[sourceLayer contents]]; + else + [currClone setContentsChanged]; + } + } +} + +void GraphicsLayerMac::syncCompositingState() +{ + recursiveCommitChanges(); +} + +void GraphicsLayerMac::syncCompositingStateForThisLayerOnly() +{ + commitLayerChangesBeforeSublayers(); + commitLayerChangesAfterSublayers(); +} + +void GraphicsLayerMac::recursiveCommitChanges() +{ + commitLayerChangesBeforeSublayers(); + + if (m_maskLayer) + static_cast(m_maskLayer)->commitLayerChangesBeforeSublayers(); + + const Vector& childLayers = children(); + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerMac* curChild = static_cast(childLayers[i]); + curChild->recursiveCommitChanges(); + } + + if (m_replicaLayer) + static_cast(m_replicaLayer)->recursiveCommitChanges(); + + if (m_maskLayer) + static_cast(m_maskLayer)->commitLayerChangesAfterSublayers(); + + commitLayerChangesAfterSublayers(); +} + +void GraphicsLayerMac::commitLayerChangesBeforeSublayers() +{ + if (!m_uncommittedChanges) + return; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + // Need to handle Preserves3DChanged first, because it affects which layers subsequent properties are applied to + if (m_uncommittedChanges & (Preserves3DChanged | ReplicatedLayerChanged)) + updateStructuralLayer(); + + if (m_uncommittedChanges & NameChanged) + updateLayerNames(); + + if (m_uncommittedChanges & ContentsImageChanged) // Needs to happen before ChildrenChanged + updateContentsImage(); + + if (m_uncommittedChanges & ContentsMediaLayerChanged) // Needs to happen before ChildrenChanged + updateContentsMediaLayer(); + + if (m_uncommittedChanges & ContentsCanvasLayerChanged) // Needs to happen before ChildrenChanged + updateContentsCanvasLayer(); + + if (m_uncommittedChanges & BackgroundColorChanged) // Needs to happen before ChildrenChanged, and after updating image or video + updateLayerBackgroundColor(); + + if (m_uncommittedChanges & ChildrenChanged) + updateSublayerList(); + + if (m_uncommittedChanges & PositionChanged) + updateLayerPosition(); + + if (m_uncommittedChanges & AnchorPointChanged) + updateAnchorPoint(); + + if (m_uncommittedChanges & SizeChanged) + updateLayerSize(); + + if (m_uncommittedChanges & TransformChanged) + updateTransform(); + + if (m_uncommittedChanges & ChildrenTransformChanged) + updateChildrenTransform(); + + if (m_uncommittedChanges & MasksToBoundsChanged) + updateMasksToBounds(); + + if (m_uncommittedChanges & DrawsContentChanged) + updateLayerDrawsContent(); + + if (m_uncommittedChanges & ContentsOpaqueChanged) + updateContentsOpaque(); + + if (m_uncommittedChanges & BackfaceVisibilityChanged) + updateBackfaceVisibility(); + + if (m_uncommittedChanges & OpacityChanged) + updateOpacityOnLayer(); + + if (m_uncommittedChanges & AnimationChanged) + updateLayerAnimations(); + + if (m_uncommittedChanges & DirtyRectsChanged) + repaintLayerDirtyRects(); + + if (m_uncommittedChanges & ContentsRectChanged) + updateContentsRect(); + + if (m_uncommittedChanges & MaskLayerChanged) + updateMaskLayer(); + + if (m_uncommittedChanges & ContentsNeedsDisplay) + updateContentsNeedsDisplay(); + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerMac::commitLayerChangesAfterSublayers() +{ + if (!m_uncommittedChanges) + return; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (m_uncommittedChanges & ReplicatedLayerChanged) + updateReplicatedLayers(); + + m_uncommittedChanges = NoChange; + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerMac::updateLayerNames() +{ + switch (structuralLayerPurpose()) { + case StructuralLayerForPreserves3D: + [m_structuralLayer.get() setName:("Transform layer " + name())]; + break; + case StructuralLayerForReplicaFlattening: + [m_structuralLayer.get() setName:("Replica flattening layer " + name())]; + break; + case NoStructuralLayer: + break; + } + [m_layer.get() setName:name()]; +} + +void GraphicsLayerMac::updateSublayerList() +{ + NSMutableArray* newSublayers = nil; + + const Vector& childLayers = children(); + + if (m_structuralLayer || m_contentsLayer || childLayers.size() > 0) { + newSublayers = [[NSMutableArray alloc] init]; + + if (m_structuralLayer) { + // Add the replica layer first. + if (m_replicaLayer) + [newSublayers addObject:static_cast(m_replicaLayer)->primaryLayer()]; + // Add the primary layer. Even if we have negative z-order children, the primary layer always comes behind. + [newSublayers addObject:m_layer.get()]; + } else if (m_contentsLayer) { + // FIXME: add the contents layer in the correct order with negative z-order children. + // This does not cause visible rendering issues because currently contents layers are only used + // for replaced elements that don't have children. + [newSublayers addObject:m_contentsLayer.get()]; + } + + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerMac* curChild = static_cast(childLayers[i]); + CALayer *childLayer = curChild->layerForSuperlayer(); + [newSublayers addObject:childLayer]; + } + + [newSublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; + } + + if (m_structuralLayer) { + safeSetSublayers(m_structuralLayer.get(), newSublayers); + + if (m_contentsLayer) { + // If we have a transform layer, then the contents layer is parented in the + // primary layer (which is itself a child of the transform layer). + safeSetSublayers(m_layer.get(), nil); + [m_layer.get() addSublayer:m_contentsLayer.get()]; + } + } else + safeSetSublayers(m_layer.get(), newSublayers); + + [newSublayers release]; +} + +void GraphicsLayerMac::updateLayerPosition() +{ + FloatSize usedSize = m_usingTiledLayer ? constrainedSize() : m_size; + + // Position is offset on the layer by the layer anchor point. + CGPoint posPoint = CGPointMake(m_position.x() + m_anchorPoint.x() * usedSize.width(), + m_position.y() + m_anchorPoint.y() * usedSize.height()); + + [primaryLayer() setPosition:posPoint]; + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CGPoint clonePosition = posPoint; + if (m_replicaLayer && isReplicatedRootClone(it->first)) { + // Maintain the special-case position for the root of a clone subtree, + // which we set up in replicatedLayerRoot(). + clonePosition = positionForCloneRootLayer(); + } + CALayer *currLayer = it->second.get(); + [currLayer setPosition:clonePosition]; + } + } +} + +void GraphicsLayerMac::updateLayerSize() +{ + CGRect rect = CGRectMake(0, 0, m_size.width(), m_size.height()); + if (m_structuralLayer) { + [m_structuralLayer.get() setBounds:rect]; + + if (LayerMap* layerCloneMap = m_structuralLayerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + [it->second.get() setBounds:rect]; + } + + // The anchor of the contents layer is always at 0.5, 0.5, so the position is center-relative. + CGPoint centerPoint = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); + [m_layer.get() setPosition:centerPoint]; + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + [it->second.get() setPosition:centerPoint]; + } + } + + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); + + if (m_usingTiledLayer) { + FloatSize sizeToUse = constrainedSize(); + rect = CGRectMake(0, 0, sizeToUse.width(), sizeToUse.height()); + } + + [m_layer.get() setBounds:rect]; + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) + [it->second.get() setBounds:rect]; + } + + // Contents transform may depend on height. + updateContentsTransform(); + + // Note that we don't resize m_contentsLayer. It's up the caller to do that. + + // if we've changed the bounds, we need to recalculate the position + // of the layer, taking anchor point into account. + updateLayerPosition(); +} + +void GraphicsLayerMac::updateAnchorPoint() +{ + [primaryLayer() setAnchorPoint:FloatPoint(m_anchorPoint.x(), m_anchorPoint.y())]; +#if HAVE_MODERN_QUARTZCORE + [primaryLayer() setAnchorPointZ:m_anchorPoint.z()]; +#endif + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setAnchorPoint:FloatPoint(m_anchorPoint.x(), m_anchorPoint.y())]; +#if HAVE_MODERN_QUARTZCORE + [currLayer setAnchorPointZ:m_anchorPoint.z()]; +#endif + } + } + + updateLayerPosition(); +} + +void GraphicsLayerMac::updateTransform() +{ + CATransform3D transform; + copyTransform(transform, m_transform); + [primaryLayer() setTransform:transform]; + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + if (m_replicaLayer && isReplicatedRootClone(it->first)) { + // Maintain the special-case transform for the root of a clone subtree, + // which we set up in replicatedLayerRoot(). + [currLayer setTransform:CATransform3DIdentity]; + } else + [currLayer setTransform:transform]; + } + } +} + +void GraphicsLayerMac::updateChildrenTransform() +{ + CATransform3D transform; + copyTransform(transform, m_childrenTransform); + [primaryLayer() setSublayerTransform:transform]; + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setSublayerTransform:transform]; + } + } +} + +void GraphicsLayerMac::updateMasksToBounds() +{ + [m_layer.get() setMasksToBounds:m_masksToBounds]; + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setMasksToBounds:m_masksToBounds]; + } + } + + updateDebugIndicators(); +} + +void GraphicsLayerMac::updateContentsOpaque() +{ + [m_layer.get() setOpaque:m_contentsOpaque]; + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setOpaque:m_contentsOpaque]; + } + } +} + +void GraphicsLayerMac::updateBackfaceVisibility() +{ + if (m_structuralLayer && structuralLayerPurpose() == StructuralLayerForReplicaFlattening) { + [m_structuralLayer.get() setDoubleSided:m_backfaceVisibility]; + + if (LayerMap* layerCloneMap = m_structuralLayerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setDoubleSided:m_backfaceVisibility]; + } + } + } + + [m_layer.get() setDoubleSided:m_backfaceVisibility]; + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setDoubleSided:m_backfaceVisibility]; + } + } +} + +void GraphicsLayerMac::updateStructuralLayer() +{ + ensureStructuralLayer(structuralLayerPurpose()); +} + +void GraphicsLayerMac::ensureStructuralLayer(StructuralLayerPurpose purpose) +{ + if (purpose == NoStructuralLayer) { + if (m_structuralLayer) { + // Replace the transformLayer in the parent with this layer. + [m_layer.get() removeFromSuperlayer]; + [[m_structuralLayer.get() superlayer] replaceSublayer:m_structuralLayer.get() with:m_layer.get()]; + + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyWebkitTransform, m_structuralLayer.get(), m_layer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyOpacity, m_structuralLayer.get(), m_layer.get()); + + // Release the structural layer. + m_structuralLayer = 0; + + // Update the properties of m_layer now that we no longer have a structural layer. + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + + updateSublayerList(); + updateOpacityOnLayer(); + } + return; + } + + bool structuralLayerChanged = false; + + if (purpose == StructuralLayerForPreserves3D) { + Class transformLayerClass = NSClassFromString(@"CATransformLayer"); + if (!transformLayerClass) + return; + + if (m_structuralLayer && ![m_structuralLayer.get() isKindOfClass:transformLayerClass]) + m_structuralLayer = 0; + + if (!m_structuralLayer) { + m_structuralLayer.adoptNS([[transformLayerClass alloc] init]); + structuralLayerChanged = true; + } + } else { + if (m_structuralLayer && ![m_structuralLayer.get() isMemberOfClass:[CALayer self]]) + m_structuralLayer = 0; + + if (!m_structuralLayer) { + m_structuralLayer.adoptNS([[CALayer alloc] init]); + structuralLayerChanged = true; + } + } + + if (!structuralLayerChanged) + return; + + // Turn off default animations. + [m_structuralLayer.get() setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; + + updateLayerNames(); + + // Update the properties of the structural layer. + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + updateBackfaceVisibility(); + + // Set properties of m_layer to their default values, since these are expressed on on the structural layer. + CGPoint point = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); + [m_layer.get() setPosition:point]; + [m_layer.get() setAnchorPoint:CGPointMake(0.5f, 0.5f)]; + [m_layer.get() setTransform:CATransform3DIdentity]; + [m_layer.get() setOpacity:1]; + + // Move this layer to be a child of the transform layer. + [[m_layer.get() superlayer] replaceSublayer:m_layer.get() with:m_structuralLayer.get()]; + [m_structuralLayer.get() addSublayer:m_layer.get()]; + + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyWebkitTransform, m_layer.get(), m_structuralLayer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyOpacity, m_layer.get(), m_structuralLayer.get()); + + updateSublayerList(); + updateOpacityOnLayer(); +} + +GraphicsLayerMac::StructuralLayerPurpose GraphicsLayerMac::structuralLayerPurpose() const +{ + if (preserves3D()) + return StructuralLayerForPreserves3D; + + if (isReplicated()) + return StructuralLayerForReplicaFlattening; + + return NoStructuralLayer; +} + +void GraphicsLayerMac::updateLayerDrawsContent() +{ + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); + + if (m_drawsContent) + [m_layer.get() setNeedsDisplay]; + else + [m_layer.get() setContents:nil]; + + updateDebugIndicators(); +} + +void GraphicsLayerMac::updateLayerBackgroundColor() +{ + if (!m_contentsLayer) + return; + + // We never create the contents layer just for background color yet. + if (m_backgroundColorSet) + setLayerBackgroundColor(m_contentsLayer.get(), m_backgroundColor); + else + clearLayerBackgroundColor(m_contentsLayer.get()); +} + +void GraphicsLayerMac::updateContentsImage() +{ + if (m_pendingContentsImage) { + if (!m_contentsLayer.get()) { + WebLayer* imageLayer = [WebLayer layer]; +#ifndef NDEBUG + [imageLayer setName:@"Image Layer"]; +#endif + setupContentsLayer(imageLayer); + m_contentsLayer.adoptNS([imageLayer retain]); + // m_contentsLayer will be parented by updateSublayerList + } + + // FIXME: maybe only do trilinear if the image is being scaled down, + // but then what if the layer size changes? +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + [m_contentsLayer.get() setMinificationFilter:kCAFilterTrilinear]; +#endif + [m_contentsLayer.get() setContents:(id)m_pendingContentsImage.get()]; + m_pendingContentsImage = 0; + + if (m_contentsLayerClones) { + LayerMap::const_iterator end = m_contentsLayerClones->end(); + for (LayerMap::const_iterator it = m_contentsLayerClones->begin(); it != end; ++it) + [it->second.get() setContents:[m_contentsLayer.get() contents]]; + } + + updateContentsRect(); + } else { + // No image. + // m_contentsLayer will be removed via updateSublayerList. + m_contentsLayer = 0; + } +} + +void GraphicsLayerMac::updateContentsMediaLayer() +{ + // Video layer was set as m_contentsLayer, and will get parented in updateSublayerList(). + if (m_contentsLayer) { + setupContentsLayer(m_contentsLayer.get()); + updateContentsRect(); + } +} + +void GraphicsLayerMac::updateContentsCanvasLayer() +{ + // CanvasLayer was set as m_contentsLayer, and will get parented in updateSublayerList(). + if (m_contentsLayer) { + setupContentsLayer(m_contentsLayer.get()); + [m_contentsLayer.get() setNeedsDisplay]; + updateContentsRect(); + } +} + +void GraphicsLayerMac::updateContentsRect() +{ + if (!m_contentsLayer) + return; + + CGPoint point = CGPointMake(m_contentsRect.x(), + m_contentsRect.y()); + CGRect rect = CGRectMake(0.0f, + 0.0f, + m_contentsRect.width(), + m_contentsRect.height()); + + [m_contentsLayer.get() setPosition:point]; + [m_contentsLayer.get() setBounds:rect]; + + if (m_contentsLayerClones) { + LayerMap::const_iterator end = m_contentsLayerClones->end(); + for (LayerMap::const_iterator it = m_contentsLayerClones->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setPosition:point]; + [currLayer setBounds:rect]; + } + } +} + +void GraphicsLayerMac::updateMaskLayer() +{ + CALayer *maskCALayer = m_maskLayer ? m_maskLayer->platformLayer() : 0; + [m_layer.get() setMask:maskCALayer]; + + LayerMap* maskLayerCloneMap = m_maskLayer ? static_cast(m_maskLayer)->primaryLayerClones() : 0; + + if (LayerMap* layerCloneMap = m_layerClones.get()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + + CALayer *maskClone = maskLayerCloneMap ? maskLayerCloneMap->get(it->first).get() : 0; + [currLayer setMask:maskClone]; + } + } +} + +void GraphicsLayerMac::updateReplicatedLayers() +{ + // Clone the descendants of the replicated layer, and parent under us. + ReplicaState replicaState(ReplicaState::ReplicaBranch); + + CALayer *replicaRoot = replicatedLayerRoot(replicaState); + if (!replicaRoot) + return; + + if (m_structuralLayer) + [m_structuralLayer.get() insertSublayer:replicaRoot atIndex:0]; + else + [m_layer.get() insertSublayer:replicaRoot atIndex:0]; +} + +// For now, this assumes that layers only ever have one replica, so replicaIndices contains only 0 and 1. +GraphicsLayerMac::CloneID GraphicsLayerMac::ReplicaState::cloneID() const +{ + size_t depth = m_replicaBranches.size(); + + const size_t bitsPerUChar = sizeof(UChar) * 8; + size_t vectorSize = (depth + bitsPerUChar - 1) / bitsPerUChar; + + Vector result(vectorSize); + result.fill(0); + + // Create a string from the bit sequence which we can use to identify the clone. + // Note that the string may contain embedded nulls, but that's OK. + for (size_t i = 0; i < depth; ++i) { + UChar& currChar = result[i / bitsPerUChar]; + currChar = (currChar << 1) | m_replicaBranches[i]; + } + + return String::adopt(result); +} + +CALayer *GraphicsLayerMac::replicatedLayerRoot(ReplicaState& replicaState) +{ + // Limit replica nesting, to avoid 2^N explosion of replica layers. + if (!m_replicatedLayer || replicaState.replicaDepth() == ReplicaState::maxReplicaDepth) + return nil; + + GraphicsLayerMac* replicatedLayer = static_cast(m_replicatedLayer); + + CALayer *clonedLayerRoot = replicatedLayer->fetchCloneLayers(this, replicaState, RootCloneLevel); + FloatPoint cloneRootPosition = replicatedLayer->positionForCloneRootLayer(); + + // Replica root has no offset or transform + [clonedLayerRoot setPosition:cloneRootPosition]; + [clonedLayerRoot setTransform:CATransform3DIdentity]; + + return clonedLayerRoot; +} + +void GraphicsLayerMac::updateLayerAnimations() +{ + if (m_animationsToProcess.size()) { + AnimationsToProcessMap::const_iterator end = m_animationsToProcess.end(); + for (AnimationsToProcessMap::const_iterator it = m_animationsToProcess.begin(); it != end; ++it) { + const String& currAnimationName = it->first; + AnimationsMap::iterator animationIt = m_runningAnimations.find(currAnimationName); + if (animationIt == m_runningAnimations.end()) + continue; + + const AnimationProcessingAction& processingInfo = it->second; + const Vector& animations = animationIt->second; + for (size_t i = 0; i < animations.size(); ++i) { + const LayerPropertyAnimation& currAnimation = animations[i]; + switch (processingInfo.action) { + case Remove: + removeCAAnimationFromLayer(currAnimation.m_property, currAnimationName, currAnimation.m_index); + break; + case Pause: + pauseCAAnimationOnLayer(currAnimation.m_property, currAnimationName, currAnimation.m_index, processingInfo.timeOffset); + break; + } + } + + if (processingInfo.action == Remove) + m_runningAnimations.remove(currAnimationName); + } + + m_animationsToProcess.clear(); + } + + size_t numAnimations; + if ((numAnimations = m_uncomittedAnimations.size())) { + for (size_t i = 0; i < numAnimations; ++i) { + const LayerPropertyAnimation& pendingAnimation = m_uncomittedAnimations[i]; + setCAAnimationOnLayer(pendingAnimation.m_animation.get(), pendingAnimation.m_property, pendingAnimation.m_name, pendingAnimation.m_index, pendingAnimation.m_timeOffset); + + AnimationsMap::iterator it = m_runningAnimations.find(pendingAnimation.m_name); + if (it == m_runningAnimations.end()) { + Vector animations; + animations.append(pendingAnimation); + m_runningAnimations.add(pendingAnimation.m_name, animations); + } else { + Vector& animations = it->second; + animations.append(pendingAnimation); + } + } + + m_uncomittedAnimations.clear(); + } +} + +void GraphicsLayerMac::setCAAnimationOnLayer(CAPropertyAnimation* caAnim, AnimatedPropertyID property, const String& animationName, int index, double timeOffset) +{ + PlatformLayer* layer = animatedLayer(property); + + if (timeOffset) { + [caAnim setBeginTime:CACurrentMediaTime() - timeOffset]; + [caAnim setValue:[NSNumber numberWithBool:YES] forKey:WebKitAnimationBeginTimeSetKey]; + } + + NSString *animationID = animationIdentifier(animationName, property, index); + + [layer removeAnimationForKey:animationID]; + [layer addAnimation:caAnim forKey:animationID]; + + if (LayerMap* layerCloneMap = animatedLayerClones(property)) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + // Skip immediate replicas, since they move with the original. + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + CALayer *currLayer = it->second.get(); + [currLayer removeAnimationForKey:animationID]; + [currLayer addAnimation:caAnim forKey:animationID]; + } + } +} + +// Workaround for +static void bug7311367Workaround(CALayer* transformLayer, const TransformationMatrix& transform) +{ + if (!transformLayer) + return; + + CATransform3D caTransform; + copyTransform(caTransform, transform); + caTransform.m41 += 1; + [transformLayer setTransform:caTransform]; + + caTransform.m41 -= 1; + [transformLayer setTransform:caTransform]; +} + +bool GraphicsLayerMac::removeCAAnimationFromLayer(AnimatedPropertyID property, const String& animationName, int index) +{ + PlatformLayer* layer = animatedLayer(property); + + NSString *animationID = animationIdentifier(animationName, property, index); + + if (![layer animationForKey:animationID]) + return false; + + [layer removeAnimationForKey:animationID]; + bug7311367Workaround(m_structuralLayer.get(), m_transform); + + if (LayerMap* layerCloneMap = animatedLayerClones(property)) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + // Skip immediate replicas, since they move with the original. + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + + CALayer *currLayer = it->second.get(); + [currLayer removeAnimationForKey:animationID]; + } + } + return true; +} + +static void copyAnimationProperties(CAPropertyAnimation* from, CAPropertyAnimation* to) +{ + [to setBeginTime:[from beginTime]]; + [to setDuration:[from duration]]; + [to setRepeatCount:[from repeatCount]]; + [to setAutoreverses:[from autoreverses]]; + [to setFillMode:[from fillMode]]; + [to setRemovedOnCompletion:[from isRemovedOnCompletion]]; + [to setAdditive:[from isAdditive]]; + [to setTimingFunction:[from timingFunction]]; + +#if HAVE_MODERN_QUARTZCORE + [to setValueFunction:[from valueFunction]]; +#endif + + if (id object = [from valueForKey:WebKitAnimationBeginTimeSetKey]) + [to setValue:object forKey:WebKitAnimationBeginTimeSetKey]; +} + +void GraphicsLayerMac::pauseCAAnimationOnLayer(AnimatedPropertyID property, const String& animationName, int index, double timeOffset) +{ + PlatformLayer* layer = animatedLayer(property); + + NSString *animationID = animationIdentifier(animationName, property, index); + + CAAnimation *caAnim = [layer animationForKey:animationID]; + if (!caAnim) + return; + + // Animations on the layer are immutable, so we have to clone and modify. + CAPropertyAnimation* pausedAnim = nil; + if ([caAnim isKindOfClass:[CAKeyframeAnimation class]]) { + CAKeyframeAnimation* existingKeyframeAnim = static_cast(caAnim); + CAKeyframeAnimation* newAnim = [CAKeyframeAnimation animationWithKeyPath:[existingKeyframeAnim keyPath]]; + copyAnimationProperties(existingKeyframeAnim, newAnim); + [newAnim setValues:[existingKeyframeAnim values]]; + [newAnim setKeyTimes:[existingKeyframeAnim keyTimes]]; + [newAnim setTimingFunctions:[existingKeyframeAnim timingFunctions]]; + pausedAnim = newAnim; + } else if ([caAnim isKindOfClass:[CABasicAnimation class]]) { + CABasicAnimation* existingPropertyAnim = static_cast(caAnim); + CABasicAnimation* newAnim = [CABasicAnimation animationWithKeyPath:[existingPropertyAnim keyPath]]; + copyAnimationProperties(existingPropertyAnim, newAnim); + [newAnim setFromValue:[existingPropertyAnim fromValue]]; + [newAnim setToValue:[existingPropertyAnim toValue]]; + pausedAnim = newAnim; + } + + // pausedAnim has the beginTime of caAnim already. + [pausedAnim setSpeed:0]; + [pausedAnim setTimeOffset:timeOffset]; + + [layer addAnimation:pausedAnim forKey:animationID]; // This will replace the running animation. + + // Pause the animations on the clones too. + if (LayerMap* layerCloneMap = animatedLayerClones(property)) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + // Skip immediate replicas, since they move with the original. + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + CALayer *currLayer = it->second.get(); + [currLayer addAnimation:pausedAnim forKey:animationID]; + } + } +} + +void GraphicsLayerMac::setContentsToCanvas(PlatformLayer* canvasLayer) +{ + if (canvasLayer == m_contentsLayer) + return; + + m_contentsLayer = canvasLayer; + if (m_contentsLayer && [m_contentsLayer.get() respondsToSelector:@selector(setLayerOwner:)]) + [(id)m_contentsLayer.get() setLayerOwner:this]; + + m_contentsLayerPurpose = canvasLayer ? ContentsLayerForCanvas : NoContentsLayer; + + noteSublayersChanged(); + noteLayerPropertyChanged(ContentsCanvasLayerChanged); +} + +void GraphicsLayerMac::repaintLayerDirtyRects() +{ + if (!m_dirtyRects.size()) + return; + + for (size_t i = 0; i < m_dirtyRects.size(); ++i) + [m_layer.get() setNeedsDisplayInRect:m_dirtyRects[i]]; + + m_dirtyRects.clear(); +} + +void GraphicsLayerMac::updateContentsNeedsDisplay() +{ + if (m_contentsLayer) + [m_contentsLayer.get() setNeedsDisplay]; +} + +bool GraphicsLayerMac::createAnimationFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& animationName, double timeOffset) +{ + ASSERT(valueList.property() != AnimatedPropertyWebkitTransform); + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + + bool isKeyframe = valueList.size() > 2; + bool valuesOK; + + bool additive = false; + int animationIndex = 0; + + CAPropertyAnimation* caAnimation; + if (isKeyframe) { + CAKeyframeAnimation* keyframeAnim = createKeyframeAnimation(animation, valueList.property(), additive); + valuesOK = setAnimationKeyframes(valueList, animation, keyframeAnim); + caAnimation = keyframeAnim; + } else { + CABasicAnimation* basicAnim = createBasicAnimation(animation, valueList.property(), additive); + valuesOK = setAnimationEndpoints(valueList, animation, basicAnim); + caAnimation = basicAnim; + } + + if (!valuesOK) + return false; + + m_uncomittedAnimations.append(LayerPropertyAnimation(caAnimation, animationName, valueList.property(), animationIndex, timeOffset)); + + END_BLOCK_OBJC_EXCEPTIONS; + + return true; +} + +bool GraphicsLayerMac::createTransformAnimationsFromKeyframes(const KeyframeValueList& valueList, const Animation* animation, const String& animationName, double timeOffset, const IntSize& boxSize) +{ + ASSERT(valueList.property() == AnimatedPropertyWebkitTransform); + + TransformOperationList functionList; + bool listsMatch, hasBigRotation; + fetchTransformOperationList(valueList, functionList, listsMatch, hasBigRotation); + + // We need to fall back to software animation if we don't have setValueFunction:, and + // we would need to animate each incoming transform function separately. This is the + // case if we have a rotation >= 180 or we have more than one transform function. + if ((hasBigRotation || functionList.size() > 1) && !caValueFunctionSupported()) + return false; + + bool validMatrices = true; + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + + // If functionLists don't match we do a matrix animation, otherwise we do a component hardware animation. + // Also, we can't do component animation unless we have valueFunction, so we need to do matrix animation + // if that's not true as well. + bool isMatrixAnimation = !listsMatch || !caValueFunctionSupported(); + + size_t numAnimations = isMatrixAnimation ? 1 : functionList.size(); + bool isKeyframe = valueList.size() > 2; + + // Iterate through the transform functions, sending an animation for each one. + for (size_t animationIndex = 0; animationIndex < numAnimations; ++animationIndex) { + TransformOperation::OperationType transformOp = isMatrixAnimation ? TransformOperation::MATRIX_3D : functionList[animationIndex]; + CAPropertyAnimation* caAnimation; + +#if defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_SNOW_LEOPARD) + // CA applies animations in reverse order () so we need the last one we add (per property) + // to be non-additive. + bool additive = animationIndex < (numAnimations - 1); +#else + bool additive = animationIndex > 0; +#endif + if (isKeyframe) { + CAKeyframeAnimation* keyframeAnim = createKeyframeAnimation(animation, valueList.property(), additive); + validMatrices = setTransformAnimationKeyframes(valueList, animation, keyframeAnim, animationIndex, transformOp, isMatrixAnimation, boxSize); + caAnimation = keyframeAnim; + } else { + CABasicAnimation* basicAnim = createBasicAnimation(animation, valueList.property(), additive); + validMatrices = setTransformAnimationEndpoints(valueList, animation, basicAnim, animationIndex, transformOp, isMatrixAnimation, boxSize); + caAnimation = basicAnim; + } + + if (!validMatrices) + break; + + m_uncomittedAnimations.append(LayerPropertyAnimation(caAnimation, animationName, valueList.property(), animationIndex, timeOffset)); + } + + END_BLOCK_OBJC_EXCEPTIONS; + + return validMatrices; +} + +CABasicAnimation* GraphicsLayerMac::createBasicAnimation(const Animation* anim, AnimatedPropertyID property, bool additive) +{ + CABasicAnimation* basicAnim = [CABasicAnimation animationWithKeyPath:propertyIdToString(property)]; + setupAnimation(basicAnim, anim, additive); + return basicAnim; +} + +CAKeyframeAnimation* GraphicsLayerMac::createKeyframeAnimation(const Animation* anim, AnimatedPropertyID property, bool additive) +{ + CAKeyframeAnimation* keyframeAnim = [CAKeyframeAnimation animationWithKeyPath:propertyIdToString(property)]; + setupAnimation(keyframeAnim, anim, additive); + return keyframeAnim; +} + +void GraphicsLayerMac::setupAnimation(CAPropertyAnimation* propertyAnim, const Animation* anim, bool additive) +{ + double duration = anim->duration(); + if (duration <= 0) + duration = cAnimationAlmostZeroDuration; + + float repeatCount = anim->iterationCount(); + if (repeatCount == Animation::IterationCountInfinite) + repeatCount = FLT_MAX; + else if (anim->direction() == Animation::AnimationDirectionAlternate) + repeatCount /= 2; + + NSString *fillMode = 0; + switch (anim->fillMode()) { + case AnimationFillModeNone: + fillMode = kCAFillModeForwards; // Use "forwards" rather than "removed" because the style system will remove the animation when it is finished. This avoids a flash. + break; + case AnimationFillModeBackwards: + fillMode = kCAFillModeBoth; // Use "both" rather than "backwards" because the style system will remove the animation when it is finished. This avoids a flash. + break; + case AnimationFillModeForwards: + fillMode = kCAFillModeForwards; + break; + case AnimationFillModeBoth: + fillMode = kCAFillModeBoth; + break; + } + + [propertyAnim setDuration:duration]; + [propertyAnim setRepeatCount:repeatCount]; + [propertyAnim setAutoreverses:anim->direction()]; + [propertyAnim setRemovedOnCompletion:NO]; + [propertyAnim setAdditive:additive]; + [propertyAnim setFillMode:fillMode]; + + [propertyAnim setDelegate:m_animationDelegate.get()]; +} + +CAMediaTimingFunction* GraphicsLayerMac::timingFunctionForAnimationValue(const AnimationValue* animValue, const Animation* anim) +{ + const TimingFunction* tf = 0; + if (animValue->timingFunction()) + tf = animValue->timingFunction(); + else if (anim->isTimingFunctionSet()) + tf = anim->timingFunction().get(); + + return getCAMediaTimingFunction(tf ? tf : CubicBezierTimingFunction::create().get()); +} + +bool GraphicsLayerMac::setAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, CABasicAnimation* basicAnim) +{ + id fromValue = nil; + id toValue = nil; + + switch (valueList.property()) { + case AnimatedPropertyOpacity: { + const FloatAnimationValue* startVal = static_cast(valueList.at(0)); + const FloatAnimationValue* endVal = static_cast(valueList.at(1)); + fromValue = [NSNumber numberWithFloat:startVal->value()]; + toValue = [NSNumber numberWithFloat:endVal->value()]; + break; + } + default: + ASSERT_NOT_REACHED(); // we don't animate color yet + break; + } + + // This codepath is used for 2-keyframe animations, so we still need to look in the start + // for a timing function. + CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim); + [basicAnim setTimingFunction:timingFunction]; + + [basicAnim setFromValue:fromValue]; + [basicAnim setToValue:toValue]; + + return true; +} + +bool GraphicsLayerMac::setAnimationKeyframes(const KeyframeValueList& valueList, const Animation* anim, CAKeyframeAnimation* keyframeAnim) +{ + RetainPtr keyTimes(AdoptNS, [[NSMutableArray alloc] init]); + RetainPtr values(AdoptNS, [[NSMutableArray alloc] init]); + RetainPtr timingFunctions(AdoptNS, [[NSMutableArray alloc] init]); + + for (unsigned i = 0; i < valueList.size(); ++i) { + const AnimationValue* curValue = valueList.at(i); + [keyTimes.get() addObject:[NSNumber numberWithFloat:curValue->keyTime()]]; + + switch (valueList.property()) { + case AnimatedPropertyOpacity: { + const FloatAnimationValue* floatValue = static_cast(curValue); + [values.get() addObject:[NSNumber numberWithFloat:floatValue->value()]]; + break; + } + default: + ASSERT_NOT_REACHED(); // we don't animate color yet + break; + } + + CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, anim); + [timingFunctions.get() addObject:timingFunction]; + } + + // We toss the last tfArray value because it has to one shorter than the others. + [timingFunctions.get() removeLastObject]; + + [keyframeAnim setKeyTimes:keyTimes.get()]; + [keyframeAnim setValues:values.get()]; + [keyframeAnim setTimingFunctions:timingFunctions.get()]; + + return true; +} + +bool GraphicsLayerMac::setTransformAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, CABasicAnimation* basicAnim, int functionIndex, TransformOperation::OperationType transformOp, bool isMatrixAnimation, const IntSize& boxSize) +{ + id fromValue; + id toValue; + + ASSERT(valueList.size() == 2); + const TransformAnimationValue* startValue = static_cast(valueList.at(0)); + const TransformAnimationValue* endValue = static_cast(valueList.at(1)); + + if (isMatrixAnimation) { + TransformationMatrix fromTransform, toTransform; + startValue->value()->apply(boxSize, fromTransform); + endValue->value()->apply(boxSize, toTransform); + + // If any matrix is singular, CA won't animate it correctly. So fall back to software animation + if (!fromTransform.isInvertible() || !toTransform.isInvertible()) + return false; + + CATransform3D caTransform; + copyTransform(caTransform, fromTransform); + fromValue = [NSValue valueWithCATransform3D:caTransform]; + + copyTransform(caTransform, toTransform); + toValue = [NSValue valueWithCATransform3D:caTransform]; + } else { + fromValue = getTransformFunctionValue(startValue->value()->at(functionIndex), transformOp, boxSize); + toValue = getTransformFunctionValue(endValue->value()->at(functionIndex), transformOp, boxSize); + } + + // This codepath is used for 2-keyframe animations, so we still need to look in the start + // for a timing function. + CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim); + [basicAnim setTimingFunction:timingFunction]; + + [basicAnim setFromValue:fromValue]; + [basicAnim setToValue:toValue]; + +#if HAVE_MODERN_QUARTZCORE + if (NSString *valueFunctionName = getValueFunctionNameForTransformOperation(transformOp)) + [basicAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; +#endif + + return true; +} + +bool GraphicsLayerMac::setTransformAnimationKeyframes(const KeyframeValueList& valueList, const Animation* animation, CAKeyframeAnimation* keyframeAnim, int functionIndex, TransformOperation::OperationType transformOpType, bool isMatrixAnimation, const IntSize& boxSize) +{ + RetainPtr keyTimes(AdoptNS, [[NSMutableArray alloc] init]); + RetainPtr values(AdoptNS, [[NSMutableArray alloc] init]); + RetainPtr timingFunctions(AdoptNS, [[NSMutableArray alloc] init]); + + for (unsigned i = 0; i < valueList.size(); ++i) { + const TransformAnimationValue* curValue = static_cast(valueList.at(i)); + [keyTimes.get() addObject:[NSNumber numberWithFloat:curValue->keyTime()]]; + + if (isMatrixAnimation) { + TransformationMatrix transform; + curValue->value()->apply(boxSize, transform); + + // If any matrix is singular, CA won't animate it correctly. So fall back to software animation + if (!transform.isInvertible()) + return false; + + CATransform3D caTransform; + copyTransform(caTransform, transform); + [values.get() addObject:[NSValue valueWithCATransform3D:caTransform]]; + } else { + const TransformOperation* transformOp = curValue->value()->at(functionIndex); + [values.get() addObject:getTransformFunctionValue(transformOp, transformOpType, boxSize)]; + } + + CAMediaTimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, animation); + [timingFunctions.get() addObject:timingFunction]; + } + + // We toss the last tfArray value because it has to one shorter than the others. + [timingFunctions.get() removeLastObject]; + + [keyframeAnim setKeyTimes:keyTimes.get()]; + [keyframeAnim setValues:values.get()]; + [keyframeAnim setTimingFunctions:timingFunctions.get()]; + +#if HAVE_MODERN_QUARTZCORE + if (NSString *valueFunctionName = getValueFunctionNameForTransformOperation(transformOpType)) + [keyframeAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; +#endif + return true; +} + +void GraphicsLayerMac::suspendAnimations(double time) +{ + double t = currentTimeToMediaTime(time ? time : currentTime()); + [primaryLayer() setSpeed:0]; + [primaryLayer() setTimeOffset:t]; + + // Suspend the animations on the clones too. + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setSpeed:0]; + [currLayer setTimeOffset:t]; + } + } +} + +void GraphicsLayerMac::resumeAnimations() +{ + [primaryLayer() setSpeed:1]; + [primaryLayer() setTimeOffset:0]; + + // Resume the animations on the clones too. + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + CALayer *currLayer = it->second.get(); + [currLayer setSpeed:1]; + [currLayer setTimeOffset:0]; + } + } +} + +CALayer* GraphicsLayerMac::hostLayerForSublayers() const +{ + return m_structuralLayer.get() ? m_structuralLayer.get() : m_layer.get(); +} + +CALayer* GraphicsLayerMac::layerForSuperlayer() const +{ + return m_structuralLayer ? m_structuralLayer.get() : m_layer.get(); +} + +CALayer* GraphicsLayerMac::animatedLayer(AnimatedPropertyID property) const +{ + return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayer.get() : primaryLayer(); +} + +GraphicsLayerMac::LayerMap* GraphicsLayerMac::animatedLayerClones(AnimatedPropertyID property) const +{ + return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayerClones.get() : primaryLayerClones(); +} + +PlatformLayer* GraphicsLayerMac::platformLayer() const +{ + return primaryLayer(); +} + +void GraphicsLayerMac::setDebugBackgroundColor(const Color& color) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (color.isValid()) + setLayerBackgroundColor(m_layer.get(), color); + else + clearLayerBackgroundColor(m_layer.get()); + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerMac::setDebugBorder(const Color& color, float borderWidth) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (color.isValid()) { + setLayerBorderColor(m_layer.get(), color); + [m_layer.get() setBorderWidth:borderWidth]; + } else { + clearBorderColor(m_layer.get()); + [m_layer.get() setBorderWidth:0]; + } + + END_BLOCK_OBJC_EXCEPTIONS +} + +FloatSize GraphicsLayerMac::constrainedSize() const +{ + float tileColumns = ceilf(m_size.width() / cTiledLayerTileSize); + float tileRows = ceilf(m_size.height() / cTiledLayerTileSize); + double numTiles = tileColumns * tileRows; + + FloatSize constrainedSize = m_size; + const unsigned cMaxTileCount = 512; + while (numTiles > cMaxTileCount) { + // Constrain the wider dimension. + if (constrainedSize.width() >= constrainedSize.height()) { + tileColumns = max(floorf(cMaxTileCount / tileRows), 1.0f); + constrainedSize.setWidth(tileColumns * cTiledLayerTileSize); + } else { + tileRows = max(floorf(cMaxTileCount / tileColumns), 1.0f); + constrainedSize.setHeight(tileRows * cTiledLayerTileSize); + } + numTiles = tileColumns * tileRows; + } + + return constrainedSize; +} + +bool GraphicsLayerMac::requiresTiledLayer(const FloatSize& size) const +{ + if (!m_drawsContent) + return false; + + // FIXME: catch zero-size height or width here (or earlier)? + return size.width() > cMaxPixelDimension || size.height() > cMaxPixelDimension; +} + +void GraphicsLayerMac::swapFromOrToTiledLayer(bool useTiledLayer) +{ + if (useTiledLayer == m_usingTiledLayer) + return; + + CGSize tileSize = CGSizeMake(cTiledLayerTileSize, cTiledLayerTileSize); + + RetainPtr oldLayer = m_layer.get(); + + Class layerClass = useTiledLayer ? [WebTiledLayer self] : [WebLayer self]; + m_layer.adoptNS([[layerClass alloc] init]); + + m_usingTiledLayer = useTiledLayer; + + if (useTiledLayer) { + WebTiledLayer* tiledLayer = (WebTiledLayer*)m_layer.get(); + [tiledLayer setTileSize:tileSize]; + [tiledLayer setLevelsOfDetail:1]; + [tiledLayer setLevelsOfDetailBias:0]; + [tiledLayer setContentsGravity:@"bottomLeft"]; + +#if !HAVE_MODERN_QUARTZCORE + // Tiled layer has issues with flipped coordinates. + setContentsOrientation(CompositingCoordinatesTopDown); +#endif + } else { +#if !HAVE_MODERN_QUARTZCORE + setContentsOrientation(defaultContentsOrientation()); +#endif + } + + [m_layer.get() setLayerOwner:this]; + safeSetSublayers(m_layer.get(), [oldLayer.get() sublayers]); + + [[oldLayer.get() superlayer] replaceSublayer:oldLayer.get() with:m_layer.get()]; + + updateContentsTransform(); + + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + updateMasksToBounds(); + updateContentsOpaque(); + updateBackfaceVisibility(); + updateLayerBackgroundColor(); + + updateOpacityOnLayer(); + +#ifndef NDEBUG + String name = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + m_name; + [m_layer.get() setName:name]; +#endif + + // move over animations + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyWebkitTransform, oldLayer.get(), m_layer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyOpacity, oldLayer.get(), m_layer.get()); + moveOrCopyAnimationsForProperty(Move, AnimatedPropertyBackgroundColor, oldLayer.get(), m_layer.get()); + + // need to tell new layer to draw itself + setNeedsDisplay(); + + updateDebugIndicators(); +} + +GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayerMac::defaultContentsOrientation() const +{ +#if !HAVE_MODERN_QUARTZCORE + // Older QuartzCore does not support -geometryFlipped, so we manually flip the root + // layer geometry, and then flip the contents of each layer back so that the CTM for CG + // is unflipped, allowing it to do the correct font auto-hinting. + return CompositingCoordinatesBottomUp; +#else + return CompositingCoordinatesTopDown; +#endif +} + +void GraphicsLayerMac::updateContentsTransform() +{ +#if !HAVE_MODERN_QUARTZCORE + if (contentsOrientation() == CompositingCoordinatesBottomUp) { + CGAffineTransform contentsTransform = CGAffineTransformMakeScale(1, -1); + contentsTransform = CGAffineTransformTranslate(contentsTransform, 0, -[m_layer.get() bounds].size.height); + [m_layer.get() setContentsTransform:contentsTransform]; + } +#endif +} + +void GraphicsLayerMac::setupContentsLayer(CALayer* contentsLayer) +{ + // Turn off implicit animations on the inner layer. + [contentsLayer setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; + [contentsLayer setMasksToBounds:YES]; + + if (defaultContentsOrientation() == CompositingCoordinatesBottomUp) { + CATransform3D flipper = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + [contentsLayer setTransform:flipper]; + [contentsLayer setAnchorPoint:CGPointMake(0.0f, 1.0f)]; + } else + [contentsLayer setAnchorPoint:CGPointZero]; + + if (showDebugBorders()) { + setLayerBorderColor(contentsLayer, Color(0, 0, 128, 180)); + [contentsLayer setBorderWidth:1.0f]; + } +} + +CALayer *GraphicsLayerMac::findOrMakeClone(CloneID cloneID, CALayer *sourceLayer, LayerMap* clones, CloneLevel cloneLevel) +{ + if (!sourceLayer) + return 0; + + CALayer *resultLayer; + + // Add with a dummy value to get an iterator for the insertion position, and a boolean that tells + // us whether there's an item there. This technique avoids two hash lookups. + RetainPtr dummy; + pair addResult = clones->add(cloneID, dummy); + if (!addResult.second) { + // Value was not added, so it exists already. + resultLayer = addResult.first->second.get(); + } else { + resultLayer = cloneLayer(sourceLayer, cloneLevel); +#ifndef NDEBUG + [resultLayer setName:[NSString stringWithFormat:@"Clone %d of layer %@", cloneID[0U], sourceLayer]]; +#endif + addResult.first->second = resultLayer; + } + + return resultLayer; +} + +void GraphicsLayerMac::ensureCloneLayers(CloneID cloneID, CALayer *& primaryLayer, CALayer *& structuralLayer, CALayer *& contentsLayer, CloneLevel cloneLevel) +{ + structuralLayer = nil; + contentsLayer = nil; + + if (!m_layerClones) + m_layerClones = new LayerMap; + + if (!m_structuralLayerClones && m_structuralLayer) + m_structuralLayerClones = new LayerMap; + + if (!m_contentsLayerClones && m_contentsLayer) + m_contentsLayerClones = new LayerMap; + + primaryLayer = findOrMakeClone(cloneID, m_layer.get(), m_layerClones.get(), cloneLevel); + structuralLayer = findOrMakeClone(cloneID, m_structuralLayer.get(), m_structuralLayerClones.get(), cloneLevel); + contentsLayer = findOrMakeClone(cloneID, m_contentsLayer.get(), m_contentsLayerClones.get(), cloneLevel); +} + +void GraphicsLayerMac::removeCloneLayers() +{ + m_layerClones = 0; + m_structuralLayerClones = 0; + m_contentsLayerClones = 0; +} + +FloatPoint GraphicsLayerMac::positionForCloneRootLayer() const +{ + // This can get called during a sync when we've just removed the m_replicaLayer. + if (!m_replicaLayer) + return FloatPoint(); + + FloatPoint replicaPosition = m_replicaLayer->replicatedLayerPosition(); + return FloatPoint(replicaPosition.x() + m_anchorPoint.x() * m_size.width(), + replicaPosition.y() + m_anchorPoint.y() * m_size.height()); +} + +void GraphicsLayerMac::propagateLayerChangeToReplicas() +{ + for (GraphicsLayer* currLayer = this; currLayer; currLayer = currLayer->parent()) { + GraphicsLayerMac* currLayerCA = static_cast(currLayer); + if (!currLayerCA->hasCloneLayers()) + break; + + if (currLayerCA->replicaLayer()) + static_cast(currLayerCA->replicaLayer())->noteLayerPropertyChanged(ReplicatedLayerChanged); + } +} + +CALayer *GraphicsLayerMac::fetchCloneLayers(GraphicsLayer* replicaRoot, ReplicaState& replicaState, CloneLevel cloneLevel) +{ + CALayer *primaryLayer; + CALayer *structuralLayer; + CALayer *contentsLayer; + ensureCloneLayers(replicaState.cloneID(), primaryLayer, structuralLayer, contentsLayer, cloneLevel); + + if (m_maskLayer) { + CALayer *maskClone = static_cast(m_maskLayer)->fetchCloneLayers(replicaRoot, replicaState, IntermediateCloneLevel); + [primaryLayer setMask:maskClone]; + } + + if (m_replicatedLayer) { + // We are a replica being asked for clones of our layers. + CALayer *replicaRoot = replicatedLayerRoot(replicaState); + if (!replicaRoot) + return nil; + + if (structuralLayer) { + [structuralLayer insertSublayer:replicaRoot atIndex:0]; + return structuralLayer; + } + + [primaryLayer insertSublayer:replicaRoot atIndex:0]; + return primaryLayer; + } + + const Vector& childLayers = children(); + NSMutableArray* clonalSublayers = nil; + + CALayer *replicaLayer = nil; + if (m_replicaLayer && m_replicaLayer != replicaRoot) { + // We have nested replicas. Ask the replica layer for a clone of its contents. + replicaState.setBranchType(ReplicaState::ReplicaBranch); + replicaLayer = static_cast(m_replicaLayer)->fetchCloneLayers(replicaRoot, replicaState, RootCloneLevel); + replicaState.setBranchType(ReplicaState::ChildBranch); + } + + if (replicaLayer || structuralLayer || contentsLayer || childLayers.size() > 0) { + clonalSublayers = [[NSMutableArray alloc] init]; + + if (structuralLayer) { + // Replicas render behind the actual layer content. + if (replicaLayer) + [clonalSublayers addObject:replicaLayer]; + + // Add the primary layer next. Even if we have negative z-order children, the primary layer always comes behind. + [clonalSublayers addObject:primaryLayer]; + } else if (contentsLayer) { + // FIXME: add the contents layer in the correct order with negative z-order children. + // This does not cause visible rendering issues because currently contents layers are only used + // for replaced elements that don't have children. + [clonalSublayers addObject:contentsLayer]; + } + + replicaState.push(ReplicaState::ChildBranch); + + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerMac* curChild = static_cast(childLayers[i]); + + CALayer *childLayer = curChild->fetchCloneLayers(replicaRoot, replicaState, IntermediateCloneLevel); + if (childLayer) + [clonalSublayers addObject:childLayer]; + } + + replicaState.pop(); + + [clonalSublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; + } + + CALayer *result; + if (structuralLayer) { + [structuralLayer setSublayers:clonalSublayers]; + + if (contentsLayer) { + // If we have a transform layer, then the contents layer is parented in the + // primary layer (which is itself a child of the transform layer). + [primaryLayer setSublayers:nil]; + [primaryLayer addSublayer:contentsLayer]; + } + + result = structuralLayer; + } else { + [primaryLayer setSublayers:clonalSublayers]; + result = primaryLayer; + } + + [clonalSublayers release]; + return result; +} + +CALayer *GraphicsLayerMac::cloneLayer(CALayer *layer, CloneLevel cloneLevel) +{ + static Class transformLayerClass = NSClassFromString(@"CATransformLayer"); + CALayer *newLayer = nil; + if ([layer isKindOfClass:transformLayerClass]) + newLayer = [transformLayerClass layer]; + else + newLayer = [CALayer layer]; + + [newLayer setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; + + [newLayer setPosition:[layer position]]; + [newLayer setBounds:[layer bounds]]; + [newLayer setAnchorPoint:[layer anchorPoint]]; +#if HAVE_MODERN_QUARTZCORE + [newLayer setAnchorPointZ:[layer anchorPointZ]]; +#endif + [newLayer setTransform:[layer transform]]; + [newLayer setSublayerTransform:[layer sublayerTransform]]; + [newLayer setContents:[layer contents]]; + [newLayer setMasksToBounds:[layer masksToBounds]]; + [newLayer setDoubleSided:[layer isDoubleSided]]; + [newLayer setOpaque:[layer isOpaque]]; + [newLayer setBackgroundColor:[layer backgroundColor]]; + + if (cloneLevel == IntermediateCloneLevel) { + [newLayer setOpacity:[layer opacity]]; + moveOrCopyAnimationsForProperty(Copy, AnimatedPropertyWebkitTransform, layer, newLayer); + moveOrCopyAnimationsForProperty(Copy, AnimatedPropertyOpacity, layer, newLayer); + } + + if (showDebugBorders()) { + setLayerBorderColor(newLayer, Color(255, 122, 251)); + [newLayer setBorderWidth:2]; + } + + return newLayer; +} + +void GraphicsLayerMac::setOpacityInternal(float accumulatedOpacity) +{ + LayerMap* layerCloneMap = 0; + + if (preserves3D()) { + [m_layer.get() setOpacity:accumulatedOpacity]; + layerCloneMap = m_layerClones.get(); + } else { + [primaryLayer() setOpacity:accumulatedOpacity]; + layerCloneMap = primaryLayerClones(); + } + + if (layerCloneMap) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + CALayer *currLayer = it->second.get(); + [currLayer setOpacity:m_opacity]; + } + } +} + +void GraphicsLayerMac::updateOpacityOnLayer() +{ +#if !HAVE_MODERN_QUARTZCORE + // Distribute opacity either to our own layer or to our children. We pass in the + // contribution from our parent(s). + distributeOpacity(parent() ? parent()->accumulatedOpacity() : 1); +#else + [primaryLayer() setOpacity:m_opacity]; + + if (LayerMap* layerCloneMap = primaryLayerClones()) { + LayerMap::const_iterator end = layerCloneMap->end(); + for (LayerMap::const_iterator it = layerCloneMap->begin(); it != end; ++it) { + if (m_replicaLayer && isReplicatedRootClone(it->first)) + continue; + + CALayer *currLayer = it->second.get(); + [currLayer setOpacity:m_opacity]; + } + + } +#endif +} + +void GraphicsLayerMac::noteSublayersChanged() +{ + noteLayerPropertyChanged(ChildrenChanged); + propagateLayerChangeToReplicas(); +} + +void GraphicsLayerMac::noteLayerPropertyChanged(LayerChangeFlags flags) +{ + if (!m_uncommittedChanges && m_client) + m_client->notifySyncRequired(this); + + m_uncommittedChanges |= flags; +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h index 876bc16..95ab456 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h @@ -55,7 +55,7 @@ class WebCoreMovieObserver; namespace WebCore { -class MediaPlayerPrivate : public MediaPlayerPrivateInterface { +class MediaPlayerPrivateQTKit : public MediaPlayerPrivateInterface { public: static void registerMediaEngine(MediaEngineRegistrar); @@ -67,8 +67,8 @@ public: void didEnd(); private: - MediaPlayerPrivate(MediaPlayer*); - ~MediaPlayerPrivate(); + MediaPlayerPrivateQTKit(MediaPlayer*); + ~MediaPlayerPrivateQTKit(); // engine support static MediaPlayerPrivateInterface* create(MediaPlayer* player); @@ -163,7 +163,7 @@ private: void updateStates(); void doSeek(); void cancelSeek(); - void seekTimerFired(Timer*); + void seekTimerFired(Timer*); float maxTimeLoaded() const; void disableUnsupportedTracks(); @@ -175,6 +175,8 @@ private: virtual float mediaTimeForTimeValue(float) const; + virtual double maximumDurationToCacheMediaTime() const { return 5; } + MediaPlayer* m_player; RetainPtr m_qtMovie; RetainPtr m_qtMovieView; @@ -182,7 +184,7 @@ private: RetainPtr m_objcObserver; String m_movieURL; float m_seekTo; - Timer m_seekTimer; + Timer m_seekTimer; MediaPlayer::NetworkState m_networkState; MediaPlayer::ReadyState m_readyState; IntRect m_rect; diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm index 06c7924..a2325da 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -164,11 +164,11 @@ using namespace std; @interface WebCoreMovieObserver : NSObject { - MediaPlayerPrivate* m_callback; + MediaPlayerPrivateQTKit* m_callback; NSView* m_view; BOOL m_delayCallbacks; } --(id)initWithCallback:(MediaPlayerPrivate*)callback; +-(id)initWithCallback:(MediaPlayerPrivateQTKit*)callback; -(void)disconnect; -(void)setView:(NSView*)view; -(void)repaint; @@ -192,22 +192,22 @@ static const long minimumQuickTimeVersion = 0x07300000; // 7.3 #endif -MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +MediaPlayerPrivateInterface* MediaPlayerPrivateQTKit::create(MediaPlayer* player) { - return new MediaPlayerPrivate(player); + return new MediaPlayerPrivateQTKit(player); } -void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) +void MediaPlayerPrivateQTKit::registerMediaEngine(MediaEngineRegistrar registrar) { if (isAvailable()) registrar(create, getSupportedTypes, supportsType); } -MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) +MediaPlayerPrivateQTKit::MediaPlayerPrivateQTKit(MediaPlayer* player) : m_player(player) , m_objcObserver(AdoptNS, [[WebCoreMovieObserver alloc] initWithCallback:this]) , m_seekTo(-1) - , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired) + , m_seekTimer(this, &MediaPlayerPrivateQTKit::seekTimerFired) , m_networkState(MediaPlayer::Empty) , m_readyState(MediaPlayer::HaveNothing) , m_rect() @@ -232,7 +232,7 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) { } -MediaPlayerPrivate::~MediaPlayerPrivate() +MediaPlayerPrivateQTKit::~MediaPlayerPrivateQTKit() { tearDownVideoRendering(); @@ -240,24 +240,52 @@ MediaPlayerPrivate::~MediaPlayerPrivate() [m_objcObserver.get() disconnect]; } -void MediaPlayerPrivate::createQTMovie(const String& url) +void MediaPlayerPrivateQTKit::createQTMovie(const String& url) { NSURL *cocoaURL = KURL(ParsedURLString, url); - NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys: + NSMutableDictionary *movieAttributes = [NSMutableDictionary dictionaryWithObjectsAndKeys: cocoaURL, QTMovieURLAttribute, [NSNumber numberWithBool:m_player->preservesPitch()], QTMovieRateChangesPreservePitchAttribute, [NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute, [NSNumber numberWithBool:YES], QTSecurityPolicyNoCrossSiteAttribute, [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute, [NSNumber numberWithBool:NO], QTMovieLoopsAttribute, -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) - [NSNumber numberWithBool:YES], @"QTMovieOpenForPlaybackAttribute", -#endif #ifndef BUILDING_ON_TIGER QTMovieApertureModeClean, QTMovieApertureModeAttribute, #endif nil]; +#if defined(BUILDING_ON_SNOW_LEOPARD) + CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings(); + CFArrayRef proxiesForURL = CFNetworkCopyProxiesForURL((CFURLRef)cocoaURL, proxySettings); + BOOL willUseProxy = YES; + + if (!proxiesForURL || !CFArrayGetCount(proxiesForURL)) + willUseProxy = NO; + + if (CFArrayGetCount(proxiesForURL) == 1) { + CFDictionaryRef proxy = (CFDictionaryRef)CFArrayGetValueAtIndex(proxiesForURL, 0); + ASSERT(CFGetTypeID(proxy) == CFDictionaryGetTypeID()); + + CFStringRef proxyType = (CFStringRef)CFDictionaryGetValue(proxy, kCFProxyTypeKey); + ASSERT(CFGetTypeID(proxyType) == CFStringGetTypeID()); + + if (CFStringCompare(proxyType, kCFProxyTypeNone, 0) == kCFCompareEqualTo) + willUseProxy = NO; + } + + if (!willUseProxy) { + // Only pass the QTMovieOpenForPlaybackAttribute flag if there are no proxy servers, due + // to rdar://problem/7531776. + [movieAttributes setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenForPlaybackAttribute"]; + } + + if (proxiesForURL) + CFRelease(proxiesForURL); + if (proxySettings) + CFRelease(proxySettings); +#endif + createQTMovie(cocoaURL, movieAttributes); } @@ -292,7 +320,7 @@ static void disableComponentsOnce() wkQTMovieDisableComponent(componentsToDisable[i]); } -void MediaPlayerPrivate::createQTMovie(NSURL *url, NSDictionary *movieAttributes) +void MediaPlayerPrivateQTKit::createQTMovie(NSURL *url, NSDictionary *movieAttributes) { disableComponentsOnce(); @@ -375,7 +403,7 @@ static Class QTVideoRendererClass() return QTVideoRendererWebKitOnlyClass; } -void MediaPlayerPrivate::createQTMovieView() +void MediaPlayerPrivateQTKit::createQTMovieView() { detachQTMovieView(); @@ -420,7 +448,7 @@ void MediaPlayerPrivate::createQTMovieView() [m_objcObserver.get() setDelayCallbacks:NO]; } -void MediaPlayerPrivate::detachQTMovieView() +void MediaPlayerPrivateQTKit::detachQTMovieView() { if (m_qtMovieView) { [m_objcObserver.get() setView:nil]; @@ -435,7 +463,7 @@ void MediaPlayerPrivate::detachQTMovieView() } } -void MediaPlayerPrivate::createQTVideoRenderer(QTVideoRendererMode rendererMode) +void MediaPlayerPrivateQTKit::createQTVideoRenderer(QTVideoRendererMode rendererMode) { destroyQTVideoRenderer(); @@ -455,7 +483,7 @@ void MediaPlayerPrivate::createQTVideoRenderer(QTVideoRendererMode rendererMode) } } -void MediaPlayerPrivate::destroyQTVideoRenderer() +void MediaPlayerPrivateQTKit::destroyQTVideoRenderer() { if (!m_qtVideoRenderer) return; @@ -471,7 +499,7 @@ void MediaPlayerPrivate::destroyQTVideoRenderer() m_qtVideoRenderer = nil; } -void MediaPlayerPrivate::createQTMovieLayer() +void MediaPlayerPrivateQTKit::createQTMovieLayer() { #if USE(ACCELERATED_COMPOSITING) if (!m_qtMovie) @@ -493,7 +521,7 @@ void MediaPlayerPrivate::createQTMovieLayer() #endif } -void MediaPlayerPrivate::destroyQTMovieLayer() +void MediaPlayerPrivateQTKit::destroyQTMovieLayer() { #if USE(ACCELERATED_COMPOSITING) if (!m_qtVideoLayer) @@ -505,7 +533,7 @@ void MediaPlayerPrivate::destroyQTMovieLayer() #endif } -MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::currentRenderingMode() const +MediaPlayerPrivateQTKit::MediaRenderingMode MediaPlayerPrivateQTKit::currentRenderingMode() const { if (m_qtMovieView) return MediaRenderingMovieView; @@ -519,7 +547,7 @@ MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::currentRenderingMode( return MediaRenderingNone; } -MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::preferredRenderingMode() const +MediaPlayerPrivateQTKit::MediaRenderingMode MediaPlayerPrivateQTKit::preferredRenderingMode() const { if (!m_player->frameView() || !m_qtMovie) return MediaRenderingNone; @@ -535,7 +563,7 @@ MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::preferredRenderingMod return MediaRenderingSoftwareRenderer; } -void MediaPlayerPrivate::setUpVideoRendering() +void MediaPlayerPrivateQTKit::setUpVideoRendering() { if (!isReadyForVideoSetup()) return; @@ -566,7 +594,7 @@ void MediaPlayerPrivate::setUpVideoRendering() m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); } -void MediaPlayerPrivate::tearDownVideoRendering() +void MediaPlayerPrivateQTKit::tearDownVideoRendering() { if (m_qtMovieView) detachQTMovieView(); @@ -576,14 +604,14 @@ void MediaPlayerPrivate::tearDownVideoRendering() destroyQTMovieLayer(); } -bool MediaPlayerPrivate::hasSetUpVideoRendering() const +bool MediaPlayerPrivateQTKit::hasSetUpVideoRendering() const { return m_qtMovieView || m_qtVideoLayer || m_qtVideoRenderer; } -QTTime MediaPlayerPrivate::createQTTime(float time) const +QTTime MediaPlayerPrivateQTKit::createQTTime(float time) const { if (!metaDataAvailable()) return QTMakeTime(0, 600); @@ -591,7 +619,7 @@ QTTime MediaPlayerPrivate::createQTTime(float time) const return QTMakeTime(time * timeScale, timeScale); } -void MediaPlayerPrivate::resumeLoad() +void MediaPlayerPrivateQTKit::resumeLoad() { m_delayingLoad = false; @@ -599,7 +627,7 @@ void MediaPlayerPrivate::resumeLoad() loadInternal(m_movieURL); } -void MediaPlayerPrivate::load(const String& url) +void MediaPlayerPrivateQTKit::load(const String& url) { m_movieURL = url; @@ -613,7 +641,7 @@ void MediaPlayerPrivate::load(const String& url) loadInternal(url); } -void MediaPlayerPrivate::loadInternal(const String& url) +void MediaPlayerPrivateQTKit::loadInternal(const String& url) { if (m_networkState != MediaPlayer::Loading) { m_networkState = MediaPlayer::Loading; @@ -634,13 +662,13 @@ void MediaPlayerPrivate::loadInternal(const String& url) [m_objcObserver.get() setDelayCallbacks:NO]; } -void MediaPlayerPrivate::prepareToPlay() +void MediaPlayerPrivateQTKit::prepareToPlay() { if (!m_qtMovie || m_delayingLoad) resumeLoad(); } -PlatformMedia MediaPlayerPrivate::platformMedia() const +PlatformMedia MediaPlayerPrivateQTKit::platformMedia() const { PlatformMedia pm; pm.type = PlatformMedia::QTMovieType; @@ -649,13 +677,13 @@ PlatformMedia MediaPlayerPrivate::platformMedia() const } #if USE(ACCELERATED_COMPOSITING) -PlatformLayer* MediaPlayerPrivate::platformLayer() const +PlatformLayer* MediaPlayerPrivateQTKit::platformLayer() const { return m_qtVideoLayer.get(); } #endif -void MediaPlayerPrivate::play() +void MediaPlayerPrivateQTKit::play() { if (!metaDataAvailable()) return; @@ -668,7 +696,7 @@ void MediaPlayerPrivate::play() [m_objcObserver.get() setDelayCallbacks:NO]; } -void MediaPlayerPrivate::pause() +void MediaPlayerPrivateQTKit::pause() { if (!metaDataAvailable()) return; @@ -681,7 +709,7 @@ void MediaPlayerPrivate::pause() [m_objcObserver.get() setDelayCallbacks:NO]; } -float MediaPlayerPrivate::duration() const +float MediaPlayerPrivateQTKit::duration() const { if (!metaDataAvailable()) return 0; @@ -695,7 +723,7 @@ float MediaPlayerPrivate::duration() const return static_cast(time.timeValue) / time.timeScale; } -float MediaPlayerPrivate::currentTime() const +float MediaPlayerPrivateQTKit::currentTime() const { if (!metaDataAvailable()) return 0; @@ -703,7 +731,7 @@ float MediaPlayerPrivate::currentTime() const return static_cast(time.timeValue) / time.timeScale; } -void MediaPlayerPrivate::seek(float time) +void MediaPlayerPrivateQTKit::seek(float time) { // Nothing to do if we are already in the middle of a seek to the same time. if (time == m_seekTo) @@ -724,7 +752,7 @@ void MediaPlayerPrivate::seek(float time) m_seekTimer.start(0, 0.5f); } -void MediaPlayerPrivate::doSeek() +void MediaPlayerPrivateQTKit::doSeek() { QTTime qttime = createQTTime(m_seekTo); // setCurrentTime generates several event callbacks, update afterwards @@ -744,13 +772,13 @@ void MediaPlayerPrivate::doSeek() [m_objcObserver.get() setDelayCallbacks:NO]; } -void MediaPlayerPrivate::cancelSeek() +void MediaPlayerPrivateQTKit::cancelSeek() { m_seekTo = -1; m_seekTimer.stop(); } -void MediaPlayerPrivate::seekTimerFired(Timer*) +void MediaPlayerPrivateQTKit::seekTimerFired(Timer*) { if (!metaDataAvailable()|| !seeking() || currentTime() == m_seekTo) { cancelSeek(); @@ -771,21 +799,21 @@ void MediaPlayerPrivate::seekTimerFired(Timer*) } } -bool MediaPlayerPrivate::paused() const +bool MediaPlayerPrivateQTKit::paused() const { if (!metaDataAvailable()) return true; return [m_qtMovie.get() rate] == 0; } -bool MediaPlayerPrivate::seeking() const +bool MediaPlayerPrivateQTKit::seeking() const { if (!metaDataAvailable()) return false; return m_seekTo >= 0; } -IntSize MediaPlayerPrivate::naturalSize() const +IntSize MediaPlayerPrivateQTKit::naturalSize() const { if (!metaDataAvailable()) return IntSize(); @@ -801,21 +829,21 @@ IntSize MediaPlayerPrivate::naturalSize() const return IntSize(naturalSize.width * m_scaleFactor.width(), naturalSize.height * m_scaleFactor.height()); } -bool MediaPlayerPrivate::hasVideo() const +bool MediaPlayerPrivateQTKit::hasVideo() const { if (!metaDataAvailable()) return false; return [[m_qtMovie.get() attributeForKey:QTMovieHasVideoAttribute] boolValue]; } -bool MediaPlayerPrivate::hasAudio() const +bool MediaPlayerPrivateQTKit::hasAudio() const { if (!m_qtMovie) return false; return [[m_qtMovie.get() attributeForKey:QTMovieHasAudioAttribute] boolValue]; } -bool MediaPlayerPrivate::supportsFullscreen() const +bool MediaPlayerPrivateQTKit::supportsFullscreen() const { #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) return true; @@ -825,20 +853,20 @@ bool MediaPlayerPrivate::supportsFullscreen() const #endif } -void MediaPlayerPrivate::setVolume(float volume) +void MediaPlayerPrivateQTKit::setVolume(float volume) { if (m_qtMovie) [m_qtMovie.get() setVolume:volume]; } -bool MediaPlayerPrivate::hasClosedCaptions() const +bool MediaPlayerPrivateQTKit::hasClosedCaptions() const { if (!metaDataAvailable()) return false; return wkQTMovieHasClosedCaptions(m_qtMovie.get()); } -void MediaPlayerPrivate::setClosedCaptionsVisible(bool closedCaptionsVisible) +void MediaPlayerPrivateQTKit::setClosedCaptionsVisible(bool closedCaptionsVisible) { if (metaDataAvailable()) { wkQTMovieSetShowClosedCaptions(m_qtMovie.get(), closedCaptionsVisible); @@ -852,13 +880,13 @@ void MediaPlayerPrivate::setClosedCaptionsVisible(bool closedCaptionsVisible) } } -void MediaPlayerPrivate::setRate(float rate) +void MediaPlayerPrivateQTKit::setRate(float rate) { if (m_qtMovie) [m_qtMovie.get() setRate:rate]; } -void MediaPlayerPrivate::setPreservesPitch(bool preservesPitch) +void MediaPlayerPrivateQTKit::setPreservesPitch(bool preservesPitch) { if (!m_qtMovie) return; @@ -876,7 +904,7 @@ void MediaPlayerPrivate::setPreservesPitch(bool preservesPitch) createQTMovie([movieAttributes valueForKey:QTMovieURLAttribute], movieAttributes); } -PassRefPtr MediaPlayerPrivate::buffered() const +PassRefPtr MediaPlayerPrivateQTKit::buffered() const { RefPtr timeRanges = TimeRanges::create(); float loaded = maxTimeLoaded(); @@ -885,7 +913,7 @@ PassRefPtr MediaPlayerPrivate::buffered() const return timeRanges.release(); } -float MediaPlayerPrivate::maxTimeSeekable() const +float MediaPlayerPrivateQTKit::maxTimeSeekable() const { if (!metaDataAvailable()) return 0; @@ -897,14 +925,14 @@ float MediaPlayerPrivate::maxTimeSeekable() const return wkQTMovieMaxTimeSeekable(m_qtMovie.get()); } -float MediaPlayerPrivate::maxTimeLoaded() const +float MediaPlayerPrivateQTKit::maxTimeLoaded() const { if (!metaDataAvailable()) return 0; return wkQTMovieMaxTimeLoaded(m_qtMovie.get()); } -unsigned MediaPlayerPrivate::bytesLoaded() const +unsigned MediaPlayerPrivateQTKit::bytesLoaded() const { float dur = duration(); if (!dur) @@ -912,14 +940,14 @@ unsigned MediaPlayerPrivate::bytesLoaded() const return totalBytes() * maxTimeLoaded() / dur; } -unsigned MediaPlayerPrivate::totalBytes() const +unsigned MediaPlayerPrivateQTKit::totalBytes() const { if (!metaDataAvailable()) return 0; return [[m_qtMovie.get() attributeForKey:QTMovieDataSizeAttribute] intValue]; } -void MediaPlayerPrivate::cancelLoad() +void MediaPlayerPrivateQTKit::cancelLoad() { // FIXME: Is there a better way to check for this? if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) @@ -931,7 +959,7 @@ void MediaPlayerPrivate::cancelLoad() updateStates(); } -void MediaPlayerPrivate::cacheMovieScale() +void MediaPlayerPrivateQTKit::cacheMovieScale() { NSSize initialSize = NSZeroSize; NSSize naturalSize = [[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]; @@ -956,12 +984,12 @@ void MediaPlayerPrivate::cacheMovieScale() m_scaleFactor.setHeight(initialSize.height / naturalSize.height); } -bool MediaPlayerPrivate::isReadyForVideoSetup() const +bool MediaPlayerPrivateQTKit::isReadyForVideoSetup() const { return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible(); } -void MediaPlayerPrivate::prepareForRendering() +void MediaPlayerPrivateQTKit::prepareForRendering() { if (m_isAllowedToRender) return; @@ -976,7 +1004,7 @@ void MediaPlayerPrivate::prepareForRendering() m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); } -void MediaPlayerPrivate::updateStates() +void MediaPlayerPrivateQTKit::updateStates() { MediaPlayer::NetworkState oldNetworkState = m_networkState; MediaPlayer::ReadyState oldReadyState = m_readyState; @@ -1091,13 +1119,13 @@ void MediaPlayerPrivate::updateStates() } } -void MediaPlayerPrivate::loadStateChanged() +void MediaPlayerPrivateQTKit::loadStateChanged() { if (!m_hasUnsupportedTracks) updateStates(); } -void MediaPlayerPrivate::rateChanged() +void MediaPlayerPrivateQTKit::rateChanged() { if (m_hasUnsupportedTracks) return; @@ -1106,13 +1134,13 @@ void MediaPlayerPrivate::rateChanged() m_player->rateChanged(); } -void MediaPlayerPrivate::sizeChanged() +void MediaPlayerPrivateQTKit::sizeChanged() { if (!m_hasUnsupportedTracks) m_player->sizeChanged(); } -void MediaPlayerPrivate::timeChanged() +void MediaPlayerPrivateQTKit::timeChanged() { if (m_hasUnsupportedTracks) return; @@ -1128,7 +1156,7 @@ void MediaPlayerPrivate::timeChanged() m_player->timeChanged(); } -void MediaPlayerPrivate::didEnd() +void MediaPlayerPrivateQTKit::didEnd() { if (m_hasUnsupportedTracks) return; @@ -1150,7 +1178,7 @@ void MediaPlayerPrivate::didEnd() m_player->timeChanged(); } -void MediaPlayerPrivate::setSize(const IntSize&) +void MediaPlayerPrivateQTKit::setSize(const IntSize&) { // Don't resize the view now because [view setFrame] also resizes the movie itself, and because // the renderer calls this function immediately when we report a size change (QTMovieSizeDidChangeNotification) @@ -1161,7 +1189,7 @@ void MediaPlayerPrivate::setSize(const IntSize&) // REGRESSION: rtsp movie does not resize correctly } -void MediaPlayerPrivate::setVisible(bool b) +void MediaPlayerPrivateQTKit::setVisible(bool b) { if (m_visible != b) { m_visible = b; @@ -1172,7 +1200,7 @@ void MediaPlayerPrivate::setVisible(bool b) } } -bool MediaPlayerPrivate::hasAvailableVideoFrame() const +bool MediaPlayerPrivateQTKit::hasAvailableVideoFrame() const { // When using a QTMovieLayer return true as soon as the movie reaches QTMovieLoadStatePlayable // because although we don't *know* when the first frame has decoded, by the time we get and @@ -1186,7 +1214,7 @@ bool MediaPlayerPrivate::hasAvailableVideoFrame() const return m_videoFrameHasDrawn; } -void MediaPlayerPrivate::repaint() +void MediaPlayerPrivateQTKit::repaint() { if (m_hasUnsupportedTracks) return; @@ -1204,7 +1232,7 @@ void MediaPlayerPrivate::repaint() m_player->repaint(); } -void MediaPlayerPrivate::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& r) +void MediaPlayerPrivateQTKit::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& r) { id qtVideoRenderer = m_qtVideoRenderer.get(); if (!qtVideoRenderer && currentRenderingMode() == MediaRenderingMovieLayer) { @@ -1218,7 +1246,7 @@ void MediaPlayerPrivate::paintCurrentFrameInContext(GraphicsContext* context, co paint(context, r); } -void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r) +void MediaPlayerPrivateQTKit::paint(GraphicsContext* context, const IntRect& r) { if (context->paintingDisabled() || m_hasUnsupportedTracks) return; @@ -1355,7 +1383,7 @@ static HashSet mimeModernTypesCache() return cache; } -void MediaPlayerPrivate::getSupportedTypes(HashSet& supportedTypes) +void MediaPlayerPrivateQTKit::getSupportedTypes(HashSet& supportedTypes) { supportedTypes = mimeModernTypesCache(); @@ -1368,7 +1396,7 @@ void MediaPlayerPrivate::getSupportedTypes(HashSet& supportedTypes) supportedTypes.add(*it); } -MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) +MediaPlayer::SupportsType MediaPlayerPrivateQTKit::supportsType(const String& type, const String& codecs) { // Only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an // extended MIME type yet. @@ -1380,7 +1408,7 @@ MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, c return MediaPlayer::IsNotSupported; } -bool MediaPlayerPrivate::isAvailable() +bool MediaPlayerPrivateQTKit::isAvailable() { #ifdef BUILDING_ON_TIGER SInt32 version; @@ -1401,7 +1429,7 @@ bool MediaPlayerPrivate::isAvailable() #endif } -void MediaPlayerPrivate::disableUnsupportedTracks() +void MediaPlayerPrivateQTKit::disableUnsupportedTracks() { if (!m_qtMovie) { m_enabledTrackCount = 0; @@ -1491,35 +1519,35 @@ void MediaPlayerPrivate::disableUnsupportedTracks() } } -void MediaPlayerPrivate::sawUnsupportedTracks() +void MediaPlayerPrivateQTKit::sawUnsupportedTracks() { m_hasUnsupportedTracks = true; m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player); } #if USE(ACCELERATED_COMPOSITING) -bool MediaPlayerPrivate::supportsAcceleratedRendering() const +bool MediaPlayerPrivateQTKit::supportsAcceleratedRendering() const { // Also don't claim to support accelerated rendering when in the media document, as we will then render // via QTMovieView which is already accelerated. return isReadyForVideoSetup() && getQTMovieLayerClass() != Nil && !m_player->inMediaDocument(); } -void MediaPlayerPrivate::acceleratedRenderingStateChanged() +void MediaPlayerPrivateQTKit::acceleratedRenderingStateChanged() { // Set up or change the rendering path if necessary. setUpVideoRendering(); } #endif -bool MediaPlayerPrivate::hasSingleSecurityOrigin() const +bool MediaPlayerPrivateQTKit::hasSingleSecurityOrigin() const { // We tell quicktime to disallow resources that come from different origins // so we know all media is single origin. return true; } -MediaPlayer::MovieLoadType MediaPlayerPrivate::movieLoadType() const +MediaPlayer::MovieLoadType MediaPlayerPrivateQTKit::movieLoadType() const { if (!m_qtMovie) return MediaPlayer::Unknown; @@ -1533,14 +1561,14 @@ MediaPlayer::MovieLoadType MediaPlayerPrivate::movieLoadType() const return movieType; } -void MediaPlayerPrivate::setPreload(MediaPlayer::Preload preload) +void MediaPlayerPrivateQTKit::setPreload(MediaPlayer::Preload preload) { m_preload = preload; if (m_delayingLoad && m_preload != MediaPlayer::None) resumeLoad(); } -float MediaPlayerPrivate::mediaTimeForTimeValue(float timeValue) const +float MediaPlayerPrivateQTKit::mediaTimeForTimeValue(float timeValue) const { if (!metaDataAvailable()) return timeValue; @@ -1553,7 +1581,7 @@ float MediaPlayerPrivate::mediaTimeForTimeValue(float timeValue) const @implementation WebCoreMovieObserver -- (id)initWithCallback:(MediaPlayerPrivate*)callback +- (id)initWithCallback:(MediaPlayerPrivateQTKit*)callback { m_callback = callback; return [super init]; diff --git a/WebCore/platform/graphics/mac/SimpleFontDataCoreText.cpp b/WebCore/platform/graphics/mac/SimpleFontDataCoreText.cpp index 01d75ee..db6de49 100644 --- a/WebCore/platform/graphics/mac/SimpleFontDataCoreText.cpp +++ b/WebCore/platform/graphics/mac/SimpleFontDataCoreText.cpp @@ -47,7 +47,7 @@ CFDictionaryRef SimpleFontData::getCFStringAttributes(TypesettingFeatures typese if (!addResult.second) return attributesDictionary.get(); - bool allowLigatures = platformData().allowsLigatures() || (typesettingFeatures & Ligatures); + bool allowLigatures = (orientation() == Horizontal && platformData().allowsLigatures()) || (typesettingFeatures & Ligatures); static const int ligaturesNotAllowedValue = 0; static CFNumberRef ligaturesNotAllowed = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ligaturesNotAllowedValue); @@ -60,14 +60,14 @@ CFDictionaryRef SimpleFontData::getCFStringAttributes(TypesettingFeatures typese static CFNumberRef kerningAdjustment = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &kerningAdjustmentValue); static const void* keysWithKerningDisabled[] = { kCTFontAttributeName, kCTKernAttributeName, kCTLigatureAttributeName, kCTVerticalFormsAttributeName }; const void* valuesWithKerningDisabled[] = { platformData().ctFont(), kerningAdjustment, allowLigatures - ? ligaturesAllowed : ligaturesNotAllowed, platformData().orientation() == Vertical ? kCFBooleanTrue : kCFBooleanFalse }; + ? ligaturesAllowed : ligaturesNotAllowed, orientation() == Vertical ? kCFBooleanTrue : kCFBooleanFalse }; attributesDictionary.adoptCF(CFDictionaryCreate(0, keysWithKerningDisabled, valuesWithKerningDisabled, sizeof(keysWithKerningDisabled) / sizeof(*keysWithKerningDisabled), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); } else { // By omitting the kCTKernAttributeName attribute, we get Core Text's standard kerning. static const void* keysWithKerningEnabled[] = { kCTFontAttributeName, kCTLigatureAttributeName, kCTVerticalFormsAttributeName }; - const void* valuesWithKerningEnabled[] = { platformData().ctFont(), allowLigatures ? ligaturesAllowed : ligaturesNotAllowed, platformData().orientation() == Vertical ? kCFBooleanTrue : kCFBooleanFalse }; + const void* valuesWithKerningEnabled[] = { platformData().ctFont(), allowLigatures ? ligaturesAllowed : ligaturesNotAllowed, orientation() == Vertical ? kCFBooleanTrue : kCFBooleanFalse }; attributesDictionary.adoptCF(CFDictionaryCreate(0, keysWithKerningEnabled, valuesWithKerningEnabled, sizeof(keysWithKerningEnabled) / sizeof(*keysWithKerningEnabled), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); diff --git a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm index fd57630..e1d3f43 100644 --- a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm +++ b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm @@ -260,6 +260,24 @@ void SimpleFontData::platformInit() m_descent = 3; } + if (m_orientation == Vertical) { + // Ignore vertical orientation when the font doesn't support vertical metrics. + // The check doesn't look neat but this is what AppKit does for vertical writing... + RetainPtr tableTags(AdoptCF, CTFontCopyAvailableTables(m_platformData.ctFont(), kCTFontTableOptionExcludeSynthetic)); + CFIndex numTables = CFArrayGetCount(tableTags.get()); + bool found = false; + for (CFIndex index = 0; index < numTables; ++index) { + CTFontTableTag tag = (CTFontTableTag)(uintptr_t)CFArrayGetValueAtIndex(tableTags.get(), index); + if (tag == kCTFontTableVhea || tag == kCTFontTableVORG) { + found = true; + break; + } + } + + if (found == false) + m_orientation = Horizontal; + } + // Measure the actual character "x", because AppKit synthesizes X height rather than getting it from the font. // Unfortunately, NSFont will round this for us so we don't quite get the right value. GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); @@ -339,6 +357,11 @@ void SimpleFontData::platformCharWidthInit() void SimpleFontData::platformDestroy() { + if (m_smallCapsFontData && !isCustomFont()) { + fontCache()->releaseFontData(m_smallCapsFontData); + m_smallCapsFontData = 0; + } + #ifdef BUILDING_ON_TIGER if (m_styleGroup) wkReleaseStyleGroup(m_styleGroup); @@ -359,8 +382,8 @@ SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDes m_smallCapsFontData = new SimpleFontData(smallCapsFontData, true, false); } else { BEGIN_BLOCK_OBJC_EXCEPTIONS; - float size = [m_platformData.font() pointSize] * smallCapsFontSizeMultiplier; - FontPlatformData smallCapsFont([[NSFontManager sharedFontManager] convertFont:m_platformData.font() toSize:size]); + float size = m_platformData.size() * smallCapsFontSizeMultiplier; + FontPlatformData smallCapsFont([[NSFontManager sharedFontManager] convertFont:m_platformData.font() toSize:size], size); // AppKit resets the type information (screen/printer) when you convert a font to a different size. // We have to fix up the font that we're handed back. @@ -423,7 +446,7 @@ FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const FloatRect boundingBox; #ifndef BUILDING_ON_TIGER boundingBox = CTFontGetBoundingRectsForGlyphs(m_platformData.ctFont(), - m_platformData.orientation() == Vertical ? kCTFontVerticalOrientation : kCTFontHorizontalOrientation, &glyph, 0, 1); + orientation() == Vertical ? kCTFontVerticalOrientation : kCTFontHorizontalOrientation, &glyph, 0, 1); boundingBox.setY(-boundingBox.bottom()); #else // FIXME: Custom fonts don't have NSFonts, so this function doesn't compute correct bounds for these on Tiger. @@ -441,7 +464,7 @@ FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { CGSize advance; - if (m_platformData.orientation() == Horizontal) { + if (orientation() == Horizontal || m_isBrokenIdeographFont) { NSFont* font = platformData().font(); float pointSize = platformData().m_size; CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize); -- cgit v1.1