Rebuild aura::Window's property system on top of mojo::View.

BUG=431047
R=sky@chromium.org

Review URL: https://codereview.chromium.org/732223002
diff --git a/mojo/services/public/cpp/view_manager/BUILD.gn b/mojo/services/public/cpp/view_manager/BUILD.gn
index 7192ef7..2c7c607 100644
--- a/mojo/services/public/cpp/view_manager/BUILD.gn
+++ b/mojo/services/public/cpp/view_manager/BUILD.gn
@@ -18,6 +18,7 @@
     "view_manager_context.h",
     "view_manager_delegate.h",
     "view_observer.h",
+    "view_property.h",
     "view_tracker.cc",
     "view_tracker.h",
     "window_manager_delegate.h",
diff --git a/mojo/services/public/cpp/view_manager/lib/view.cc b/mojo/services/public/cpp/view_manager/lib/view.cc
index 7de1861..bedac6b 100644
--- a/mojo/services/public/cpp/view_manager/lib/view.cc
+++ b/mojo/services/public/cpp/view_manager/lib/view.cc
@@ -228,8 +228,8 @@
   FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanged(this));
 }
 
-void View::SetProperty(const std::string& name,
-                       const std::vector<uint8_t>* value) {
+void View::SetSharedProperty(const std::string& name,
+                             const std::vector<uint8_t>* value) {
   std::vector<uint8_t> old_value;
   std::vector<uint8_t>* old_value_ptr = nullptr;
   auto it = properties_.find(name);
@@ -251,8 +251,9 @@
     properties_.erase(it);
   }
 
-  FOR_EACH_OBSERVER(ViewObserver, observers_,
-                    OnViewPropertyChanged(this, name, old_value_ptr, value));
+  FOR_EACH_OBSERVER(
+      ViewObserver, observers_,
+      OnViewSharedPropertyChanged(this, name, old_value_ptr, value));
 }
 
 bool View::IsDrawn() const {
@@ -389,6 +390,14 @@
   //             ViewManagerClientImpl.
   if (manager_)
     static_cast<ViewManagerClientImpl*>(manager_)->RemoveView(id_);
+
+  // Clear properties.
+  for (auto& pair : prop_map_) {
+    if (pair.second.deallocator)
+      (*pair.second.deallocator)(pair.second.value);
+  }
+  prop_map_.clear();
+
   FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroyed(this));
 }
 
@@ -403,6 +412,34 @@
       drawn_(false) {
 }
 
+int64 View::SetLocalPropertyInternal(const void* key,
+                                     const char* name,
+                                     PropertyDeallocator deallocator,
+                                     int64 value,
+                                     int64 default_value) {
+  int64 old = GetLocalPropertyInternal(key, default_value);
+  if (value == default_value) {
+    prop_map_.erase(key);
+  } else {
+    Value prop_value;
+    prop_value.name = name;
+    prop_value.value = value;
+    prop_value.deallocator = deallocator;
+    prop_map_[key] = prop_value;
+  }
+  FOR_EACH_OBSERVER(ViewObserver, observers_,
+                    OnViewLocalPropertyChanged(this, key, old));
+  return old;
+}
+
+int64 View::GetLocalPropertyInternal(const void* key,
+                                     int64 default_value) const {
+  std::map<const void*, Value>::const_iterator iter = prop_map_.find(key);
+  if (iter == prop_map_.end())
+    return default_value;
+  return iter->second.value;
+}
+
 void View::LocalDestroy() {
   delete this;
 }
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc
index 9421e18..e70ec2b 100644
--- a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc
@@ -341,7 +341,7 @@
     ViewPrivate(view).LocalSetDrawn(drawn);
 }
 
-void ViewManagerClientImpl::OnViewPropertyChanged(
+void ViewManagerClientImpl::OnViewSharedPropertyChanged(
     Id view_id,
     const String& name,
     Array<uint8_t> new_data) {
@@ -354,7 +354,7 @@
       data_ptr = &data;
     }
 
-    view->SetProperty(name, data_ptr);
+    view->SetSharedProperty(name, data_ptr);
   }
 }
 
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h
index 2c941da..2707ad6 100644
--- a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h
+++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h
@@ -108,9 +108,9 @@
   void OnViewDeleted(Id view_id) override;
   void OnViewVisibilityChanged(Id view_id, bool visible) override;
   void OnViewDrawnStateChanged(Id view_id, bool drawn) override;
-  void OnViewPropertyChanged(Id view_id,
-                             const String& name,
-                             Array<uint8_t> new_data) override;
+  void OnViewSharedPropertyChanged(Id view_id,
+                                   const String& name,
+                                   Array<uint8_t> new_data) override;
   void OnViewInputEvent(Id view_id,
                         EventPtr event,
                         const Callback<void()>& callback) override;
diff --git a/mojo/services/public/cpp/view_manager/tests/view_unittest.cc b/mojo/services/public/cpp/view_manager/tests/view_unittest.cc
index eb7276c..b0bd08b 100644
--- a/mojo/services/public/cpp/view_manager/tests/view_unittest.cc
+++ b/mojo/services/public/cpp/view_manager/tests/view_unittest.cc
@@ -9,6 +9,7 @@
 #include "mojo/services/public/cpp/view_manager/lib/view_private.h"
 #include "mojo/services/public/cpp/view_manager/util.h"
 #include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/cpp/view_manager/view_property.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace mojo {
@@ -102,6 +103,88 @@
   EXPECT_FALSE(v11.IsDrawn());
 }
 
+namespace {
+DEFINE_VIEW_PROPERTY_KEY(int, kIntKey, -2);
+DEFINE_VIEW_PROPERTY_KEY(const char*, kStringKey, "squeamish");
+}
+
+TEST_F(ViewTest, Property) {
+  TestView v;
+
+  // Non-existent properties should return the default values.
+  EXPECT_EQ(-2, v.GetLocalProperty(kIntKey));
+  EXPECT_EQ(std::string("squeamish"), v.GetLocalProperty(kStringKey));
+
+  // A set property value should be returned again (even if it's the default
+  // value).
+  v.SetLocalProperty(kIntKey, INT_MAX);
+  EXPECT_EQ(INT_MAX, v.GetLocalProperty(kIntKey));
+  v.SetLocalProperty(kIntKey, -2);
+  EXPECT_EQ(-2, v.GetLocalProperty(kIntKey));
+  v.SetLocalProperty(kIntKey, INT_MIN);
+  EXPECT_EQ(INT_MIN, v.GetLocalProperty(kIntKey));
+
+  v.SetLocalProperty(kStringKey, static_cast<const char*>(NULL));
+  EXPECT_EQ(NULL, v.GetLocalProperty(kStringKey));
+  v.SetLocalProperty(kStringKey, "squeamish");
+  EXPECT_EQ(std::string("squeamish"), v.GetLocalProperty(kStringKey));
+  v.SetLocalProperty(kStringKey, "ossifrage");
+  EXPECT_EQ(std::string("ossifrage"), v.GetLocalProperty(kStringKey));
+
+  // ClearProperty should restore the default value.
+  v.ClearLocalProperty(kIntKey);
+  EXPECT_EQ(-2, v.GetLocalProperty(kIntKey));
+  v.ClearLocalProperty(kStringKey);
+  EXPECT_EQ(std::string("squeamish"), v.GetLocalProperty(kStringKey));
+}
+
+namespace {
+
+class TestProperty {
+ public:
+  TestProperty() {}
+  virtual ~TestProperty() { last_deleted_ = this; }
+  static TestProperty* last_deleted() { return last_deleted_; }
+
+ private:
+  static TestProperty* last_deleted_;
+  DISALLOW_COPY_AND_ASSIGN(TestProperty);
+};
+
+TestProperty* TestProperty::last_deleted_ = NULL;
+
+DEFINE_OWNED_VIEW_PROPERTY_KEY(TestProperty, kOwnedKey, NULL);
+
+}  // namespace
+
+TEST_F(ViewTest, OwnedProperty) {
+  TestProperty* p3 = NULL;
+  {
+    TestView v;
+    EXPECT_EQ(NULL, v.GetLocalProperty(kOwnedKey));
+    TestProperty* p1 = new TestProperty();
+    v.SetLocalProperty(kOwnedKey, p1);
+    EXPECT_EQ(p1, v.GetLocalProperty(kOwnedKey));
+    EXPECT_EQ(NULL, TestProperty::last_deleted());
+
+    TestProperty* p2 = new TestProperty();
+    v.SetLocalProperty(kOwnedKey, p2);
+    EXPECT_EQ(p2, v.GetLocalProperty(kOwnedKey));
+    EXPECT_EQ(p1, TestProperty::last_deleted());
+
+    v.ClearLocalProperty(kOwnedKey);
+    EXPECT_EQ(NULL, v.GetLocalProperty(kOwnedKey));
+    EXPECT_EQ(p2, TestProperty::last_deleted());
+
+    p3 = new TestProperty();
+    v.SetLocalProperty(kOwnedKey, p3);
+    EXPECT_EQ(p3, v.GetLocalProperty(kOwnedKey));
+    EXPECT_EQ(p2, TestProperty::last_deleted());
+  }
+
+  EXPECT_EQ(p3, TestProperty::last_deleted());
+}
+
 // ViewObserver --------------------------------------------------------
 
 typedef testing::Test ViewObserverTest;
@@ -614,12 +697,12 @@
 
 namespace {
 
-class PropertyChangeObserver : public ViewObserver {
+class SharedPropertyChangeObserver : public ViewObserver {
  public:
-  explicit PropertyChangeObserver(View* view) : view_(view) {
+  explicit SharedPropertyChangeObserver(View* view) : view_(view) {
     view_->AddObserver(this);
   }
-  virtual ~PropertyChangeObserver() { view_->RemoveObserver(this); }
+  virtual ~SharedPropertyChangeObserver() { view_->RemoveObserver(this); }
 
   Changes GetAndClearChanges() {
     Changes changes;
@@ -629,16 +712,15 @@
 
  private:
   // Overridden from ViewObserver:
-  void OnViewPropertyChanged(View* view,
-                             const std::string& name,
-                             const std::vector<uint8_t>* old_data,
-                             const std::vector<uint8_t>* new_data) override {
+  void OnViewSharedPropertyChanged(
+      View* view,
+      const std::string& name,
+      const std::vector<uint8_t>* old_data,
+      const std::vector<uint8_t>* new_data) override {
     changes_.push_back(base::StringPrintf(
-        "view=%s property changed key=%s old_value=%s new_value=%s",
-        ViewIdToString(view->id()).c_str(),
-        name.c_str(),
-        VectorToString(old_data).c_str(),
-        VectorToString(new_data).c_str()));
+        "view=%s shared property changed key=%s old_value=%s new_value=%s",
+        ViewIdToString(view->id()).c_str(), name.c_str(),
+        VectorToString(old_data).c_str(), VectorToString(new_data).c_str()));
   }
 
   std::string VectorToString(const std::vector<uint8_t>* data) {
@@ -653,50 +735,110 @@
   View* view_;
   Changes changes_;
 
-  DISALLOW_COPY_AND_ASSIGN(PropertyChangeObserver);
+  DISALLOW_COPY_AND_ASSIGN(SharedPropertyChangeObserver);
 };
 
 }  // namespace
 
-TEST_F(ViewObserverTest, SetProperty) {
+TEST_F(ViewObserverTest, SetLocalProperty) {
   TestView v1;
   std::vector<uint8_t> one(1, '1');
 
   {
     // Change visibility from true to false and make sure we get notifications.
-    PropertyChangeObserver observer(&v1);
-    v1.SetProperty("one", &one);
+    SharedPropertyChangeObserver observer(&v1);
+    v1.SetSharedProperty("one", &one);
     Changes changes = observer.GetAndClearChanges();
     ASSERT_EQ(1U, changes.size());
-    EXPECT_EQ("view=0,1 property changed key=one old_value=NULL new_value=1",
-              changes[0]);
-    EXPECT_EQ(1U, v1.properties().size());
+    EXPECT_EQ(
+        "view=0,1 shared property changed key=one old_value=NULL new_value=1",
+        changes[0]);
+    EXPECT_EQ(1U, v1.shared_properties().size());
   }
   {
     // Set visible to existing value and verify no notifications.
-    PropertyChangeObserver observer(&v1);
-    v1.SetProperty("one", &one);
+    SharedPropertyChangeObserver observer(&v1);
+    v1.SetSharedProperty("one", &one);
     EXPECT_TRUE(observer.GetAndClearChanges().empty());
-    EXPECT_EQ(1U, v1.properties().size());
+    EXPECT_EQ(1U, v1.shared_properties().size());
   }
   {
     // Set the value to NULL to delete it.
     // Change visibility from true to false and make sure we get notifications.
-    PropertyChangeObserver observer(&v1);
-    v1.SetProperty("one", NULL);
+    SharedPropertyChangeObserver observer(&v1);
+    v1.SetSharedProperty("one", NULL);
     Changes changes = observer.GetAndClearChanges();
     ASSERT_EQ(1U, changes.size());
-    EXPECT_EQ("view=0,1 property changed key=one old_value=1 new_value=NULL",
-              changes[0]);
-    EXPECT_EQ(0U, v1.properties().size());
+    EXPECT_EQ(
+        "view=0,1 shared property changed key=one old_value=1 new_value=NULL",
+        changes[0]);
+    EXPECT_EQ(0U, v1.shared_properties().size());
   }
   {
     // Setting a null property to null shouldn't update us.
-    PropertyChangeObserver observer(&v1);
-    v1.SetProperty("one", NULL);
+    SharedPropertyChangeObserver observer(&v1);
+    v1.SetSharedProperty("one", NULL);
     EXPECT_TRUE(observer.GetAndClearChanges().empty());
-    EXPECT_EQ(0U, v1.properties().size());
+    EXPECT_EQ(0U, v1.shared_properties().size());
   }
 }
 
+namespace {
+
+typedef std::pair<const void*, intptr_t> PropertyChangeInfo;
+
+class LocalPropertyChangeObserver : public ViewObserver {
+ public:
+  explicit LocalPropertyChangeObserver(View* view)
+      : view_(view),
+        property_key_(nullptr),
+        old_property_value_(-1) {
+    view_->AddObserver(this);
+  }
+  virtual ~LocalPropertyChangeObserver() { view_->RemoveObserver(this); }
+
+  PropertyChangeInfo PropertyChangeInfoAndClear() {
+    PropertyChangeInfo result(property_key_, old_property_value_);
+    property_key_ = NULL;
+    old_property_value_ = -3;
+    return result;
+  }
+
+ private:
+  void OnViewLocalPropertyChanged(View* window,
+                                  const void* key,
+                                  intptr_t old) override {
+    property_key_ = key;
+    old_property_value_ = old;
+  }
+
+  View* view_;
+  const void* property_key_;
+  intptr_t old_property_value_;
+
+  DISALLOW_COPY_AND_ASSIGN(LocalPropertyChangeObserver);
+};
+
+}  // namespace
+
+TEST_F(ViewObserverTest, LocalPropertyChanged) {
+  TestView v1;
+  LocalPropertyChangeObserver o(&v1);
+
+  static const ViewProperty<int> prop = {-2};
+
+  v1.SetLocalProperty(&prop, 1);
+  EXPECT_EQ(PropertyChangeInfo(&prop, -2), o.PropertyChangeInfoAndClear());
+  v1.SetLocalProperty(&prop, -2);
+  EXPECT_EQ(PropertyChangeInfo(&prop, 1), o.PropertyChangeInfoAndClear());
+  v1.SetLocalProperty(&prop, 3);
+  EXPECT_EQ(PropertyChangeInfo(&prop, -2), o.PropertyChangeInfoAndClear());
+  v1.ClearLocalProperty(&prop);
+  EXPECT_EQ(PropertyChangeInfo(&prop, 3), o.PropertyChangeInfoAndClear());
+
+  // Sanity check to see if |PropertyChangeInfoAndClear| really clears.
+  EXPECT_EQ(PropertyChangeInfo(
+      reinterpret_cast<const void*>(NULL), -3), o.PropertyChangeInfoAndClear());
+}
+
 }  // namespace mojo
diff --git a/mojo/services/public/cpp/view_manager/view.h b/mojo/services/public/cpp/view_manager/view.h
index 72f044c..85011ba 100644
--- a/mojo/services/public/cpp/view_manager/view.h
+++ b/mojo/services/public/cpp/view_manager/view.h
@@ -23,6 +23,10 @@
 class ViewManager;
 class ViewObserver;
 
+// Defined in view_property.h (which we do not include)
+template <typename T>
+struct ViewProperty;
+
 // Views are owned by the ViewManager.
 // TODO(beng): Right now, you'll have to implement a ViewObserver to track
 //             destruction and NULL any pointers you have.
@@ -49,11 +53,41 @@
   bool visible() const { return visible_; }
   void SetVisible(bool value);
 
-  const std::map<std::string, std::vector<uint8_t>>& properties() const {
+  // Returns the set of string to bag of byte properties. These properties are
+  // shared with the view manager.
+  const std::map<std::string, std::vector<uint8_t>>& shared_properties() const {
     return properties_;
   }
   // Sets a property. If |data| is null, this property is deleted.
-  void SetProperty(const std::string& name, const std::vector<uint8_t>* data);
+  void SetSharedProperty(const std::string& name,
+                         const std::vector<uint8_t>* data);
+
+  // Sets the |value| of the given window |property|. Setting to the default
+  // value (e.g., NULL) removes the property. The caller is responsible for the
+  // lifetime of any object set as a property on the View.
+  //
+  // These properties are not visible to the view manager.
+  template <typename T>
+  void SetLocalProperty(const ViewProperty<T>* property, T value);
+
+  // Returns the value of the given window |property|.  Returns the
+  // property-specific default value if the property was not previously set.
+  //
+  // These properties are only visible in the current process and are not
+  // shared with other mojo services.
+  template <typename T>
+  T GetLocalProperty(const ViewProperty<T>* property) const;
+
+  // Sets the |property| to its default value. Useful for avoiding a cast when
+  // setting to NULL.
+  //
+  // These properties are only visible in the current process and are not
+  // shared with other mojo services.
+  template <typename T>
+  void ClearLocalProperty(const ViewProperty<T>* property);
+
+  // Type of a function to delete a property that this view owns.
+  typedef void (*PropertyDeallocator)(int64 value);
 
   // A View is drawn if the View and all its ancestors are visible and the
   // View is attached to the root.
@@ -101,6 +135,14 @@
 
   explicit View(ViewManager* manager);
 
+  // Called by the public {Set,Get,Clear}Property functions.
+  int64 SetLocalPropertyInternal(const void* key,
+                                 const char* name,
+                                 PropertyDeallocator deallocator,
+                                 int64 value,
+                                 int64 default_value);
+  int64 GetLocalPropertyInternal(const void* key, int64 default_value) const;
+
   void LocalDestroy();
   void LocalAddChild(View* child);
   void LocalRemoveChild(View* child);
@@ -126,6 +168,17 @@
   // state. This field is only used if the view has no parent (eg it's a root).
   bool drawn_;
 
+  // Value struct to keep the name and deallocator for this property.
+  // Key cannot be used for this purpose because it can be char* or
+  // WindowProperty<>.
+  struct Value {
+    const char* name;
+    int64 value;
+    PropertyDeallocator deallocator;
+  };
+
+  std::map<const void*, Value> prop_map_;
+
   DISALLOW_COPY_AND_ASSIGN(View);
 };
 
diff --git a/mojo/services/public/cpp/view_manager/view_observer.h b/mojo/services/public/cpp/view_manager/view_observer.h
index 480f660..fd31c97 100644
--- a/mojo/services/public/cpp/view_manager/view_observer.h
+++ b/mojo/services/public/cpp/view_manager/view_observer.h
@@ -63,10 +63,26 @@
   virtual void OnViewVisibilityChanging(View* view) {}
   virtual void OnViewVisibilityChanged(View* view) {}
 
-  virtual void OnViewPropertyChanged(View* view,
-                                     const std::string& name,
-                                     const std::vector<uint8_t>* old_data,
-                                     const std::vector<uint8_t>* new_data) {}
+  // Invoked when this View's shared properties have changed. This can either
+  // be caused by SetSharedProperty() being called locally, or by us receiving
+  // a mojo message that this property has changed. If this property has been
+  // added, |old_data| is null. If this property was removed, |new_data| is
+  // null.
+  virtual void OnViewSharedPropertyChanged(
+      View* view,
+      const std::string& name,
+      const std::vector<uint8_t>* old_data,
+      const std::vector<uint8_t>* new_data) {}
+
+  // Invoked when SetProperty() or ClearProperty() is called on the window.
+  // |key| is either a WindowProperty<T>* (SetProperty, ClearProperty). Either
+  // way, it can simply be compared for equality with the property
+  // constant. |old| is the old property value, which must be cast to the
+  // appropriate type before use.
+  virtual void OnViewLocalPropertyChanged(
+      View* view,
+      const void* key,
+      intptr_t old) {}
 
   virtual void OnViewEmbeddedAppDisconnected(View* view) {}
 
diff --git a/mojo/services/public/cpp/view_manager/view_property.h b/mojo/services/public/cpp/view_manager/view_property.h
new file mode 100644
index 0000000..11e5e49
--- /dev/null
+++ b/mojo/services/public/cpp/view_manager/view_property.h
@@ -0,0 +1,140 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_PROPERTY_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_PROPERTY_H_
+
+#include "base/basictypes.h"
+
+// This header should be included by code that defines ViewProperties. It
+// should not be included by code that only gets and sets ViewProperties.
+//
+// To define a new ViewProperty:
+//
+//  #include "mojo/services/public/cpp/view_manager/view_property.h"
+//
+//  DECLARE_EXPORTED_VIEW_PROPERTY_TYPE(FOO_EXPORT, MyType);
+//  namespace foo {
+//    // Use this to define an exported property that is premitive,
+//    // or a pointer you don't want automatically deleted.
+//    DEFINE_VIEW_PROPERTY_KEY(MyType, kMyKey, MyDefault);
+//
+//    // Use this to define an exported property whose value is a heap
+//    // allocated object, and has to be owned and freed by the view.
+//    DEFINE_OWNED_VIEW_PROPERTY_KEY(gfx::Rect, kRestoreBoundsKey, nullptr);
+//
+//    // Use this to define a non exported property that is primitive,
+//    // or a pointer you don't want to automatically deleted, and is used
+//    // only in a specific file. This will define the property in an unnamed
+//    // namespace which cannot be accessed from another file.
+//    DEFINE_LOCAL_VIEW_PROPERTY_KEY(MyType, kMyKey, MyDefault);
+//
+//  }  // foo namespace
+//
+// To define a new type used for ViewProperty.
+//
+//  // outside all namespaces:
+//  DECLARE_EXPORTED_VIEW_PROPERTY_TYPE(FOO_EXPORT, MyType)
+//
+// If a property type is not exported, use DECLARE_VIEW_PROPERTY_TYPE(MyType)
+// which is a shorthand for DECLARE_EXPORTED_VIEW_PROPERTY_TYPE(, MyType).
+
+namespace mojo {
+namespace {
+
+// No single new-style cast works for every conversion to/from int64, so we
+// need this helper class. A third specialization is needed for bool because
+// MSVC warning C4800 (forcing value to bool) is not suppressed by an explicit
+// cast (!).
+template <typename T>
+class ViewPropertyCaster {
+ public:
+  static int64 ToInt64(T x) { return static_cast<int64>(x); }
+  static T FromInt64(int64 x) { return static_cast<T>(x); }
+};
+template <typename T>
+class ViewPropertyCaster<T*> {
+ public:
+  static int64 ToInt64(T* x) { return reinterpret_cast<int64>(x); }
+  static T* FromInt64(int64 x) { return reinterpret_cast<T*>(x); }
+};
+template <>
+class ViewPropertyCaster<bool> {
+ public:
+  static int64 ToInt64(bool x) { return static_cast<int64>(x); }
+  static bool FromInt64(int64 x) { return x != 0; }
+};
+
+}  // namespace
+
+template <typename T>
+struct ViewProperty {
+  T default_value;
+  const char* name;
+  View::PropertyDeallocator deallocator;
+};
+
+template <typename T>
+void View::SetLocalProperty(const ViewProperty<T>* property, T value) {
+  int64 old = SetLocalPropertyInternal(
+      property, property->name,
+      value == property->default_value ? nullptr : property->deallocator,
+      ViewPropertyCaster<T>::ToInt64(value),
+      ViewPropertyCaster<T>::ToInt64(property->default_value));
+  if (property->deallocator &&
+      old != ViewPropertyCaster<T>::ToInt64(property->default_value)) {
+    (*property->deallocator)(old);
+  }
+}
+
+template <typename T>
+T View::GetLocalProperty(const ViewProperty<T>* property) const {
+  return ViewPropertyCaster<T>::FromInt64(GetLocalPropertyInternal(
+      property, ViewPropertyCaster<T>::ToInt64(property->default_value)));
+}
+
+template <typename T>
+void View::ClearLocalProperty(const ViewProperty<T>* property) {
+  SetLocalProperty(property, property->default_value);
+}
+
+}  // namespace mojo
+
+// Macros to instantiate the property getter/setter template functions.
+#define DECLARE_EXPORTED_VIEW_PROPERTY_TYPE(EXPORT, T)                         \
+  template EXPORT void mojo::View::SetLocalProperty(                           \
+      const mojo::ViewProperty<T>*, T);                                        \
+  template EXPORT T mojo::View::GetLocalProperty(const mojo::ViewProperty<T>*) \
+      const;                                                                   \
+  template EXPORT void mojo::View::ClearLocalProperty(                         \
+      const mojo::ViewProperty<T>*);
+#define DECLARE_VIEW_PROPERTY_TYPE(T) DECLARE_EXPORTED_VIEW_PROPERTY_TYPE(, T)
+
+#define DEFINE_VIEW_PROPERTY_KEY(TYPE, NAME, DEFAULT)                      \
+  COMPILE_ASSERT(sizeof(TYPE) <= sizeof(int64), property_type_too_large);  \
+  namespace {                                                              \
+  const mojo::ViewProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, nullptr}; \
+  }                                                                        \
+  const mojo::ViewProperty<TYPE>* const NAME = &NAME##_Value;
+
+#define DEFINE_LOCAL_VIEW_PROPERTY_KEY(TYPE, NAME, DEFAULT)                \
+  COMPILE_ASSERT(sizeof(TYPE) <= sizeof(int64), property_type_too_large);  \
+  namespace {                                                              \
+  const mojo::ViewProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, nullptr}; \
+  const mojo::ViewProperty<TYPE>* const NAME = &NAME##_Value;              \
+  }
+
+#define DEFINE_OWNED_VIEW_PROPERTY_KEY(TYPE, NAME, DEFAULT)            \
+  namespace {                                                          \
+  void Deallocator##NAME(int64 p) {                                    \
+    enum { type_must_be_complete = sizeof(TYPE) };                     \
+    delete mojo::ViewPropertyCaster<TYPE*>::FromInt64(p);              \
+  }                                                                    \
+  const mojo::ViewProperty<TYPE*> NAME##_Value = {DEFAULT,             \
+                                                  #NAME,               \
+                                                  &Deallocator##NAME}; \
+  }                                                                    \
+  const mojo::ViewProperty<TYPE*>* const NAME = &NAME##_Value;
+
+#endif  // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_PROPERTY_H_
diff --git a/mojo/services/public/interfaces/view_manager/view_manager.mojom b/mojo/services/public/interfaces/view_manager/view_manager.mojom
index 25e1f06..a79d4c6 100644
--- a/mojo/services/public/interfaces/view_manager/view_manager.mojom
+++ b/mojo/services/public/interfaces/view_manager/view_manager.mojom
@@ -191,7 +191,7 @@
 
   // Invoked when a view property is changed. If this change is a removal,
   // |new_data| is null.
-  OnViewPropertyChanged(uint32 view, string name, array<uint8>? new_data);
+  OnViewSharedPropertyChanged(uint32 view, string name, array<uint8>? new_data);
 
   // Invoked when an event is targeted at the specified view.
   OnViewInputEvent(uint32 view, mojo.Event event) => ();
diff --git a/mojo/services/view_manager/connection_manager.cc b/mojo/services/view_manager/connection_manager.cc
index b671b97..c80c1a3 100644
--- a/mojo/services/view_manager/connection_manager.cc
+++ b/mojo/services/view_manager/connection_manager.cc
@@ -273,7 +273,7 @@
   }
 }
 
-void ConnectionManager::OnViewPropertyChanged(
+void ConnectionManager::OnViewSharedPropertyChanged(
     const ServerView* view,
     const std::string& name,
     const std::vector<uint8_t>* new_data) {
diff --git a/mojo/services/view_manager/connection_manager.h b/mojo/services/view_manager/connection_manager.h
index 44e1b76..9f0e9b1 100644
--- a/mojo/services/view_manager/connection_manager.h
+++ b/mojo/services/view_manager/connection_manager.h
@@ -172,9 +172,10 @@
                        const ServerView* relative,
                        OrderDirection direction) override;
   void OnWillChangeViewVisibility(const ServerView* view) override;
-  void OnViewPropertyChanged(const ServerView* view,
-                             const std::string& name,
-                             const std::vector<uint8_t>* new_data) override;
+  void OnViewSharedPropertyChanged(
+      const ServerView* view,
+      const std::string& name,
+      const std::vector<uint8_t>* new_data) override;
 
   // WindowManagerInternalClient:
   void DispatchInputEventToView(Id transport_view_id,
diff --git a/mojo/services/view_manager/server_view.cc b/mojo/services/view_manager/server_view.cc
index 3bb9e64..937f437 100644
--- a/mojo/services/view_manager/server_view.cc
+++ b/mojo/services/view_manager/server_view.cc
@@ -139,7 +139,7 @@
     properties_.erase(it);
   }
 
-  delegate_->OnViewPropertyChanged(this, name, value);
+  delegate_->OnViewSharedPropertyChanged(this, name, value);
 }
 
 bool ServerView::IsDrawn(const ServerView* root) const {
diff --git a/mojo/services/view_manager/server_view_delegate.h b/mojo/services/view_manager/server_view_delegate.h
index 14f456a..2fbe0c0 100644
--- a/mojo/services/view_manager/server_view_delegate.h
+++ b/mojo/services/view_manager/server_view_delegate.h
@@ -40,9 +40,10 @@
 
   virtual void OnWillChangeViewVisibility(const ServerView* view) = 0;
 
-  virtual void OnViewPropertyChanged(const ServerView* view,
-                                     const std::string& name,
-                                     const std::vector<uint8_t>* new_data) = 0;
+  virtual void OnViewSharedPropertyChanged(
+      const ServerView* view,
+      const std::string& name,
+      const std::vector<uint8_t>* new_data) = 0;
 
  protected:
   virtual ~ServerViewDelegate() {}
diff --git a/mojo/services/view_manager/test_change_tracker.cc b/mojo/services/view_manager/test_change_tracker.cc
index 0a3a0c1..4241ecb 100644
--- a/mojo/services/view_manager/test_change_tracker.cc
+++ b/mojo/services/view_manager/test_change_tracker.cc
@@ -249,9 +249,9 @@
   AddChange(change);
 }
 
-void TestChangeTracker::OnViewPropertyChanged(Id view_id,
-                                              String name,
-                                              Array<uint8_t> data) {
+void TestChangeTracker::OnViewSharedPropertyChanged(Id view_id,
+                                                    String name,
+                                                    Array<uint8_t> data) {
   Change change;
   change.type = CHANGE_TYPE_PROPERTY_CHANGED;
   change.view_id = view_id;
diff --git a/mojo/services/view_manager/test_change_tracker.h b/mojo/services/view_manager/test_change_tracker.h
index 261c106..368c2ad 100644
--- a/mojo/services/view_manager/test_change_tracker.h
+++ b/mojo/services/view_manager/test_change_tracker.h
@@ -133,9 +133,9 @@
   void OnViewVisibilityChanged(Id view_id, bool visible);
   void OnViewDrawnStateChanged(Id view_id, bool drawn);
   void OnViewInputEvent(Id view_id, EventPtr event);
-  void OnViewPropertyChanged(Id view_id,
-                             String name,
-                             Array<uint8_t> data);
+  void OnViewSharedPropertyChanged(Id view_id,
+                                   String name,
+                                   Array<uint8_t> data);
   void DelegateEmbed(const String& url);
 
  private:
diff --git a/mojo/services/view_manager/view_manager_service_apptest.cc b/mojo/services/view_manager/view_manager_service_apptest.cc
index 231aece..b49221b 100644
--- a/mojo/services/view_manager/view_manager_service_apptest.cc
+++ b/mojo/services/view_manager/view_manager_service_apptest.cc
@@ -265,10 +265,10 @@
                         const Callback<void()>& callback) override {
     tracker()->OnViewInputEvent(view_id, event.Pass());
   }
-  void OnViewPropertyChanged(uint32_t view,
-                             const String& name,
-                             Array<uint8_t> new_data) override {
-    tracker_.OnViewPropertyChanged(view, name, new_data.Pass());
+  void OnViewSharedPropertyChanged(uint32_t view,
+                                   const String& name,
+                                   Array<uint8_t> new_data) override {
+    tracker_.OnViewSharedPropertyChanged(view, name, new_data.Pass());
   }
 
   TestChangeTracker tracker_;
diff --git a/mojo/services/view_manager/view_manager_service_impl.cc b/mojo/services/view_manager/view_manager_service_impl.cc
index f7842ad..e205fee 100644
--- a/mojo/services/view_manager/view_manager_service_impl.cc
+++ b/mojo/services/view_manager/view_manager_service_impl.cc
@@ -133,10 +133,8 @@
   if (new_data)
     data = Array<uint8_t>::From(*new_data);
 
-  client()->OnViewPropertyChanged(
-      ViewIdToTransportId(view->id()),
-      String(name),
-      data.Pass());
+  client()->OnViewSharedPropertyChanged(ViewIdToTransportId(view->id()),
+                                        String(name), data.Pass());
 }
 
 void ViewManagerServiceImpl::ProcessViewHierarchyChanged(
diff --git a/mojo/services/view_manager/view_manager_service_unittest.cc b/mojo/services/view_manager/view_manager_service_unittest.cc
index 92363f1..1079c93 100644
--- a/mojo/services/view_manager/view_manager_service_unittest.cc
+++ b/mojo/services/view_manager/view_manager_service_unittest.cc
@@ -73,10 +73,10 @@
   void OnViewDrawnStateChanged(uint32_t view, bool drawn) override {
     tracker_.OnViewDrawnStateChanged(view, drawn);
   }
-  void OnViewPropertyChanged(uint32_t view,
-                             const String& name,
-                             Array<uint8_t> new_data) override {
-    tracker_.OnViewPropertyChanged(view, name, new_data.Pass());
+  void OnViewSharedPropertyChanged(uint32_t view,
+                                   const String& name,
+                                   Array<uint8_t> new_data) override {
+    tracker_.OnViewSharedPropertyChanged(view, name, new_data.Pass());
   }
   void OnViewInputEvent(uint32_t view,
                         EventPtr event,
diff --git a/mojo/services/view_manager/view_manager_unittest.cc b/mojo/services/view_manager/view_manager_unittest.cc
index 1deeb22..5aa3715 100644
--- a/mojo/services/view_manager/view_manager_unittest.cc
+++ b/mojo/services/view_manager/view_manager_unittest.cc
@@ -397,10 +397,10 @@
                         const Callback<void()>& callback) override {
     tracker()->OnViewInputEvent(view_id, event.Pass());
   }
-  void OnViewPropertyChanged(uint32_t view,
-                             const String& name,
-                             Array<uint8_t> new_data) override {
-    tracker_.OnViewPropertyChanged(view, name, new_data.Pass());
+  void OnViewSharedPropertyChanged(uint32_t view,
+                                   const String& name,
+                                   Array<uint8_t> new_data) override {
+    tracker_.OnViewSharedPropertyChanged(view, name, new_data.Pass());
   }
 
  private: