This change allows users of PropertyObserver to compose a full keypath for each of the modified properties.
Also this contains a demonstration of how to implement this in CustomPropertyManager.

Change-Id: If4770e47b87ed76c98a85de3c235ab27c913dbc0
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/269696
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skottie/include/SkottieProperty.h b/modules/skottie/include/SkottieProperty.h
index 010bcaf..2355984 100644
--- a/modules/skottie/include/SkottieProperty.h
+++ b/modules/skottie/include/SkottieProperty.h
@@ -119,6 +119,8 @@
                                      const LazyHandle<TextPropertyHandle>&);
     virtual void onTransformProperty(const char node_name[],
                                      const LazyHandle<TransformPropertyHandle>&);
+    virtual void onEnterNode(const char node_name[]);
+    virtual void onLeavingNode(const char node_name[]);
 };
 
 } // namespace skottie
diff --git a/modules/skottie/src/SkottiePriv.h b/modules/skottie/src/SkottiePriv.h
index e764e70..c07144f 100644
--- a/modules/skottie/src/SkottiePriv.h
+++ b/modules/skottie/src/SkottiePriv.h
@@ -133,12 +133,15 @@
             : fBuilder(builder)
             , fPrevContext(builder->fPropertyObserverContext) {
             if (fBuilder->fPropertyObserver) {
-                this->updateContext(builder->fPropertyObserver.get(), obj);
+                auto observer = builder->fPropertyObserver.get();
+                this->updateContext(observer, obj);
+                observer->onEnterNode(fBuilder->fPropertyObserverContext);
             }
         }
 
         ~AutoPropertyTracker() {
             if (fBuilder->fPropertyObserver) {
+                fBuilder->fPropertyObserver->onLeavingNode(fBuilder->fPropertyObserverContext);
                 fBuilder->fPropertyObserverContext = fPrevContext;
             }
         }
diff --git a/modules/skottie/src/SkottieProperty.cpp b/modules/skottie/src/SkottieProperty.cpp
index a323b08..9702bf1 100644
--- a/modules/skottie/src/SkottieProperty.cpp
+++ b/modules/skottie/src/SkottieProperty.cpp
@@ -124,4 +124,8 @@
 void PropertyObserver::onTransformProperty(const char[],
                                            const LazyHandle<TransformPropertyHandle>&) {}
 
-} // namespace skottie
+void PropertyObserver::onEnterNode(const char node_name[]) {}
+
+void PropertyObserver::onLeavingNode(const char node_name[]) {}
+
+}  // namespace skottie
diff --git a/modules/skottie/src/SkottieTest.cpp b/modules/skottie/src/SkottieTest.cpp
index 7e7b317..c5317aa 100644
--- a/modules/skottie/src/SkottieTest.cpp
+++ b/modules/skottie/src/SkottieTest.cpp
@@ -154,6 +154,7 @@
         void onColorProperty(const char node_name[],
                 const PropertyObserver::LazyHandle<ColorPropertyHandle>& lh) override {
             fColors.push_back({SkString(node_name), lh()});
+            fColorsWithFullKeypath.push_back({SkString(fCurrentNode.c_str()), lh()});
         }
 
         void onOpacityProperty(const char node_name[],
@@ -171,16 +172,33 @@
             fTransforms.push_back({SkString(node_name), lh()});
         }
 
+        void onEnterNode(const char node_name[]) override {
+            fCurrentNode = fCurrentNode.empty() ? node_name : fCurrentNode + "." + node_name;
+        }
+
+        void onLeavingNode(const char node_name[]) override {
+            auto length = strlen(node_name);
+            fCurrentNode =
+                    fCurrentNode.length() > length
+                            ? fCurrentNode.substr(0, fCurrentNode.length() - strlen(node_name) - 1)
+                            : "";
+        }
+
         const std::vector<ColorInfo>& colors() const { return fColors; }
         const std::vector<OpacityInfo>& opacities() const { return fOpacities; }
         const std::vector<TextInfo>& texts() const { return fTexts; }
         const std::vector<TransformInfo>& transforms() const { return fTransforms; }
+        const std::vector<ColorInfo>& colorsWithFullKeypath() const {
+            return fColorsWithFullKeypath;
+        }
 
     private:
         std::vector<ColorInfo>     fColors;
         std::vector<OpacityInfo>   fOpacities;
         std::vector<TextInfo>      fTexts;
         std::vector<TransformInfo> fTransforms;
+        std::string                fCurrentNode;
+        std::vector<ColorInfo>     fColorsWithFullKeypath;
     };
 
     // Returns a single specified typeface for all requests.
@@ -246,6 +264,13 @@
     REPORTER_ASSERT(reporter, colors[1].node_name.equals("fill_effect_0"));
     REPORTER_ASSERT(reporter, colors[1].handle->get() == 0xff00ff00);
 
+    const auto& colorsWithFullKeypath = observer->colorsWithFullKeypath();
+    REPORTER_ASSERT(reporter, colorsWithFullKeypath.size() == 2);
+    REPORTER_ASSERT(reporter, colorsWithFullKeypath[0].node_name.equals("layer_0.fill_0"));
+    REPORTER_ASSERT(reporter, colorsWithFullKeypath[0].handle->get() == 0xffff0000);
+    REPORTER_ASSERT(reporter, colorsWithFullKeypath[1].node_name.equals("layer_0.fill_effect_0"));
+    REPORTER_ASSERT(reporter, colorsWithFullKeypath[1].handle->get() == 0xff00ff00);
+
     const auto& opacities = observer->opacities();
     REPORTER_ASSERT(reporter, opacities.size() == 3);
     REPORTER_ASSERT(reporter, opacities[0].node_name.equals("shape_transform_0"));
diff --git a/modules/skottie/utils/SkottieUtils.cpp b/modules/skottie/utils/SkottieUtils.cpp
index 9c9ba56..41b071c 100644
--- a/modules/skottie/utils/SkottieUtils.cpp
+++ b/modules/skottie/utils/SkottieUtils.cpp
@@ -15,26 +15,37 @@
 
     void onColorProperty(const char node_name[],
                          const LazyHandle<skottie::ColorPropertyHandle>& c) override {
-        const auto key = fMgr->acceptKey(node_name);
-        if (!key.empty()) {
-            fMgr->fColorMap[key].push_back(c());
-        }
+        const auto markedKey = fMgr->acceptKey(node_name);
+        const auto key = markedKey.empty() ? markedKey : fMgr->fCurrentNode + ".Color";
+        fMgr->fColorMap[key].push_back(c());
     }
 
     void onOpacityProperty(const char node_name[],
                            const LazyHandle<skottie::OpacityPropertyHandle>& o) override {
-        const auto key = fMgr->acceptKey(node_name);
-        if (!key.empty()) {
-            fMgr->fOpacityMap[key].push_back(o());
-        }
+        const auto markedKey = fMgr->acceptKey(node_name);
+        const auto key = markedKey.empty() ? markedKey : fMgr->fCurrentNode + ".Opacity";
+        fMgr->fOpacityMap[key].push_back(o());
     }
 
     void onTransformProperty(const char node_name[],
                              const LazyHandle<skottie::TransformPropertyHandle>& t) override {
-        const auto key = fMgr->acceptKey(node_name);
-        if (!key.empty()) {
-            fMgr->fTransformMap[key].push_back(t());
-        }
+        const auto markedKey = fMgr->acceptKey(node_name);
+        const auto key = markedKey.empty() ? markedKey : fMgr->fCurrentNode + ".Transform";
+        fMgr->fTransformMap[key].push_back(t());
+    }
+
+    void onEnterNode(const char node_name[]) override {
+        fMgr->fCurrentNode =
+                fMgr->fCurrentNode.empty() ? node_name : fMgr->fCurrentNode + "." + node_name;
+    }
+
+    void onLeavingNode(const char node_name[]) override {
+        auto length = strlen(node_name);
+        fMgr->fCurrentNode =
+                fMgr->fCurrentNode.length() > length
+                        ? fMgr->fCurrentNode.substr(
+                                  0, fMgr->fCurrentNode.length() - strlen(node_name) - 1)
+                        : "";
     }
 
     void onTextProperty(const char node_name[],
diff --git a/modules/skottie/utils/SkottieUtils.h b/modules/skottie/utils/SkottieUtils.h
index 0aa6669..5cf8982 100644
--- a/modules/skottie/utils/SkottieUtils.h
+++ b/modules/skottie/utils/SkottieUtils.h
@@ -100,6 +100,7 @@
     PropMap<skottie::TransformPropertyHandle> fTransformMap;
     PropMap<skottie::TextPropertyHandle>      fTextMap;
     std::vector<MarkerInfo>                   fMarkers;
+    std::string                               fCurrentNode;
 };
 
 } // namespace skottie_utils