Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010 Google Inc. All rights reserved. | 2 * Copyright (C) 2010 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 #ifndef ScriptWrappable_h | 31 #ifndef ScriptWrappable_h |
| 32 #define ScriptWrappable_h | 32 #define ScriptWrappable_h |
| 33 | 33 |
| 34 #include "bindings/core/v8/WrapperTypeInfo.h" | 34 #include "bindings/core/v8/WrapperTypeInfo.h" |
| 35 #include "platform/ScriptForbiddenScope.h" | 35 #include "platform/ScriptForbiddenScope.h" |
| 36 #include "platform/heap/Handle.h" | 36 #include "platform/heap/Handle.h" |
| 37 #include <v8.h> | 37 #include <v8.h> |
| 38 | 38 |
| 39 // Helper to call webCoreInitializeScriptWrappableForInterface in the global nam espace. | |
| 40 template <class C> inline void initializeScriptWrappableHelper(C* object) | |
| 41 { | |
| 42 void webCoreInitializeScriptWrappableForInterface(C*); | |
| 43 webCoreInitializeScriptWrappableForInterface(object); | |
| 44 } | |
| 45 | |
| 46 namespace blink { | 39 namespace blink { |
| 47 | 40 |
| 48 /** | 41 /** |
| 49 * The base class of all wrappable objects. | 42 * The base class of all wrappable objects. |
| 50 * | 43 * |
| 51 * This class provides the internal pointer to be stored in the wrapper objects, | 44 * This class provides the internal pointer to be stored in the wrapper objects, |
| 52 * and its conversions from / to the DOM instances. | 45 * and its conversions from / to the DOM instances. |
| 53 * | 46 * |
| 54 * Note that this class must not have vtbl (any virtual function) or any member | 47 * Note that this class must not have vtbl (any virtual function) or any member |
| 55 * variable which increase the size of instances. Some of the classes sensitive | 48 * variable which increase the size of instances. Some of the classes sensitive |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 100 { | 93 { |
| 101 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(object.IsEmpty() | 94 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(object.IsEmpty() |
| 102 || object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectInde x) == toScriptWrappableBase()); | 95 || object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectInde x) == toScriptWrappableBase()); |
| 103 } | 96 } |
| 104 }; | 97 }; |
| 105 | 98 |
| 106 /** | 99 /** |
| 107 * ScriptWrappable wraps a V8 object and its WrapperTypeInfo. | 100 * ScriptWrappable wraps a V8 object and its WrapperTypeInfo. |
| 108 * | 101 * |
| 109 * ScriptWrappable acts much like a v8::Persistent<> in that it keeps a | 102 * ScriptWrappable acts much like a v8::Persistent<> in that it keeps a |
| 110 * V8 object alive. Under the hood, however, it keeps either a TypeInfo | 103 * V8 object alive. |
| 111 * object or an actual v8 persistent (or is empty). | |
| 112 * | |
| 113 * The physical state space of ScriptWrappable is: | |
| 114 * - uintptr_t m_wrapperOrTypeInfo; | |
| 115 * - if 0: the ScriptWrappable is uninitialized/empty. | |
| 116 * - if even: a pointer to blink::TypeInfo | |
| 117 * - if odd: a pointer to v8::Persistent<v8::Object> + 1. | |
| 118 * | |
| 119 * In other words, one integer represents one of two object pointers, | |
| 120 * depending on its least signficiant bit, plus an uninitialized state. | |
| 121 * This class is meant to mask the logistics behind this. | |
| 122 * | |
| 123 * typeInfo() and newLocalWrapper will return appropriate values (possibly | |
| 124 * 0/empty) in all physical states. | |
| 125 * | 104 * |
| 126 * The state transitions are: | 105 * The state transitions are: |
| 127 * - new: an empty and invalid ScriptWrappable. | 106 * - new: an empty ScriptWrappable. |
| 128 * - init (to be called by all subclasses in their constructor): | |
| 129 * needs to call setTypeInfo | |
| 130 * - setTypeInfo: install a WrapperTypeInfo | |
| 131 * - setWrapper: install a v8::Persistent (or empty) | 107 * - setWrapper: install a v8::Persistent (or empty) |
| 132 * - disposeWrapper (via setWeakCallback, triggered by V8 garbage collecter): | 108 * - disposeWrapper (via setWeakCallback, triggered by V8 garbage collecter): |
| 133 * remove v8::Persistent and install a TypeInfo of the previous value. | 109 * remove v8::Persistent and become empty. |
| 134 */ | 110 */ |
| 135 class ScriptWrappable : public ScriptWrappableBase { | 111 class ScriptWrappable : public ScriptWrappableBase { |
| 136 public: | 112 public: |
| 137 ScriptWrappable() : m_wrapperOrTypeInfo(0) { } | 113 ScriptWrappable() : m_wrapper(0) { } |
| 138 | |
| 139 // Wrappables need to be initialized with their most derrived type for which | |
| 140 // bindings exist, in much the same way that certain other types need to be | |
| 141 // adopted and so forth. The overloaded initializeScriptWrappableForInterfac e() | |
| 142 // functions are implemented by the generated V8 bindings code. Declaring th e | |
| 143 // extern function in the template avoids making a centralized header of all | |
| 144 // the bindings in the universe. C++11's extern template feature may provide | |
| 145 // a cleaner solution someday. | |
| 146 template <class C> static void init(C* object) | |
| 147 { | |
| 148 initializeScriptWrappableHelper(object); | |
| 149 } | |
| 150 | 114 |
| 151 // Returns the WrapperTypeInfo of the instance. | 115 // Returns the WrapperTypeInfo of the instance. |
| 152 // | 116 // |
| 153 // This method must be overridden by DEFINE_WRAPPERTYPEINFO macro. | 117 // This method must be overridden by DEFINE_WRAPPERTYPEINFO macro. |
| 154 virtual const WrapperTypeInfo* wrapperTypeInfo() const = 0; | 118 virtual const WrapperTypeInfo* wrapperTypeInfo() const = 0; |
| 155 | 119 |
| 156 // Creates and returns a new wrapper object. | 120 // Creates and returns a new wrapper object. |
| 157 virtual v8::Handle<v8::Object> wrap(v8::Handle<v8::Object> creationContext, v8::Isolate*); | 121 virtual v8::Handle<v8::Object> wrap(v8::Handle<v8::Object> creationContext, v8::Isolate*); |
| 158 | 122 |
| 159 void setWrapper(v8::Handle<v8::Object> wrapper, v8::Isolate* isolate, const WrapperTypeInfo* wrapperTypeInfo) | 123 void setWrapper(v8::Handle<v8::Object> wrapper, v8::Isolate* isolate, const WrapperTypeInfo* wrapperTypeInfo) |
| 160 { | 124 { |
| 161 ASSERT(!containsWrapper()); | 125 ASSERT(!containsWrapper()); |
| 162 if (!*wrapper) { | 126 if (!*wrapper) { |
| 163 m_wrapperOrTypeInfo = 0; | 127 m_wrapper = 0; |
| 164 return; | 128 return; |
| 165 } | 129 } |
| 166 v8::Persistent<v8::Object> persistent(isolate, wrapper); | 130 v8::Persistent<v8::Object> persistent(isolate, wrapper); |
| 167 wrapperTypeInfo->configureWrapper(&persistent); | 131 wrapperTypeInfo->configureWrapper(&persistent); |
| 168 persistent.SetWeak(this, &setWeakCallback); | 132 persistent.SetWeak(this, &setWeakCallback); |
| 169 m_wrapperOrTypeInfo = reinterpret_cast<uintptr_t>(persistent.ClearAndLea k()) | 1; | 133 m_wrapper = persistent.ClearAndLeak(); |
| 170 ASSERT(containsWrapper()); | 134 ASSERT(containsWrapper()); |
| 171 } | 135 } |
| 172 | 136 |
| 173 v8::Local<v8::Object> newLocalWrapper(v8::Isolate* isolate) const | 137 v8::Local<v8::Object> newLocalWrapper(v8::Isolate* isolate) const |
| 174 { | 138 { |
| 175 v8::Persistent<v8::Object> persistent; | 139 v8::Persistent<v8::Object> persistent; |
| 176 getPersistent(&persistent); | 140 getPersistent(&persistent); |
| 177 return v8::Local<v8::Object>::New(isolate, persistent); | 141 return v8::Local<v8::Object>::New(isolate, persistent); |
| 178 } | 142 } |
| 179 | 143 |
| 180 const WrapperTypeInfo* typeInfo() | |
| 181 { | |
| 182 if (containsTypeInfo()) | |
| 183 return reinterpret_cast<const WrapperTypeInfo*>(m_wrapperOrTypeInfo) ; | |
| 184 | |
| 185 if (containsWrapper()) { | |
| 186 v8::Persistent<v8::Object> persistent; | |
| 187 getPersistent(&persistent); | |
| 188 return toWrapperTypeInfo(persistent); | |
| 189 } | |
| 190 | |
| 191 return 0; | |
| 192 } | |
| 193 | |
| 194 void setTypeInfo(const WrapperTypeInfo* typeInfo) | |
| 195 { | |
| 196 m_wrapperOrTypeInfo = reinterpret_cast<uintptr_t>(typeInfo); | |
| 197 ASSERT(containsTypeInfo()); | |
| 198 } | |
| 199 | |
| 200 bool isEqualTo(const v8::Local<v8::Object>& other) const | 144 bool isEqualTo(const v8::Local<v8::Object>& other) const |
| 201 { | 145 { |
| 202 v8::Persistent<v8::Object> persistent; | 146 v8::Persistent<v8::Object> persistent; |
| 203 getPersistent(&persistent); | 147 getPersistent(&persistent); |
| 204 return persistent == other; | 148 return persistent == other; |
| 205 } | 149 } |
| 206 | 150 |
| 207 static bool wrapperCanBeStoredInObject(const void*) { return false; } | 151 static bool wrapperCanBeStoredInObject(const void*) { return false; } |
| 208 static bool wrapperCanBeStoredInObject(const ScriptWrappable*) { return true ; } | 152 static bool wrapperCanBeStoredInObject(const ScriptWrappable*) { return true ; } |
| 209 | 153 |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 224 getPersistent(&persistent); | 168 getPersistent(&persistent); |
| 225 returnValue.Set(persistent); | 169 returnValue.Set(persistent); |
| 226 return containsWrapper(); | 170 return containsWrapper(); |
| 227 } | 171 } |
| 228 | 172 |
| 229 void markAsDependentGroup(ScriptWrappable* groupRoot, v8::Isolate* isolate) | 173 void markAsDependentGroup(ScriptWrappable* groupRoot, v8::Isolate* isolate) |
| 230 { | 174 { |
| 231 ASSERT(containsWrapper()); | 175 ASSERT(containsWrapper()); |
| 232 ASSERT(groupRoot && groupRoot->containsWrapper()); | 176 ASSERT(groupRoot && groupRoot->containsWrapper()); |
| 233 | 177 |
| 234 v8::UniqueId groupId(groupRoot->m_wrapperOrTypeInfo); | 178 v8::UniqueId groupId(reinterpret_cast<intptr_t>(groupRoot->m_wrapper)); |
| 235 v8::Persistent<v8::Object> wrapper; | 179 v8::Persistent<v8::Object> wrapper; |
| 236 getPersistent(&wrapper); | 180 getPersistent(&wrapper); |
| 237 wrapper.MarkPartiallyDependent(); | 181 wrapper.MarkPartiallyDependent(); |
| 238 isolate->SetObjectGroupId(v8::Persistent<v8::Value>::Cast(wrapper), grou pId); | 182 isolate->SetObjectGroupId(v8::Persistent<v8::Value>::Cast(wrapper), grou pId); |
| 239 } | 183 } |
| 240 | 184 |
| 241 void setReference(const v8::Persistent<v8::Object>& parent, v8::Isolate* iso late) | 185 void setReference(const v8::Persistent<v8::Object>& parent, v8::Isolate* iso late) |
| 242 { | 186 { |
| 243 v8::Persistent<v8::Object> persistent; | 187 v8::Persistent<v8::Object> persistent; |
| 244 getPersistent(&persistent); | 188 getPersistent(&persistent); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 257 static void assertWrapperSanity(void* object, T* objectAsT) | 201 static void assertWrapperSanity(void* object, T* objectAsT) |
| 258 { | 202 { |
| 259 ASSERT_NOT_REACHED(); | 203 ASSERT_NOT_REACHED(); |
| 260 } | 204 } |
| 261 | 205 |
| 262 template<typename V8T, typename T> | 206 template<typename V8T, typename T> |
| 263 static void assertWrapperSanity(ScriptWrappable* object, T* objectAsT) | 207 static void assertWrapperSanity(ScriptWrappable* object, T* objectAsT) |
| 264 { | 208 { |
| 265 ASSERT(object); | 209 ASSERT(object); |
| 266 ASSERT(objectAsT); | 210 ASSERT(objectAsT); |
| 267 v8::Object* value = object->getRawValue(); | 211 v8::Object* value = object->m_wrapper; |
| 268 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(value == 0 | 212 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(value == 0 |
| 269 || value->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex ) == V8T::toScriptWrappableBase(objectAsT)); | 213 || value->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex ) == V8T::toScriptWrappableBase(objectAsT)); |
| 270 } | 214 } |
| 271 | 215 |
| 272 using ScriptWrappableBase::assertWrapperSanity; | 216 using ScriptWrappableBase::assertWrapperSanity; |
| 273 | 217 |
| 274 inline bool containsWrapper() const { return (m_wrapperOrTypeInfo & 1); } | 218 bool containsWrapper() const { return m_wrapper != 0; } |
|
haraken
2014/09/05 16:46:16
return !!m_wrapper;
Blink's coding style doesn't
Yuki
2014/09/06 09:40:06
Done.
| |
| 275 inline bool containsTypeInfo() const { return m_wrapperOrTypeInfo && !(m_wra pperOrTypeInfo & 1); } | |
| 276 | 219 |
| 277 #if !ENABLE(OILPAN) | 220 #if !ENABLE(OILPAN) |
| 278 protected: | 221 protected: |
| 279 virtual ~ScriptWrappable() | 222 virtual ~ScriptWrappable() |
| 280 { | 223 { |
| 281 // We must not get deleted as long as we contain a wrapper. If this happ ens, we screwed up ref | 224 // We must not get deleted as long as we contain a wrapper. If this happ ens, we screwed up ref |
| 282 // counting somewhere. Crash here instead of crashing during a later gc cycle. | 225 // counting somewhere. Crash here instead of crashing during a later gc cycle. |
| 283 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!containsWrapper()); | 226 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!containsWrapper()); |
| 284 ASSERT(m_wrapperOrTypeInfo); // Assert initialization via init() even if not subsequently wrapped. | 227 m_wrapper = 0; // Break UAF attempts to wrap. |
| 285 m_wrapperOrTypeInfo = 0; // Break UAF attempts to wrap. | |
| 286 } | 228 } |
| 287 #endif | 229 #endif |
| 288 // With Oilpan we don't need a ScriptWrappable destructor. | 230 // With Oilpan we don't need a ScriptWrappable destructor. |
| 289 // | 231 // |
| 290 // - 'RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!containsWrapper())' is not n eeded | 232 // - 'RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!containsWrapper())' is not n eeded |
| 291 // because Oilpan is not using reference counting at all. If containsWrapper () is true, | 233 // because Oilpan is not using reference counting at all. If containsWrapper () is true, |
| 292 // it means that ScriptWrappable still has a wrapper. In this case, the dest ructor | 234 // it means that ScriptWrappable still has a wrapper. In this case, the dest ructor |
| 293 // must not be called since the wrapper has a persistent handle back to this ScriptWrappable object. | 235 // must not be called since the wrapper has a persistent handle back to this ScriptWrappable object. |
| 294 // Assuming that Oilpan's GC is correct (If we cannot assume this, a lot of more things are | 236 // Assuming that Oilpan's GC is correct (If we cannot assume this, a lot of more things are |
| 295 // already broken), we must not hit the RELEASE_ASSERT. | 237 // already broken), we must not hit the RELEASE_ASSERT. |
| 296 // | 238 // |
| 297 // - 'm_wrapperOrTypeInfo = 0' is not needed because Oilpan's GC zeroes out memory when | 239 // - 'm_wrapper = 0' is not needed because Oilpan's GC zeroes out memory whe n |
| 298 // the memory is collected and added to a free list. | 240 // the memory is collected and added to a free list. |
| 299 | 241 |
| 300 private: | 242 private: |
| 301 void getPersistent(v8::Persistent<v8::Object>* persistent) const | 243 void getPersistent(v8::Persistent<v8::Object>* persistent) const |
| 302 { | 244 { |
| 303 ASSERT(persistent); | 245 ASSERT(persistent); |
| 304 | 246 |
| 305 // Horrible and super unsafe: Cast the Persistent to an Object*, so | 247 // Horrible and super unsafe: Cast the Persistent to an Object*, so |
| 306 // that we can inject the wrapped value. This only works because | 248 // that we can inject the wrapped value. This only works because |
| 307 // we previously 'stole' the object pointer from a Persistent in | 249 // we previously 'stole' the object pointer from a Persistent in |
| 308 // the setWrapper() method. | 250 // the setWrapper() method. |
| 309 *reinterpret_cast<v8::Object**>(persistent) = getRawValue(); | 251 *reinterpret_cast<v8::Object**>(persistent) = m_wrapper; |
| 310 } | 252 } |
| 311 | 253 |
| 312 inline v8::Object* getRawValue() const | 254 void disposeWrapper(v8::Local<v8::Object> wrapper) |
| 313 { | |
| 314 v8::Object* object = containsWrapper() ? reinterpret_cast<v8::Object*>(m _wrapperOrTypeInfo & ~1) : 0; | |
| 315 return object; | |
| 316 } | |
| 317 | |
| 318 inline void disposeWrapper(v8::Local<v8::Object> wrapper) | |
| 319 { | 255 { |
| 320 ASSERT(containsWrapper()); | 256 ASSERT(containsWrapper()); |
| 321 | 257 |
| 322 v8::Persistent<v8::Object> persistent; | 258 v8::Persistent<v8::Object> persistent; |
| 323 getPersistent(&persistent); | 259 getPersistent(&persistent); |
| 324 | 260 |
| 325 ASSERT(wrapper == persistent); | 261 ASSERT(wrapper == persistent); |
| 326 persistent.Reset(); | 262 persistent.Reset(); |
| 327 setTypeInfo(toWrapperTypeInfo(wrapper)); | 263 m_wrapper = 0; |
| 328 } | 264 } |
| 329 | 265 |
| 330 // If zero, then this contains nothing, otherwise: | |
| 331 // If the bottom bit it set, then this contains a pointer to a wrapper obj ect in the remainging bits. | |
| 332 // If the bottom bit is clear, then this contains a pointer to the wrapper type info in the remaining bits. | |
| 333 uintptr_t m_wrapperOrTypeInfo; | |
| 334 | |
| 335 static void setWeakCallback(const v8::WeakCallbackData<v8::Object, ScriptWra ppable>& data) | 266 static void setWeakCallback(const v8::WeakCallbackData<v8::Object, ScriptWra ppable>& data) |
| 336 { | 267 { |
| 337 v8::Persistent<v8::Object> persistent; | 268 v8::Persistent<v8::Object> persistent; |
| 338 data.GetParameter()->getPersistent(&persistent); | 269 data.GetParameter()->getPersistent(&persistent); |
| 339 ASSERT(persistent == data.GetValue()); | 270 ASSERT(persistent == data.GetValue()); |
| 340 data.GetParameter()->disposeWrapper(data.GetValue()); | 271 data.GetParameter()->disposeWrapper(data.GetValue()); |
| 341 | 272 |
| 342 // FIXME: I noticed that 50%~ of minor GC cycle times can be consumed | 273 // FIXME: I noticed that 50%~ of minor GC cycle times can be consumed |
| 343 // inside data.GetParameter()->deref(), which causes Node destructions. We should | 274 // inside data.GetParameter()->deref(), which causes Node destructions. We should |
| 344 // make Node destructions incremental. | 275 // make Node destructions incremental. |
| 345 releaseObject(data.GetValue()); | 276 releaseObject(data.GetValue()); |
| 346 } | 277 } |
| 278 | |
| 279 v8::Object* m_wrapper; | |
| 347 }; | 280 }; |
| 348 | 281 |
| 349 // Defines 'wrapperTypeInfo' virtual method which returns the WrapperTypeInfo of | 282 // Defines 'wrapperTypeInfo' virtual method which returns the WrapperTypeInfo of |
| 350 // the instance. Also declares a static member of type WrapperTypeInfo, of which | 283 // the instance. Also declares a static member of type WrapperTypeInfo, of which |
| 351 // the definition is given by the IDL code generator. | 284 // the definition is given by the IDL code generator. |
| 352 // | 285 // |
| 353 // Every DOM Class T must meet either of the following conditions: | 286 // Every DOM Class T must meet either of the following conditions: |
| 354 // - T.idl inherits from [NotScriptWrappable]. | 287 // - T.idl inherits from [NotScriptWrappable]. |
| 355 // - T inherits from ScriptWrappable and has DEFINE_WRAPPERTYPEINFO(). | 288 // - T inherits from ScriptWrappable and has DEFINE_WRAPPERTYPEINFO(). |
| 356 // | 289 // |
| 357 // If a DOM class T does not inherit from ScriptWrappable, you have to write | 290 // If a DOM class T does not inherit from ScriptWrappable, you have to write |
| 358 // [NotScriptWrappable] in the IDL file as an extended attribute in order to let | 291 // [NotScriptWrappable] in the IDL file as an extended attribute in order to let |
| 359 // IDL code generator know that T does not inherit from ScriptWrappable. Note | 292 // IDL code generator know that T does not inherit from ScriptWrappable. Note |
| 360 // that [NotScriptWrappable] is inheritable. | 293 // that [NotScriptWrappable] is inheritable. |
| 361 // | 294 // |
| 362 // All the derived classes of ScriptWrappable, regardless of directly or | 295 // All the derived classes of ScriptWrappable, regardless of directly or |
| 363 // indirectly, must write this macro in the class definition. | 296 // indirectly, must write this macro in the class definition. |
| 364 #define DEFINE_WRAPPERTYPEINFO() \ | 297 #define DEFINE_WRAPPERTYPEINFO() \ |
| 365 public: \ | 298 public: \ |
| 366 virtual const WrapperTypeInfo* wrapperTypeInfo() const OVERRIDE \ | 299 virtual const WrapperTypeInfo* wrapperTypeInfo() const OVERRIDE \ |
| 367 { \ | 300 { \ |
| 368 return &s_wrapperTypeInfo; \ | 301 return &s_wrapperTypeInfo; \ |
| 369 } \ | 302 } \ |
| 370 private: \ | 303 private: \ |
| 371 static const WrapperTypeInfo& s_wrapperTypeInfo | 304 static const WrapperTypeInfo& s_wrapperTypeInfo |
| 372 | 305 |
| 373 } // namespace blink | 306 } // namespace blink |
| 374 | 307 |
| 375 #endif // ScriptWrappable_h | 308 #endif // ScriptWrappable_h |
| OLD | NEW |