Import 2.10943 2016-09-14
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc index 5f8d205..74d4c2d 100644 --- a/src/cobalt/audio/audio_device.cc +++ b/src/cobalt/audio/audio_device.cc
@@ -66,8 +66,11 @@ template <> int16 ConvertSample<float, int16>(float sample) { - DCHECK(-1.0 <= sample && sample <= 1.0) - << "Sample of type float32 must lie on interval [-1.0, 1.0]"; + if (!(-1.0 <= sample && sample <= 1.0)) { + DLOG(WARNING) << + "Sample of type float32 must lie on interval [-1.0, 1.0], got: " << + sample << "."; + } return static_cast<int16>(sample * kMaxInt16AsFloat32); }
diff --git a/src/cobalt/base/address_sanitizer.h b/src/cobalt/base/address_sanitizer.h new file mode 100644 index 0000000..e443cc9 --- /dev/null +++ b/src/cobalt/base/address_sanitizer.h
@@ -0,0 +1,33 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COBALT_BASE_ADDRESS_SANITIZER_H_ +#define COBALT_BASE_ADDRESS_SANITIZER_H_ + +namespace base { + +// ASAN requires much larger stack sizes. The value |kAsanAdditionalStackSize| +// can be added to thread stack sizes if stack overflow issues are encountered +// due to ASAN being active. +#if defined(ADDRESS_SANITIZER) +const size_t kAsanAdditionalStackSize = 4 * 1024 * 1024; +#else +const size_t kAsanAdditionalStackSize = 0; +#endif // defined(ADDRESS_SANITIZER) + +} // namespace base + +#endif // COBALT_BASE_ADDRESS_SANITIZER_H_
diff --git a/src/cobalt/base/base.gyp b/src/cobalt/base/base.gyp index 6dc3864..a2f0dda 100644 --- a/src/cobalt/base/base.gyp +++ b/src/cobalt/base/base.gyp
@@ -21,6 +21,7 @@ 'product_name': 'cobalt_base', 'type': 'static_library', 'sources': [ + 'address_sanitizer.h', 'clock.h', 'cobalt_paths.h', 'compiler.h',
diff --git a/src/cobalt/bindings/generated/jsc/testing/JSCGarbageCollectionTestInterface.cc b/src/cobalt/bindings/generated/jsc/testing/JSCGarbageCollectionTestInterface.cc new file mode 100644 index 0000000..1be5fcd --- /dev/null +++ b/src/cobalt/bindings/generated/jsc/testing/JSCGarbageCollectionTestInterface.cc
@@ -0,0 +1,706 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY! +// Auto-generated from template: bindings/javascriptcore/templates/interface.cc.template + +// clang-format off + +#include "third_party/WebKit/Source/JavaScriptCore/config.h" + +#include "JSCGarbageCollectionTestInterface.h" + +#include "base/debug/trace_event.h" +#include "cobalt/base/polymorphic_downcast.h" +#include "cobalt/script/global_object_proxy.h" +#include "cobalt/script/opaque_handle.h" +#include "cobalt/script/script_object.h" + +#include "cobalt/script/javascriptcore/constructor_base.h" +#include "cobalt/script/javascriptcore/conversion_helpers.h" +#include "cobalt/script/javascriptcore/prototype_base.h" +#include "cobalt/script/javascriptcore/jsc_callback_function.h" +#include "cobalt/script/javascriptcore/jsc_callback_interface_holder.h" +#include "cobalt/script/javascriptcore/jsc_exception_state.h" +#include "cobalt/script/javascriptcore/jsc_global_object.h" +#include "cobalt/script/javascriptcore/jsc_global_object_proxy.h" +#include "cobalt/script/javascriptcore/jsc_object_handle.h" +#include "cobalt/script/javascriptcore/jsc_object_handle_holder.h" +#include "cobalt/script/javascriptcore/type_traits.h" +#include "cobalt/script/javascriptcore/util/binding_helpers.h" +#include "cobalt/script/javascriptcore/util/exception_helpers.h" +#include "third_party/WebKit/Source/JavaScriptCore/interpreter/Interpreter.h" +#include "third_party/WebKit/Source/JavaScriptCore/runtime/Error.h" +#include "third_party/WebKit/Source/JavaScriptCore/runtime/FunctionPrototype.h" +#include "third_party/WebKit/Source/JavaScriptCore/runtime/Identifier.h" +#include "third_party/WebKit/Source/JavaScriptCore/runtime/JSFunction.h" +#include "third_party/WebKit/Source/JavaScriptCore/runtime/JSGlobalObject.h" +#include "third_party/WebKit/Source/JavaScriptCore/runtime/ObjectPrototype.h" + +namespace { +using cobalt::bindings::testing::GarbageCollectionTestInterface; +using cobalt::bindings::testing::JSCGarbageCollectionTestInterface; +using cobalt::script::CallbackInterfaceTraits; +using cobalt::script::GlobalObjectProxy; +using cobalt::script::OpaqueHandle; +using cobalt::script::OpaqueHandleHolder; +using cobalt::script::ScriptObject; +using cobalt::script::Wrappable; + +using cobalt::script::javascriptcore::kConversionFlagNullable; +using cobalt::script::javascriptcore::kConversionFlagRestricted; +using cobalt::script::javascriptcore::kConversionFlagTreatNullAsEmptyString; +using cobalt::script::javascriptcore::kConversionFlagTreatUndefinedAsEmptyString; +using cobalt::script::javascriptcore::kNoConversionFlags; +using cobalt::script::javascriptcore::ConstructorBase; +using cobalt::script::javascriptcore::GetWrappableOrSetException; +using cobalt::script::javascriptcore::FromJSValue; +using cobalt::script::javascriptcore::FromWTFString; +using cobalt::script::javascriptcore::JSCCallbackFunction; +using cobalt::script::javascriptcore::JSCCallbackFunctionHolder; +using cobalt::script::javascriptcore::JSCCallbackInterfaceHolder; +using cobalt::script::javascriptcore::JSCEngine; +using cobalt::script::javascriptcore::JSCExceptionState; +using cobalt::script::javascriptcore::JSCObjectHandle; +using cobalt::script::javascriptcore::JSCObjectHandleHolder; +using cobalt::script::javascriptcore::JSCGlobalObject; +using cobalt::script::javascriptcore::JSCGlobalObjectProxy; +using cobalt::script::javascriptcore::JSObjectToWrappable; +using cobalt::script::javascriptcore::ScriptObjectRegistry; +using cobalt::script::javascriptcore::ToJSValue; +using cobalt::script::javascriptcore::ToWTFString; +using cobalt::script::javascriptcore::TypeTraits; +using cobalt::script::javascriptcore::PrototypeBase; +using cobalt::script::javascriptcore::ThreadLocalHashTable; +using cobalt::script::javascriptcore::WrapperBase; +using cobalt::script::javascriptcore::util::HasPropertyOnPrototype; +using cobalt::script::javascriptcore::util::GetStackTrace; +} // namespace + +namespace cobalt { +namespace bindings { +namespace testing { + +namespace { +JSC::EncodedJSValue constructorJSGarbageCollectionTestInterface(JSC::ExecState*); + +// These are declared unconditionally, but only defined if needed by the +// interface. +JSC::JSValue NamedPropertyGetter(JSC::ExecState* exec_state, + JSC::JSValue slot_base, JSC::PropertyName property_name); +void NamedPropertySetter(JSC::JSCell* cell, JSC::ExecState* exec_state, + JSC::PropertyName property_name, JSC::JSValue jsc_value); +bool NamedPropertyDeleter(JSC::JSCell* cell, JSC::ExecState* exec_state, + JSC::PropertyName property_name); +bool QueryNamedProperty(JSC::JSCell* cell, JSC::ExecState* exec_state, + JSC::PropertyName property_name); +JSC::JSValue OnGetMissingProperty(JSC::ExecState* exec_state, + JSC::JSValue slot_base, JSC::PropertyName property_name); +bool OnSetMissingProperty(JSC::JSCell* cell, JSC::ExecState* exec_state, + JSC::PropertyName property_name, JSC::JSValue value); + +const bool s_has_named_getter = false; +const bool s_has_named_setter = false; +#if !defined(COBALT_BUILD_TYPE_GOLD) +const bool s_use_debug_missing_property_handler = true; +#else +const bool s_use_debug_missing_property_handler = false; +#endif +} // namespace + +// Class that defines a JS Object representing this interface's Interface Object +// https://www.w3.org/TR/WebIDL/#interface-object +class JSCGarbageCollectionTestInterface::InterfaceObject : public ConstructorBase { + public: + // Get the Interface Object. Will create a new Interface Object if necessary, + // otherwise it will return the cached one. + static JSC::JSObject* GetInstance(JSC::ExecState* exec); + DECLARE_CLASSINFO(); + + // Needed when JSC::OverridesGetOwnPropertySlot StructureFlag is set + // Must be public so that it can be accessible from getStaticValueSlot<>. + static bool getOwnPropertySlot(JSC::JSCell* cell, JSC::ExecState* exec, + JSC::PropertyName property_name, + JSC::PropertySlot& slot) { + InterfaceObject* this_object = JSC::jsCast<InterfaceObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(this_object, &s_info); + + // Same process as JSC::getStaticPropertySlot<>, which is defined in Lookup.h + // Since JSFunction::getOwnPropertySlot is protected, we can't call it from + // the helper function. + const JSC::HashEntry* entry = + GetPropertyTable(exec)->entry(exec, property_name); + + if (!entry) // not found, forward to parent + return Base::getOwnPropertySlot(this_object, exec, property_name, slot); + + if (entry->attributes() & JSC::Function) + return setUpStaticFunctionSlot(exec, entry, this_object, property_name, slot); + + slot.setCacheableCustom(this_object, entry->propertyGetter()); + return true; + } + + // static override. Needed to support setting a property. + static void put(JSC::JSCell* cell, JSC::ExecState* exec_state, + JSC::PropertyName property_name, JSC::JSValue value, + JSC::PutPropertySlot& slot) { + InterfaceObject* this_object = JSC::jsCast<InterfaceObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(this_object, &s_info); + bool found_property = JSC::lookupPut<InterfaceObject>( + exec_state, property_name, value, GetPropertyTable(exec_state), + this_object, slot.isStrictMode()); + DLOG_IF(INFO, !found_property) << "Did not find property named " << + WTF::String(property_name.publicName()).utf8().data() << + " to set on interface object for JSCGarbageCollectionTestInterface"; + if (!found_property) { + BaseClass::put(cell, exec_state, property_name, value, slot); + } + } + + // static override. This prevents this object from being called as a normal + // function, throwing a TypeError if the user attempts to do so. + static JSC::CallType getCallData(JSC::JSCell*, JSC::CallData&) { + return JSC::CallTypeNone; + } + + + private: + typedef ConstructorBase BaseClass; + + static const unsigned StructureFlags = + JSC::ImplementsHasInstance | + JSC::OverridesGetOwnPropertySlot | + BaseClass::StructureFlags; + + InterfaceObject(JSC::ExecState* exec_state, JSC::JSGlobalObject* global_object, JSC::Structure* structure) + : BaseClass(exec_state, global_object, structure) { } + void finishCreation(JSC::ExecState* exec_state, + JSC::NativeExecutable* native_executable, int length, + const String& name); + + static const JSC::HashTable* GetPropertyTable(JSC::ExecState* exec_state); + + static const JSC::HashTableValue property_table_values[]; + static const JSC::HashTable property_table_prototype; +}; + +const JSC::HashTableValue JSCGarbageCollectionTestInterface::InterfaceObject::property_table_values[] = { + // static functions will also go here. + { 0, 0, 0, 0, static_cast<JSC::Intrinsic>(0) } +}; // JSCGarbageCollectionTestInterface::InterfaceObject::property_table_values + +// static +const JSC::HashTable +JSCGarbageCollectionTestInterface::InterfaceObject::property_table_prototype = { + // Sizes will be calculated based on the number of static functions as well. + 2, // compactSize + 1, // compactSizeMask + property_table_values, + NULL // table allocated at runtime +}; // JSCGarbageCollectionTestInterface::InterfaceObject::property_table_prototype + +// static +const JSC::HashTable* +JSCGarbageCollectionTestInterface::InterfaceObject::GetPropertyTable( + JSC::ExecState* exec_state) { + return ThreadLocalHashTable::GetInstance()->GetHashTable( + JSCGarbageCollectionTestInterface::InterfaceObject::s_classinfo(), + property_table_prototype); +} + +const JSC::ClassInfo JSCGarbageCollectionTestInterface::InterfaceObject::s_info = { + "GarbageCollectionTestInterfaceConstructor", // className + BaseClass::s_classinfo(), // parentClass + NULL, // static hash-table of properties (not used) + GetPropertyTable, // function pointer to get hash-table of properties + CREATE_METHOD_TABLE(JSCGarbageCollectionTestInterface::InterfaceObject) +}; // JSCGarbageCollectionTestInterface::InterfaceObject::s_info + +void JSCGarbageCollectionTestInterface::InterfaceObject::finishCreation( + JSC::ExecState* exec_state, JSC::NativeExecutable* native_executable, + int length, const String& name) { + BaseClass::finishCreation(exec_state, native_executable, length, name); + ASSERT(inherits(&s_info)); + // Add a 'prototype' property whose value is the prototype object. + putDirect(exec_state->globalData(), + exec_state->propertyNames().prototype, + JSCGarbageCollectionTestInterface::GetPrototype(exec_state->lexicalGlobalObject()), + JSC::DontDelete | JSC::ReadOnly | JSC::DontEnum); + DCHECK(hasOwnProperty(exec_state, JSC::Identifier(exec_state, "prototype"))); +} + +// static +JSC::JSObject* JSCGarbageCollectionTestInterface::InterfaceObject::GetInstance( + JSC::ExecState* exec_state) { + JSCGlobalObject* global_object = + static_cast<JSCGlobalObject*>(exec_state->lexicalGlobalObject()); + ASSERT_GC_OBJECT_INHERITS(global_object, JSCGlobalObject::s_classinfo()); + + // Try to get the cached interface object, and create a new one if needed. + JSC::JSObject* interface_object = global_object->object_cache()->GetCachedConstructor(&s_info); + if (interface_object == NULL) { + JSC::JSGlobalData& global_data = global_object->globalData(); + JSC::JSObject* parent_prototype = global_object->functionPrototype(); + JSC::TypeInfo type_info(JSC::ObjectType, StructureFlags); + JSC::Structure* structure = JSC::Structure::create( + global_data, + global_object, + JSC::JSValue(parent_prototype), + type_info, + &s_info); + + const int kNumArguments = 0; + JSC::NativeExecutable* executable = global_data.getHostFunction(NULL, &constructorJSGarbageCollectionTestInterface); + + // Create the new interface object. + InterfaceObject* new_interface_object = + new (NotNull, JSC::allocateCell<InterfaceObject>(global_data.heap)) + InterfaceObject(exec_state, global_object, structure); + new_interface_object->finishCreation(exec_state, executable, kNumArguments, "GarbageCollectionTestInterface"); + // Add the interface object to the cache. + global_object->object_cache()->CacheConstructor(&s_info, new_interface_object); + interface_object = new_interface_object; + } + DCHECK_EQ(global_object->object_cache()->GetCachedConstructor(&s_info), interface_object); + return interface_object; +} + +// End of JSCGarbageCollectionTestInterface::InterfaceObject class + +// Class that defines a JS Object representing this interface's prototype +class JSCGarbageCollectionTestInterface::Prototype : public PrototypeBase { + public: + // Get the prototype. Will create a new prototype if necessary, otherwise it + // will return a cached prototype. + static JSC::JSObject* GetInstance(JSC::JSGlobalObject* global_object); + DECLARE_CLASSINFO(); + + // Needed when JSC::OverridesGetOwnPropertySlot StructureFlag is set + // Must be public so that it can be accessible from getStaticValueSlot<>. + static bool getOwnPropertySlot(JSC::JSCell*, JSC::ExecState*, + JSC::PropertyName, + JSC::PropertySlot&); + + private: + typedef PrototypeBase BaseClass; + + static const unsigned StructureFlags = + JSC::OverridesGetOwnPropertySlot | + BaseClass::StructureFlags; + + Prototype(JSC::JSGlobalObject* global_object, JSC::Structure* structure) + : BaseClass(global_object, structure) { } + + static JSC::JSValue GetConstructor(JSC::ExecState* exec_state, + JSC::JSValue slot_base, + JSC::PropertyName property_name); + static const JSC::HashTable* GetPropertyTable(JSC::ExecState* exec_state); + + static const JSC::HashTableValue property_table_values[]; + static const JSC::HashTable property_table_prototype; +}; + +const JSC::HashTableValue JSCGarbageCollectionTestInterface::Prototype::property_table_values[] = { + { "constructor", + JSC::DontDelete | JSC::DontEnum, + reinterpret_cast<intptr_t>(JSCGarbageCollectionTestInterface::Prototype::GetConstructor), + static_cast<intptr_t>(0), + JSC::NoIntrinsic + }, + { 0, 0, 0, 0, static_cast<JSC::Intrinsic>(0) } +}; // JSCGarbageCollectionTestInterface::Prototype::property_table_values + +// static +const JSC::HashTable JSCGarbageCollectionTestInterface::Prototype::property_table_prototype = { + 4, // compactSize + 3, // compactSizeMask + property_table_values, + NULL // table allocated at runtime +}; // JSCGarbageCollectionTestInterface::Prototype::property_table_prototype + +// static +const JSC::HashTable* JSCGarbageCollectionTestInterface::Prototype::GetPropertyTable( + JSC::ExecState* exec_state) { + return ThreadLocalHashTable::GetInstance()->GetHashTable( + JSCGarbageCollectionTestInterface::Prototype::s_classinfo(), property_table_prototype); +} + +const JSC::ClassInfo JSCGarbageCollectionTestInterface::Prototype::s_info = { + "GarbageCollectionTestInterfacePrototype", // className + BaseClass::s_classinfo(), // parentClass + NULL, // static hash-table of properties (not used) + GetPropertyTable, // function pointer to get hash-table of properties + CREATE_METHOD_TABLE(JSCGarbageCollectionTestInterface::Prototype) +}; // JSCGarbageCollectionTestInterface::Prototype::s_info + +// Look up property slot for querying property values. +bool JSCGarbageCollectionTestInterface::Prototype::getOwnPropertySlot(JSC::JSCell* cell, + JSC::ExecState* exec, JSC::PropertyName property_name, + JSC::PropertySlot& slot) { + Prototype* this_object = JSC::jsCast<Prototype*>(cell); + ASSERT_GC_OBJECT_INHERITS(this_object, &s_info); + return JSC::getStaticPropertySlot<Prototype, JSC::JSObject>( + exec, GetPropertyTable(exec), this_object, property_name, slot); +} + +// static +JSC::JSObject* JSCGarbageCollectionTestInterface::Prototype::GetInstance( + JSC::JSGlobalObject* base_global_object) { + JSCGlobalObject* global_object = + static_cast<JSCGlobalObject*>(base_global_object); + ASSERT_GC_OBJECT_INHERITS(global_object, JSCGlobalObject::s_classinfo()); + + // Try to get the cached prototype, and create a new one if needed. + JSC::JSObject* prototype = global_object->object_cache()->GetCachedPrototype(&s_info); + if (prototype == NULL) { + JSC::JSGlobalData& global_data = global_object->globalData(); + JSC::JSLockHolder lock(&global_data); + + JSC::JSObject* parent_prototype = global_object->objectPrototype(); + JSC::TypeInfo type_info(JSC::ObjectType, StructureFlags); + JSC::Structure* structure = JSC::Structure::create( + global_data, + global_object, + JSC::JSValue(parent_prototype), + type_info, + &s_info); + + // Create the new prototype object. + Prototype* new_prototype = + new (NotNull, JSC::allocateCell<Prototype>( + global_data.heap)) + Prototype(global_object, structure); + new_prototype->finishCreation(global_data); + // Add the prototype to the cache. + global_object->object_cache()->CachePrototype(&s_info, new_prototype); + prototype = new_prototype; + } + DCHECK_EQ(global_object->object_cache()->GetCachedPrototype(&s_info), prototype); + return prototype; +} + +// End of JSCGarbageCollectionTestInterface::Prototype class + +const JSC::HashTableValue JSCGarbageCollectionTestInterface::property_table_values[] = { + { 0, 0, 0, 0, static_cast<JSC::Intrinsic>(0) } +}; // JSCGarbageCollectionTestInterface::property_table_values + +// static +const JSC::HashTable JSCGarbageCollectionTestInterface::property_table_prototype = { + 2, // compactSize + 1, // compactSizeMask + property_table_values, + NULL // table allocated at runtime +}; // JSCGarbageCollectionTestInterface::property_table_prototype + +// static +const JSC::HashTable* JSCGarbageCollectionTestInterface::GetPropertyTable( + JSC::ExecState* exec_state) { + return ThreadLocalHashTable::GetInstance()->GetHashTable( + JSCGarbageCollectionTestInterface::s_classinfo(), property_table_prototype); +} + +#ifdef __LB_SHELL__FORCE_LOGGING__ +base::LazyInstance<JSCGarbageCollectionTestInterface::NonTrivialStaticFields> + JSCGarbageCollectionTestInterface::non_trivial_static_fields = LAZY_INSTANCE_INITIALIZER; +#endif // __LB_SHELL__FORCE_LOGGING__ + +const JSC::ClassInfo JSCGarbageCollectionTestInterface::s_info = { + "GarbageCollectionTestInterface", // className + BaseClass::s_classinfo(), // parentClass + NULL, // static hash-table of properties (not used) + GetPropertyTable, // function pointer to get hash-table of properties + CREATE_METHOD_TABLE(JSCGarbageCollectionTestInterface) +}; // JSCGarbageCollectionTestInterface::s_info + +// static +JSC::JSObject* JSCGarbageCollectionTestInterface::GetPrototype( + JSC::JSGlobalObject* global_object) { + return Prototype::GetInstance(global_object); +} + +// static +JSC::JSObject* JSCGarbageCollectionTestInterface::GetConstructor( + JSC::ExecState* exec_state) { + return InterfaceObject::GetInstance(exec_state); +} + +// static +JSC::JSValue JSCGarbageCollectionTestInterface::Prototype::GetConstructor( + JSC::ExecState* exec_state, + JSC::JSValue slot_base, + JSC::PropertyName property_name) { + return JSC::JSValue(InterfaceObject::GetInstance(exec_state)); +} + +// static +JSC::JSObject* JSCGarbageCollectionTestInterface::Create( + JSCGlobalObject* global_object, + const scoped_refptr<Wrappable>& wrappable) { + if (!(wrappable->GetWrappableType() == GarbageCollectionTestInterface::GarbageCollectionTestInterfaceWrappableType())) { + NOTREACHED() << "Type of wrappable does not match GarbageCollectionTestInterface::GarbageCollectionTestInterfaceWrappableType()"; + return NULL; + } + GarbageCollectionTestInterface* impl_ptr = + base::polymorphic_downcast<GarbageCollectionTestInterface*>(wrappable.get()); + + JSC::JSGlobalData& global_data = global_object->globalData(); + + // Get or Create the prototype object for this interface. + JSC::JSObject* prototype = Prototype::GetInstance(global_object); + DCHECK(prototype); + + JSC::JSLockHolder lock(global_data); + // Create a JSC::Structure object for this instance. + JSC::TypeInfo type_info(JSC::ObjectType, StructureFlags); + JSC::Structure* structure = JSC::Structure::create( + global_data, + global_object, + JSC::JSValue(prototype), + type_info, + &s_info); + + // Instantiate a new garbage-collected wrapper object. + JSCGarbageCollectionTestInterface* wrapper = + new (NotNull, JSC::allocateCell<JSCGarbageCollectionTestInterface>(global_data.heap)) + JSCGarbageCollectionTestInterface( + &global_data, + structure, + global_object->script_object_registry(), + make_scoped_refptr(impl_ptr)); + wrapper->finishCreation(global_data); + return wrapper; +} +JSCGarbageCollectionTestInterface::JSCGarbageCollectionTestInterface( + JSC::JSGlobalData* global_data, + JSC::Structure* structure, + ScriptObjectRegistry* script_object_registry, + const scoped_refptr<GarbageCollectionTestInterface>& impl) + : BaseClass(global_data, structure, script_object_registry, impl) { +} + +void JSCGarbageCollectionTestInterface::finishCreation(JSC::JSGlobalData& global_data) { + BaseClass::finishCreation(global_data); + DCHECK(inherits(&s_info)); +} + +JSCGarbageCollectionTestInterface::~JSCGarbageCollectionTestInterface() { + // Empty destructor +} + +// Look up property slot for querying property values. +bool JSCGarbageCollectionTestInterface::getOwnPropertySlot(JSC::JSCell* cell, + JSC::ExecState* exec, JSC::PropertyName property_name, + JSC::PropertySlot& slot) { + JSCGarbageCollectionTestInterface* this_object = JSC::jsCast<JSCGarbageCollectionTestInterface*>(cell); + ASSERT_GC_OBJECT_INHERITS(this_object, &s_info); + bool found_property_slot = JSC::getStaticValueSlot<JSCGarbageCollectionTestInterface, BaseClass>( + exec, GetPropertyTable(exec), this_object, property_name, slot); + if (s_has_named_getter || s_use_debug_missing_property_handler) { + bool found_property_on_prototype_chain = false; + if (!found_property_slot && cell->isObject()) { + JSC::JSValue prototype_value = JSC::asObject(cell)->prototype(); + if (prototype_value.isObject()) { + JSC::JSObject* prototype = JSC::asObject(prototype_value); + found_property_on_prototype_chain = + prototype->hasProperty(exec, property_name); + } + } + if (s_has_named_getter) { + if (!found_property_slot && !found_property_on_prototype_chain) { + if (QueryNamedProperty(this_object, exec, property_name)) { + slot.setCustom(cell, &NamedPropertyGetter); + found_property_slot = true; + } + } + } + if (s_use_debug_missing_property_handler) { + // The property was not found as an own-property, nor was it found on the + // prototype chain, so set the missing property handler to be called + // when getting this property value. + if (!found_property_slot && !found_property_on_prototype_chain) { + slot.setCustom(cell, &OnGetMissingProperty); + found_property_slot = true; + } + } + } + return found_property_slot; +} + +// Look up property slot and put the |value|. +void JSCGarbageCollectionTestInterface::put(JSC::JSCell* cell, JSC::ExecState* exec, + JSC::PropertyName property_name, JSC::JSValue value, + JSC::PutPropertySlot& slot) { + JSCGarbageCollectionTestInterface* this_object = JSC::jsCast<JSCGarbageCollectionTestInterface*>(cell); + ASSERT_GC_OBJECT_INHERITS(this_object, &s_info); + bool property_handled = false; + if (s_has_named_setter || s_use_debug_missing_property_handler) { + // Need to look up the property manually. + bool has_property = HasOwnPropertyOrPrototypeProperty( + cell, exec, property_name); + + if (s_has_named_setter) { + // We didn't find the property on the object or prototype chain, so + // set or create a new named property. + if (!has_property) { + std::string property_name_utf8 = FromWTFString(property_name.publicName()); + NamedPropertySetter(cell, exec, property_name, value); + property_handled = true; + } + } + if (s_use_debug_missing_property_handler) { + if (!has_property && !property_handled) { + property_handled = OnSetMissingProperty(cell, exec, property_name, value); + } + } +#ifdef __LB_SHELL__FORCE_LOGGING__ + std::string property_name_utf8 = FromWTFString(property_name.publicName()); + + base::AutoLock lock(non_trivial_static_fields.Get().lock_); + base::hash_set<std::string>& properties_warned_about = + non_trivial_static_fields.Get().properties_warned_about; + + if (properties_warned_about.find(property_name_utf8) == + properties_warned_about.end()) { + properties_warned_about.insert(property_name_utf8); + WTF::String class_name = cell->className(); + DLOG_IF(WARNING, !has_property) << "Did not find property named " << + property_name_utf8 << " to set on wrapper for " + << FromWTFString(class_name) + << std::endl << StackTraceToString(GetStackTrace(exec, 32)) + << std::endl; + } +#endif // __LB_SHELL__FORCE_LOGGING__ + } + + if (!property_handled) { + JSC::lookupPut<JSCGarbageCollectionTestInterface, BaseClass>( + exec, property_name, value, GetPropertyTable(exec), this_object, slot); + } +} + +bool JSCGarbageCollectionTestInterface::HasOwnPropertyOrPrototypeProperty( + JSC::JSCell* cell, JSC::ExecState* exec_state, + JSC::PropertyName property_name) { + JSCGarbageCollectionTestInterface* this_object = JSC::jsCast<JSCGarbageCollectionTestInterface*>(cell); + JSC::PropertySlot lookup_slot; + bool has_property = JSC::getStaticPropertySlot<JSCGarbageCollectionTestInterface, BaseClass>( + exec_state, GetPropertyTable(exec_state), this_object, property_name, + lookup_slot); + return has_property || HasPropertyOnPrototype(exec_state, cell, property_name); +} + +namespace { + +JSC::EncodedJSValue constructorJSGarbageCollectionTestInterface(JSC::ExecState* exec_state) { + JSCGlobalObject* global_object = + JSC::jsCast<JSCGlobalObject*>(exec_state->lexicalGlobalObject()); + JSCExceptionState exception_state(global_object); + scoped_refptr<GarbageCollectionTestInterface> new_object = + new GarbageCollectionTestInterface(); + return JSC::JSValue::encode(ToJSValue(global_object, new_object)); + +} +JSC::JSValue NamedPropertyGetter(JSC::ExecState* exec_state, + JSC::JSValue slot_base, JSC::PropertyName property_name) { + NOTREACHED(); + return JSC::jsUndefined(); +} +bool QueryNamedProperty(JSC::JSCell* cell, JSC::ExecState* exec_state, + JSC::PropertyName property_name) { + NOTREACHED(); + return false; +} +void NamedPropertySetter(JSC::JSCell* cell, JSC::ExecState* exec_state, + JSC::PropertyName property_name, JSC::JSValue jsc_value) { + NOTREACHED(); +} + +bool NamedPropertyDeleter(JSC::JSCell* cell, JSC::ExecState* exec_state, + JSC::PropertyName property_name) { + return false; +} + +#if !defined(COBALT_BUILD_TYPE_GOLD) +JSC::JSValue OnGetMissingProperty(JSC::ExecState* exec_state, + JSC::JSValue slot_base, JSC::PropertyName property_name) { + JSCGlobalObject* global_object = + JSC::jsCast<JSCGlobalObject*>(exec_state->lexicalGlobalObject()); + JSC::JSValue callable = global_object->get( + exec_state, JSC::Identifier(exec_state, "__onGetMissingProperty")); + if (!callable.isUndefined()) { + JSC::CallData call_data; + JSC::CallType call_type = JSC::getCallData(callable, call_data); + if (call_type != JSC::CallTypeNone) { + // The function called __onGetMissingProperty exists, so call this and + // return the result as the value for this property. + JSC::MarkedArgumentBuffer args; + args.append(slot_base); + args.append(JSC::JSValue( + JSC::JSString::create( + global_object->globalData(), property_name.publicName()))); + JSC::JSValue retval = JSC::call( + exec_state, callable, call_type, call_data, global_object, args); + return retval; + } + } + return JSC::jsUndefined(); +} + +bool OnSetMissingProperty(JSC::JSCell* cell, JSC::ExecState* exec_state, + JSC::PropertyName property_name, JSC::JSValue value) { + JSCGlobalObject* global_object = + JSC::jsCast<JSCGlobalObject*>(exec_state->lexicalGlobalObject()); + JSC::JSValue callable = global_object->get( + exec_state, JSC::Identifier(exec_state, "__onSetMissingProperty")); + if (!callable.isUndefined()) { + JSC::CallData call_data; + JSC::CallType call_type = JSC::getCallData(callable, call_data); + if (call_type != JSC::CallTypeNone) { + // The function called __onSetMissingProperty exists, so call this with + // the value to be set. The missing property handler returns true if it + // has handled the setting of this property. + JSC::MarkedArgumentBuffer args; + args.append(cell); + args.append(JSC::JSValue( + JSC::JSString::create( + global_object->globalData(), property_name.publicName()))); + args.append(value); + JSC::JSValue retval = JSC::call( + exec_state, callable, call_type, call_data, global_object, args); + return retval.toBoolean(exec_state); + } + } + return false; +} +#else +JSC::JSValue OnGetMissingProperty(JSC::ExecState* exec_state, + JSC::JSValue slot_base, JSC::PropertyName property_name) { + NOTREACHED(); + return JSC::jsUndefined(); +} +bool OnSetMissingProperty(JSC::JSCell* cell, JSC::ExecState* exec_state, + JSC::PropertyName property_name, JSC::JSValue value) { + NOTREACHED(); + return false; +} +#endif +} // namespace + +} // namespace testing +} // namespace bindings +} // namespace cobalt
diff --git a/src/cobalt/bindings/generated/jsc/testing/JSCGarbageCollectionTestInterface.h b/src/cobalt/bindings/generated/jsc/testing/JSCGarbageCollectionTestInterface.h new file mode 100644 index 0000000..96ab43e --- /dev/null +++ b/src/cobalt/bindings/generated/jsc/testing/JSCGarbageCollectionTestInterface.h
@@ -0,0 +1,130 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY! +// Auto-generated from template: bindings/javascriptcore/templates/interface.h.template + +// clang-format off + +#ifndef JSCGarbageCollectionTestInterface_h +#define JSCGarbageCollectionTestInterface_h + +#include "base/hash_tables.h" +#include "base/lazy_instance.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" +#include "cobalt/base/polymorphic_downcast.h" +#include "cobalt/script/wrappable.h" +#include "cobalt/bindings/testing/garbage_collection_test_interface.h" + +#include "base/threading/thread_local_storage.h" +#include "cobalt/script/javascriptcore/jsc_global_object.h" +#include "cobalt/script/javascriptcore/script_object_registry.h" +#include "cobalt/script/javascriptcore/thread_local_hash_table.h" +#include "cobalt/script/javascriptcore/wrapper_base.h" +#include "cobalt/script/javascriptcore/wrapper_factory.h" +#include "third_party/WebKit/Source/JavaScriptCore/config.h" +#include "third_party/WebKit/Source/JavaScriptCore/runtime/ClassInfo.h" +#include "third_party/WebKit/Source/JavaScriptCore/runtime/JSObject.h" +#include "third_party/WebKit/Source/JavaScriptCore/runtime/Lookup.h" + +namespace cobalt { +namespace bindings { +namespace testing { + +class JSCGarbageCollectionTestInterface + : public script::javascriptcore::InterfaceBase { + typedef script::javascriptcore::InterfaceBase BaseClass; + public: + + // Get the prototype object for this wrapper class. + static JSC::JSObject* GetPrototype(JSC::JSGlobalObject* global_object); + + // Get the interface object for this wrapper class. + static JSC::JSObject* GetConstructor(JSC::ExecState* exec_state); + + // JavaScriptCore functions and members + + DECLARE_CLASSINFO(); + + // Needed when JSC::OverridesGetOwnPropertySlot StructureFlag is set + // Must be public so that it can be accessible from getStaticValueSlot<>. + static bool getOwnPropertySlot(JSC::JSCell*, JSC::ExecState*, + JSC::PropertyName, + JSC::PropertySlot&); + + // static override. Needed to support setting a property. + static void put(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, + JSC::JSValue, JSC::PutPropertySlot&); + + // static override. This function will be called after a new object has + // been created. + void finishCreation(JSC::JSGlobalData& global_data); + + static script::javascriptcore::WrapperFactory::CreateWrapperFunction + GetCreateWrapperFunction() { + return base::Bind(&Create); + } + + private: + // Create a new wrapper for |wrappable|, which will be cast to GarbageCollectionTestInterface. + static JSC::JSObject* Create( + script::javascriptcore::JSCGlobalObject* global_object, + const scoped_refptr<script::Wrappable>& wrappable); + + protected: + + static const unsigned StructureFlags = + JSC::OverridesGetOwnPropertySlot | + BaseClass::StructureFlags; + + JSCGarbageCollectionTestInterface( + JSC::JSGlobalData* global_data, + JSC::Structure* structure, + script::javascriptcore::ScriptObjectRegistry* script_object_registry, + const scoped_refptr<GarbageCollectionTestInterface>& impl); + ~JSCGarbageCollectionTestInterface(); + + private: + class InterfaceObject; + class Prototype; + + static const JSC::HashTableValue property_table_values[]; + static const JSC::HashTable property_table_prototype; + static base::LazyInstance< + cobalt::script::javascriptcore::ThreadLocalHashTable> + thread_local_property_table; + + static const JSC::HashTable* GetPropertyTable(JSC::ExecState* exec_state); + + static bool HasOwnPropertyOrPrototypeProperty(JSC::JSCell* cell, + JSC::ExecState* exec_state, JSC::PropertyName property_name); + +#ifdef __LB_SHELL__FORCE_LOGGING__ + struct NonTrivialStaticFields { + // TODO: Only log attempts of usage of unsupported Web APIs. + base::hash_set<std::string> properties_warned_about; + base::Lock lock_; + }; + static base::LazyInstance<NonTrivialStaticFields> non_trivial_static_fields; +#endif // __LB_SHELL__FORCE_LOGGING__ +}; + +} // namespace bindings +} // namespace testing +} // namespace cobalt + +#endif // JSCGarbageCollectionTestInterface_h
diff --git a/src/cobalt/bindings/generated/jsc/testing/JSCWindow.cc b/src/cobalt/bindings/generated/jsc/testing/JSCWindow.cc index 1fb9cf2..45c9bf3 100644 --- a/src/cobalt/bindings/generated/jsc/testing/JSCWindow.cc +++ b/src/cobalt/bindings/generated/jsc/testing/JSCWindow.cc
@@ -48,6 +48,7 @@ #include "JSCExceptionObjectInterface.h" #include "JSCExceptionsInterface.h" #include "JSCExtendedIDLAttributesInterface.h" +#include "JSCGarbageCollectionTestInterface.h" #include "JSCGetOpaqueRootInterface.h" #include "JSCGlobalInterfaceParent.h" #include "JSCImplementedInterface.h" @@ -92,6 +93,7 @@ #include "cobalt/bindings/testing/exception_object_interface.h" #include "cobalt/bindings/testing/exceptions_interface.h" #include "cobalt/bindings/testing/extended_idl_attributes_interface.h" +#include "cobalt/bindings/testing/garbage_collection_test_interface.h" #include "cobalt/bindings/testing/get_opaque_root_interface.h" #include "cobalt/bindings/testing/global_interface_parent.h" #include "cobalt/bindings/testing/implemented_interface.h" @@ -165,6 +167,7 @@ using cobalt::bindings::testing::ExceptionObjectInterface; using cobalt::bindings::testing::ExceptionsInterface; using cobalt::bindings::testing::ExtendedIDLAttributesInterface; +using cobalt::bindings::testing::GarbageCollectionTestInterface; using cobalt::bindings::testing::GetOpaqueRootInterface; using cobalt::bindings::testing::GlobalInterfaceParent; using cobalt::bindings::testing::ImplementedInterface; @@ -194,6 +197,7 @@ using cobalt::bindings::testing::JSCExceptionObjectInterface; using cobalt::bindings::testing::JSCExceptionsInterface; using cobalt::bindings::testing::JSCExtendedIDLAttributesInterface; +using cobalt::bindings::testing::JSCGarbageCollectionTestInterface; using cobalt::bindings::testing::JSCGetOpaqueRootInterface; using cobalt::bindings::testing::JSCGlobalInterfaceParent; using cobalt::bindings::testing::JSCImplementedInterface; @@ -770,6 +774,10 @@ JSCExtendedIDLAttributesInterface::s_classinfo(), JSCExtendedIDLAttributesInterface::GetCreateWrapperFunction()); wrapper_factory->RegisterWrappableType( + GarbageCollectionTestInterface::GarbageCollectionTestInterfaceWrappableType(), + JSCGarbageCollectionTestInterface::s_classinfo(), + JSCGarbageCollectionTestInterface::GetCreateWrapperFunction()); + wrapper_factory->RegisterWrappableType( GetOpaqueRootInterface::GetOpaqueRootInterfaceWrappableType(), JSCGetOpaqueRootInterface::s_classinfo(), JSCGetOpaqueRootInterface::GetCreateWrapperFunction());
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsBooleanTypeTestInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsBooleanTypeTestInterface.cc index e0895ab..190790f 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsBooleanTypeTestInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsBooleanTypeTestInterface.cc
@@ -254,8 +254,7 @@ wrapper_private->wrappable<BooleanTypeTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackFunctionInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackFunctionInterface.cc index 90f8065..60f4a71 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackFunctionInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackFunctionInterface.cc
@@ -302,8 +302,7 @@ wrapper_private->wrappable<CallbackFunctionInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -349,8 +348,7 @@ wrapper_private->wrappable<CallbackFunctionInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -396,8 +394,7 @@ wrapper_private->wrappable<CallbackFunctionInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -443,8 +440,7 @@ wrapper_private->wrappable<CallbackFunctionInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -490,8 +486,7 @@ wrapper_private->wrappable<CallbackFunctionInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackInterfaceInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackInterfaceInterface.cc index 7f68bd2..a827b91 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackInterfaceInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackInterfaceInterface.cc
@@ -258,8 +258,7 @@ wrapper_private->wrappable<CallbackInterfaceInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsConstructorInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsConstructorInterface.cc index 1ce35d8..4a0d825 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsConstructorInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsConstructorInterface.cc
@@ -389,8 +389,7 @@ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -443,8 +442,7 @@ // http://heycam.github.io/webidl/#dfn-overload-resolution-algorithm // 4. If S is empty, then throw a TypeError. MozjsExceptionState exception_state(context); - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Invalid number of arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } } // namespace
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsConstructorWithArgumentsInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsConstructorWithArgumentsInterface.cc index fcb47c4..c10d31d 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsConstructorWithArgumentsInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsConstructorWithArgumentsInterface.cc
@@ -457,8 +457,7 @@ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); const size_t kMinArguments = 2; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsDerivedGetterSetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsDerivedGetterSetterInterface.cc index eda9b8c..0831779 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsDerivedGetterSetterInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsDerivedGetterSetterInterface.cc
@@ -458,8 +458,7 @@ wrapper_private->wrappable<DerivedGetterSetterInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -511,8 +510,7 @@ wrapper_private->wrappable<DerivedGetterSetterInterface>().get(); const size_t kMinArguments = 2; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsEnumerationInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsEnumerationInterface.cc index f9cf774..6810f89 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsEnumerationInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsEnumerationInterface.cc
@@ -496,9 +496,7 @@ *out_enum = EnumerationInterface::kGamma; } else { // 2. If S is not one of E's enumeration values, then throw a TypeError. - exception_state-> - SetSimpleException(ExceptionState::kTypeError, - "Cannot convert JavaScript value to Enum."); + exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed); return; } }
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.cc new file mode 100644 index 0000000..98eff0f --- /dev/null +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.cc
@@ -0,0 +1,389 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY! +// Auto-generated from template: bindings/mozjs/templates/interface.cc.template + +// clang-format off + +#include "MozjsGarbageCollectionTestInterface.h" + +#include "base/debug/trace_event.h" +#include "cobalt/base/polymorphic_downcast.h" +#include "cobalt/script/global_object_proxy.h" +#include "cobalt/script/opaque_handle.h" +#include "cobalt/script/script_object.h" + +#include "base/lazy_instance.h" +#include "cobalt/script/mozjs/callback_function_conversion.h" +#include "cobalt/script/exception_state.h" +#include "cobalt/script/mozjs/conversion_helpers.h" +#include "cobalt/script/mozjs/mozjs_exception_state.h" +#include "cobalt/script/mozjs/mozjs_callback_function.h" +#include "cobalt/script/mozjs/mozjs_global_object_proxy.h" +#include "cobalt/script/mozjs/mozjs_object_handle.h" +#include "cobalt/script/mozjs/mozjs_property_enumerator.h" +#include "cobalt/script/mozjs/mozjs_user_object_holder.h" +#include "cobalt/script/mozjs/proxy_handler.h" +#include "cobalt/script/mozjs/type_traits.h" +#include "cobalt/script/mozjs/wrapper_factory.h" +#include "cobalt/script/mozjs/wrapper_private.h" +#include "cobalt/script/property_enumerator.h" +#include "third_party/mozjs/js/src/jsapi.h" +#include "third_party/mozjs/js/src/jsfriendapi.h" + +namespace { +using cobalt::bindings::testing::GarbageCollectionTestInterface; +using cobalt::bindings::testing::MozjsGarbageCollectionTestInterface; +using cobalt::script::CallbackInterfaceTraits; +using cobalt::script::GlobalObjectProxy; +using cobalt::script::OpaqueHandle; +using cobalt::script::OpaqueHandleHolder; +using cobalt::script::ScriptObject; +using cobalt::script::Wrappable; + +using cobalt::script::CallbackFunction; +using cobalt::script::CallbackInterfaceTraits; +using cobalt::script::ExceptionState; +using cobalt::script::mozjs::FromJSValue; +using cobalt::script::mozjs::kConversionFlagNullable; +using cobalt::script::mozjs::kConversionFlagRestricted; +using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString; +using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString; +using cobalt::script::mozjs::kNoConversionFlags; +using cobalt::script::mozjs::InterfaceData; +using cobalt::script::mozjs::MozjsCallbackFunction; +using cobalt::script::mozjs::MozjsExceptionState; +using cobalt::script::mozjs::MozjsGlobalObjectProxy; +using cobalt::script::mozjs::MozjsUserObjectHolder; +using cobalt::script::mozjs::MozjsPropertyEnumerator; +using cobalt::script::mozjs::ProxyHandler; +using cobalt::script::mozjs::ToJSValue; +using cobalt::script::mozjs::TypeTraits; +using cobalt::script::mozjs::WrapperPrivate; +using cobalt::script::mozjs::WrapperFactory; +using cobalt::script::Wrappable; +} // namespace + +namespace cobalt { +namespace bindings { +namespace testing { + +namespace { + +class MozjsGarbageCollectionTestInterfaceHandler : public ProxyHandler { + public: + MozjsGarbageCollectionTestInterfaceHandler() + : ProxyHandler(indexed_property_hooks, named_property_hooks) {} + + private: + static NamedPropertyHooks named_property_hooks; + static IndexedPropertyHooks indexed_property_hooks; +}; + +ProxyHandler::NamedPropertyHooks +MozjsGarbageCollectionTestInterfaceHandler::named_property_hooks = { + NULL, + NULL, + NULL, + NULL, + NULL, +}; +ProxyHandler::IndexedPropertyHooks +MozjsGarbageCollectionTestInterfaceHandler::indexed_property_hooks = { + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +static base::LazyInstance<MozjsGarbageCollectionTestInterfaceHandler> + proxy_handler; + +JSBool Constructor(JSContext* context, unsigned int argc, JS::Value* vp); +JSBool HasInstance(JSContext *context, JS::HandleObject type, + JS::MutableHandleValue vp, JSBool *success) { + JS::RootedObject global_object( + context, JS_GetGlobalForObject(context, type)); + DCHECK(global_object); + + JS::RootedObject prototype( + context, MozjsGarbageCollectionTestInterface::GetPrototype(context, global_object)); + + // |IsDelegate| walks the prototype chain of an object returning true if + // .prototype is found. + bool is_delegate; + if (!IsDelegate(context, prototype, vp, &is_delegate)) { + *success = false; + return false; + } + + *success = is_delegate; + return true; +} + +InterfaceData* CreateCachedInterfaceData() { + InterfaceData* interface_data = new InterfaceData(); + memset(&interface_data->instance_class_definition, 0, + sizeof(interface_data->instance_class_definition)); + memset(&interface_data->prototype_class_definition, 0, + sizeof(interface_data->prototype_class_definition)); + memset(&interface_data->interface_object_class_definition, 0, + sizeof(interface_data->interface_object_class_definition)); + + JSClass* instance_class = &interface_data->instance_class_definition; + const int kGlobalFlags = 0; + instance_class->name = "GarbageCollectionTestInterface"; + instance_class->flags = kGlobalFlags | JSCLASS_HAS_PRIVATE; + instance_class->addProperty = JS_PropertyStub; + instance_class->delProperty = JS_DeletePropertyStub; + instance_class->getProperty = JS_PropertyStub; + instance_class->setProperty = JS_StrictPropertyStub; + instance_class->enumerate = JS_EnumerateStub; + instance_class->resolve = JS_ResolveStub; + instance_class->convert = JS_ConvertStub; + // Function to be called before on object of this class is garbage collected. + instance_class->finalize = &WrapperPrivate::Finalizer; + // Called to trace objects that can be referenced from this object. + instance_class->trace = &WrapperPrivate::Trace; + + JSClass* prototype_class = &interface_data->prototype_class_definition; + prototype_class->name = "GarbageCollectionTestInterfacePrototype"; + prototype_class->flags = 0; + prototype_class->addProperty = JS_PropertyStub; + prototype_class->delProperty = JS_DeletePropertyStub; + prototype_class->getProperty = JS_PropertyStub; + prototype_class->setProperty = JS_StrictPropertyStub; + prototype_class->enumerate = JS_EnumerateStub; + prototype_class->resolve = JS_ResolveStub; + prototype_class->convert = JS_ConvertStub; + + JSClass* interface_object_class = + &interface_data->interface_object_class_definition; + interface_object_class->name = "GarbageCollectionTestInterfaceConstructor"; + interface_object_class->flags = 0; + interface_object_class->addProperty = JS_PropertyStub; + interface_object_class->delProperty = JS_DeletePropertyStub; + interface_object_class->getProperty = JS_PropertyStub; + interface_object_class->setProperty = JS_StrictPropertyStub; + interface_object_class->enumerate = JS_EnumerateStub; + interface_object_class->resolve = JS_ResolveStub; + interface_object_class->convert = JS_ConvertStub; + interface_object_class->hasInstance = &HasInstance; + interface_object_class->construct = Constructor; + return interface_data; +} + + +const JSPropertySpec prototype_properties[] = { + JS_PS_END +}; + +const JSFunctionSpec prototype_functions[] = { + JS_FS_END +}; + +const JSPropertySpec interface_object_properties[] = { + JS_PS_END +}; + +const JSFunctionSpec interface_object_functions[] = { + JS_FS_END +}; + +const JSPropertySpec own_properties[] = { + JS_PS_END +}; + +void InitializePrototypeAndInterfaceObject( + InterfaceData* interface_data, JSContext* context, + JS::HandleObject global_object) { + DCHECK(!interface_data->prototype); + DCHECK(!interface_data->interface_object); + DCHECK(JS_IsGlobalObject(global_object)); + + JS::RootedObject parent_prototype( + context, JS_GetObjectPrototype(context, global_object)); + DCHECK(parent_prototype); + + // Create the Prototype object. + interface_data->prototype = JS_NewObjectWithGivenProto( + context, &interface_data->prototype_class_definition, parent_prototype, + NULL); + bool success = JS_DefineProperties( + context, interface_data->prototype, prototype_properties); + DCHECK(success); + success = JS_DefineFunctions( + context, interface_data->prototype, prototype_functions); + DCHECK(success); + + JS::RootedObject function_prototype( + context, JS_GetFunctionPrototype(context, global_object)); + DCHECK(function_prototype); + // Create the Interface object. + interface_data->interface_object = JS_NewObjectWithGivenProto( + context, &interface_data->interface_object_class_definition, + function_prototype, NULL); + + // Add the InterfaceObject.name property. + JS::RootedObject rooted_interface_object( + context, interface_data->interface_object); + JS::RootedValue name_value(context); + const char name[] = + "GarbageCollectionTestInterface"; + name_value.setString(JS_NewStringCopyZ(context, name)); + success = + JS_DefineProperty(context, rooted_interface_object, "name", name_value, + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_READONLY); + DCHECK(success); + + // Add the InterfaceObject.length property. It is set to the length of the + // shortest argument list of all overload constructors. + JS::RootedValue length_value(context); + length_value.setInt32(0); + success = + JS_DefineProperty(context, rooted_interface_object, "length", + length_value, JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_READONLY); + DCHECK(success); + + // Define interface object properties (including constants). + success = JS_DefineProperties(context, rooted_interface_object, + interface_object_properties); + DCHECK(success); + // Define interface object functions (static). + success = JS_DefineFunctions(context, rooted_interface_object, + interface_object_functions); + DCHECK(success); + + + // Set the Prototype.constructor and Constructor.prototype properties. + DCHECK(interface_data->interface_object); + DCHECK(interface_data->prototype); + JS::RootedObject rooted_prototype(context, interface_data->prototype); + success = JS_LinkConstructorAndPrototype( + context, + rooted_interface_object, + rooted_prototype); + DCHECK(success); +} + +InterfaceData* GetInterfaceData(JSContext* context) { + MozjsGlobalObjectProxy* global_object_proxy = + static_cast<MozjsGlobalObjectProxy*>(JS_GetContextPrivate(context)); + // Use the address of the properties definition for this interface as a + // unique key for looking up the InterfaceData for this interface. + intptr_t key = reinterpret_cast<intptr_t>(&own_properties); + InterfaceData* interface_data = global_object_proxy->GetInterfaceData(key); + if (!interface_data) { + interface_data = CreateCachedInterfaceData(); + DCHECK(interface_data); + global_object_proxy->CacheInterfaceData(key, interface_data); + DCHECK_EQ(interface_data, global_object_proxy->GetInterfaceData(key)); + } + return interface_data; +} + +} // namespace + +// static +JSObject* MozjsGarbageCollectionTestInterface::CreateProxy( + JSContext* context, const scoped_refptr<Wrappable>& wrappable) { + DCHECK(MozjsGlobalObjectProxy::GetFromContext(context)); + JS::RootedObject global_object( + context, + MozjsGlobalObjectProxy::GetFromContext(context)->global_object()); + DCHECK(global_object); + + InterfaceData* interface_data = GetInterfaceData(context); + JS::RootedObject prototype(context, GetPrototype(context, global_object)); + DCHECK(prototype); + JS::RootedObject new_object(context, JS_NewObjectWithGivenProto( + context, &interface_data->instance_class_definition, prototype, NULL)); + DCHECK(new_object); + JS::RootedObject proxy(context, + ProxyHandler::NewProxy(context, new_object, prototype, NULL, + proxy_handler.Pointer())); + WrapperPrivate::AddPrivateData(context, proxy, wrappable); + return proxy; +} + +//static +const JSClass* MozjsGarbageCollectionTestInterface::PrototypeClass( + JSContext* context) { + DCHECK(MozjsGlobalObjectProxy::GetFromContext(context)); + JS::RootedObject global_object( + context, + MozjsGlobalObjectProxy::GetFromContext(context)->global_object()); + DCHECK(global_object); + + JS::RootedObject prototype(context, GetPrototype(context, global_object)); + JSClass* proto_class = JS_GetClass(*prototype.address()); + return proto_class; +} + +// static +JSObject* MozjsGarbageCollectionTestInterface::GetPrototype( + JSContext* context, JS::HandleObject global_object) { + DCHECK(JS_IsGlobalObject(global_object)); + + InterfaceData* interface_data = GetInterfaceData(context); + if (!interface_data->prototype) { + // Create new prototype that has all the props and methods + InitializePrototypeAndInterfaceObject( + interface_data, context, global_object); + } + DCHECK(interface_data->prototype); + return interface_data->prototype; +} + +// static +JSObject* MozjsGarbageCollectionTestInterface::GetInterfaceObject( + JSContext* context, JS::HandleObject global_object) { + DCHECK(JS_IsGlobalObject(global_object)); + + InterfaceData* interface_data = GetInterfaceData(context); + if (!interface_data->interface_object) { + InitializePrototypeAndInterfaceObject( + interface_data, context, global_object); + } + DCHECK(interface_data->interface_object); + return interface_data->interface_object; +} + + +namespace { +JSBool Constructor(JSContext* context, unsigned int argc, JS::Value* vp) { + MozjsExceptionState exception_state(context); + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + + scoped_refptr<GarbageCollectionTestInterface> new_object = + new GarbageCollectionTestInterface(); + JS::RootedValue result_value(context); + ToJSValue(context, new_object, &result_value); + DCHECK(result_value.isObject()); + JS::RootedObject result_object(context, JSVAL_TO_OBJECT(result_value)); + args.rval().setObject(*result_object); + return true; +} +} // namespace + + +} // namespace testing +} // namespace bindings +} // namespace cobalt
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.h b/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.h new file mode 100644 index 0000000..9cd73f4 --- /dev/null +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.h
@@ -0,0 +1,54 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY! +// Auto-generated from template: bindings/mozjs/templates/interface.h.template + +// clang-format off + +#ifndef MozjsGarbageCollectionTestInterface_h +#define MozjsGarbageCollectionTestInterface_h + +#include "base/hash_tables.h" +#include "base/lazy_instance.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" +#include "cobalt/base/polymorphic_downcast.h" +#include "cobalt/script/wrappable.h" +#include "cobalt/bindings/testing/garbage_collection_test_interface.h" + +#include "third_party/mozjs/js/src/jsapi.h" + +namespace cobalt { +namespace bindings { +namespace testing { + +class MozjsGarbageCollectionTestInterface { + public: + static JSObject* CreateProxy(JSContext* context, + const scoped_refptr<script::Wrappable>& wrappable); + static const JSClass* PrototypeClass(JSContext* context); + static JSObject* GetPrototype(JSContext* context, + JS::HandleObject global_object); + static JSObject* GetInterfaceObject(JSContext* context, + JS::HandleObject global_object); +}; + +} // namespace bindings +} // namespace testing +} // namespace cobalt + +#endif // MozjsGarbageCollectionTestInterface_h
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsIndexedGetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsIndexedGetterInterface.cc index b950039..b998abc 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsIndexedGetterInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsIndexedGetterInterface.cc
@@ -338,8 +338,7 @@ wrapper_private->wrappable<IndexedGetterInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -385,8 +384,7 @@ wrapper_private->wrappable<IndexedGetterInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -438,8 +436,7 @@ wrapper_private->wrappable<IndexedGetterInterface>().get(); const size_t kMinArguments = 2; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedGetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedGetterInterface.cc index f0e1c02..90f7900 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedGetterInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedGetterInterface.cc
@@ -316,8 +316,7 @@ wrapper_private->wrappable<NamedGetterInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -363,8 +362,7 @@ wrapper_private->wrappable<NamedGetterInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -416,8 +414,7 @@ wrapper_private->wrappable<NamedGetterInterface>().get(); const size_t kMinArguments = 2; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedIndexedGetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedIndexedGetterInterface.cc index bca075c..da310df 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedIndexedGetterInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedIndexedGetterInterface.cc
@@ -458,8 +458,7 @@ wrapper_private->wrappable<NamedIndexedGetterInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -511,8 +510,7 @@ wrapper_private->wrappable<NamedIndexedGetterInterface>().get(); const size_t kMinArguments = 2; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -570,8 +568,7 @@ wrapper_private->wrappable<NamedIndexedGetterInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -623,8 +620,7 @@ wrapper_private->wrappable<NamedIndexedGetterInterface>().get(); const size_t kMinArguments = 2; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsNullableTypesTestInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsNullableTypesTestInterface.cc index 5ac3c47..bd4bfc0 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsNullableTypesTestInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsNullableTypesTestInterface.cc
@@ -390,8 +390,7 @@ wrapper_private->wrappable<NullableTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -471,8 +470,7 @@ wrapper_private->wrappable<NullableTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -552,8 +550,7 @@ wrapper_private->wrappable<NullableTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -633,8 +630,7 @@ wrapper_private->wrappable<NullableTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsNumericTypesTestInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsNumericTypesTestInterface.cc index 4c45a29..d686d2b 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsNumericTypesTestInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsNumericTypesTestInterface.cc
@@ -650,8 +650,7 @@ wrapper_private->wrappable<NumericTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -731,8 +730,7 @@ wrapper_private->wrappable<NumericTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -812,8 +810,7 @@ wrapper_private->wrappable<NumericTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -859,8 +856,7 @@ wrapper_private->wrappable<NumericTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -974,8 +970,7 @@ wrapper_private->wrappable<NumericTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -1055,8 +1050,7 @@ wrapper_private->wrappable<NumericTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -1136,8 +1130,7 @@ wrapper_private->wrappable<NumericTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -1217,8 +1210,7 @@ wrapper_private->wrappable<NumericTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -1264,8 +1256,7 @@ wrapper_private->wrappable<NumericTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -1379,8 +1370,7 @@ wrapper_private->wrappable<NumericTypesTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsOperationsTestInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsOperationsTestInterface.cc index 20a6a3c..aac9e9a 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsOperationsTestInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsOperationsTestInterface.cc
@@ -326,8 +326,7 @@ wrapper_private->wrappable<OperationsTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -511,8 +510,7 @@ wrapper_private->wrappable<OperationsTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -558,8 +556,7 @@ wrapper_private->wrappable<OperationsTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -605,8 +602,7 @@ wrapper_private->wrappable<OperationsTestInterface>().get(); const size_t kMinArguments = 3; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -676,8 +672,7 @@ wrapper_private->wrappable<OperationsTestInterface>().get(); const size_t kMinArguments = 3; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -781,8 +776,7 @@ // http://heycam.github.io/webidl/#dfn-overload-resolution-algorithm // 4. If S is empty, then throw a TypeError. MozjsExceptionState exception_state(context); - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Invalid number of arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } @@ -810,8 +804,7 @@ wrapper_private->wrappable<OperationsTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -857,8 +850,7 @@ wrapper_private->wrappable<OperationsTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -906,8 +898,7 @@ // http://heycam.github.io/webidl/#dfn-overload-resolution-algorithm // 4. If S is empty, then throw a TypeError. MozjsExceptionState exception_state(context); - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Invalid number of arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } @@ -1103,8 +1094,7 @@ wrapper_private->wrappable<OperationsTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -1178,8 +1168,7 @@ wrapper_private->wrappable<OperationsTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -1225,8 +1214,7 @@ wrapper_private->wrappable<OperationsTestInterface>().get(); const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -1256,8 +1244,7 @@ const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -1287,8 +1274,7 @@ const size_t kMinArguments = 2; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -1349,8 +1335,7 @@ // http://heycam.github.io/webidl/#dfn-overload-resolution-algorithm // 4. If S is empty, then throw a TypeError. MozjsExceptionState exception_state(context); - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Invalid number of arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; }
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsSingleOperationInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsSingleOperationInterface.cc index 38b509c..aaf933d 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsSingleOperationInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsSingleOperationInterface.cc
@@ -58,6 +58,7 @@ bool* had_exception) const { bool success = false; base::optional<int32_t > cobalt_return_value; + JSExceptionState* previous_exception_state = JS_SaveExceptionState(context_); // This could be set to NULL if it was garbage collected. JS::RootedObject implementing_object(context_, implementing_object_.Get()); @@ -100,6 +101,7 @@ } *had_exception = !success; + JS_RestoreExceptionState(context_, previous_exception_state); return cobalt_return_value; }
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsStaticPropertiesInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsStaticPropertiesInterface.cc index fe8a2ad..e913609 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsStaticPropertiesInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsStaticPropertiesInterface.cc
@@ -245,8 +245,7 @@ const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -276,8 +275,7 @@ const size_t kMinArguments = 1; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -307,8 +305,7 @@ const size_t kMinArguments = 3; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -362,8 +359,7 @@ const size_t kMinArguments = 3; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } // Non-optional arguments @@ -467,8 +463,7 @@ // http://heycam.github.io/webidl/#dfn-overload-resolution-algorithm // 4. If S is empty, then throw a TypeError. MozjsExceptionState exception_state(context); - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Invalid number of arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; }
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierAnonymousOperationInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierAnonymousOperationInterface.cc index bf5fab5..0646f33 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierAnonymousOperationInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierAnonymousOperationInterface.cc
@@ -206,8 +206,7 @@ StringifierAnonymousOperationInterface* impl = wrapper_private->wrappable<StringifierAnonymousOperationInterface>().get(); if (!impl) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Stringifier problem."); + exception_state.SetSimpleException(cobalt::script::kStringifierProblem); NOTREACHED(); return false; }
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierAttributeInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierAttributeInterface.cc index e7ab56b..552e45b 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierAttributeInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierAttributeInterface.cc
@@ -250,8 +250,7 @@ StringifierAttributeInterface* impl = wrapper_private->wrappable<StringifierAttributeInterface>().get(); if (!impl) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Stringifier problem."); + exception_state.SetSimpleException(cobalt::script::kStringifierProblem); NOTREACHED(); return false; }
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierOperationInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierOperationInterface.cc index 915226a..db390ad 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierOperationInterface.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierOperationInterface.cc
@@ -240,8 +240,7 @@ StringifierOperationInterface* impl = wrapper_private->wrappable<StringifierOperationInterface>().get(); if (!impl) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Stringifier problem."); + exception_state.SetSimpleException(cobalt::script::kStringifierProblem); NOTREACHED(); return false; }
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsWindow.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsWindow.cc index a2f79de..59ff86f 100644 --- a/src/cobalt/bindings/generated/mozjs/testing/MozjsWindow.cc +++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsWindow.cc
@@ -46,6 +46,7 @@ #include "MozjsExceptionObjectInterface.h" #include "MozjsExceptionsInterface.h" #include "MozjsExtendedIDLAttributesInterface.h" +#include "MozjsGarbageCollectionTestInterface.h" #include "MozjsGetOpaqueRootInterface.h" #include "MozjsGlobalInterfaceParent.h" #include "MozjsImplementedInterface.h" @@ -90,6 +91,7 @@ #include "cobalt/bindings/testing/exception_object_interface.h" #include "cobalt/bindings/testing/exceptions_interface.h" #include "cobalt/bindings/testing/extended_idl_attributes_interface.h" +#include "cobalt/bindings/testing/garbage_collection_test_interface.h" #include "cobalt/bindings/testing/get_opaque_root_interface.h" #include "cobalt/bindings/testing/global_interface_parent.h" #include "cobalt/bindings/testing/implemented_interface.h" @@ -160,6 +162,7 @@ using cobalt::bindings::testing::ExceptionObjectInterface; using cobalt::bindings::testing::ExceptionsInterface; using cobalt::bindings::testing::ExtendedIDLAttributesInterface; +using cobalt::bindings::testing::GarbageCollectionTestInterface; using cobalt::bindings::testing::GetOpaqueRootInterface; using cobalt::bindings::testing::GlobalInterfaceParent; using cobalt::bindings::testing::ImplementedInterface; @@ -189,6 +192,7 @@ using cobalt::bindings::testing::MozjsExceptionObjectInterface; using cobalt::bindings::testing::MozjsExceptionsInterface; using cobalt::bindings::testing::MozjsExtendedIDLAttributesInterface; +using cobalt::bindings::testing::MozjsGarbageCollectionTestInterface; using cobalt::bindings::testing::MozjsGetOpaqueRootInterface; using cobalt::bindings::testing::MozjsGlobalInterfaceParent; using cobalt::bindings::testing::MozjsImplementedInterface; @@ -260,6 +264,11 @@ using cobalt::script::mozjs::WrapperPrivate; using cobalt::script::mozjs::WrapperFactory; using cobalt::script::Wrappable; +JSObject* DummyFunctor( + JSContext* context, const scoped_refptr<Wrappable>& wrappable) { + NOTREACHED(); + return NULL; +} } // namespace namespace cobalt { @@ -664,7 +673,6 @@ global_object_proxy->SetGlobalObjectProxyAndWrapper(proxy, wrappable); return proxy; } - //static const JSClass* MozjsWindow::PrototypeClass( JSContext* context) { @@ -818,6 +826,10 @@ base::Bind(MozjsExtendedIDLAttributesInterface::CreateProxy), base::Bind(MozjsExtendedIDLAttributesInterface::PrototypeClass)); wrapper_factory->RegisterWrappableType( + GarbageCollectionTestInterface::GarbageCollectionTestInterfaceWrappableType(), + base::Bind(MozjsGarbageCollectionTestInterface::CreateProxy), + base::Bind(MozjsGarbageCollectionTestInterface::PrototypeClass)); + wrapper_factory->RegisterWrappableType( GetOpaqueRootInterface::GetOpaqueRootInterfaceWrappableType(), base::Bind(MozjsGetOpaqueRootInterface::CreateProxy), base::Bind(MozjsGetOpaqueRootInterface::PrototypeClass)); @@ -905,6 +917,10 @@ UnionTypesInterface::UnionTypesInterfaceWrappableType(), base::Bind(MozjsUnionTypesInterface::CreateProxy), base::Bind(MozjsUnionTypesInterface::PrototypeClass)); + wrapper_factory->RegisterWrappableType( + Window::WindowWrappableType(), + base::Bind(DummyFunctor), + base::Bind(MozjsWindow::PrototypeClass)); }
diff --git a/src/cobalt/bindings/mozjs/templates/callback-interface.cc.template b/src/cobalt/bindings/mozjs/templates/callback-interface.cc.template index 3a668f8..f307bbc 100644 --- a/src/cobalt/bindings/mozjs/templates/callback-interface.cc.template +++ b/src/cobalt/bindings/mozjs/templates/callback-interface.cc.template
@@ -56,6 +56,7 @@ {% if overload.type != 'void' %} {{overload.type}} cobalt_return_value; {% endif %} + JSExceptionState* previous_exception_state = JS_SaveExceptionState(context_); // This could be set to NULL if it was garbage collected. JS::RootedObject implementing_object(context_, implementing_object_.Get()); @@ -102,6 +103,7 @@ } *had_exception = !success; + JS_RestoreExceptionState(context_, previous_exception_state); {% if overload.type != 'void' %} return cobalt_return_value; {% endif %}
diff --git a/src/cobalt/bindings/mozjs/templates/interface.cc.template b/src/cobalt/bindings/mozjs/templates/interface.cc.template index 992ad67..4d4cf06 100644 --- a/src/cobalt/bindings/mozjs/templates/interface.cc.template +++ b/src/cobalt/bindings/mozjs/templates/interface.cc.template
@@ -85,6 +85,15 @@ {% endfor %} {% endif %} {% endblock enumeration_declarations %} +{% block top_level_unnamed_namespace %} +{% if is_global_interface %} +JSObject* DummyFunctor( + JSContext* context, const scoped_refptr<Wrappable>& wrappable) { + NOTREACHED(); + return NULL; +} +{% endif %} +{% endblock top_level_unnamed_namespace %} {% block implementation %} namespace { @@ -505,8 +514,7 @@ {{impl_class}}* impl = wrapper_private->wrappable<{{impl_class}}>().get(); if (!impl) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Stringifier problem."); + exception_state.SetSimpleException(cobalt::script::kStringifierProblem); NOTREACHED(); return false; } @@ -797,7 +805,6 @@ global_object_proxy->SetGlobalObjectProxyAndWrapper(proxy, wrappable); return proxy; } - {% else %} // static JSObject* {{binding_class}}::CreateProxy( @@ -904,9 +911,13 @@ {% if interface.conditional %} #if defined({{interface.conditional}}) {% endif %} - {# Don't register a create method for the global interface, since we do not - create a wrapper for it directly. #} - {% if interface.name != impl_class %} + {# Pass in a dummy CreateProxy for global interface #} + {% if interface.name == impl_class %} + wrapper_factory->RegisterWrappableType( + {{interface.name}}::{{interface.name}}WrappableType(), + base::Bind(DummyFunctor), + base::Bind(Mozjs{{interface.name}}::PrototypeClass)); + {% else %} wrapper_factory->RegisterWrappableType( {{interface.name}}::{{interface.name}}WrappableType(), base::Bind(Mozjs{{interface.name}}::CreateProxy), @@ -958,9 +969,7 @@ *out_enum = {{impl_class}}::{{value}}; }{% endfor %} else { // 2. If S is not one of E's enumeration values, then throw a TypeError. - exception_state-> - SetSimpleException(ExceptionState::kTypeError, - "Cannot convert JavaScript value to Enum."); + exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed); return; } }
diff --git a/src/cobalt/bindings/mozjs/templates/macros.cc.template b/src/cobalt/bindings/mozjs/templates/macros.cc.template index dd3bdca..2eac967 100644 --- a/src/cobalt/bindings/mozjs/templates/macros.cc.template +++ b/src/cobalt/bindings/mozjs/templates/macros.cc.template
@@ -115,8 +115,7 @@ {%- if non_optional_arguments|length > 0 %} const size_t kMinArguments = {{non_optional_arguments|length}}; if (args.length() < kMinArguments) { - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Not enough arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; } {% endif -%} @@ -388,7 +387,6 @@ // http://heycam.github.io/webidl/#dfn-overload-resolution-algorithm // 4. If S is empty, then throw a TypeError. MozjsExceptionState exception_state(context); - exception_state.SetSimpleException( - script::ExceptionState::kTypeError, "Invalid number of arguments."); + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); return false; {%- endmacro %}
diff --git a/src/cobalt/bindings/templates/interface-base.cc.template b/src/cobalt/bindings/templates/interface-base.cc.template index 9bc9ea7..5da3faa 100644 --- a/src/cobalt/bindings/templates/interface-base.cc.template +++ b/src/cobalt/bindings/templates/interface-base.cc.template
@@ -75,6 +75,8 @@ {% block enumeration_declarations %} {% endblock enumeration_declarations %} {% endif %} +{% block top_level_unnamed_namespace %} +{% endblock top_level_unnamed_namespace %} } // namespace namespace cobalt {
diff --git a/src/cobalt/bindings/testing/GarbageCollectionTestInterface.idl b/src/cobalt/bindings/testing/GarbageCollectionTestInterface.idl new file mode 100644 index 0000000..e60c747 --- /dev/null +++ b/src/cobalt/bindings/testing/GarbageCollectionTestInterface.idl
@@ -0,0 +1,18 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +[Constructor] +interface GarbageCollectionTestInterface { +};
diff --git a/src/cobalt/bindings/testing/exceptions_bindings_test.cc b/src/cobalt/bindings/testing/exceptions_bindings_test.cc index 13d6d0e..71ef437 100644 --- a/src/cobalt/bindings/testing/exceptions_bindings_test.cc +++ b/src/cobalt/bindings/testing/exceptions_bindings_test.cc
@@ -14,6 +14,7 @@ * limitations under the License. */ +#include "base/stringprintf.h" #include "cobalt/bindings/testing/bindings_test_base.h" #include "cobalt/bindings/testing/exception_object_interface.h" #include "cobalt/bindings/testing/exceptions_interface.h" @@ -38,16 +39,13 @@ class SimpleExceptionSetter { public: - SimpleExceptionSetter(script::ExceptionState::SimpleExceptionType type, - const std::string& message) - : type_(type), message_(message) {} + explicit SimpleExceptionSetter(script::MessageType type) : type_(type) {} void FireException(script::ExceptionState* exception) { - exception->SetSimpleException(type_, message_); + exception->SetSimpleException(type_); } private: - script::ExceptionState::SimpleExceptionType type_; - std::string message_; + script::MessageType type_; }; class ExceptionObjectSetter { @@ -62,11 +60,20 @@ private: scoped_refptr<script::ScriptException> exception_object_; }; + +std::string GetExceptionMessageString(script::MessageType message_type, ...) { + va_list arguments; + va_start(arguments, message_type); + std::string error_string = + base::StringPrintV(GetExceptionMessageFormat(message_type), arguments); + va_end(arguments); + return error_string; +} + } // namespace TEST_F(ExceptionsBindingsTest, ThrowExceptionFromConstructor) { - SimpleExceptionSetter exception_setter(script::ExceptionState::kError, - "generic error"); + SimpleExceptionSetter exception_setter(script::kSimpleError); EXPECT_CALL(ExceptionsInterface::constructor_implementation_mock.Get(), Constructor(_)) .WillOnce( @@ -74,23 +81,21 @@ std::string result; EXPECT_FALSE(EvaluateScript("var foo = new ExceptionsInterface();", &result)); - EXPECT_SUBSTRING("Error: generic error", result.c_str()); + EXPECT_SUBSTRING("Error", result.c_str()); } TEST_F(ExceptionsBindingsTest, ThrowExceptionFromOperation) { - SimpleExceptionSetter exception_setter(script::ExceptionState::kTypeError, - "this is a type error"); + SimpleExceptionSetter exception_setter(script::kSimpleTypeError); EXPECT_CALL(test_mock(), FunctionThrowsException(_)).WillOnce( Invoke(&exception_setter, &SimpleExceptionSetter::FireException)); std::string result; EXPECT_FALSE(EvaluateScript("test.functionThrowsException();", &result)); - EXPECT_SUBSTRING("TypeError: this is a type error", result.c_str()); + EXPECT_SUBSTRING("TypeError", result.c_str()); } TEST_F(ExceptionsBindingsTest, ThrowExceptionFromGetter) { - SimpleExceptionSetter exception_setter(script::ExceptionState::kRangeError, - "this is a range error"); + SimpleExceptionSetter exception_setter(script::kSimpleRangeError); EXPECT_CALL(test_mock(), attribute_throws_exception(_)).WillOnce( DoAll(Invoke(&exception_setter, &SimpleExceptionSetter::FireException), Return(false))); @@ -98,12 +103,11 @@ std::string result; EXPECT_FALSE( EvaluateScript("var foo = test.attributeThrowsException;", &result)); - EXPECT_SUBSTRING("RangeError: this is a range error", result.c_str()); + EXPECT_SUBSTRING("RangeError", result.c_str()); } TEST_F(ExceptionsBindingsTest, ThrowExceptionFromSetter) { - SimpleExceptionSetter exception_setter( - script::ExceptionState::kReferenceError, "this is a reference error"); + SimpleExceptionSetter exception_setter(script::kSimpleReferenceError); EXPECT_CALL(test_mock(), set_attribute_throws_exception(_, _)) .WillOnce(WithArg<1>( Invoke(&exception_setter, &SimpleExceptionSetter::FireException))); @@ -111,7 +115,7 @@ std::string result; EXPECT_FALSE( EvaluateScript("test.attributeThrowsException = true;", &result)); - EXPECT_SUBSTRING("ReferenceError: this is a reference error", result.c_str()); + EXPECT_SUBSTRING("ReferenceError", result.c_str()); } TEST_F(ExceptionsBindingsTest, ThrowExceptionObject) { @@ -144,6 +148,16 @@ EXPECT_STREQ("the message", result.c_str()); } +TEST_F(ExceptionsBindingsTest, GetExceptionMessageStringTest) { + std::string error_message = + GetExceptionMessageString(script::kWrongByteLengthMultiple, 8); + EXPECT_STREQ("Byte length should be a multiple of 8.", error_message.c_str()); + error_message = + GetExceptionMessageString(script::kWrongByteOffsetMultiple, 16); + EXPECT_STREQ("Byte offset should be a multiple of 16.", + error_message.c_str()); +} + } // namespace testing } // namespace bindings } // namespace cobalt
diff --git a/src/cobalt/bindings/testing/garbage_collection_test.cc b/src/cobalt/bindings/testing/garbage_collection_test.cc new file mode 100644 index 0000000..b73dce0 --- /dev/null +++ b/src/cobalt/bindings/testing/garbage_collection_test.cc
@@ -0,0 +1,80 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cobalt/bindings/testing/bindings_test_base.h" +#include "cobalt/bindings/testing/garbage_collection_test_interface.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace cobalt { +namespace bindings { +namespace testing { + +namespace { +typedef BindingsTestBase GarbageCollectionTest; +} // namespace + +TEST_F(GarbageCollectionTest, JSObjectHoldsReferenceToPlatformObject) { + EXPECT_EQ(GarbageCollectionTestInterface::instances().size(), 0); + EXPECT_TRUE( + EvaluateScript("var obj = new GarbageCollectionTestInterface();", NULL)); + + // Ensure that this is kept alive after GC is run. + EXPECT_EQ(GarbageCollectionTestInterface::instances().size(), 1); + CollectGarbage(); + EXPECT_EQ(GarbageCollectionTestInterface::instances().size(), 1); + + // Ensure that this is destroyed when there are no more references to it from + // JavaScript. + EXPECT_TRUE(EvaluateScript("var obj = undefined;", NULL)); + CollectGarbage(); +#if !defined(ENGINE_USES_CONSERVATIVE_ROOTING) + EXPECT_EQ(GarbageCollectionTestInterface::instances().size(), 0); +#endif +} + +TEST_F(GarbageCollectionTest, PreventGarbageCollection) { + EXPECT_EQ(GarbageCollectionTestInterface::instances().size(), 0); + EXPECT_TRUE( + EvaluateScript("var obj = new GarbageCollectionTestInterface();", NULL)); + + // Keep this instance alive using PreventGarbageCollection + ASSERT_EQ(GarbageCollectionTestInterface::instances().size(), 1); + global_object_proxy_->PreventGarbageCollection( + make_scoped_refptr<script::Wrappable>( + GarbageCollectionTestInterface::instances()[0])); + // Remove the only reference to this object from JavaScript. + EXPECT_TRUE(EvaluateScript("var obj = undefined;", NULL)); + + // Ensure that the object is kept alive. + CollectGarbage(); + ASSERT_EQ(GarbageCollectionTestInterface::instances().size(), 1); + + // Allow this object to be garbage collected once more. + global_object_proxy_->AllowGarbageCollection( + make_scoped_refptr<script::Wrappable>( + GarbageCollectionTestInterface::instances()[0])); + + // Ensure that the object is destroyed by garbage collection. + CollectGarbage(); +#if !defined(ENGINE_USES_CONSERVATIVE_ROOTING) + EXPECT_EQ(GarbageCollectionTestInterface::instances().size(), 0); +#endif +} + +} // namespace testing +} // namespace bindings +} // namespace cobalt
diff --git a/src/cobalt/bindings/testing/garbage_collection_test_interface.cc b/src/cobalt/bindings/testing/garbage_collection_test_interface.cc new file mode 100644 index 0000000..5b25309 --- /dev/null +++ b/src/cobalt/bindings/testing/garbage_collection_test_interface.cc
@@ -0,0 +1,51 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cobalt/bindings/testing/garbage_collection_test_interface.h" + +#include "base/lazy_instance.h" + +namespace cobalt { +namespace bindings { +namespace testing { +namespace { +base::LazyInstance< + GarbageCollectionTestInterface::GarbageCollectionTestInterfaceVector> + instances; +} // namespace + +GarbageCollectionTestInterface::GarbageCollectionTestInterface() { + instances().push_back(this); +} + +GarbageCollectionTestInterface::~GarbageCollectionTestInterface() { + for (GarbageCollectionTestInterfaceVector::iterator it = instances().begin(); + it != instances().end(); ++it) { + if (*it == this) { + instances().erase(it); + break; + } + } +} + +GarbageCollectionTestInterface::GarbageCollectionTestInterfaceVector& +GarbageCollectionTestInterface::instances() { + return ::cobalt::bindings::testing::instances.Get(); +} + +} // namespace testing +} // namespace bindings +} // namespace cobalt
diff --git a/src/cobalt/bindings/testing/garbage_collection_test_interface.h b/src/cobalt/bindings/testing/garbage_collection_test_interface.h new file mode 100644 index 0000000..e4c0456 --- /dev/null +++ b/src/cobalt/bindings/testing/garbage_collection_test_interface.h
@@ -0,0 +1,44 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COBALT_BINDINGS_TESTING_GARBAGE_COLLECTION_TEST_INTERFACE_H_ +#define COBALT_BINDINGS_TESTING_GARBAGE_COLLECTION_TEST_INTERFACE_H_ + +#include <vector> + +#include "cobalt/script/wrappable.h" + +namespace cobalt { +namespace bindings { +namespace testing { + +class GarbageCollectionTestInterface : public script::Wrappable { + public: + GarbageCollectionTestInterface(); + ~GarbageCollectionTestInterface(); + + typedef std::vector<GarbageCollectionTestInterface*> + GarbageCollectionTestInterfaceVector; + static GarbageCollectionTestInterfaceVector& instances(); + + DEFINE_WRAPPABLE_TYPE(GarbageCollectionTestInterface); +}; + +} // namespace testing +} // namespace bindings +} // namespace cobalt + +#endif // COBALT_BINDINGS_TESTING_GARBAGE_COLLECTION_TEST_INTERFACE_H_
diff --git a/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp b/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp index 93f3f87..1f9a3fc 100644 --- a/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp +++ b/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp
@@ -18,6 +18,7 @@ ExceptionObjectInterface.idl ExceptionsInterface.idl ExtendedIDLAttributesInterface.idl +GarbageCollectionTestInterface.idl GetOpaqueRootInterface.idl GlobalInterfaceParent.idl IndexedGetterInterface.idl
diff --git a/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp b/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp index 7b3f83a..c1c7594 100644 --- a/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp +++ b/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp
@@ -18,6 +18,7 @@ ExceptionObjectInterface.idl ExceptionsInterface.idl ExtendedIDLAttributesInterface.idl +GarbageCollectionTestInterface.idl GetOpaqueRootInterface.idl GlobalInterfaceParent.idl IndexedGetterInterface.idl
diff --git a/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp b/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp index 5b5baa3..ff5e547 100644 --- a/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp +++ b/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp
@@ -18,6 +18,7 @@ ExceptionObjectInterface.idl ExceptionsInterface.idl ExtendedIDLAttributesInterface.idl +GarbageCollectionTestInterface.idl GetOpaqueRootInterface.idl GlobalInterfaceParent.idl IndexedGetterInterface.idl
diff --git a/src/cobalt/bindings/testing/testing.gyp b/src/cobalt/bindings/testing/testing.gyp index b820608..0286dcd 100644 --- a/src/cobalt/bindings/testing/testing.gyp +++ b/src/cobalt/bindings/testing/testing.gyp
@@ -45,6 +45,7 @@ 'ExceptionObjectInterface.idl', 'ExceptionsInterface.idl', 'ExtendedIDLAttributesInterface.idl', + 'GarbageCollectionTestInterface.idl', 'GetOpaqueRootInterface.idl', 'GlobalInterfaceParent.idl', 'IndexedGetterInterface.idl', @@ -102,6 +103,7 @@ 'constants_interface.cc', 'constructor_interface.cc', 'exceptions_interface.cc', + 'garbage_collection_test_interface.cc', 'named_constructor_interface.cc', 'operations_test_interface.cc', 'put_forwards_interface.cc', @@ -129,6 +131,7 @@ 'enumeration_bindings_test.cc', 'exceptions_bindings_test.cc', 'extended_attributes_test.cc', + 'garbage_collection_test.cc', 'getter_setter_test.cc', 'get_opaque_root_test.cc', 'global_interface_bindings_test.cc',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc index 9dfbf01..55e33b9 100644 --- a/src/cobalt/browser/browser_module.cc +++ b/src/cobalt/browser/browser_module.cc
@@ -119,7 +119,7 @@ array_buffer_cache_(new dom::ArrayBuffer::Cache(3 * 1024 * 1024)), #endif // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR) media_module_(media::MediaModule::Create( - renderer_module_.render_target()->GetSize(), + system_window, renderer_module_.render_target()->GetSize(), renderer_module_.pipeline()->GetResourceProvider(), options.media_module_options)), network_module_(&storage_manager_, system_window->event_dispatcher(), @@ -174,7 +174,8 @@ debug_console_.reset(new DebugConsole( base::Bind(&BrowserModule::OnDebugConsoleRenderTreeProduced, base::Unretained(this)), - media_module_.get(), &network_module_, system_window->GetWindowSize(), + media_module_.get(), &network_module_, + renderer_module_.render_target()->GetSize(), renderer_module_.pipeline()->GetResourceProvider(), kLayoutMaxRefreshFrequencyInHz, base::Bind(&BrowserModule::GetDebugServer, base::Unretained(this))));
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h index 4ae4fb4..742a4d3 100644 --- a/src/cobalt/browser/browser_module.h +++ b/src/cobalt/browser/browser_module.h
@@ -37,6 +37,7 @@ #include "cobalt/network/network_module.h" #include "cobalt/renderer/renderer_module.h" #include "cobalt/storage/storage_manager.h" +#include "cobalt/system_window/system_window.h" #include "cobalt/webdriver/session_driver.h" #include "googleurl/src/gurl.h" #if defined(ENABLE_DEBUG_CONSOLE)
diff --git a/src/cobalt/browser/debug_console/console_values.js b/src/cobalt/browser/debug_console/console_values.js index 66e37be..c773ee2 100644 --- a/src/cobalt/browser/debug_console/console_values.js +++ b/src/cobalt/browser/debug_console/console_values.js
@@ -21,11 +21,18 @@ // Default key used for auto-save, or if the user doesn't specify another. this.DEFAULT_KEY = 'default'; // Reduced space-separated list of CVal prefixes to display at start-up. - this.DEFAULT_ACTIVE_SET = 'Cobalt DevTools Memory'; + this.DEFAULT_ACTIVE_SET = + 'Cobalt DevTools Memory.CPU Memory.MainWebModule ' + + 'Event.Duration.MainWebModule.KeyDown Renderer.Rasterize.Duration'; + var names = window.debugHub.getConsoleValueNames(); this.allCVals = names.split(' '); - this.initActiveSet(); - this.cvalTree = this.buildTree(this.activeCVals); + // If true, we will always pull our list of CVals from + // this.DEFAULT_ACTIVE_SET. + this.useDefaultActiveSet = true; + + // Do a single update to initialize everything. + this.update(); } // Console value tree node constructor @@ -152,6 +159,10 @@ // Updates the complete list of registered CVals. // If any active CVals are no longer registered, remove them from the active set. ConsoleValues.prototype.updateRegistered = function() { + if (this.useDefaultActiveSet) { + this.setActiveSetToDefault(); + } + var names = window.debugHub.getConsoleValueNames(); this.allCVals = names.split(' '); for (var i = 0; i < this.activeCVals.length; i++) { @@ -176,12 +187,6 @@ } } -// Initializes the active set of CVals from the default. -ConsoleValues.prototype.initActiveSet = function() { - this.activeCVals = []; - this.addActive(this.DEFAULT_ACTIVE_SET); -} - // Lists all registered cvals ConsoleValues.prototype.listAll = function() { var result = ''; @@ -237,6 +242,13 @@ // Saves the current active set using the web local storage. ConsoleValues.prototype.saveActiveSet = function(key) { + if (this.useDefaultActiveSet) { + // If we are using the default CVals and we go to save our CVal set, lock + // it in to the currently displayed list. + this.updateRegistered(); + this.useDefaultActiveSet = false; + } + if (!key || !key.length) { key = this.DEFAULT_KEY; } @@ -260,6 +272,8 @@ var longKey = this.KEY_PREFIX + '.' + key; var value = window.localStorage.getItem(longKey); if (value && value.length > 0) { + // If we load CVals from disk, we no longer use the default set. + this.useDefaultActiveSet = false; this.activeCVals = value.split(' '); return 'Loaded CVal display set from "' + longKey + '"'; } else { @@ -267,10 +281,22 @@ } } +// Sets the CVal active set to the set of CVals the match the default prefixes. +// Any of the registered console values that match one of the space-separated +ConsoleValues.prototype.setActiveSetToDefault = function() { + this.activeCVals = []; + this.updateActiveSet(this.addSingleActive, this.DEFAULT_ACTIVE_SET); +} + // Adds one or more CVals to the active list. // Any of the registered console values that match one of the space-separated // set of prefixes will be added to the active set. ConsoleValues.prototype.addActive = function(prefixesToMatch) { + if (this.useDefaultActiveSet) { + // If we modify our list of CVals, we should no longer rely on the defaults. + this.updateRegistered(); + this.useDefaultActiveSet = false; + } return this.updateActiveSet(this.addSingleActive, prefixesToMatch); } @@ -278,5 +304,10 @@ // Any of the registered console values that match one of the space-separated // set of prefixes will be removed from the active set. ConsoleValues.prototype.removeActive = function(prefixesToMatch) { + if (this.useDefaultActiveSet) { + // If we modify our list of CVals, we should no longer rely on the defaults. + this.updateRegistered(); + this.useDefaultActiveSet = false; + } return this.updateActiveSet(this.removeSingleActive, prefixesToMatch); }
diff --git a/src/cobalt/browser/testdata/media-element-demo/progressive-demo.html b/src/cobalt/browser/testdata/media-element-demo/progressive-demo.html new file mode 100644 index 0000000..2d10dde2 --- /dev/null +++ b/src/cobalt/browser/testdata/media-element-demo/progressive-demo.html
@@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> +<head> + <title>Progressive Demo</title> + <style> + body { + background-color: rgb(255, 255, 255); + color: #0047ab; + font-size: 100px; + } + .small { + margin: 100px; + border: 10px solid blue; + width: 960px; + height: 540px; + } + .big { + margin: 10px; + border: 10px solid blue; + width: 1280px; + height: 720px; + } + </style> +</head> +<body> + <div>Progressive Demo</div> + <video autoplay loop id="v" class="small" src="progressive.mp4"></video> + <script> + window.setInterval(function() { + if (document.getElementById('v').className === 'big') + document.getElementById('v').className = 'small'; + else + document.getElementById('v').className = 'big'; + }, 3000); + </script> +</body> +</html>
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc index 073f8b7..0ad6dc9 100644 --- a/src/cobalt/browser/web_module.cc +++ b/src/cobalt/browser/web_module.cc
@@ -23,6 +23,7 @@ #include "base/message_loop_proxy.h" #include "base/optional.h" #include "base/stringprintf.h" +#include "cobalt/base/address_sanitizer.h" #include "cobalt/base/tokens.h" #include "cobalt/browser/switches.h" #include "cobalt/browser/web_module_stat_tracker.h" @@ -37,6 +38,10 @@ namespace { +// The maximum number of element depth in the DOM tree. Elements at a level +// deeper than this could be discarded, and will not be rendered. +const int kDOMMaxElementDepth = 32; + #if defined(ENABLE_PARTIAL_LAYOUT_CONTROL) // Help string for the 'partial_layout' command. const char kPartialLayoutCommandShortHelp[] = @@ -243,6 +248,7 @@ DCHECK(css_parser_); dom_parser_.reset(new dom_parser::Parser( + kDOMMaxElementDepth, base::Bind(&WebModule::Impl::OnError, base::Unretained(this)))); DCHECK(dom_parser_); @@ -310,7 +316,7 @@ DCHECK(window_weak_); environment_settings_.reset(new dom::DOMSettings( - fetcher_factory_.get(), data.network_module, window_, + kDOMMaxElementDepth, fetcher_factory_.get(), data.network_module, window_, media_source_registry_.get(), javascript_engine_.get(), global_object_proxy_.get(), data.options.dom_settings_options)); DCHECK(environment_settings_); @@ -327,8 +333,8 @@ layout_manager_.reset(new layout::LayoutManager( window_.get(), base::Bind(&WebModule::Impl::OnRenderTreeProduced, base::Unretained(this)), - data.options.layout_trigger, data.layout_refresh_rate, - data.network_module->preferred_language(), + data.options.layout_trigger, data.dom_max_element_depth, + data.layout_refresh_rate, data.network_module->preferred_language(), web_module_stat_tracker_->layout_stat_tracker())); DCHECK(layout_manager_); @@ -545,22 +551,21 @@ : thread_(options.name.c_str()) { ConstructionData construction_data( initial_url, render_tree_produced_callback, error_callback, media_module, - network_module, window_dimensions, resource_provider, layout_refresh_rate, - options); + network_module, window_dimensions, resource_provider, kDOMMaxElementDepth, + layout_refresh_rate, options); + +#if defined(COBALT_BUILD_TYPE_DEBUG) + // Non-optimized builds require a bigger stack size. + const size_t kBaseStackSize = 2 * 1024 * 1024; +#else + const size_t kBaseStackSize = 256 * 1024; +#endif // Start the dedicated thread and create the internal implementation // object on that thread. -#if defined(ADDRESS_SANITIZER) - // ASAN requires a much bigger stack size here. - const int kStackSize = 4096 * 1024; -#elif defined(COBALT_BUILD_TYPE_DEBUG) - // Non-optimized builds require a bigger stack size. - const int kStackSize = 2 * 1024 * 1024; -#else - const int kStackSize = 256 * 1024; -#endif + size_t stack_size = kBaseStackSize + base::kAsanAdditionalStackSize; thread_.StartWithOptions( - base::Thread::Options(MessageLoop::TYPE_DEFAULT, kStackSize)); + base::Thread::Options(MessageLoop::TYPE_DEFAULT, stack_size)); DCHECK(message_loop()); message_loop()->PostTask(
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h index 3aeb2e2..07fc9b4 100644 --- a/src/cobalt/browser/web_module.h +++ b/src/cobalt/browser/web_module.h
@@ -185,7 +185,8 @@ network::NetworkModule* network_module, const math::Size& window_dimensions, render_tree::ResourceProvider* resource_provider, - float layout_refresh_rate, const Options& options) + int dom_max_element_depth, float layout_refresh_rate, + const Options& options) : initial_url(initial_url), render_tree_produced_callback(render_tree_produced_callback), error_callback(error_callback), @@ -193,6 +194,7 @@ network_module(network_module), window_dimensions(window_dimensions), resource_provider(resource_provider), + dom_max_element_depth(dom_max_element_depth), layout_refresh_rate(layout_refresh_rate), options(options) {} @@ -203,6 +205,7 @@ network::NetworkModule* network_module; math::Size window_dimensions; render_tree::ResourceProvider* resource_provider; + int dom_max_element_depth; float layout_refresh_rate; Options options; };
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id index 2eed42f..7eac871 100644 --- a/src/cobalt/build/build.id +++ b/src/cobalt/build/build.id
@@ -1 +1 @@ -10571 \ No newline at end of file +10943 \ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi index 04a0d5e..cfbf8bf 100644 --- a/src/cobalt/build/config/base.gypi +++ b/src/cobalt/build/config/base.gypi
@@ -50,13 +50,17 @@ # force a stub graphics implementation or software graphics implementation. # It can be one of the following options: # 'hardware' -- As much hardware acceleration of graphics commands as - # possible. + # possible. Required for 360 rendering. # 'software' -- Perform most rasterization using the CPU and only interact # with the GPU to send the final image to the output window. # 'stub' -- Stub graphics rasterization. A rasterizer object will # still be available and valid, but it will do nothing. 'rasterizer_type%': 'hardware', + # Modify this value to adjust the default rasterizer setting for your + # platform. + 'default_renderer_options_dependency%': '<(DEPTH)/cobalt/renderer/default_options_starboard.gyp:default_options', + # The variables allow changing the target type on platforms where the # native code may require an additional packaging step (ex. Android). 'gtest_target_type%': 'executable',
diff --git a/src/cobalt/cssom/compound_selector.cc b/src/cobalt/cssom/compound_selector.cc index bc2457c..22efc1d 100644 --- a/src/cobalt/cssom/compound_selector.cc +++ b/src/cobalt/cssom/compound_selector.cc
@@ -40,7 +40,10 @@ } // namespace -CompoundSelector::CompoundSelector() : left_combinator_(NULL) {} +CompoundSelector::CompoundSelector() + : left_combinator_(NULL), + has_pseudo_element_(false), + requires_rule_matching_verification_visit_(false) {} CompoundSelector::~CompoundSelector() {} @@ -51,6 +54,21 @@ void CompoundSelector::AppendSelector( scoped_ptr<SimpleSelector> simple_selector) { specificity_.AddFrom(simple_selector->GetSpecificity()); + has_pseudo_element_ = + has_pseudo_element_ || simple_selector->AsPseudoElement() != NULL; + + // There are two cases where the selectors require a visit: + // 1. There are multiple selectors. Gathering only tests against the first + // selector and the later selectors must also be verified to match. + // 2. The single selector's AlwaysRequiresRuleMatchingVerificationVisit() call + // returns true. This indicates that being gathered as a candidate is not + // sufficient to prove a match and that additional verification checks are + // required. + requires_rule_matching_verification_visit_ = + requires_rule_matching_verification_visit_ || + !simple_selectors_.empty() || + simple_selector->AlwaysRequiresRuleMatchingVerificationVisit(); + bool should_sort = !simple_selectors_.empty() && SimpleSelectorsLessThan(simple_selector.get(), simple_selectors_.back());
diff --git a/src/cobalt/cssom/compound_selector.h b/src/cobalt/cssom/compound_selector.h index 48e0a6c..b4e1121 100644 --- a/src/cobalt/cssom/compound_selector.h +++ b/src/cobalt/cssom/compound_selector.h
@@ -53,10 +53,12 @@ void AppendSelector(scoped_ptr<SimpleSelector> selector); const SimpleSelectors& simple_selectors() { return simple_selectors_; } PseudoElement* pseudo_element() { - for (SimpleSelectors::iterator iter = simple_selectors_.begin(); - iter != simple_selectors_.end(); ++iter) { - if ((*iter)->AsPseudoElement()) { - return (*iter)->AsPseudoElement(); + if (has_pseudo_element_) { + for (SimpleSelectors::iterator iter = simple_selectors_.begin(); + iter != simple_selectors_.end(); ++iter) { + if ((*iter)->AsPseudoElement()) { + return (*iter)->AsPseudoElement(); + } } } return NULL; @@ -70,6 +72,10 @@ Combinator* right_combinator() { return right_combinator_.get(); } void set_right_combinator(scoped_ptr<Combinator> combinator); + bool requires_rule_matching_verification_visit() const { + return requires_rule_matching_verification_visit_; + } + bool operator<(const CompoundSelector& that) const { if (simple_selectors_.size() < that.simple_selectors_.size()) { return true; @@ -109,6 +115,19 @@ scoped_ptr<Combinator> right_combinator_; SimpleSelectors simple_selectors_; Specificity specificity_; + bool has_pseudo_element_; + // This flag tracks whether or not during rule matching, after the initial + // candidate gathering phase, the simple selectors additional checks during + // the verification phase to determine a match; otherwise, the act of + // being gathered itself proves the match. + // There are two cases where the selectors require a visit: + // 1. There are multiple selectors. Gathering only tests against the first + // selector and the later selectors must also be verified to match. + // 2. The single selector's AlwaysRequiresRuleMatchingVerificationVisit() call + // returns true. This indicates that being gathered as a candidate is not + // sufficient to prove a match and that additional verification checks are + // required. + bool requires_rule_matching_verification_visit_; DISALLOW_COPY_AND_ASSIGN(CompoundSelector); };
diff --git a/src/cobalt/cssom/css_style_declaration.cc b/src/cobalt/cssom/css_style_declaration.cc index 6c170d5..047a0ab 100644 --- a/src/cobalt/cssom/css_style_declaration.cc +++ b/src/cobalt/cssom/css_style_declaration.cc
@@ -1066,7 +1066,19 @@ std::string CSSStyleDeclaration::GetPropertyValue( const std::string& property_name) { - return GetDeclaredPropertyValueStringByKey(GetPropertyKey(property_name)); + PropertyKey key = GetPropertyKey(property_name); + if (key > kMaxLonghandPropertyKey) { + // Shorthand properties are never directly stored as declared properties, + // but are expanded into their longhand property components during parsing. + // TODO: Implement serialization of css values, see + // https://www.w3.org/TR/cssom-1/#serializing-css-values + DCHECK_LE(key, kMaxEveryPropertyKey); + DLOG(WARNING) << "Unsupported property query for \"" << property_name + << "\": Returning of property value strings is only " + "supported for longhand properties."; + return std::string(); + } + return GetDeclaredPropertyValueStringByKey(key); } void CSSStyleDeclaration::SetPropertyValueStringByKey(
diff --git a/src/cobalt/cssom/not_pseudo_class.h b/src/cobalt/cssom/not_pseudo_class.h index bfbb661..812c4c6 100644 --- a/src/cobalt/cssom/not_pseudo_class.h +++ b/src/cobalt/cssom/not_pseudo_class.h
@@ -43,6 +43,10 @@ void Accept(SelectorVisitor* visitor) OVERRIDE; // From SimpleSelector. + bool AlwaysRequiresRuleMatchingVerificationVisit() const OVERRIDE { + return true; + } + void IndexSelectorTreeNode(SelectorTree::Node* parent_node, SelectorTree::Node* child_node, CombinatorType combinator) OVERRIDE;
diff --git a/src/cobalt/cssom/selector_tree.h b/src/cobalt/cssom/selector_tree.h index 8badb33..b7ab626 100644 --- a/src/cobalt/cssom/selector_tree.h +++ b/src/cobalt/cssom/selector_tree.h
@@ -49,7 +49,7 @@ class Node; - // This class can be used to store unique Nodes. It stores the Nodes in its + // This class can be used to store Nodes. It stores the Nodes in its // internal buffer whose size can be configured via template parameter. // After the internal buffer is used up the extra Nodes will be stored inside // the contained std::vector. @@ -75,13 +75,19 @@ }; NodeSet() : size_(0) {} - void insert(const Node* node) { - // Check if we already have it. - for (size_t i = 0; i < size_; ++i) { - if (GetNode(i) == node) { - return; + void insert(const Node* node, bool check_for_duplicate = false) { + // If |check_for_duplicate| is true, then check if the node is already + // contained. In nearly all cases, this check is unnecessary because it is + // already known that the node is not a duplicate. As a result, the caller + // must explicitly request the check when needed. + if (check_for_duplicate) { + for (size_t i = 0; i < size_; ++i) { + if (GetNode(i) == node) { + return; + } } } + if (size_ < InternalCacheSize) { nodes_[size_] = node; } else { @@ -90,9 +96,10 @@ ++size_; } template <class ConstIterator> - void insert(ConstIterator begin, ConstIterator end) { + void insert(ConstIterator begin, ConstIterator end, + bool check_for_duplicate = false) { while (begin != end) { - insert(*begin); + insert(*begin, check_for_duplicate); ++begin; } } @@ -149,7 +156,12 @@ typedef std::vector<SimpleSelectorNode> SimpleSelectorNodes; typedef std::vector<PseudoClassNode> PseudoClassNodes; - typedef std::map<base::Token, SimpleSelectorNodes> SelectorTextToNodesMap; + // The vast majority of SelectorTextToNodesMap objects have 4 or fewer + // selectors. However, they occasionally can number in the hundreds. Using + // a SmallMap with an array size of 4, allows both cases to be handled + // quickly. + typedef base::SmallMap<base::hash_map<base::Token, SimpleSelectorNodes>, 4> + SelectorTextToNodesMap; class Node { public: @@ -166,6 +178,7 @@ const PseudoClassNodes& pseudo_class_nodes() const { return pseudo_class_nodes_; } + bool HasAnyPseudoClass() const { return pseudo_class_mask_ != 0; } bool HasPseudoClass(PseudoClassType pseudo_class_type, CombinatorType combinator_type) const { return (pseudo_class_mask_ & @@ -180,8 +193,8 @@ (1u << (pseudo_class_type * kCombinatorCount + combinator_type)); } - const SelectorTextToNodesMap& selector_nodes_map() const { - return selector_nodes_map_; + const SelectorTextToNodesMap* selector_nodes_map() const { + return selector_nodes_map_.get(); } bool HasSimpleSelector(SimpleSelectorType simple_selector_type, CombinatorType combinator_type) const { @@ -191,9 +204,15 @@ void AppendSimpleSelector(base::Token name, SimpleSelectorType simple_selector_type, CombinatorType combinator_type, Node* node) { + // Create the SelectorTextToNodesMap if this is the first simple selector + // being appended. + if (!selector_nodes_map_) { + selector_nodes_map_.reset(new SelectorTextToNodesMap()); + } + SimpleSelectorNode child_node = {simple_selector_type, combinator_type, node}; - selector_nodes_map_[name].push_back(child_node); + (*selector_nodes_map_)[name].push_back(child_node); selector_mask_ |= (1u << (simple_selector_type * kCombinatorCount + combinator_type)); } @@ -214,8 +233,9 @@ // Sum of specificity from root to this node. Specificity cumulative_specificity_; - // Indexes for the children. - SelectorTextToNodesMap selector_nodes_map_; + // Indexes for the children. This is a scoped_ptr because the majority of + // nodes do not contain any children. + scoped_ptr<SelectorTextToNodesMap> selector_nodes_map_; // Bit mask used to quickly reject a certain selector type and combinator // combination. uint32 selector_mask_;
diff --git a/src/cobalt/cssom/simple_selector.h b/src/cobalt/cssom/simple_selector.h index 38d549e..675656d 100644 --- a/src/cobalt/cssom/simple_selector.h +++ b/src/cobalt/cssom/simple_selector.h
@@ -49,6 +49,10 @@ // Rest of public methods. virtual PseudoElement* AsPseudoElement() { return NULL; } + virtual bool AlwaysRequiresRuleMatchingVerificationVisit() const { + return false; + } + // Used to sort simple selectors when normalizing compound selector. SimpleSelectorType type() const { return type_; }
diff --git a/src/cobalt/dom/data_view.cc b/src/cobalt/dom/data_view.cc index 5e3a938..55fc772 100644 --- a/src/cobalt/dom/data_view.cc +++ b/src/cobalt/dom/data_view.cc
@@ -25,9 +25,7 @@ byte_offset_(0), byte_length_(buffer ? buffer->byte_length() : 0) { if (!buffer) { - exception_state->SetSimpleException( - script::ExceptionState::kTypeError, - "First argument to DataView constructor must be an ArrayBuffer."); + exception_state->SetSimpleException(script::kNotAnArrayBuffer); } } @@ -37,13 +35,9 @@ byte_offset_(byte_offset), byte_length_(buffer ? buffer->byte_length() - byte_offset : 0) { if (!buffer) { - exception_state->SetSimpleException( - script::ExceptionState::kTypeError, - "First argument to DataView constructor must be an ArrayBuffer."); + exception_state->SetSimpleException(script::kNotAnArrayBuffer); } else if (byte_offset_ > buffer_->byte_length()) { - exception_state->SetSimpleException( - script::ExceptionState::kRangeError, - "Start offset is outside the bounds of the buffer."); + exception_state->SetSimpleException(script::kOutsideBounds); } } @@ -51,16 +45,11 @@ uint32 byte_length, script::ExceptionState* exception_state) : buffer_(buffer), byte_offset_(byte_offset), byte_length_(byte_length) { if (!buffer) { - exception_state->SetSimpleException( - script::ExceptionState::kTypeError, - "First argument to DataView constructor must be an ArrayBuffer."); + exception_state->SetSimpleException(script::kNotAnArrayBuffer); } else if (byte_offset_ > buffer_->byte_length()) { - exception_state->SetSimpleException( - script::ExceptionState::kRangeError, - "Start offset is outside the bounds of the buffer."); + exception_state->SetSimpleException(script::kOutsideBounds); } else if (byte_offset_ + byte_length_ > buffer_->byte_length()) { - exception_state->SetSimpleException(script::ExceptionState::kRangeError, - "Invalid data view length."); + exception_state->SetSimpleException(script::kInvalidLength); } }
diff --git a/src/cobalt/dom/data_view.h b/src/cobalt/dom/data_view.h index 8aef023..4e9572e 100644 --- a/src/cobalt/dom/data_view.h +++ b/src/cobalt/dom/data_view.h
@@ -105,9 +105,7 @@ ElementType GetElement(uint32 byte_offset, bool little_endian, script::ExceptionState* exception_state) const { if (byte_offset + sizeof(ElementType) > byte_length_) { - exception_state->SetSimpleException( - script::ExceptionState::kRangeError, - "Offset is outside the bounds of the DataView."); + exception_state->SetSimpleException(script::kOutsideBounds); // The return value will be ignored. return ElementType(); } @@ -121,9 +119,7 @@ void SetElement(uint32 byte_offset, ElementType value, bool little_endian, script::ExceptionState* exception_state) { if (byte_offset + sizeof(ElementType) > byte_length_) { - exception_state->SetSimpleException( - script::ExceptionState::kRangeError, - "Offset is outside the bounds of the DataView."); + exception_state->SetSimpleException(script::kOutsideBounds); return; } CopyBytes(reinterpret_cast<uint8*>(&value), sizeof(value), little_endian,
diff --git a/src/cobalt/dom/data_view_test.cc b/src/cobalt/dom/data_view_test.cc index c1972f3..dd42134 100644 --- a/src/cobalt/dom/data_view_test.cc +++ b/src/cobalt/dom/data_view_test.cc
@@ -70,25 +70,25 @@ TEST(DataViewTest, ExceptionInConstructors) { StrictMock<MockExceptionState> exception_state; - script::ExceptionState::SimpleExceptionType exception_type; + script::MessageType message_type; scoped_refptr<ArrayBuffer> array_buffer = new ArrayBuffer(NULL, 10); - EXPECT_CALL(exception_state, SetSimpleException(_, _)) - .WillOnce(SaveArg<0>(&exception_type)); + EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) + .WillOnce(SaveArg<0>(&message_type)); scoped_refptr<DataView> data_view = new DataView(NULL, &exception_state); - EXPECT_EQ(script::ExceptionState::kTypeError, exception_type); + EXPECT_EQ(script::kTypeError, GetSimpleExceptionType(message_type)); - EXPECT_CALL(exception_state, SetSimpleException(_, _)) - .WillOnce(SaveArg<0>(&exception_type)); + EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) + .WillOnce(SaveArg<0>(&message_type)); data_view = new DataView(array_buffer, array_buffer->byte_length() + 1, &exception_state); - EXPECT_EQ(script::ExceptionState::kRangeError, exception_type); + EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type)); - EXPECT_CALL(exception_state, SetSimpleException(_, _)) - .WillOnce(SaveArg<0>(&exception_type)); + EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) + .WillOnce(SaveArg<0>(&message_type)); data_view = new DataView(array_buffer, 0, array_buffer->byte_length() + 1, &exception_state); - EXPECT_EQ(script::ExceptionState::kRangeError, exception_type); + EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type)); } TEST(DataViewTest, Getters) { @@ -122,18 +122,18 @@ TEST(DataViewTest, GettersWithException) { StrictMock<MockExceptionState> exception_state; - script::ExceptionState::SimpleExceptionType exception_type; + script::MessageType message_type; scoped_refptr<ArrayBuffer> array_buffer = new ArrayBuffer(NULL, 13); scoped_refptr<DataView> data_view = new DataView(array_buffer, &exception_state); #define DEFINE_DATA_VIEW_GETTER_WITH_EXCEPTION_TEST(DomType, CppType) \ { \ - EXPECT_CALL(exception_state, SetSimpleException(_, _)) \ - .WillOnce(SaveArg<0>(&exception_type)); \ + EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) \ + .WillOnce(SaveArg<0>(&message_type)); \ data_view->Get##DomType(array_buffer->byte_length() - sizeof(CppType) + 1, \ &exception_state); \ - EXPECT_EQ(script::ExceptionState::kRangeError, exception_type); \ + EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type)); \ } DATA_VIEW_ACCESSOR_FOR_EACH(DEFINE_DATA_VIEW_GETTER_WITH_EXCEPTION_TEST) #undef DEFINE_DATA_VIEW_GETTER_WITH_EXCEPTION_TEST @@ -188,18 +188,18 @@ TEST(DataViewTest, SettersWithException) { StrictMock<MockExceptionState> exception_state; - script::ExceptionState::SimpleExceptionType exception_type; + script::MessageType message_type; scoped_refptr<ArrayBuffer> array_buffer = new ArrayBuffer(NULL, 13); scoped_refptr<DataView> data_view = new DataView(array_buffer, &exception_state); #define DEFINE_DATA_VIEW_SETTER_WITH_EXCEPTION_TEST(DomType, CppType) \ { \ - EXPECT_CALL(exception_state, SetSimpleException(_, _)) \ - .WillOnce(SaveArg<0>(&exception_type)); \ + EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) \ + .WillOnce(SaveArg<0>(&message_type)); \ data_view->Set##DomType(array_buffer->byte_length() - sizeof(CppType) + 1, \ 0, &exception_state); \ - EXPECT_EQ(script::ExceptionState::kRangeError, exception_type); \ + EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type)); \ } DATA_VIEW_ACCESSOR_FOR_EACH(DEFINE_DATA_VIEW_SETTER_WITH_EXCEPTION_TEST) #undef DEFINE_DATA_VIEW_SETTER_WITH_EXCEPTION_TEST
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc index 0a294ac..8e3c80d 100644 --- a/src/cobalt/dom/document.cc +++ b/src/cobalt/dom/document.cc
@@ -357,10 +357,11 @@ void Document::Accept(ConstNodeVisitor* visitor) const { visitor->Visit(this); } scoped_refptr<Node> Document::Duplicate() const { - // Note: Documents should not be duplicated by clients, we just need to - // provide an implementation since Document inherits from Node. - NOTIMPLEMENTED(); - return NULL; + // For Document, copy Its encoding, content type, URL, its mode (quirks mode, + // limited quirks mode, or no-quirks mode), and its type (XML document or HTML + // document). + // https://www.w3.org/TR/dom/#concept-node-clone + return new Document(html_element_context_, Document::Options(url_as_gurl())); } scoped_refptr<HTMLHtmlElement> Document::html() const {
diff --git a/src/cobalt/dom/dom_settings.cc b/src/cobalt/dom/dom_settings.cc index 67b6701..894d524 100644 --- a/src/cobalt/dom/dom_settings.cc +++ b/src/cobalt/dom/dom_settings.cc
@@ -21,14 +21,16 @@ namespace cobalt { namespace dom { -DOMSettings::DOMSettings(loader::FetcherFactory* fetcher_factory, +DOMSettings::DOMSettings(const int max_dom_element_depth, + loader::FetcherFactory* fetcher_factory, network::NetworkModule* network_module, const scoped_refptr<Window>& window, MediaSource::Registry* media_source_registry, script::JavaScriptEngine* engine, script::GlobalObjectProxy* global_object_proxy, const Options& options) - : fetcher_factory_(fetcher_factory), + : max_dom_element_depth_(max_dom_element_depth), + fetcher_factory_(fetcher_factory), network_module_(network_module), window_(window), array_buffer_allocator_(options.array_buffer_allocator),
diff --git a/src/cobalt/dom/dom_settings.h b/src/cobalt/dom/dom_settings.h index ed4fbd8..ea6f74b 100644 --- a/src/cobalt/dom/dom_settings.h +++ b/src/cobalt/dom/dom_settings.h
@@ -57,7 +57,8 @@ ArrayBuffer::Cache* array_buffer_cache; }; - DOMSettings(loader::FetcherFactory* fetcher_factory, + DOMSettings(const int max_dom_element_depth, + loader::FetcherFactory* fetcher_factory, network::NetworkModule* network_module, const scoped_refptr<Window>& window, MediaSource::Registry* media_source_registry, @@ -66,6 +67,8 @@ const Options& options = Options()); ~DOMSettings() OVERRIDE; + int max_dom_element_depth() { return max_dom_element_depth_; } + void set_window(const scoped_refptr<Window>& window) { window_ = window; } scoped_refptr<Window> window() const { return window_; } @@ -97,6 +100,7 @@ virtual GURL base_url() const; private: + const int max_dom_element_depth_; loader::FetcherFactory* fetcher_factory_; network::NetworkModule* network_module_; scoped_refptr<Window> window_;
diff --git a/src/cobalt/dom/dom_string_map.cc b/src/cobalt/dom/dom_string_map.cc index 0172418..c7f8290 100644 --- a/src/cobalt/dom/dom_string_map.cc +++ b/src/cobalt/dom/dom_string_map.cc
@@ -138,8 +138,8 @@ if (attribute_name) { return element_->GetAttribute(*attribute_name); } else { - exception_state->SetSimpleException(script::ExceptionState::kSyntaxError, - property_name); + exception_state->SetSimpleException(script::kPropertySyntaxError, + property_name.c_str()); return base::nullopt; } } @@ -152,8 +152,8 @@ if (attribute_name) { element_->SetAttribute(*attribute_name, value); } else { - exception_state->SetSimpleException(script::ExceptionState::kSyntaxError, - property_name); + exception_state->SetSimpleException(script::kPropertySyntaxError, + property_name.c_str()); } }
diff --git a/src/cobalt/dom/dom_string_map_test.cc b/src/cobalt/dom/dom_string_map_test.cc index 5faca86..1825b19 100644 --- a/src/cobalt/dom/dom_string_map_test.cc +++ b/src/cobalt/dom/dom_string_map_test.cc
@@ -165,12 +165,12 @@ TEST_F(DOMStringMapTest, InvalidPropertyName) { EXPECT_CALL(exception_state_, - SetSimpleException(script::ExceptionState::kSyntaxError, _)); + SetSimpleExceptionVA(script::kPropertySyntaxError, _)); dom_string_map_->AnonymousNamedSetter("hyphen-lowercase", "Los Angeles", &exception_state_); EXPECT_CALL(exception_state_, - SetSimpleException(script::ExceptionState::kSyntaxError, _)); + SetSimpleExceptionVA(script::kPropertySyntaxError, _)); dom_string_map_->AnonymousNamedGetter("hyphen-lowercase", &exception_state_); }
diff --git a/src/cobalt/dom/dom_token_list.cc b/src/cobalt/dom/dom_token_list.cc index d2ece24..2002c79 100644 --- a/src/cobalt/dom/dom_token_list.cc +++ b/src/cobalt/dom/dom_token_list.cc
@@ -170,19 +170,6 @@ return result; } -base::Token DOMTokenList::NonNullItem(unsigned int index) const { - MaybeRefresh(); - - // 1. If index is equal to or greater than the number of tokens in tokens, - // return an empty string rather than NULL. - if (index >= tokens_.size()) { - return base::Token(); - } - - // 2. Return the indexth token in tokens. - return tokens_[index]; -} - bool DOMTokenList::ContainsValid(base::Token valid_token) const { MaybeRefresh(); @@ -201,6 +188,11 @@ return false; } +const std::vector<base::Token>& DOMTokenList::GetTokens() const { + MaybeRefresh(); + return tokens_; +} + DOMTokenList::~DOMTokenList() { GlobalStats::GetInstance()->Remove(this); } // Algorithm for RunUpdateSteps: @@ -230,8 +222,10 @@ element_node_generation_ = element_->node_generation(); std::string attribute = element_->GetAttribute(attr_name_).value_or(""); std::vector<std::string> tokens; + tokens.reserve(tokens_.size()); base::SplitStringAlongWhitespace(attribute, &tokens); tokens_.clear(); + tokens_.reserve(tokens.size()); for (size_t i = 0; i < tokens.size(); ++i) { tokens_.push_back(base::Token(tokens[i])); }
diff --git a/src/cobalt/dom/dom_token_list.h b/src/cobalt/dom/dom_token_list.h index 7f52b5e..abed102 100644 --- a/src/cobalt/dom/dom_token_list.h +++ b/src/cobalt/dom/dom_token_list.h
@@ -47,14 +47,13 @@ // Custom, not in any spec. - // This is a variation of Item that should only be called in cases where NULL - // is not needed for invalid indices. - base::Token NonNullItem(unsigned int index) const; - // This is a variation of Contains that should only be called in cases where // the token has already been validated. bool ContainsValid(base::Token valid_token) const; + // Returns a reference to the contained tokens for rapid iteration. + const std::vector<base::Token>& GetTokens() const; + // The associated element. Element* element() { return element_; }
diff --git a/src/cobalt/dom/element.cc b/src/cobalt/dom/element.cc index 7ccde0b..41204da 100644 --- a/src/cobalt/dom/element.cc +++ b/src/cobalt/dom/element.cc
@@ -117,7 +117,7 @@ return named_node_map; } -scoped_refptr<DOMTokenList> Element::class_list() { +const scoped_refptr<DOMTokenList>& Element::class_list() { if (!class_list_) { // Create a new instance and store a reference to it. Because of the // negative performance impact of having to constantly recreate DomTokenList
diff --git a/src/cobalt/dom/element.h b/src/cobalt/dom/element.h index 9176493..2c33a1e 100644 --- a/src/cobalt/dom/element.h +++ b/src/cobalt/dom/element.h
@@ -84,8 +84,7 @@ SetAttribute("class", value); } - scoped_refptr<DOMTokenList> class_list(); - + const scoped_refptr<DOMTokenList>& class_list(); scoped_refptr<NamedNodeMap> attributes(); base::optional<std::string> GetAttribute(const std::string& name) const;
diff --git a/src/cobalt/dom/element_test.cc b/src/cobalt/dom/element_test.cc index f2ea69d..30acb76 100644 --- a/src/cobalt/dom/element_test.cc +++ b/src/cobalt/dom/element_test.cc
@@ -305,13 +305,6 @@ EXPECT_EQ(std::string("d"), class_list->Item(1)); EXPECT_EQ(std::string("c"), class_list->Item(2)); EXPECT_EQ(base::nullopt, class_list->Item(3)); - - // Custom, not in any spec - // NonNullItem - EXPECT_EQ(std::string("b"), class_list->NonNullItem(0)); - EXPECT_EQ(std::string("d"), class_list->NonNullItem(1)); - EXPECT_EQ(std::string("c"), class_list->NonNullItem(2)); - EXPECT_EQ(std::string(""), class_list->NonNullItem(3)); } TEST_F(ElementTest, GetElementsByClassName) {
diff --git a/src/cobalt/dom/font_list.cc b/src/cobalt/dom/font_list.cc index ea90d51..f91dc45 100644 --- a/src/cobalt/dom/font_list.cc +++ b/src/cobalt/dom/font_list.cc
@@ -169,16 +169,7 @@ } float FontList::GetSpaceWidth() { - // The space width is lazily generated. If it hasn't been set yet, it's time - // to set it. - if (!is_space_width_set_) { - is_space_width_set_ = true; - const scoped_refptr<render_tree::Font>& primary_font = GetPrimaryFont(); - render_tree::GlyphIndex space_glyph = - primary_font->GetGlyphForCharacter(' '); - space_width_ = primary_font->GetGlyphWidth(space_glyph); - } - + GenerateSpaceWidth(); return space_width_; } @@ -292,10 +283,23 @@ void FontList::GenerateEllipsisInfo() { if (!is_ellipsis_info_set_) { - is_ellipsis_info_set_ = true; render_tree::GlyphIndex ellipsis_glyph = render_tree::kInvalidGlyphIndex; ellipsis_font_ = GetCharacterFont(GetEllipsisValue(), &ellipsis_glyph); ellipsis_width_ = ellipsis_font_->GetGlyphWidth(ellipsis_glyph); + + is_ellipsis_info_set_ = true; + } +} + +void FontList::GenerateSpaceWidth() { + if (!is_space_width_set_) { + const scoped_refptr<render_tree::Font>& primary_font = GetPrimaryFont(); + render_tree::GlyphIndex space_glyph = + primary_font->GetGlyphForCharacter(' '); + space_width_ = primary_font->GetGlyphWidth(space_glyph); + DCHECK_GT(space_width_, 0); + + is_space_width_set_ = true; } }
diff --git a/src/cobalt/dom/font_list.h b/src/cobalt/dom/font_list.h index 555b871..af2cdde 100644 --- a/src/cobalt/dom/font_list.h +++ b/src/cobalt/dom/font_list.h
@@ -156,7 +156,7 @@ // the width has not already been calculated, it lazily generates it. float GetEllipsisWidth(); - // Returns the width of the space in the firts font in the font list that + // Returns the width of the space in the first font in the font list that // supports the space character. In the case where the width has not already // been calculated, it lazily generates it. float GetSpaceWidth(); @@ -194,10 +194,14 @@ // |font_| and |character_map_| are non-NULL after this call. void RequestFont(size_t index); - // Lazily generates the ellipsis ellipsis font and ellipsis width. If the info - // is already generated, it is immediately returned. + // Lazily generates the ellipsis font and ellipsis width. If it is already + // generated then it immediately returns. void GenerateEllipsisInfo(); + // Lazily generates the space width. If it is already generated then it + // immediately returns. + void GenerateSpaceWidth(); + // The font cache, which provides both font family fonts and character // fallback fonts to the font list. FontCache* const font_cache_;
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc index 0d57ebd..2ed74fd 100644 --- a/src/cobalt/dom/html_element.cc +++ b/src/cobalt/dom/html_element.cc
@@ -394,7 +394,10 @@ // ancestors. scoped_refptr<HTMLElement> offset_parent_html_element = offset_parent_element->AsHTMLElement(); - DCHECK(offset_parent_html_element); + if (!offset_parent_html_element) { + return layout_boxes_->GetBorderEdgeTop(); + } + DCHECK(offset_parent_html_element->layout_boxes()); return layout_boxes_->GetBorderEdgeTop() - offset_parent_html_element->layout_boxes()->GetPaddingEdgeTop(); @@ -430,7 +433,10 @@ // ancestors. scoped_refptr<HTMLElement> offset_parent_html_element = offset_parent_element->AsHTMLElement(); - DCHECK(offset_parent_html_element); + if (!offset_parent_html_element) { + return layout_boxes_->GetBorderEdgeLeft(); + } + DCHECK(offset_parent_html_element->layout_boxes()); return layout_boxes_->GetBorderEdgeLeft() - offset_parent_html_element->layout_boxes()->GetPaddingEdgeLeft(); @@ -600,8 +606,9 @@ for (Element* element = first_element_child(); element; element = element->next_element_sibling()) { HTMLElement* html_element = element->AsHTMLElement(); - DCHECK(html_element); - html_element->InvalidateMatchingRulesRecursively(); + if (html_element) { + html_element->InvalidateMatchingRulesRecursively(); + } } // Invalidate matching rules on all following siblings if sibling combinators @@ -610,8 +617,9 @@ for (Element* element = next_element_sibling(); element; element = element->next_element_sibling()) { HTMLElement* html_element = element->AsHTMLElement(); - DCHECK(html_element); - html_element->InvalidateMatchingRulesRecursively(); + if (html_element) { + html_element->InvalidateMatchingRulesRecursively(); + } } } } @@ -646,10 +654,11 @@ for (Element* element = first_element_child(); element; element = element->next_element_sibling()) { HTMLElement* html_element = element->AsHTMLElement(); - DCHECK(html_element); - html_element->UpdateComputedStyleRecursively( - css_computed_style_declaration(), root_computed_style, - style_change_event_time, is_valid); + if (html_element) { + html_element->UpdateComputedStyleRecursively( + css_computed_style_declaration(), root_computed_style, + style_change_event_time, is_valid); + } } }
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h index 5e7fcae..2075d05 100644 --- a/src/cobalt/dom/html_element.h +++ b/src/cobalt/dom/html_element.h
@@ -91,10 +91,14 @@ // https://www.w3.org/TR/html5/dom.html#htmlelement class HTMLElement : public Element, public cssom::MutationObserver { public: + typedef cssom::SelectorTree::NodeSet<12> MatchingNodes; + typedef cssom::SelectorTree::NodeSet<40> DescendantPotentialNodes; + typedef cssom::SelectorTree::NodeSet<8> FollowingSiblingPotentialNodes; + struct RuleMatchingState { - cssom::SelectorTree::NodeSet<12> matching_nodes; - cssom::SelectorTree::NodeSet<40> descendant_potential_nodes; - cssom::SelectorTree::NodeSet<8> following_sibling_potential_nodes; + MatchingNodes matching_nodes; + DescendantPotentialNodes descendant_potential_nodes; + FollowingSiblingPotentialNodes following_sibling_potential_nodes; }; // Web API: HTMLElement
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc index 4f1bb38..3d7a596 100644 --- a/src/cobalt/dom/html_media_element.cc +++ b/src/cobalt/dom/html_media_element.cc
@@ -497,6 +497,7 @@ media_source_->SetPlayer(player_.get()); } node_document()->OnDOMMutation(); + InvalidateLayoutBoxesFromNodeAndAncestors(); } void HTMLMediaElement::ScheduleLoad() {
diff --git a/src/cobalt/dom/html_video_element.cc b/src/cobalt/dom/html_video_element.cc index 89d0f1e..1d25415 100644 --- a/src/cobalt/dom/html_video_element.cc +++ b/src/cobalt/dom/html_video_element.cc
@@ -23,6 +23,7 @@ namespace dom { using ::media::ShellVideoFrameProvider; +using ::media::WebMediaPlayer; const char HTMLVideoElement::kTagName[] = "video"; @@ -88,5 +89,9 @@ return player() ? player()->GetVideoFrameProvider() : NULL; } +WebMediaPlayer::SetBoundsCB HTMLVideoElement::GetSetBoundsCB() { + return player() ? player()->GetSetBoundsCB() : WebMediaPlayer::SetBoundsCB(); +} + } // namespace dom } // namespace cobalt
diff --git a/src/cobalt/dom/html_video_element.h b/src/cobalt/dom/html_video_element.h index 2ac5c3f..d9f0953 100644 --- a/src/cobalt/dom/html_video_element.h +++ b/src/cobalt/dom/html_video_element.h
@@ -21,6 +21,7 @@ #include "cobalt/dom/html_media_element.h" #include "cobalt/dom/video_playback_quality.h" +#include "cobalt/math/rect.h" #include "media/base/shell_video_frame_provider.h" namespace cobalt { @@ -56,6 +57,8 @@ // a better way to support concurrent video playbacks. scoped_refptr<ShellVideoFrameProvider> GetVideoFrameProvider(); + WebMediaPlayer::SetBoundsCB GetSetBoundsCB(); + DEFINE_WRAPPABLE_TYPE(HTMLVideoElement); private:
diff --git a/src/cobalt/dom/node.cc b/src/cobalt/dom/node.cc index 041836c..59aed34 100644 --- a/src/cobalt/dom/node.cc +++ b/src/cobalt/dom/node.cc
@@ -139,10 +139,12 @@ // https://www.w3.org/TR/2015/WD-dom-20150618/#dom-node-clonenode scoped_refptr<Node> Node::CloneNode(bool deep) const { scoped_refptr<Node> new_node = Duplicate(); + DCHECK(new_node); if (deep) { scoped_refptr<Node> child = first_child_; while (child) { scoped_refptr<Node> new_child = child->CloneNode(true); + DCHECK(new_child); new_node->AppendChild(new_child); child = child->next_sibling_; }
diff --git a/src/cobalt/dom/rule_matching.cc b/src/cobalt/dom/rule_matching.cc index 7dc27d3..411223f 100644 --- a/src/cobalt/dom/rule_matching.cc +++ b/src/cobalt/dom/rule_matching.cc
@@ -17,6 +17,7 @@ #include "cobalt/dom/rule_matching.h" #include <algorithm> +#include <vector> #include "base/bind.h" #include "base/callback.h" @@ -169,8 +170,7 @@ // tree. // https://www.w3.org/TR/selectors4/#type-selector void VisitTypeSelector(cssom::TypeSelector* type_selector) OVERRIDE { - if (!LowerCaseEqualsASCII(type_selector->element_name().c_str(), - element_->tag_name().c_str())) { + if (type_selector->element_name() != element_->tag_name()) { element_ = NULL; } } @@ -259,6 +259,7 @@ void VisitCompoundSelector( cssom::CompoundSelector* compound_selector) OVERRIDE { DCHECK_GT(compound_selector->simple_selectors().size(), 0U); + // Iterate through all the simple selectors. If any of the simple selectors // doesn't match, the compound selector doesn't match. for (cssom::CompoundSelector::SimpleSelectors::const_iterator @@ -305,6 +306,7 @@ if (!element) { return NULL; } + SelectorMatcher selector_matcher(element, matching_combinators); selector->Accept(&selector_matcher); return selector_matcher.element(); @@ -364,12 +366,10 @@ void GatherCandidateNodesFromMap( cssom::SimpleSelectorType simple_selector_type, cssom::CombinatorType combinator_type, - const SelectorTree::SelectorTextToNodesMap& map, base::Token key, + const SelectorTree::SelectorTextToNodesMap* map, base::Token key, SelectorTree::NodeSet<kRuleMatchingNodeSetSize>* candidate_nodes) { - // TODO: The hit rate here is only ~20%, use a Token specific map - // to pre-check if the key is in the map. - SelectorTree::SelectorTextToNodesMap::const_iterator it = map.find(key); - if (it != map.end()) { + SelectorTree::SelectorTextToNodesMap::const_iterator it = map->find(key); + if (it != map->end()) { const SelectorTree::SimpleSelectorNodes& nodes = it->second; for (SelectorTree::SimpleSelectorNodes::const_iterator nodes_iterator = nodes.begin(); @@ -403,6 +403,12 @@ HTMLElement* element, base::Callback<void(HTMLElement* element, const SelectorTree::Node*)> callback) { + // Gathering Phase: Generate candidate nodes from the node set. + SelectorTree::NodeSet<kRuleMatchingNodeSetSize> candidate_nodes; + + const std::vector<base::Token>& element_class_list = + element->class_list()->GetTokens(); + // Iterate through all nodes in node_set. for (typename NodeSet::const_iterator node_iterator = node_set.begin(); node_iterator != node_set.end(); ++node_iterator) { @@ -412,72 +418,87 @@ continue; } - SelectorTree::NodeSet<kRuleMatchingNodeSetSize> candidate_nodes; - // Gather candidate sets in node's children under the given combinator. - // Universal selector. - if (node->HasSimpleSelector(cssom::kUniversalSelector, combinator_type)) { - GatherCandidateNodesFromMap(cssom::kUniversalSelector, combinator_type, - node->selector_nodes_map(), base::Token(), - &candidate_nodes); - } + const SelectorTree::SelectorTextToNodesMap* selector_nodes_map = + node->selector_nodes_map(); + if (selector_nodes_map) { + // Universal selector. + if (node->HasSimpleSelector(cssom::kUniversalSelector, combinator_type)) { + GatherCandidateNodesFromMap(cssom::kUniversalSelector, combinator_type, + selector_nodes_map, base::Token(), + &candidate_nodes); + } - // Type selector. - if (node->HasSimpleSelector(cssom::kTypeSelector, combinator_type)) { - GatherCandidateNodesFromMap(cssom::kTypeSelector, combinator_type, - node->selector_nodes_map(), - element->tag_name(), &candidate_nodes); - } + // Type selector. + if (node->HasSimpleSelector(cssom::kTypeSelector, combinator_type)) { + GatherCandidateNodesFromMap(cssom::kTypeSelector, combinator_type, + selector_nodes_map, element->tag_name(), + &candidate_nodes); + } - // Class selector. - if (node->HasSimpleSelector(cssom::kClassSelector, combinator_type)) { - scoped_refptr<DOMTokenList> class_list = element->class_list(); - for (unsigned int index = 0; index < class_list->length(); ++index) { - GatherCandidateNodesFromMap( - cssom::kClassSelector, combinator_type, node->selector_nodes_map(), - class_list->NonNullItem(index), &candidate_nodes); + // Class selector. + if (node->HasSimpleSelector(cssom::kClassSelector, combinator_type)) { + for (size_t index = 0; index < element_class_list.size(); ++index) { + GatherCandidateNodesFromMap( + cssom::kClassSelector, combinator_type, selector_nodes_map, + element_class_list[index], &candidate_nodes); + } + } + + // Id selector. + if (node->HasSimpleSelector(cssom::kIdSelector, combinator_type)) { + GatherCandidateNodesFromMap(cssom::kIdSelector, combinator_type, + selector_nodes_map, element->id(), + &candidate_nodes); } } - // Id selector. - if (node->HasSimpleSelector(cssom::kIdSelector, combinator_type)) { - GatherCandidateNodesFromMap(cssom::kIdSelector, combinator_type, - node->selector_nodes_map(), element->id(), - &candidate_nodes); - } - - // Empty pseudo class. - if (node->HasPseudoClass(cssom::kEmptyPseudoClass, combinator_type) && - element->IsEmpty()) { - GatherCandidateNodesFromSet(cssom::kEmptyPseudoClass, combinator_type, - node->pseudo_class_nodes(), &candidate_nodes); - } - - // Focus pseudo class. - if (node->HasPseudoClass(cssom::kFocusPseudoClass, combinator_type) && - element->HasFocus()) { - GatherCandidateNodesFromSet(cssom::kFocusPseudoClass, combinator_type, - node->pseudo_class_nodes(), &candidate_nodes); - } - - // Not pseudo class. - if (node->HasPseudoClass(cssom::kNotPseudoClass, combinator_type)) { - GatherCandidateNodesFromSet(cssom::kNotPseudoClass, combinator_type, - node->pseudo_class_nodes(), &candidate_nodes); - } - - // Check all candidate nodes and run callback for matching nodes. - for (SelectorTree::NodeSet<kRuleMatchingNodeSetSize>::const_iterator - candidate_node_iterator = candidate_nodes.begin(); - candidate_node_iterator != candidate_nodes.end(); - ++candidate_node_iterator) { - const SelectorTree::Node* candidate_node = *candidate_node_iterator; - - if (MatchSelectorAndElement(candidate_node->compound_selector(), element, - false)) { - callback.Run(element, candidate_node); + if (node->HasAnyPseudoClass()) { + // Empty pseudo class. + if (node->HasPseudoClass(cssom::kEmptyPseudoClass, combinator_type) && + element->IsEmpty()) { + GatherCandidateNodesFromSet(cssom::kEmptyPseudoClass, combinator_type, + node->pseudo_class_nodes(), + &candidate_nodes); } + + // Focus pseudo class. + if (node->HasPseudoClass(cssom::kFocusPseudoClass, combinator_type) && + element->HasFocus()) { + GatherCandidateNodesFromSet(cssom::kFocusPseudoClass, combinator_type, + node->pseudo_class_nodes(), + &candidate_nodes); + } + + // Not pseudo class. + if (node->HasPseudoClass(cssom::kNotPseudoClass, combinator_type)) { + GatherCandidateNodesFromSet(cssom::kNotPseudoClass, combinator_type, + node->pseudo_class_nodes(), + &candidate_nodes); + } + } + } + + // Verifying Phase: Check all candidate nodes and run callback for matching + // nodes. + for (SelectorTree::NodeSet<kRuleMatchingNodeSetSize>::const_iterator + candidate_node_iterator = candidate_nodes.begin(); + candidate_node_iterator != candidate_nodes.end(); + ++candidate_node_iterator) { + const SelectorTree::Node* candidate_node = *candidate_node_iterator; + + // Verify that this node is a match: + // 1. If the candidate node's compound selector doesn't require a visit to + // verify the match, then the act of gathering the node as a candidate + // proved the match. + // 2. Otherwise, the node requires additional verification checks, so call + // MatchSelectorAndElement(). + if (!candidate_node->compound_selector() + ->requires_rule_matching_verification_visit() || + MatchSelectorAndElement(candidate_node->compound_selector(), element, + false)) { + callback.Run(element, candidate_node); } } } @@ -559,9 +580,20 @@ current_element->rule_matching_state()->descendant_potential_nodes.insert( selector_tree->root()); } - current_element->rule_matching_state()->descendant_potential_nodes.insert( - current_element->rule_matching_state()->matching_nodes.begin(), - current_element->rule_matching_state()->matching_nodes.end()); + // Walk all of the matching nodes, adding any that have a descendant + // combinator as a descendant potential node. Any missing that combinator can + // never match descendants. + for (dom::HTMLElement::MatchingNodes::const_iterator iter = + current_element->rule_matching_state()->matching_nodes.begin(); + iter != current_element->rule_matching_state()->matching_nodes.end(); + ++iter) { + if ((*iter)->HasCombinator(cssom::kDescendantCombinator)) { + // It is possible for the two lists to contain duplicate nodes, so only + // add the node if it isn't a duplicate. + current_element->rule_matching_state()->descendant_potential_nodes.insert( + *iter, true /*check_for_duplicate*/); + } + } // Calculate current element's following sibling potential nodes. if (selector_tree->has_sibling_combinators()) { @@ -573,10 +605,21 @@ previous_sibling->rule_matching_state() ->following_sibling_potential_nodes.end()); } - current_element->rule_matching_state() - ->following_sibling_potential_nodes.insert( - current_element->rule_matching_state()->matching_nodes.begin(), - current_element->rule_matching_state()->matching_nodes.end()); + // Walk all of the matching nodes, adding any that have a following sibling + // combinator as a following sibling potential node. Any missing that + // combinator can never match following siblings.. + for (dom::HTMLElement::MatchingNodes::const_iterator iter = + current_element->rule_matching_state()->matching_nodes.begin(); + iter != current_element->rule_matching_state()->matching_nodes.end(); + ++iter) { + if ((*iter)->HasCombinator(cssom::kFollowingSiblingCombinator)) { + // It is possible for the two lists to contain duplicate nodes, so only + // add the node if it isn't a duplicate. + current_element->rule_matching_state() + ->following_sibling_potential_nodes.insert( + *iter, true /*check_for_duplicate*/); + } + } } }
diff --git a/src/cobalt/dom/time_ranges_test.cc b/src/cobalt/dom/time_ranges_test.cc index d31023a..4404477 100644 --- a/src/cobalt/dom/time_ranges_test.cc +++ b/src/cobalt/dom/time_ranges_test.cc
@@ -30,15 +30,10 @@ dom_exception_ = make_scoped_refptr( base::polymorphic_downcast<DOMException*>(exception.get())); } - void SetSimpleException( - script::ExceptionState::SimpleExceptionType simple_exception_type, - const std::string& message) OVERRIDE { - simple_exception_ = simple_exception_type; - simple_exception_message_ = message; + void SetSimpleException(script::MessageType /*message_type*/, ...) OVERRIDE { + // no-op } scoped_refptr<DOMException> dom_exception_; - script::ExceptionState::SimpleExceptionType simple_exception_; - std::string simple_exception_message_; }; } // namespace
diff --git a/src/cobalt/dom/typed_array.h b/src/cobalt/dom/typed_array.h index 8cafc15..266e002 100644 --- a/src/cobalt/dom/typed_array.h +++ b/src/cobalt/dom/typed_array.h
@@ -68,10 +68,8 @@ script::ExceptionState* exception_state) : ArrayBufferView(buffer) { if (buffer->byte_length() % kBytesPerElement != 0) { - exception_state->SetSimpleException( - script::ExceptionState::kRangeError, - base::StringPrintf("Byte length should be a multiple of %d", - kBytesPerElement)); + exception_state->SetSimpleException(script::kWrongByteLengthMultiple, + kBytesPerElement); } } @@ -80,15 +78,11 @@ : ArrayBufferView(buffer, byte_offset, buffer->byte_length() - byte_offset) { if (this->byte_offset() % kBytesPerElement != 0) { - exception_state->SetSimpleException( - script::ExceptionState::kRangeError, - base::StringPrintf("Byte offset should be a multiple of %d", - kBytesPerElement)); + exception_state->SetSimpleException(script::kWrongByteOffsetMultiple, + kBytesPerElement); } else if (buffer->byte_length() % kBytesPerElement != 0) { - exception_state->SetSimpleException( - script::ExceptionState::kRangeError, - base::StringPrintf("Byte length should be a multiple of %d", - kBytesPerElement)); + exception_state->SetSimpleException(script::kWrongByteLengthMultiple, + kBytesPerElement); } } @@ -96,14 +90,11 @@ uint32 length, script::ExceptionState* exception_state) : ArrayBufferView(buffer, byte_offset, length * kBytesPerElement) { if (this->byte_offset() % kBytesPerElement != 0) { - exception_state->SetSimpleException( - script::ExceptionState::kRangeError, - base::StringPrintf("Byte offset should be a multiple of %d", - kBytesPerElement)); + exception_state->SetSimpleException(script::kWrongByteOffsetMultiple, + kBytesPerElement); } else if (byte_offset + length * kBytesPerElement > buffer->byte_length()) { - exception_state->SetSimpleException(script::ExceptionState::kRangeError, - "Invalid typed array length"); + exception_state->SetSimpleException(script::kInvalidLength); } } @@ -127,8 +118,7 @@ void Set(const scoped_refptr<TypedArray>& source, uint32 offset, script::ExceptionState* exception_state) { if (offset >= length() || length() - offset < source->length()) { - exception_state->SetSimpleException(script::ExceptionState::kRangeError, - "Source is too large"); + exception_state->SetSimpleException(script::kInvalidLength); return; } uint32 source_offset = 0; @@ -239,6 +229,6 @@ \ private: \ DISALLOW_COPY_AND_ASSIGN(SubarrayType); \ - } + }; #endif // COBALT_DOM_TYPED_ARRAY_H_
diff --git a/src/cobalt/dom/typed_array_test.cc b/src/cobalt/dom/typed_array_test.cc index a305942..3f627c5 100644 --- a/src/cobalt/dom/typed_array_test.cc +++ b/src/cobalt/dom/typed_array_test.cc
@@ -337,10 +337,10 @@ } StrictMock<MockExceptionState> exception_state; - script::ExceptionState::SimpleExceptionType exception_type; + script::MessageType message_type; - EXPECT_CALL(exception_state, SetSimpleException(_, _)) - .WillOnce(SaveArg<0>(&exception_type)); + EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) + .WillOnce(SaveArg<0>(&message_type)); // Create an ArrayBuffer whose size isn't a multiple of // TypeParam::kBytesPerElement. @@ -350,30 +350,30 @@ // TypeParam::kBytesPerElement. scoped_refptr<TypeParam> array = new TypeParam(NULL, array_buffer, &exception_state); - EXPECT_EQ(script::ExceptionState::kRangeError, exception_type); + EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type)); - EXPECT_CALL(exception_state, SetSimpleException(_, _)) - .WillOnce(SaveArg<0>(&exception_type)); + EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) + .WillOnce(SaveArg<0>(&message_type)); // Neither the size of the array_buffer nor the byte_offset is a multiple of // TypeParam::kBytesPerElement, but SetSimpleException() should only be called // once. array = new TypeParam(NULL, array_buffer, 1, &exception_state); - EXPECT_EQ(script::ExceptionState::kRangeError, exception_type); + EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type)); - EXPECT_CALL(exception_state, SetSimpleException(_, _)) - .WillOnce(SaveArg<0>(&exception_type)); + EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) + .WillOnce(SaveArg<0>(&message_type)); // Now the size of the array_buffer is a multiple of // TypeParam::kBytesPerElement but the byte_offset isn't. array_buffer = new ArrayBuffer(NULL, TypeParam::kBytesPerElement); array = new TypeParam(NULL, array_buffer, 1, &exception_state); - EXPECT_EQ(script::ExceptionState::kRangeError, exception_type); + EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type)); - EXPECT_CALL(exception_state, SetSimpleException(_, _)) - .WillOnce(SaveArg<0>(&exception_type)); + EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) + .WillOnce(SaveArg<0>(&message_type)); // Both the size of the array_buffer and the byte_offset are multiples of // TypeParam::kBytesPerElement but array_buffer cannot hold 2 elements. array = new TypeParam(NULL, array_buffer, 0, 2, &exception_state); - EXPECT_EQ(script::ExceptionState::kRangeError, exception_type); + EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type)); // The size of the array_buffer isn't a multiple of // TypeParam::kBytesPerElement but the byte_offset is. Also the whole typed @@ -391,10 +391,10 @@ } StrictMock<MockExceptionState> exception_state; - script::ExceptionState::SimpleExceptionType exception_type; + script::MessageType message_type; - EXPECT_CALL(exception_state, SetSimpleException(_, _)) - .WillOnce(SaveArg<0>(&exception_type)); + EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) + .WillOnce(SaveArg<0>(&message_type)); static const uint32 kLength = 5; scoped_refptr<TypeParam> source = @@ -402,14 +402,14 @@ scoped_refptr<TypeParam> dest = new TypeParam(NULL, kLength, &exception_state); dest->Set(source, &exception_state); - EXPECT_EQ(script::ExceptionState::kRangeError, exception_type); + EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type)); - EXPECT_CALL(exception_state, SetSimpleException(_, _)) - .WillOnce(SaveArg<0>(&exception_type)); + EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) + .WillOnce(SaveArg<0>(&message_type)); source = new TypeParam(NULL, kLength, &exception_state); dest = new TypeParam(NULL, kLength + 1, &exception_state); dest->Set(source, 2, &exception_state); - EXPECT_EQ(script::ExceptionState::kRangeError, exception_type); + EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type)); dest->Set(source, 1, &exception_state); }
diff --git a/src/cobalt/dom_parser/html_decoder.cc b/src/cobalt/dom_parser/html_decoder.cc index ec744b2..ddda365 100644 --- a/src/cobalt/dom_parser/html_decoder.cc +++ b/src/cobalt/dom_parser/html_decoder.cc
@@ -28,12 +28,12 @@ const scoped_refptr<dom::Document>& document, const scoped_refptr<dom::Node>& parent_node, const scoped_refptr<dom::Node>& reference_node, - const base::SourceLocation& input_location, + const int dom_max_element_depth, const base::SourceLocation& input_location, const base::Closure& done_callback, const base::Callback<void(const std::string&)>& error_callback) - : libxml_html_parser_wrapper_( - new LibxmlHTMLParserWrapper(document, parent_node, reference_node, - input_location, error_callback)), + : libxml_html_parser_wrapper_(new LibxmlHTMLParserWrapper( + document, parent_node, reference_node, dom_max_element_depth, + input_location, error_callback)), document_(document), done_callback_(done_callback) {}
diff --git a/src/cobalt/dom_parser/html_decoder.h b/src/cobalt/dom_parser/html_decoder.h index 370184f..853fd47 100644 --- a/src/cobalt/dom_parser/html_decoder.h +++ b/src/cobalt/dom_parser/html_decoder.h
@@ -46,6 +46,7 @@ HTMLDecoder(const scoped_refptr<dom::Document>& document, const scoped_refptr<dom::Node>& parent_node, const scoped_refptr<dom::Node>& reference_node, + const int dom_max_element_depth, const base::SourceLocation& input_location, const base::Closure& done_callback, const base::Callback<void(const std::string&)>& error_callback);
diff --git a/src/cobalt/dom_parser/html_decoder_test.cc b/src/cobalt/dom_parser/html_decoder_test.cc index d9ca19a..e00a271 100644 --- a/src/cobalt/dom_parser/html_decoder_test.cc +++ b/src/cobalt/dom_parser/html_decoder_test.cc
@@ -36,6 +36,8 @@ namespace cobalt { namespace dom_parser { +const int kDOMMaxElementDepth = 32; + class MockErrorCallback : public base::Callback<void(const std::string&)> { public: MOCK_METHOD1(Run, void(const std::string&)); @@ -78,9 +80,9 @@ TEST_F(HTMLDecoderTest, CanParseEmptyDocument) { const std::string input = ""; html_decoder_.reset(new HTMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -100,9 +102,9 @@ unsigned char temp[] = {0x0, 0x0}; html_decoder_.reset(new HTMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(reinterpret_cast<char*>(temp), sizeof(temp)); html_decoder_->Finish(); @@ -118,9 +120,9 @@ TEST_F(HTMLDecoderTest, DISABLED_CanParseDocumentWithOnlySpaces) { const std::string input = " "; html_decoder_.reset(new HTMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -136,9 +138,9 @@ TEST_F(HTMLDecoderTest, DISABLED_CanParseDocumentWithOnlyHTML) { const std::string input = "<html>"; html_decoder_.reset(new HTMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -154,9 +156,9 @@ TEST_F(HTMLDecoderTest, CanParseDocumentWithOnlyBody) { const std::string input = "<body>"; html_decoder_.reset(new HTMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -172,9 +174,9 @@ TEST_F(HTMLDecoderTest, DecodingWholeDocumentShouldAddImpliedTags) { const std::string input = "<p></p>"; html_decoder_.reset(new HTMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -194,10 +196,10 @@ TEST_F(HTMLDecoderTest, DecodingDocumentFragmentShouldNotAddImpliedTags) { const std::string input = "<p></p>"; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -209,10 +211,10 @@ TEST_F(HTMLDecoderTest, CanParseAttributesWithAndWithoutValue) { const std::string input = "<div a b=2 c d></div>"; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -233,10 +235,10 @@ TEST_F(HTMLDecoderTest, CanParseIncompleteAttributesAssignment) { const std::string input = "<div a= b=2 c=></div>"; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -253,10 +255,10 @@ TEST_F(HTMLDecoderTest, CanParseSelfClosingTags) { const std::string input = "<p><p>"; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -271,10 +273,10 @@ TEST_F(HTMLDecoderTest, CanParseNormalCharacters) { const std::string input = "<p>text</p>"; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -290,10 +292,10 @@ TEST_F(HTMLDecoderTest, ShouldIgnoreNonUTF8Input) { unsigned char temp[] = {0xff, 0xff}; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(reinterpret_cast<char*>(temp), sizeof(temp)); html_decoder_->Finish(); @@ -303,10 +305,10 @@ // Test a decimal and hex escaped supplementary (not in BMP) character. TEST_F(HTMLDecoderTest, CanParseEscapedCharacters) { const std::string input = "<p>💩</p><p>💩</p>"; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -330,10 +332,10 @@ // Test an escaped invalid Unicode character. TEST_F(HTMLDecoderTest, CanParseEscapedInvalidUnicodeCharacters) { const std::string input = "<p></p>"; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -349,10 +351,10 @@ // Test a UTF8 encoded supplementary (not in BMP) character. TEST_F(HTMLDecoderTest, CanParseUTF8EncodedSupplementaryCharacters) { const std::string input = "<p>💩</p>"; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -371,10 +373,10 @@ // The current version DOES NOT handle the error as outlined in the link above. TEST_F(HTMLDecoderTest, CanParseMisnestedTags1) { const std::string input = "<p>1<b>2<i>3</b>4</i>5</p>"; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -397,10 +399,10 @@ // The current version DOES NOT handle the error as outlined in the link above. TEST_F(HTMLDecoderTest, CanParseMisnestedTags2) { const std::string input = "<b>1<p>2</b>3</p>"; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -415,10 +417,10 @@ TEST_F(HTMLDecoderTest, TagNamesShouldBeCaseInsensitive) { const std::string input = "<DIV></DIV>"; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -429,10 +431,10 @@ TEST_F(HTMLDecoderTest, AttributesShouldBeCaseInsensitive) { const std::string input = "<div A></div>"; - html_decoder_.reset( - new HTMLDecoder(document_, root_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + html_decoder_.reset(new HTMLDecoder( + document_, root_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish(); @@ -453,9 +455,9 @@ "<body>陈绮贞</body>" "</html>"; html_decoder_.reset(new HTMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); html_decoder_->DecodeChunk(input.c_str(), input.length()); html_decoder_->Finish();
diff --git a/src/cobalt/dom_parser/libxml_html_parser_wrapper.h b/src/cobalt/dom_parser/libxml_html_parser_wrapper.h index 24bb4cb..f553818 100644 --- a/src/cobalt/dom_parser/libxml_html_parser_wrapper.h +++ b/src/cobalt/dom_parser/libxml_html_parser_wrapper.h
@@ -38,10 +38,12 @@ const scoped_refptr<dom::Document>& document, const scoped_refptr<dom::Node>& parent_node, const scoped_refptr<dom::Node>& reference_node, + const int dom_max_element_depth, const base::SourceLocation& input_location, const base::Callback<void(const std::string&)>& error_callback) : LibxmlParserWrapper(document, parent_node, reference_node, - input_location, error_callback), + dom_max_element_depth, input_location, + error_callback), html_parser_context_(NULL) { DCHECK(!document->IsXMLDocument()); }
diff --git a/src/cobalt/dom_parser/libxml_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_parser_wrapper.cc index 993e453..3acaf88 100644 --- a/src/cobalt/dom_parser/libxml_parser_wrapper.cc +++ b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
@@ -160,7 +160,7 @@ attributes[i].value.as_string()); } - if (node_stack_.size() < kMaxStackDepth) { + if (static_cast<int>(node_stack_.size()) <= dom_max_element_depth_) { element->OnParserStartTag(GetSourceLocation()); node_stack_.top()->InsertBefore(element, reference_node_); } else { @@ -178,7 +178,7 @@ scoped_refptr<dom::Element> element = node_stack_.top()->AsElement(); node_stack_.pop(); - if (node_stack_.size() < kMaxStackDepth) { + if (static_cast<int>(node_stack_.size()) <= dom_max_element_depth_) { element->OnParserEndTag(); }
diff --git a/src/cobalt/dom_parser/libxml_parser_wrapper.h b/src/cobalt/dom_parser/libxml_parser_wrapper.h index 4f39651..61f3d04 100644 --- a/src/cobalt/dom_parser/libxml_parser_wrapper.h +++ b/src/cobalt/dom_parser/libxml_parser_wrapper.h
@@ -78,19 +78,17 @@ }; typedef std::vector<ParserAttribute> ParserAttributeVector; - // This restricts the depth of the nodes in the DOM tree. All elements at a - // depth deeper than this will be discarded. - static const size_t kMaxStackDepth = 32; - LibxmlParserWrapper( const scoped_refptr<dom::Document>& document, const scoped_refptr<dom::Node>& parent_node, const scoped_refptr<dom::Node>& reference_node, + const int dom_max_element_depth, const base::SourceLocation& first_chunk_location, const base::Callback<void(const std::string&)>& error_callback) : document_(document), parent_node_(parent_node), reference_node_(reference_node), + dom_max_element_depth_(dom_max_element_depth), first_chunk_location_(first_chunk_location), error_callback_(error_callback), depth_limit_exceeded_(false), @@ -140,8 +138,12 @@ const scoped_refptr<dom::Document> document_; const scoped_refptr<dom::Node> parent_node_; const scoped_refptr<dom::Node> reference_node_; + // Max number the depth of the elements in the DOM tree. All elements at a + // depth deeper than this will be discarded. + const int dom_max_element_depth_; const base::SourceLocation first_chunk_location_; const base::Callback<void(const std::string&)> error_callback_; + bool depth_limit_exceeded_; IssueSeverity issue_level_;
diff --git a/src/cobalt/dom_parser/libxml_xml_parser_wrapper.h b/src/cobalt/dom_parser/libxml_xml_parser_wrapper.h index 8eea743..f53c729 100644 --- a/src/cobalt/dom_parser/libxml_xml_parser_wrapper.h +++ b/src/cobalt/dom_parser/libxml_xml_parser_wrapper.h
@@ -38,10 +38,12 @@ const scoped_refptr<dom::XMLDocument>& xml_document, const scoped_refptr<dom::Node>& parent_node, const scoped_refptr<dom::Node>& reference_node, + const int dom_max_element_depth, const base::SourceLocation& input_location, const base::Callback<void(const std::string&)>& error_callback) : LibxmlParserWrapper(xml_document, parent_node, reference_node, - input_location, error_callback), + dom_max_element_depth, input_location, + error_callback), xml_parser_context_(NULL) {} ~LibxmlXMLParserWrapper() OVERRIDE;
diff --git a/src/cobalt/dom_parser/parser.cc b/src/cobalt/dom_parser/parser.cc index 98be631..1c8c1ee 100644 --- a/src/cobalt/dom_parser/parser.cc +++ b/src/cobalt/dom_parser/parser.cc
@@ -30,8 +30,8 @@ const base::SourceLocation& input_location) { scoped_refptr<dom::Document> document = new dom::Document(html_element_context, dom::Document::Options()); - HTMLDecoder html_decoder(document, document, NULL, input_location, - base::Closure(), error_callback_); + HTMLDecoder html_decoder(document, document, NULL, dom_max_element_depth_, + input_location, base::Closure(), error_callback_); html_decoder.DecodeChunk(input.c_str(), input.length()); html_decoder.Finish(); return document; @@ -41,7 +41,8 @@ const std::string& input, const base::SourceLocation& input_location) { scoped_refptr<dom::XMLDocument> xml_document = new dom::XMLDocument(); XMLDecoder xml_decoder( - xml_document, xml_document, NULL, input_location, base::Closure(), + xml_document, xml_document, NULL, dom_max_element_depth_, input_location, + base::Closure(), base::Bind(&Parser::ErrorCallback, base::Unretained(this))); xml_decoder.DecodeChunk(input.c_str(), input.length()); xml_decoder.Finish(); @@ -54,7 +55,8 @@ const scoped_refptr<dom::Node>& reference_node, const base::SourceLocation& input_location) { HTMLDecoder html_decoder(document, parent_node, reference_node, - input_location, base::Closure(), error_callback_); + dom_max_element_depth_, input_location, + base::Closure(), error_callback_); html_decoder.DecodeChunk(input.c_str(), input.length()); html_decoder.Finish(); } @@ -66,8 +68,8 @@ const scoped_refptr<dom::Node>& reference_node, const base::SourceLocation& input_location) { XMLDecoder xml_decoder( - xml_document, parent_node, reference_node, input_location, - base::Closure(), + xml_document, parent_node, reference_node, dom_max_element_depth_, + input_location, base::Closure(), base::Bind(&Parser::ErrorCallback, base::Unretained(this))); xml_decoder.DecodeChunk(input.c_str(), input.length()); xml_decoder.Finish(); @@ -77,16 +79,16 @@ const scoped_refptr<dom::Document>& document, const base::SourceLocation& input_location) { return scoped_ptr<loader::Decoder>( - new HTMLDecoder(document, document, NULL, input_location, base::Closure(), - error_callback_)); + new HTMLDecoder(document, document, NULL, dom_max_element_depth_, + input_location, base::Closure(), error_callback_)); } scoped_ptr<loader::Decoder> Parser::ParseXMLDocumentAsync( const scoped_refptr<dom::XMLDocument>& xml_document, const base::SourceLocation& input_location) { return scoped_ptr<loader::Decoder>( - new XMLDecoder(xml_document, xml_document, NULL, input_location, - base::Closure(), error_callback_)); + new XMLDecoder(xml_document, xml_document, NULL, dom_max_element_depth_, + input_location, base::Closure(), error_callback_)); } void Parser::ErrorCallback(const std::string& error) {
diff --git a/src/cobalt/dom_parser/parser.h b/src/cobalt/dom_parser/parser.h index 700c9c9..cefbf7f 100644 --- a/src/cobalt/dom_parser/parser.h +++ b/src/cobalt/dom_parser/parser.h
@@ -29,11 +29,17 @@ class Parser : public dom::Parser { public: Parser() - : ALLOW_THIS_IN_INITIALIZER_LIST(error_callback_( + : dom_max_element_depth_(kDefaultDOMMaxElementDepth), + ALLOW_THIS_IN_INITIALIZER_LIST(error_callback_( base::Bind(&Parser::ErrorCallback, base::Unretained(this)))) {} explicit Parser( const base::Callback<void(const std::string&)>& error_callback) - : error_callback_(error_callback) {} + : dom_max_element_depth_(kDefaultDOMMaxElementDepth), + error_callback_(error_callback) {} + Parser(const int dom_max_element_depth, + const base::Callback<void(const std::string&)>& error_callback) + : dom_max_element_depth_(dom_max_element_depth), + error_callback_(error_callback) {} ~Parser() OVERRIDE {} // From dom::Parser. @@ -68,10 +74,13 @@ const base::SourceLocation& input_location) OVERRIDE; private: - void ErrorCallback(const std::string& error); + static const int kDefaultDOMMaxElementDepth = 32; + const int dom_max_element_depth_; const base::Callback<void(const std::string&)> error_callback_; + void ErrorCallback(const std::string& error); + DISALLOW_COPY_AND_ASSIGN(Parser); };
diff --git a/src/cobalt/dom_parser/xml_decoder.cc b/src/cobalt/dom_parser/xml_decoder.cc index a840ac3..a1b1b1f 100644 --- a/src/cobalt/dom_parser/xml_decoder.cc +++ b/src/cobalt/dom_parser/xml_decoder.cc
@@ -25,12 +25,12 @@ const scoped_refptr<dom::XMLDocument>& xml_document, const scoped_refptr<dom::Node>& parent_node, const scoped_refptr<dom::Node>& reference_node, - const base::SourceLocation& input_location, + const int dom_max_element_depth, const base::SourceLocation& input_location, const base::Closure& done_callback, const base::Callback<void(const std::string&)>& error_callback) - : libxml_xml_parser_wrapper_( - new LibxmlXMLParserWrapper(xml_document, parent_node, reference_node, - input_location, error_callback)), + : libxml_xml_parser_wrapper_(new LibxmlXMLParserWrapper( + xml_document, parent_node, reference_node, dom_max_element_depth, + input_location, error_callback)), done_callback_(done_callback) {} XMLDecoder::~XMLDecoder() {}
diff --git a/src/cobalt/dom_parser/xml_decoder.h b/src/cobalt/dom_parser/xml_decoder.h index 5784da6..1be11a7 100644 --- a/src/cobalt/dom_parser/xml_decoder.h +++ b/src/cobalt/dom_parser/xml_decoder.h
@@ -42,6 +42,7 @@ XMLDecoder(const scoped_refptr<dom::XMLDocument>& xml_document, const scoped_refptr<dom::Node>& parent_node, const scoped_refptr<dom::Node>& reference_node, + const int dom_max_element_depth, const base::SourceLocation& input_location, const base::Closure& done_callback, const base::Callback<void(const std::string&)>& error_callback);
diff --git a/src/cobalt/dom_parser/xml_decoder_test.cc b/src/cobalt/dom_parser/xml_decoder_test.cc index 3f4b3cf..ecc6b41 100644 --- a/src/cobalt/dom_parser/xml_decoder_test.cc +++ b/src/cobalt/dom_parser/xml_decoder_test.cc
@@ -29,6 +29,8 @@ namespace cobalt { namespace dom_parser { +const int kDOMMaxElementDepth = 32; + class MockErrorCallback : public base::Callback<void(const std::string&)> { public: MOCK_METHOD1(Run, void(const std::string&)); @@ -52,9 +54,9 @@ TEST_F(XMLDecoderTest, ShouldNotAddImpliedTags) { const std::string input = "<ELEMENT></ELEMENT>"; xml_decoder_.reset(new XMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); xml_decoder_->DecodeChunk(input.c_str(), input.length()); xml_decoder_->Finish(); @@ -76,9 +78,9 @@ "]]>" "</ELEMENT>"; xml_decoder_.reset(new XMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); xml_decoder_->DecodeChunk(input.c_str(), input.length()); xml_decoder_->Finish(); @@ -99,9 +101,9 @@ TEST_F(XMLDecoderTest, CanParseAttributesWithValue) { const std::string input = "<ELEMENT a=\"1\" b=\"2\"></ELEMENT>"; xml_decoder_.reset(new XMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); xml_decoder_->DecodeChunk(input.c_str(), input.length()); xml_decoder_->Finish(); @@ -119,9 +121,9 @@ TEST_F(XMLDecoderTest, TagNamesShouldBeCaseSensitive) { const std::string input = "<ELEMENT><element></element></ELEMENT>"; xml_decoder_.reset(new XMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); xml_decoder_->DecodeChunk(input.c_str(), input.length()); xml_decoder_->Finish(); @@ -137,9 +139,9 @@ TEST_F(XMLDecoderTest, AttributesShouldBeCaseSensitive) { const std::string input = "<ELEMENT A=\"1\" a=\"2\"></ELEMENT>"; xml_decoder_.reset(new XMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); xml_decoder_->DecodeChunk(input.c_str(), input.length()); xml_decoder_->Finish(); @@ -159,9 +161,9 @@ "<!DOCTYPE doc [ <!ENTITY ent SYSTEM \"file:///dev/tty\"> ]>" "<element>&ent;</element>"; xml_decoder_.reset(new XMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); xml_decoder_->DecodeChunk(input.c_str(), input.length()); xml_decoder_->Finish(); @@ -175,9 +177,9 @@ "<!DOCTYPE doc [ <!ENTITY ent SYSTEM \"file:///dev/tty\"> ]>" "<element>&ent;</element>"; xml_decoder_.reset(new XMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); xml_decoder_->DecodeChunk(input.c_str(), input.length()); xml_decoder_->Finish(); @@ -191,9 +193,9 @@ "<!DOCTYPE doc [ <!ENTITY % ent SYSTEM \"http://www.google.com/\"> ]>" "<element>&ent;</element>"; xml_decoder_.reset(new XMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); xml_decoder_->DecodeChunk(input.c_str(), input.length()); xml_decoder_->Finish(); @@ -207,9 +209,9 @@ "<!DOCTYPE doc [ <!ENTITY % ent SYSTEM \"file:///dev/tty\"> ]>" "<element>hey</element>"; xml_decoder_.reset(new XMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); xml_decoder_->DecodeChunk(input.c_str(), input.length()); xml_decoder_->Finish(); @@ -223,9 +225,9 @@ "<!DOCTYPE doc SYSTEM \"file:///dev/tty\">" "<element>hey</element>"; xml_decoder_.reset(new XMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); xml_decoder_->DecodeChunk(input.c_str(), input.length()); xml_decoder_->Finish(); @@ -255,9 +257,9 @@ "]>" "<element>&ha15;</element>"; xml_decoder_.reset(new XMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); xml_decoder_->DecodeChunk(input.c_str(), input.length()); xml_decoder_->Finish(); @@ -275,9 +277,9 @@ "</child>" "</root>"; xml_decoder_.reset(new XMLDecoder( - document_, document_, NULL, source_location_, base::Closure(), - base::Bind(&MockErrorCallback::Run, - base::Unretained(&mock_error_callback_)))); + document_, document_, NULL, kDOMMaxElementDepth, source_location_, + base::Closure(), base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)))); xml_decoder_->DecodeChunk(input.c_str(), input.length()); xml_decoder_->Finish();
diff --git a/src/cobalt/input/key_repeat_filter.cc b/src/cobalt/input/key_repeat_filter.cc index 04436c3..98eaaec 100644 --- a/src/cobalt/input/key_repeat_filter.cc +++ b/src/cobalt/input/key_repeat_filter.cc
@@ -25,7 +25,7 @@ const base::TimeDelta kRepeatInitialDelay = base::TimeDelta::FromMilliseconds(500); -const float kRepeatRateInHertz = 10.0f; +const float kRepeatRateInHertz = 20.0f; const base::TimeDelta kRepeatRate = base::TimeDelta::FromMilliseconds( base::Time::kMillisecondsPerSecond / kRepeatRateInHertz);
diff --git a/src/cobalt/layout/block_level_replaced_box.cc b/src/cobalt/layout/block_level_replaced_box.cc index e05916f..14658fa 100644 --- a/src/cobalt/layout/block_level_replaced_box.cc +++ b/src/cobalt/layout/block_level_replaced_box.cc
@@ -24,15 +24,16 @@ BlockLevelReplacedBox::BlockLevelReplacedBox( const scoped_refptr<cssom::CSSComputedStyleDeclaration>& css_computed_style_declaration, - const ReplaceImageCB& replace_image_cb, + const ReplaceImageCB& replace_image_cb, const SetBoundsCB& set_bounds_cb, const scoped_refptr<Paragraph>& paragraph, int32 text_position, const base::optional<LayoutUnit>& maybe_intrinsic_width, const base::optional<LayoutUnit>& maybe_intrinsic_height, const base::optional<float>& maybe_intrinsic_ratio, UsedStyleProvider* used_style_provider, LayoutStatTracker* layout_stat_tracker) - : ReplacedBox(css_computed_style_declaration, replace_image_cb, paragraph, - text_position, maybe_intrinsic_width, maybe_intrinsic_height, + : ReplacedBox(css_computed_style_declaration, replace_image_cb, + set_bounds_cb, paragraph, text_position, + maybe_intrinsic_width, maybe_intrinsic_height, maybe_intrinsic_ratio, used_style_provider, layout_stat_tracker) {}
diff --git a/src/cobalt/layout/block_level_replaced_box.h b/src/cobalt/layout/block_level_replaced_box.h index f461485..bd7982b 100644 --- a/src/cobalt/layout/block_level_replaced_box.h +++ b/src/cobalt/layout/block_level_replaced_box.h
@@ -34,7 +34,7 @@ BlockLevelReplacedBox( const scoped_refptr<cssom::CSSComputedStyleDeclaration>& css_computed_style_declaration, - const ReplaceImageCB& replace_image_cb, + const ReplaceImageCB& replace_image_cb, const SetBoundsCB& set_bounds_cb, const scoped_refptr<Paragraph>& paragraph, int32 text_position, const base::optional<LayoutUnit>& maybe_intrinsic_width, const base::optional<LayoutUnit>& maybe_intrinsic_height,
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc index 2cfb06b..d19d55e 100644 --- a/src/cobalt/layout/box_generator.cc +++ b/src/cobalt/layout/box_generator.cc
@@ -72,11 +72,13 @@ const scoped_refptr<cssom::CSSComputedStyleDeclaration>& parent_css_computed_style_declaration, const scoped_refptr<const web_animations::AnimationSet>& parent_animations, - scoped_refptr<Paragraph>* paragraph, const Context* context) + scoped_refptr<Paragraph>* paragraph, const int dom_element_depth, + const Context* context) : parent_css_computed_style_declaration_( parent_css_computed_style_declaration), parent_animations_(parent_animations), paragraph_(paragraph), + dom_element_depth_(dom_element_depth), context_(context) {} BoxGenerator::~BoxGenerator() { @@ -92,8 +94,15 @@ } void BoxGenerator::Visit(dom::Element* element) { + if (dom_element_depth_ > context_->dom_max_element_depth) { + LOG(WARNING) << "Elements too deep in the DOM tree are ignored in layout."; + return; + } + scoped_refptr<dom::HTMLElement> html_element = element->AsHTMLElement(); - DCHECK(html_element); + if (!html_element) { + return; + } generating_html_element_ = html_element; bool partial_layout_is_enabled = true; @@ -148,6 +157,7 @@ ReplacedBoxGenerator(const scoped_refptr<cssom::CSSComputedStyleDeclaration>& css_computed_style_declaration, const ReplacedBox::ReplaceImageCB& replace_image_cb, + const ReplacedBox::SetBoundsCB& set_bounds_cb, const scoped_refptr<Paragraph>& paragraph, int32 text_position, const base::optional<LayoutUnit>& maybe_intrinsic_width, @@ -156,6 +166,7 @@ const BoxGenerator::Context* context) : css_computed_style_declaration_(css_computed_style_declaration), replace_image_cb_(replace_image_cb), + set_bounds_cb_(set_bounds_cb), paragraph_(paragraph), text_position_(text_position), maybe_intrinsic_width_(maybe_intrinsic_width), @@ -171,6 +182,7 @@ const scoped_refptr<cssom::CSSComputedStyleDeclaration> css_computed_style_declaration_; const ReplacedBox::ReplaceImageCB replace_image_cb_; + const ReplacedBox::SetBoundsCB set_bounds_cb_; const scoped_refptr<Paragraph> paragraph_; const int32 text_position_; const base::optional<LayoutUnit> maybe_intrinsic_width_; @@ -187,10 +199,10 @@ // Generate a block-level replaced box. case cssom::KeywordValue::kBlock: replaced_box_ = make_scoped_refptr(new BlockLevelReplacedBox( - css_computed_style_declaration_, replace_image_cb_, paragraph_, - text_position_, maybe_intrinsic_width_, maybe_intrinsic_height_, - maybe_intrinsic_ratio_, context_->used_style_provider, - context_->layout_stat_tracker)); + css_computed_style_declaration_, replace_image_cb_, set_bounds_cb_, + paragraph_, text_position_, maybe_intrinsic_width_, + maybe_intrinsic_height_, maybe_intrinsic_ratio_, + context_->used_style_provider, context_->layout_stat_tracker)); break; // Generate an inline-level replaced box. There is no need to distinguish // between inline replaced elements and inline-block replaced elements @@ -199,10 +211,10 @@ case cssom::KeywordValue::kInline: case cssom::KeywordValue::kInlineBlock: replaced_box_ = make_scoped_refptr(new InlineLevelReplacedBox( - css_computed_style_declaration_, replace_image_cb_, paragraph_, - text_position_, maybe_intrinsic_width_, maybe_intrinsic_height_, - maybe_intrinsic_ratio_, context_->used_style_provider, - context_->layout_stat_tracker)); + css_computed_style_declaration_, replace_image_cb_, set_bounds_cb_, + paragraph_, text_position_, maybe_intrinsic_width_, + maybe_intrinsic_height_, maybe_intrinsic_ratio_, + context_->used_style_provider, context_->layout_stat_tracker)); break; // The element generates no boxes and has no effect on layout. case cssom::KeywordValue::kNone: @@ -283,8 +295,8 @@ video_element->GetVideoFrameProvider() ? base::Bind(GetVideoFrame, video_element->GetVideoFrameProvider()) : ReplacedBox::ReplaceImageCB(), - *paragraph_, text_position, base::nullopt, base::nullopt, base::nullopt, - context_); + video_element->GetSetBoundsCB(), *paragraph_, text_position, + base::nullopt, base::nullopt, base::nullopt, context_); video_element->computed_style()->display()->Accept(&replaced_box_generator); scoped_refptr<ReplacedBox> replaced_box = @@ -784,7 +796,7 @@ pseudo_element->css_computed_style_declaration(), use_html_element_animations ? html_element->animations() : pseudo_element->animations(), - paragraph_, context_); + paragraph_, dom_element_depth_ + 1, context_); child_node->Accept(&child_box_generator); const Boxes& child_boxes = child_box_generator.boxes(); for (Boxes::const_iterator child_box_iterator = child_boxes.begin(); @@ -855,7 +867,7 @@ BoxGenerator child_box_generator( html_element->css_computed_style_declaration(), html_element->css_computed_style_declaration()->animations(), - paragraph_, context_); + paragraph_, dom_element_depth_ + 1, context_); child_node->Accept(&child_box_generator); const Boxes& child_boxes = child_box_generator.boxes(); for (Boxes::const_iterator child_box_iterator = child_boxes.begin();
diff --git a/src/cobalt/layout/box_generator.h b/src/cobalt/layout/box_generator.h index b2898c8..2b89c95 100644 --- a/src/cobalt/layout/box_generator.h +++ b/src/cobalt/layout/box_generator.h
@@ -54,12 +54,14 @@ LayoutStatTracker* layout_stat_tracker, icu::BreakIterator* line_break_iterator, icu::BreakIterator* character_break_iterator, - dom::HTMLElement* ignore_background_element) + dom::HTMLElement* ignore_background_element, + int dom_max_element_depth) : used_style_provider(used_style_provider), layout_stat_tracker(layout_stat_tracker), line_break_iterator(line_break_iterator), character_break_iterator(character_break_iterator), - ignore_background_element(ignore_background_element) {} + ignore_background_element(ignore_background_element), + dom_max_element_depth(dom_max_element_depth) {} UsedStyleProvider* used_style_provider; LayoutStatTracker* layout_stat_tracker; icu::BreakIterator* line_break_iterator; @@ -70,6 +72,9 @@ // re-use those properties on that element. This value will track that // element. dom::HTMLElement* ignore_background_element; + + // The maximum element depth for layout. + int dom_max_element_depth; }; BoxGenerator(const scoped_refptr<cssom::CSSComputedStyleDeclaration>& @@ -81,7 +86,8 @@ // animation inheritance is implemented. const scoped_refptr<const web_animations::AnimationSet>& parent_animations, - scoped_refptr<Paragraph>* paragraph, const Context* context); + scoped_refptr<Paragraph>* paragraph, + const int dom_element_depth_, const Context* context); ~BoxGenerator(); void Visit(dom::CDATASection* cdata_section) OVERRIDE; @@ -106,7 +112,10 @@ parent_css_computed_style_declaration_; const scoped_refptr<const web_animations::AnimationSet>& parent_animations_; scoped_refptr<Paragraph>* paragraph_; + // The current element depth. + const int dom_element_depth_; const Context* context_; + scoped_refptr<dom::HTMLElement> generating_html_element_; // The result of a box generator is zero or more root boxes.
diff --git a/src/cobalt/layout/container_box.cc b/src/cobalt/layout/container_box.cc index 4ee4b0b..1d55190 100644 --- a/src/cobalt/layout/container_box.cc +++ b/src/cobalt/layout/container_box.cc
@@ -400,10 +400,7 @@ Vector2dLayoutUnit GetOffsetFromContainingBlockToStackingContext( Box* child_box) { - DCHECK((child_box->computed_style()->position() == - cssom::KeywordValue::GetFixed()) || - (child_box->computed_style()->position() == - cssom::KeywordValue::GetAbsolute())); + DCHECK(child_box->IsPositioned()); Vector2dLayoutUnit relative_position; for (Box *containing_block = child_box->GetContainingBlock(), @@ -411,14 +408,19 @@ current_box != containing_block; current_box = current_box->GetContainingBlock()) { if (!current_box) { - NOTREACHED() << "Unable to find stacking context while " - "traversing containing blocks."; + DLOG(WARNING) + << "Unsupported stacking context and containing block relation."; break; } +#if !defined(NDEBUG) // We should not determine a used position through a transform, as // rectangles may not remain rectangles past it, and thus obtaining // a position may be misleading. - DCHECK(!current_box->IsTransformed()); + if (current_box->IsTransformed()) { + DLOG(WARNING) << "Boxes with stacking contexts above containing blocks " + "with transforms may not be positioned correctly."; + } +#endif relative_position += current_box->GetContentBoxOffsetFromMarginBox(); relative_position += current_box->margin_box_offset_from_containing_block(); @@ -450,15 +452,20 @@ current_box != stacking_context; current_box = current_box->GetContainingBlock()) { if (!current_box) { - // Elements with absolute position may have their containing block farther + // Positioned elements may have their containing block farther // up the hierarchy than the stacking context, so handle this case here. - DCHECK(child_box_position == cssom::KeywordValue::GetAbsolute()); + DCHECK(child_box->IsPositioned()); return -GetOffsetFromContainingBlockToStackingContext(child_box); } +#if !defined(NDEBUG) // We should not determine a used position through a transform, as // rectangles may not remain rectangles past it, and thus obtaining // a position may be misleading. - DCHECK(!current_box->IsTransformed()); + if (current_box->IsTransformed()) { + DLOG(WARNING) << "Boxes with stacking contexts below containing blocks " + "with transforms may not be positioned correctly."; + } +#endif relative_position += current_box->GetContentBoxOffsetFromMarginBox(); relative_position += current_box->margin_box_offset_from_containing_block();
diff --git a/src/cobalt/layout/inline_level_replaced_box.cc b/src/cobalt/layout/inline_level_replaced_box.cc index 9d6072c..5eab8c8 100644 --- a/src/cobalt/layout/inline_level_replaced_box.cc +++ b/src/cobalt/layout/inline_level_replaced_box.cc
@@ -24,15 +24,16 @@ InlineLevelReplacedBox::InlineLevelReplacedBox( const scoped_refptr<cssom::CSSComputedStyleDeclaration>& css_computed_style_declaration, - const ReplaceImageCB& replace_image_cb, + const ReplaceImageCB& replace_image_cb, const SetBoundsCB& set_bounds_cb, const scoped_refptr<Paragraph>& paragraph, int32 text_position, const base::optional<LayoutUnit>& maybe_intrinsic_width, const base::optional<LayoutUnit>& maybe_intrinsic_height, const base::optional<float>& maybe_intrinsic_ratio, UsedStyleProvider* used_style_provider, LayoutStatTracker* layout_stat_tracker) - : ReplacedBox(css_computed_style_declaration, replace_image_cb, paragraph, - text_position, maybe_intrinsic_width, maybe_intrinsic_height, + : ReplacedBox(css_computed_style_declaration, replace_image_cb, + set_bounds_cb, paragraph, text_position, + maybe_intrinsic_width, maybe_intrinsic_height, maybe_intrinsic_ratio, used_style_provider, layout_stat_tracker), is_hidden_by_ellipsis_(false),
diff --git a/src/cobalt/layout/inline_level_replaced_box.h b/src/cobalt/layout/inline_level_replaced_box.h index b0f1a25..32a85aa 100644 --- a/src/cobalt/layout/inline_level_replaced_box.h +++ b/src/cobalt/layout/inline_level_replaced_box.h
@@ -36,7 +36,7 @@ InlineLevelReplacedBox( const scoped_refptr<cssom::CSSComputedStyleDeclaration>& css_computed_style_declaration, - const ReplaceImageCB& replace_image_cb, + const ReplaceImageCB& replace_image_cb, const SetBoundsCB& set_bounds_cb, const scoped_refptr<Paragraph>& paragraph, int32 text_position, const base::optional<LayoutUnit>& maybe_intrinsic_width, const base::optional<LayoutUnit>& maybe_intrinsic_height,
diff --git a/src/cobalt/layout/layout.cc b/src/cobalt/layout/layout.cc index d517a4f..46ca1c1 100644 --- a/src/cobalt/layout/layout.cc +++ b/src/cobalt/layout/layout.cc
@@ -52,7 +52,7 @@ void UpdateComputedStylesAndLayoutBoxTree( const icu::Locale& locale, const scoped_refptr<dom::Document>& document, - UsedStyleProvider* used_style_provider, + int dom_max_element_depth, UsedStyleProvider* used_style_provider, LayoutStatTracker* layout_stat_tracker, icu::BreakIterator* line_break_iterator, icu::BreakIterator* character_break_iterator, @@ -91,13 +91,14 @@ BoxGenerator::Context context( used_style_provider, layout_stat_tracker, line_break_iterator, character_break_iterator, - initial_containing_block_creation_results.background_style_source); + initial_containing_block_creation_results.background_style_source, + dom_max_element_depth); BoxGenerator root_box_generator( (*initial_containing_block)->css_computed_style_declaration(), (*initial_containing_block) ->css_computed_style_declaration() ->animations(), - &(scoped_paragraph.get()), &context); + &(scoped_paragraph.get()), 1 /* dom_element_depth */, &context); document->html()->Accept(&root_box_generator); const Boxes& root_boxes = root_box_generator.boxes(); for (Boxes::const_iterator root_box_iterator = root_boxes.begin(); @@ -130,15 +131,16 @@ scoped_refptr<render_tree::Node> Layout( const icu::Locale& locale, const scoped_refptr<dom::Document>& document, - UsedStyleProvider* used_style_provider, + int dom_max_element_depth, UsedStyleProvider* used_style_provider, LayoutStatTracker* layout_stat_tracker, icu::BreakIterator* line_break_iterator, icu::BreakIterator* character_break_iterator, scoped_refptr<BlockLevelBlockContainerBox>* initial_containing_block) { TRACE_EVENT0("cobalt::layout", "Layout()"); UpdateComputedStylesAndLayoutBoxTree( - locale, document, used_style_provider, layout_stat_tracker, - line_break_iterator, character_break_iterator, initial_containing_block); + locale, document, dom_max_element_depth, used_style_provider, + layout_stat_tracker, line_break_iterator, character_break_iterator, + initial_containing_block); // Add to render tree. render_tree::CompositionNode::Builder render_tree_root_builder;
diff --git a/src/cobalt/layout/layout.h b/src/cobalt/layout/layout.h index d9cdf7a..fa6c03f 100644 --- a/src/cobalt/layout/layout.h +++ b/src/cobalt/layout/layout.h
@@ -43,7 +43,7 @@ // Update the computed styles, then generate and layout the box tree. void UpdateComputedStylesAndLayoutBoxTree( const icu::Locale& locale, const scoped_refptr<dom::Document>& document, - UsedStyleProvider* used_style_provider, + int dom_max_element_depth, UsedStyleProvider* used_style_provider, LayoutStatTracker* layout_stat_tracker, icu::BreakIterator* line_break_iterator, icu::BreakIterator* character_break_iterator, @@ -54,7 +54,7 @@ // result of recursive layout of the given HTML element. scoped_refptr<render_tree::Node> Layout( const icu::Locale& locale, const scoped_refptr<dom::Document>& document, - UsedStyleProvider* used_style_provider, + int dom_max_element_depth, UsedStyleProvider* used_style_provider, LayoutStatTracker* layout_stat_tracker, icu::BreakIterator* line_break_iterator, icu::BreakIterator* character_break_iterator,
diff --git a/src/cobalt/layout/layout_manager.cc b/src/cobalt/layout/layout_manager.cc index c0fb5c6..da165f9 100644 --- a/src/cobalt/layout/layout_manager.cc +++ b/src/cobalt/layout/layout_manager.cc
@@ -39,8 +39,9 @@ public: Impl(const scoped_refptr<dom::Window>& window, const OnRenderTreeProducedCallback& on_render_tree_produced, - LayoutTrigger layout_trigger, float layout_refresh_rate, - const std::string& language, LayoutStatTracker* layout_stat_tracker); + LayoutTrigger layout_trigger, int dom_max_element_depth, + float layout_refresh_rate, const std::string& language, + LayoutStatTracker* layout_stat_tracker); ~Impl(); // From dom::DocumentObserver. @@ -78,6 +79,7 @@ scoped_ptr<icu::BreakIterator> character_break_iterator_; base::Timer layout_timer_; + int dom_max_element_depth_; float layout_refresh_rate_; LayoutStatTracker* const layout_stat_tracker_; @@ -92,8 +94,9 @@ LayoutManager::Impl::Impl( const scoped_refptr<dom::Window>& window, const OnRenderTreeProducedCallback& on_render_tree_produced, - LayoutTrigger layout_trigger, float layout_refresh_rate, - const std::string& language, LayoutStatTracker* layout_stat_tracker) + LayoutTrigger layout_trigger, int dom_max_element_depth, + float layout_refresh_rate, const std::string& language, + LayoutStatTracker* layout_stat_tracker) : window_(window), locale_(icu::Locale::createCanonical(language.c_str())), used_style_provider_( @@ -103,6 +106,7 @@ layout_trigger_(layout_trigger), layout_dirty_(true), layout_timer_(true, true, true), + dom_max_element_depth_(dom_max_element_depth), layout_refresh_rate_(layout_refresh_rate), layout_stat_tracker_(layout_stat_tracker) { window_->document()->AddObserver(this); @@ -163,9 +167,10 @@ void LayoutManager::Impl::DoSynchronousLayout() { TRACE_EVENT0("cobalt::layout", "LayoutManager::Impl::DoSynchronousLayout()"); layout::UpdateComputedStylesAndLayoutBoxTree( - locale_, window_->document(), used_style_provider_.get(), - layout_stat_tracker_, line_break_iterator_.get(), - character_break_iterator_.get(), &initial_containing_block_); + locale_, window_->document(), dom_max_element_depth_, + used_style_provider_.get(), layout_stat_tracker_, + line_break_iterator_.get(), character_break_iterator_.get(), + &initial_containing_block_); } #if defined(ENABLE_TEST_RUNNER) @@ -240,9 +245,10 @@ } scoped_refptr<render_tree::Node> render_tree_root = layout::Layout( - locale_, window_->document(), used_style_provider_.get(), - layout_stat_tracker_, line_break_iterator_.get(), - character_break_iterator_.get(), &initial_containing_block_); + locale_, window_->document(), dom_max_element_depth_, + used_style_provider_.get(), layout_stat_tracker_, + line_break_iterator_.get(), character_break_iterator_.get(), + &initial_containing_block_); bool run_on_render_tree_produced_callback = true; #if defined(ENABLE_TEST_RUNNER) if (layout_trigger_ == kTestRunnerMode && @@ -265,10 +271,12 @@ LayoutManager::LayoutManager( const scoped_refptr<dom::Window>& window, const OnRenderTreeProducedCallback& on_render_tree_produced, - LayoutTrigger layout_trigger, float layout_refresh_rate, - const std::string& language, LayoutStatTracker* layout_stat_tracker) + LayoutTrigger layout_trigger, const int dom_max_element_depth, + const float layout_refresh_rate, const std::string& language, + LayoutStatTracker* layout_stat_tracker) : impl_(new Impl(window, on_render_tree_produced, layout_trigger, - layout_refresh_rate, language, layout_stat_tracker)) {} + dom_max_element_depth, layout_refresh_rate, language, + layout_stat_tracker)) {} LayoutManager::~LayoutManager() {}
diff --git a/src/cobalt/layout/layout_manager.h b/src/cobalt/layout/layout_manager.h index 39e4eb7..3c60206 100644 --- a/src/cobalt/layout/layout_manager.h +++ b/src/cobalt/layout/layout_manager.h
@@ -63,8 +63,8 @@ LayoutManager(const scoped_refptr<dom::Window>& window, const OnRenderTreeProducedCallback& on_render_tree_produced, - LayoutTrigger layout_trigger, float layout_refresh_rate, - const std::string& language, + LayoutTrigger layout_trigger, const int dom_max_element_depth, + const float layout_refresh_rate, const std::string& language, LayoutStatTracker* layout_stat_tracker); ~LayoutManager();
diff --git a/src/cobalt/layout/replaced_box.cc b/src/cobalt/layout/replaced_box.cc index d8a694c..1b1c865 100644 --- a/src/cobalt/layout/replaced_box.cc +++ b/src/cobalt/layout/replaced_box.cc
@@ -29,29 +29,23 @@ #include "cobalt/math/vector2d_f.h" #include "cobalt/render_tree/brush.h" #include "cobalt/render_tree/color_rgba.h" +#include "cobalt/render_tree/filter_node.h" #include "cobalt/render_tree/image_node.h" +#include "cobalt/render_tree/map_to_mesh_filter.h" #include "cobalt/render_tree/punch_through_video_node.h" #include "cobalt/render_tree/rect_node.h" -// Here we determine if we are going to be rendering video ourselves, or if -// the Starboard Player system will be rendering video in which case we need to -// punch through our scene so that the video will be visible. -#if defined(OS_STARBOARD) -#include "starboard/configuration.h" -#define PUNCH_THROUGH_VIDEO_RENDERING SB_IS(PLAYER_PUNCHED_OUT) -#else // defined(OS_STARBOARD) -#define PUNCH_THROUGH_VIDEO_RENDERING 0 -#endif // defined(OS_STARBOARD) - namespace cobalt { namespace layout { using render_tree::animations::AnimateNode; using render_tree::CompositionNode; +using render_tree::FilterNode; using render_tree::ImageNode; using render_tree::PunchThroughVideoNode; using render_tree::RectNode; using render_tree::SolidColorBrush; +using render_tree::MapToMeshFilter; namespace { @@ -68,7 +62,7 @@ ReplacedBox::ReplacedBox( const scoped_refptr<cssom::CSSComputedStyleDeclaration>& css_computed_style_declaration, - const ReplaceImageCB& replace_image_cb, + const ReplaceImageCB& replace_image_cb, const SetBoundsCB& set_bounds_cb, const scoped_refptr<Paragraph>& paragraph, int32 text_position, const base::optional<LayoutUnit>& maybe_intrinsic_width, const base::optional<LayoutUnit>& maybe_intrinsic_height, @@ -84,6 +78,7 @@ // https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width. intrinsic_ratio_(maybe_intrinsic_ratio.value_or(kFallbackIntrinsicRatio)), replace_image_cb_(replace_image_cb), + set_bounds_cb_(set_bounds_cb), paragraph_(paragraph), text_position_(text_position) {} @@ -204,7 +199,8 @@ #endif // !PUNCH_THROUGH_VIDEO_RENDERING -void AnimateCB(ReplacedBox::ReplaceImageCB replace_image_cb, +void AnimateCB(const ReplacedBox::ReplaceImageCB& replace_image_cb, + const ReplacedBox::SetBoundsCB& set_bounds_cb, math::SizeF destination_size, CompositionNode::Builder* composition_node_builder, base::TimeDelta time) { @@ -216,9 +212,11 @@ #if PUNCH_THROUGH_VIDEO_RENDERING // For systems that have their own path to blitting video to the display, we // simply punch a hole through our scene so that the video can appear there. - composition_node_builder->AddChild( - new PunchThroughVideoNode(math::RectF(destination_size))); -#else + PunchThroughVideoNode::Builder builder(math::RectF(destination_size), + set_bounds_cb); + composition_node_builder->AddChild(new PunchThroughVideoNode(builder)); +#else // PUNCH_THROUGH_VIDEO_RENDERING + UNREFERENCED_PARAMETER(set_bounds_cb); scoped_refptr<render_tree::Image> image = replace_image_cb.Run(); // TODO: Detect better when the intrinsic video size is used for the @@ -233,7 +231,7 @@ GetLetterboxDimensions(image->GetSize(), destination_size), image, composition_node_builder); } -#endif +#endif // PUNCH_THROUGH_VIDEO_RENDERING } } // namespace @@ -258,9 +256,10 @@ AnimateNode::Builder animate_node_builder; animate_node_builder.Add( - composition_node, - base::Bind(AnimateCB, replace_image_cb_, content_box_size())); + composition_node, base::Bind(AnimateCB, replace_image_cb_, set_bounds_cb_, + content_box_size())); + // TODO: Apply map to mesh filter if present. border_node_builder->AddChild( new AnimateNode(animate_node_builder, composition_node)); }
diff --git a/src/cobalt/layout/replaced_box.h b/src/cobalt/layout/replaced_box.h index 259b2d0..aaa6cbc 100644 --- a/src/cobalt/layout/replaced_box.h +++ b/src/cobalt/layout/replaced_box.h
@@ -24,7 +24,19 @@ #include "base/time.h" #include "cobalt/layout/box.h" #include "cobalt/layout/paragraph.h" +#include "cobalt/math/rect.h" #include "cobalt/render_tree/image.h" +#include "cobalt/render_tree/punch_through_video_node.h" + +// Here we determine if we are going to be rendering video ourselves, or if +// the Starboard Player system will be rendering video in which case we need to +// punch through our scene so that the video will be visible. +#if defined(OS_STARBOARD) +#include "starboard/configuration.h" +#define PUNCH_THROUGH_VIDEO_RENDERING SB_IS(PLAYER_PUNCHED_OUT) +#else // defined(OS_STARBOARD) +#define PUNCH_THROUGH_VIDEO_RENDERING 0 +#endif // defined(OS_STARBOARD) namespace cobalt { namespace layout { @@ -38,10 +50,12 @@ class ReplacedBox : public Box { public: typedef base::Callback<scoped_refptr<render_tree::Image>()> ReplaceImageCB; + typedef render_tree::PunchThroughVideoNode::SetBoundsCB SetBoundsCB; ReplacedBox(const scoped_refptr<cssom::CSSComputedStyleDeclaration>& css_computed_style_declaration, const ReplaceImageCB& replace_image_cb, + const SetBoundsCB& set_bounds_cb, const scoped_refptr<Paragraph>& paragraph, int32 text_position, const base::optional<LayoutUnit>& maybe_intrinsic_width, const base::optional<LayoutUnit>& maybe_intrinsic_height, @@ -102,6 +116,7 @@ private: const ReplaceImageCB replace_image_cb_; + const SetBoundsCB set_bounds_cb_; const scoped_refptr<Paragraph> paragraph_; int32 text_position_;
diff --git a/src/cobalt/layout_tests/testdata/cobalt/100-dynamically-created-nested-elements-expected.png b/src/cobalt/layout_tests/testdata/cobalt/100-dynamically-created-nested-elements-expected.png new file mode 100644 index 0000000..723686a --- /dev/null +++ b/src/cobalt/layout_tests/testdata/cobalt/100-dynamically-created-nested-elements-expected.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/100-dynamically-created-nested-elements.html b/src/cobalt/layout_tests/testdata/cobalt/100-dynamically-created-nested-elements.html new file mode 100644 index 0000000..498e1a7 --- /dev/null +++ b/src/cobalt/layout_tests/testdata/cobalt/100-dynamically-created-nested-elements.html
@@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> +<head> + <style> + body { + background-color: white; + font-family: Roboto; + font-size: 30px; + } + </style> +</head> +<body> + + <!-- 100 dynamically created nested <span>s. --> + <!-- Cobalt should only render 30 <span>s along with <html> and <body>, given + that max element depth is 32. --> + +<script> + var parent = document.body; + for (var i = 1; i <= 100; i++) { + var new_child = document.createElement("span"); + new_child.textContent = i + " "; + parent.appendChild(new_child); + parent = new_child; + } +</script> + +</body> +</html>
diff --git a/src/cobalt/layout_tests/testdata/cobalt/100-nested-elements-expected.png b/src/cobalt/layout_tests/testdata/cobalt/100-nested-elements-expected.png new file mode 100644 index 0000000..723686a --- /dev/null +++ b/src/cobalt/layout_tests/testdata/cobalt/100-nested-elements-expected.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/1000-nested-elements.html b/src/cobalt/layout_tests/testdata/cobalt/100-nested-elements.html similarity index 98% rename from src/cobalt/layout_tests/testdata/cobalt/1000-nested-elements.html rename to src/cobalt/layout_tests/testdata/cobalt/100-nested-elements.html index bc583e3..2d18c87 100644 --- a/src/cobalt/layout_tests/testdata/cobalt/1000-nested-elements.html +++ b/src/cobalt/layout_tests/testdata/cobalt/100-nested-elements.html
@@ -12,8 +12,8 @@ <body> <!-- 1000 nested <span>s, closing tags are implied. --> - <!-- Cobalt should only attach 29 <span>s, since there're the document, <html> - and <body> in the stack, given that max depth is 32. --> + <!-- Cobalt should only attach 30 <span>s along with <html> and <body>, given + that max element depth is 32. --> <span>1 <span>2
diff --git a/src/cobalt/layout_tests/testdata/cobalt/1000-nested-elements-expected.png b/src/cobalt/layout_tests/testdata/cobalt/1000-nested-elements-expected.png deleted file mode 100644 index 0a80611..0000000 --- a/src/cobalt/layout_tests/testdata/cobalt/1000-nested-elements-expected.png +++ /dev/null Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt index 93e5eda..e336781 100644 --- a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt +++ b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
@@ -1,4 +1,5 @@ -1000-nested-elements +100-dynamically-created-nested-elements +100-nested-elements block-and-inline-block-display changing-css-text-triggers-layout cobalt-oxide, file:///cobalt/browser/testdata/cobalt-oxide/cobalt-oxide.html
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees.html b/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees.html new file mode 100644 index 0000000..5fabe2f --- /dev/null +++ b/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees.html
@@ -0,0 +1,72 @@ +<!DOCTYPE html> +<!-- + | Test stacking context and containing blocks, verifying that their relative + | positions can still be found when neither is in a subtree of the other. + --> +<html> +<head> + <style> + body { + font-family: Roboto; + margin: 0px; + } + div { + padding: 10px; + } + .always_containing_block { + opacity: 0.999; + transform: rotate(0deg); + } + .absolute { + position: absolute; + } + .relative { + position: relative; + } + .fixed { + position: fixed; + } + .blue1 { + background-color: #BBDEFB; + } + .blue2 { + background-color: #2196F3; + } + .blue3 { + background-color: #1976D2; + } + .blue4 { + background-color: #0D47A1; + } + </style> +</head> +<body style="background-color:#000000;"> +<div class="always_containing_block absolute"> + <div class="blue1 absolute" style="transform: rotate(0deg); width:75px; height:75px;"> + <div class="blue2 absolute" style="z-index: -1; top:25px; left:25px"> + <div class="blue3 fixed" style="top:50px; left:50px;"> + <div class="blue4 absolute" style="z-index: 1; top:5px; left:5px"> + </div> + </div> + </div> + </div> +</div> +<div class="always_containing_block absolute" style="left:100px;"> + <div class="blue1 relative" style="z-index:1; width:75px; height:75px;"> + <div class="blue2 fixed" style="top:50px; left:50px;"> + <div class="blue3 relative" style="z-index:1; top:5px; left:5px"> + </div> + </div> + </div> +</div> +<div class="always_containing_block absolute" style="left:200px;"> + <div class="blue1" style="opacity: 0.5; width:75px; height:75px;"> + <div class="blue2 absolute" style="top:25px; left:25px"> + <div class="blue3 absolute" style="z-index: 1; top:25px; left:25px"> + </div> + </div> + </div> +</div> +</body> +</html> +
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-9-1-stacking-contexts-and-containing-blocks-with-transforms.html b/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-9-1-stacking-contexts-and-containing-blocks-with-transforms.html new file mode 100644 index 0000000..9743f20 --- /dev/null +++ b/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-9-1-stacking-contexts-and-containing-blocks-with-transforms.html
@@ -0,0 +1,73 @@ +<!DOCTYPE html> +<!-- + | Test stacking context and containing blocks, verifying that the boxes are + | positioned correctly when combined with transforms. + --> +<html> +<head> + <style> + body { + font-family: Roboto; + margin: 0px; + } + div { + padding: 10px; + } + .always_containing_block { + opacity: 0.999; + transform: rotate(0deg); + } + .absolute { + position: absolute; + } + .fixed { + position: fixed; + } + .blue1 { + background-color: #BBDEFB; + } + .blue2 { + background-color: #2196F3; + } + .blue3 { + background-color: #1976D2; + } + .blue4 { + background-color: #0D47A1; + } + </style> +</head> +<body style="background-color:#000000;"> +<div class="always_containing_block absolute"> + <div class="blue1 absolute" style="transform: rotate(15deg); width:75px; height:75px;"> + <div class="blue2 absolute" style="z-index: -1; top:25px; left:25px"> + <div class="blue3 fixed" style="top:50px; left:50px;"> + <div class="blue4 absolute" style="z-index: 1; top:5px; left:5px"> + </div> + </div> + </div> + </div> +</div> +<div class="always_containing_block absolute" style="left:100px;"> + <div class="blue1 absolute" style="transform: rotate(15deg); width:75px; height:75px;"> + <div class="blue2 absolute" style="transform: rotate(-30deg); z-index: -1; top:25px; left:25px"> + <div class="blue3 fixed" style="top:50px; left:50px;"> + <div class="blue4 absolute" style="z-index: 1; top:5px; left:5px"> + </div> + </div> + </div> + </div> +</div> +<div class="always_containing_block absolute" style="left:225px;"> + <div class="blue1 absolute" style="transform: rotate(15deg); width:75px; height:75px;"> + <div class="blue2 absolute" style="z-index: -1; top:25px; left:25px"> + <div class="blue3 fixed" style="transform: rotate(-30deg); top:50px; left:50px;"> + <div class="blue4 absolute" style="z-index: 1; top:5px; left:5px"> + </div> + </div> + </div> + </div> +</div> +</body> +</html> +
diff --git a/src/cobalt/layout_tests/web_platform_tests.cc b/src/cobalt/layout_tests/web_platform_tests.cc index 0c28e1a..14e32ae 100644 --- a/src/cobalt/layout_tests/web_platform_tests.cc +++ b/src/cobalt/layout_tests/web_platform_tests.cc
@@ -157,8 +157,9 @@ // Media module render_tree::ResourceProviderStub resource_provider; - scoped_ptr<media::MediaModule> media_module(media::MediaModule::Create( - kDefaultViewportSize, &resource_provider, media::MediaModule::Options())); + scoped_ptr<media::MediaModule> media_module( + media::MediaModule::Create(NULL, kDefaultViewportSize, &resource_provider, + media::MediaModule::Options())); dom::CspDelegateFactory::GetInstance()->OverrideCreator( dom::kCspEnforcementEnable, CspDelegatePermissive::Create);
diff --git a/src/cobalt/media/fetcher_buffered_data_source.cc b/src/cobalt/media/fetcher_buffered_data_source.cc index b50aabc..33346ee 100644 --- a/src/cobalt/media/fetcher_buffered_data_source.cc +++ b/src/cobalt/media/fetcher_buffered_data_source.cc
@@ -56,7 +56,6 @@ pending_read_data_(NULL), security_callback_(security_callback) { DCHECK(message_loop_); - DCHECK(!url.path().empty()); DCHECK(network_module); }
diff --git a/src/cobalt/media/media_module.h b/src/cobalt/media/media_module.h index c055bd6..9415df5 100644 --- a/src/cobalt/media/media_module.h +++ b/src/cobalt/media/media_module.h
@@ -30,6 +30,7 @@ #include "cobalt/media/web_media_player_factory.h" #include "cobalt/render_tree/image.h" #include "cobalt/render_tree/resource_provider.h" +#include "cobalt/system_window/system_window.h" #include "media/base/shell_media_platform.h" #include "media/base/shell_video_frame_provider.h" #include "media/filters/shell_video_decoder_impl.h" @@ -127,7 +128,7 @@ // This function should be defined on individual platform to create the // platform specific MediaModule. static scoped_ptr<MediaModule> Create( - const math::Size& output_size, + system_window::SystemWindow* system_window, const math::Size& output_size, render_tree::ResourceProvider* resource_provider, const Options& options = Options());
diff --git a/src/cobalt/media/media_module_starboard.cc b/src/cobalt/media/media_module_starboard.cc index 5e388a1..4c54544 100644 --- a/src/cobalt/media/media_module_starboard.cc +++ b/src/cobalt/media/media_module_starboard.cc
@@ -18,8 +18,10 @@ #include "base/bind.h" #include "base/compiler_specific.h" +#include "cobalt/base/polymorphic_downcast.h" #include "cobalt/math/size.h" #include "cobalt/media/shell_media_platform_starboard.h" +#include "cobalt/system_window/starboard/system_window.h" #include "media/audio/null_audio_streamer.h" #include "media/audio/shell_audio_sink.h" #include "media/base/filter_collection.h" @@ -31,20 +33,26 @@ #include "media/filters/shell_video_decoder_impl.h" #include "media/player/web_media_player_impl.h" #include "starboard/media.h" +#include "starboard/window.h" namespace cobalt { namespace media { namespace { +using ::base::polymorphic_downcast; using ::media::FilterCollection; using ::media::MessageLoopFactory; +using system_window::SystemWindowStarboard; class MediaModuleStarboard : public MediaModule { public: - MediaModuleStarboard(render_tree::ResourceProvider* resource_provider, + MediaModuleStarboard(system_window::SystemWindow* system_window, + render_tree::ResourceProvider* resource_provider, const Options& options) - : options_(options), media_platform_(resource_provider) {} + : options_(options), + system_window_(system_window), + media_platform_(resource_provider) {} std::string CanPlayType(const std::string& mime_type, const std::string& key_system) OVERRIDE { @@ -76,25 +84,31 @@ DLOG(INFO) << "Use Pulse audio"; streamer = ::media::ShellAudioStreamer::Instance(); } + SbWindow window = kSbWindowInvalid; + if (system_window_) { + window = polymorphic_downcast<SystemWindowStarboard*>(system_window_) + ->GetSbWindow(); + } return make_scoped_ptr<WebMediaPlayer>(new ::media::WebMediaPlayerImpl( - client, this, media_platform_.GetVideoFrameProvider(), + window, client, this, media_platform_.GetVideoFrameProvider(), filter_collection.Pass(), new ::media::ShellAudioSink(streamer), message_loop_factory.Pass(), new ::media::MediaLog)); } private: const Options options_; + system_window::SystemWindow* system_window_; ::media::ShellMediaPlatformStarboard media_platform_; }; } // namespace scoped_ptr<MediaModule> MediaModule::Create( - const math::Size& output_size, + system_window::SystemWindow* system_window, const math::Size& output_size, render_tree::ResourceProvider* resource_provider, const Options& options) { UNREFERENCED_PARAMETER(output_size); return make_scoped_ptr<MediaModule>( - new MediaModuleStarboard(resource_provider, options)); + new MediaModuleStarboard(system_window, resource_provider, options)); } } // namespace media
diff --git a/src/cobalt/media/media_module_win.cc b/src/cobalt/media/media_module_win.cc index dffc6a5..0f614e0 100644 --- a/src/cobalt/media/media_module_win.cc +++ b/src/cobalt/media/media_module_win.cc
@@ -27,8 +27,9 @@ // There is no media stack on Windows and the XB1 media stack cannot be used // directly on Windows. So MediaModule on windows does nothing. scoped_ptr<MediaModule> MediaModule::Create( - const math::Size& output_size, + system_window::SystemWindow* system_window, const math::Size& output_size, render_tree::ResourceProvider* resource_provider, const Options& options) { + UNREFERENCED_PARAMETER(system_window); UNREFERENCED_PARAMETER(output_size); UNREFERENCED_PARAMETER(resource_provider); UNREFERENCED_PARAMETER(options);
diff --git a/src/cobalt/media/sandbox/fuzzer_app.cc b/src/cobalt/media/sandbox/fuzzer_app.cc index 4495e34..1620e5a 100644 --- a/src/cobalt/media/sandbox/fuzzer_app.cc +++ b/src/cobalt/media/sandbox/fuzzer_app.cc
@@ -42,18 +42,24 @@ DCHECK_GT(number_of_iterations_, 0); for (int i = 0; i < number_of_iterations_; ++i) { - for (size_t file_index = 0; file_index < file_entries_.size(); - ++file_index) { - LOG(INFO) << "Fuzzing \"" << file_entries_[file_index].file_name << "\" " - << "with seed " << file_entries_[file_index].fuzzer.seed(); + for (FileEntries::iterator iter = file_entries_.begin(); + iter != file_entries_.end(); ++iter) { + LOG(INFO) << "Fuzzing \"" << iter->first << "\" " + << "with seed " << iter->second.fuzzer.seed(); - Fuzz(file_entries_[file_index].file_name, - file_entries_[file_index].fuzzer.GetFuzzedContent()); - file_entries_[file_index].fuzzer.AdvanceSeed(); + Fuzz(iter->first, iter->second.fuzzer.GetFuzzedContent()); + iter->second.fuzzer.AdvanceSeed(); } } } +const std::vector<uint8>& FuzzerApp::GetFileContent( + const std::string& filename) const { + FileEntries::const_iterator iter = file_entries_.find(filename); + DCHECK(iter != file_entries_.end()); + return iter->second.file_content; +} + bool FuzzerApp::ParseInitialSeedAndNumberOfIterations(int argc, char* argv[], int* initial_seed) { DCHECK(initial_seed); @@ -137,17 +143,16 @@ if (parsed_content.empty()) { return; } - file_entries_.push_back(FileEntry(file_name, uint8_content, parsed_content, - min_ratio, max_ratio, initial_seed)); + file_entries_.insert( + std::make_pair(file_name, FileEntry(uint8_content, parsed_content, + min_ratio, max_ratio, initial_seed))); } -FuzzerApp::FileEntry::FileEntry(const std::string& file_name, - const std::vector<uint8>& file_content, +FuzzerApp::FileEntry::FileEntry(const std::vector<uint8>& file_content, const std::vector<uint8>& fuzz_content, double min_ratio, double max_ratio, int initial_seed) - : file_name(file_name), - file_content(file_content), + : file_content(file_content), fuzzer(fuzz_content, min_ratio, max_ratio, initial_seed) {} } // namespace sandbox
diff --git a/src/cobalt/media/sandbox/fuzzer_app.h b/src/cobalt/media/sandbox/fuzzer_app.h index 03292a3..ab4a791 100644 --- a/src/cobalt/media/sandbox/fuzzer_app.h +++ b/src/cobalt/media/sandbox/fuzzer_app.h
@@ -17,6 +17,7 @@ #ifndef COBALT_MEDIA_SANDBOX_FUZZER_APP_H_ #define COBALT_MEDIA_SANDBOX_FUZZER_APP_H_ +#include <map> #include <string> #include <vector> @@ -52,18 +53,20 @@ protected: ~FuzzerApp() {} + const std::vector<uint8>& GetFileContent(const std::string& filename) const; + private: struct FileEntry { - std::string file_name; std::vector<uint8> file_content; ZzufFuzzer fuzzer; - FileEntry(const std::string& file_name, - const std::vector<uint8>& file_content, + FileEntry(const std::vector<uint8>& file_content, const std::vector<uint8>& fuzz_content, double min_ratio, double max_ratio, int initial_seed); }; + typedef std::map<std::string, FileEntry> FileEntries; + bool ParseInitialSeedAndNumberOfIterations(int argc, char* argv[], int* initial_seed); void CollectFiles(const std::string& path_name, double min_ratio, @@ -72,7 +75,8 @@ int initial_seed); int number_of_iterations_; - std::vector<FileEntry> file_entries_; + // Map from filename to file data. + FileEntries file_entries_; }; } // namespace sandbox
diff --git a/src/cobalt/media/sandbox/media_sandbox.cc b/src/cobalt/media/sandbox/media_sandbox.cc index 4f3a1a3..3a023e5 100644 --- a/src/cobalt/media/sandbox/media_sandbox.cc +++ b/src/cobalt/media/sandbox/media_sandbox.cc
@@ -99,10 +99,10 @@ } #endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) - media_module_ = - MediaModule::Create(renderer_module_->render_target()->GetSize(), - renderer_module_->pipeline()->GetResourceProvider(), - media_module_options); + media_module_ = MediaModule::Create( + system_window_.get(), renderer_module_->render_target()->GetSize(), + renderer_module_->pipeline()->GetResourceProvider(), + media_module_options); SetupAndSubmitScene(); }
diff --git a/src/cobalt/media/sandbox/media_source_demuxer.cc b/src/cobalt/media/sandbox/media_source_demuxer.cc index b657f27..0044342 100644 --- a/src/cobalt/media/sandbox/media_source_demuxer.cc +++ b/src/cobalt/media/sandbox/media_source_demuxer.cc
@@ -24,6 +24,7 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/message_loop.h" +#include "base/sys_byteorder.h" #include "media/base/bind_to_loop.h" #include "media/base/demuxer.h" #include "media/base/pipeline_status.h" @@ -41,6 +42,8 @@ using ::media::DemuxerStream; using ::media::ChunkDemuxer; +typedef base::Callback<void(::media::Buffer*)> AppendBufferCB; + const char kSourceId[] = "id"; // Stub log function. @@ -80,8 +83,6 @@ // every frame. class Loader : public ::media::DemuxerHost { public: - typedef base::Callback<void(::media::Buffer*)> AppendBufferCB; - Loader(const std::vector<uint8>& content, const AppendBufferCB& append_buffer_cb) : valid_(true), @@ -214,10 +215,48 @@ ::media::VideoDecoderConfig config_; }; +// This is a very loose IVF parser for fuzzing purpose and it ignores any +// invalid structure and just retrieves the frame data. +// IVF format (all data is little endian): +// 32 bytes file header starts with DKIF +// (4 bytes frame size + 8 bytes timestamp + <size> bytes frame data)* +bool LoadIVF(const std::vector<uint8>& content, + const AppendBufferCB& append_buffer_cb) { + const size_t kIVFFileHeaderSize = 32; + const size_t kIVFFrameHeaderSize = 12; + if (content.size() < kIVFFileHeaderSize || + memcmp(&content[0], "DKIF", 4) != 0) { + return false; + } + size_t offset = kIVFFileHeaderSize; + while (offset + kIVFFrameHeaderSize < content.size()) { + uint32 frame_size = base::ByteSwapToLE32( + *reinterpret_cast<const uint32*>(&content[offset])); + offset += kIVFFrameHeaderSize; + if (offset + frame_size > content.size()) { + break; + } + append_buffer_cb.Run(::media::StreamParserBuffer::CopyFrom( + &content[offset], frame_size, false)); + offset += frame_size; + } + return true; +} + } // namespace MediaSourceDemuxer::MediaSourceDemuxer(const std::vector<uint8>& content) : valid_(true) { + // Try to load it as an ivf first. + if (LoadIVF(content, Bind(&MediaSourceDemuxer::AppendBuffer, + base::Unretained(this)))) { + config_.Initialize(::media::kCodecVP9, ::media::VP9PROFILE_MAIN, + ::media::VideoFrame::YV12, gfx::Size(1, 1), + gfx::Rect(1, 1), gfx::Size(1, 1), NULL, 0, false, false); + valid_ = descs_.size() > 0; + return; + } + Loader loader( content, Bind(&MediaSourceDemuxer::AppendBuffer, base::Unretained(this))); valid_ = loader.valid();
diff --git a/src/cobalt/media/sandbox/raw_video_decoder_fuzzer.cc b/src/cobalt/media/sandbox/raw_video_decoder_fuzzer.cc index 724287e..d0f37ef 100644 --- a/src/cobalt/media/sandbox/raw_video_decoder_fuzzer.cc +++ b/src/cobalt/media/sandbox/raw_video_decoder_fuzzer.cc
@@ -134,9 +134,13 @@ // This function replace the original data inside the original file with the // fuzzed data to created a valid container with fuzzed AUs. |filename| should // contain a file that inside a path readable by the host. +// The following statement can be used inside RawVideoDecoderFuzzerApp::Fuzz() +// to save the fuzzing content back into its original container format. +// DumpFuzzedData(filename, GetFileContent(file_name), *demuxers_[file_name], +// fuzzing_content); void DumpFuzzedData(const std::string& filename, std::vector<uint8> container, const MediaSourceDemuxer& demuxer, - const ZzufFuzzer& fuzzer) { + const std::vector<uint8>& fuzzing_content) { std::vector<uint8>::iterator last_found = container.begin(); for (size_t i = 0; i < demuxer.GetFrameCount(); ++i) { MediaSourceDemuxer::AUDescriptor desc = demuxer.GetFrame(i); @@ -145,9 +149,8 @@ std::vector<uint8>::const_iterator end = begin + desc.size; std::vector<uint8>::iterator offset = std::search(last_found, container.end(), begin, end); - std::copy(fuzzer.GetFuzzedContent().begin() + desc.offset, - fuzzer.GetFuzzedContent().begin() + desc.offset + desc.size, - offset); + std::copy(fuzzing_content.begin() + desc.offset, + fuzzing_content.begin() + desc.offset + desc.size, offset); last_found = offset + desc.size + 1; } file_util::WriteFile(FilePath(filename), @@ -170,7 +173,7 @@ const std::string& file_name, const std::vector<uint8>& file_content) OVERRIDE { std::string ext = FilePath(file_name).Extension(); - if (ext != ".webm" && ext != ".mp4") { + if (ext != ".webm" && ext != ".mp4" && ext != ".ivf") { LOG(ERROR) << "Skip unsupported file " << file_name; return std::vector<uint8>(); }
diff --git a/src/cobalt/render_tree/dump_render_tree_to_string.cc b/src/cobalt/render_tree/dump_render_tree_to_string.cc index 6fdc38e..a9e2aac 100644 --- a/src/cobalt/render_tree/dump_render_tree_to_string.cc +++ b/src/cobalt/render_tree/dump_render_tree_to_string.cc
@@ -141,8 +141,41 @@ result_ << "\n"; } +namespace { +class BrushPrinterVisitor : public render_tree::BrushVisitor { + public: + BrushPrinterVisitor() {} + + void Visit( + const cobalt::render_tree::SolidColorBrush* solid_color_brush) OVERRIDE { + UNREFERENCED_PARAMETER(solid_color_brush); + brush_type_ = "(SolidColorBrush)"; + } + void Visit(const cobalt::render_tree::LinearGradientBrush* + linear_gradient_brush) OVERRIDE { + UNREFERENCED_PARAMETER(linear_gradient_brush); + brush_type_ = "(LinearGradientBrush)"; + } + void Visit(const cobalt::render_tree::RadialGradientBrush* + radial_gradient_brush) OVERRIDE { + UNREFERENCED_PARAMETER(radial_gradient_brush); + brush_type_ = "(RadialGradientBrush)"; + } + + const std::string& brush_type() const { return brush_type_; } + + private: + std::string brush_type_; +}; +} // namespace + void DebugTreePrinter::Visit(RectNode* rect) { - AddNamedNodeString(rect, "RectNode"); + AddNamedNodeString(rect, "RectNode "); + if (rect->data().background_brush) { + BrushPrinterVisitor printer_brush_visitor; + rect->data().background_brush->Accept(&printer_brush_visitor); + result_ << printer_brush_visitor.brush_type(); + } result_ << "\n"; }
diff --git a/src/cobalt/render_tree/filter_node.cc b/src/cobalt/render_tree/filter_node.cc index e4cfc50..bca254e 100644 --- a/src/cobalt/render_tree/filter_node.cc +++ b/src/cobalt/render_tree/filter_node.cc
@@ -35,6 +35,10 @@ const scoped_refptr<render_tree::Node>& source) : source(source), blur_filter(blur_filter) {} +FilterNode::Builder::Builder(const MapToMeshFilter& map_to_mesh_filter, + const scoped_refptr<render_tree::Node>& source) + : source(source), map_to_mesh_filter(map_to_mesh_filter) {} + FilterNode::FilterNode(const OpacityFilter& opacity_filter, const scoped_refptr<render_tree::Node>& source) : data_(opacity_filter, source) {} @@ -47,6 +51,10 @@ const scoped_refptr<render_tree::Node>& source) : data_(blur_filter, source) {} +FilterNode::FilterNode(const MapToMeshFilter& map_to_mesh_filter, + const scoped_refptr<render_tree::Node>& source) + : data_(map_to_mesh_filter, source) {} + void FilterNode::Accept(NodeVisitor* visitor) { visitor->Visit(this); } math::RectF FilterNode::Builder::GetBounds() const {
diff --git a/src/cobalt/render_tree/filter_node.h b/src/cobalt/render_tree/filter_node.h index 7e2a941..f5b3b2d 100644 --- a/src/cobalt/render_tree/filter_node.h +++ b/src/cobalt/render_tree/filter_node.h
@@ -21,6 +21,7 @@ #include "base/optional.h" #include "cobalt/base/type_id.h" #include "cobalt/render_tree/blur_filter.h" +#include "cobalt/render_tree/map_to_mesh_filter.h" #include "cobalt/render_tree/node.h" #include "cobalt/render_tree/opacity_filter.h" #include "cobalt/render_tree/shadow.h" @@ -47,6 +48,9 @@ Builder(const BlurFilter& blur_filter, const scoped_refptr<render_tree::Node>& source); + Builder(const MapToMeshFilter& map_to_mesh_filter, + const scoped_refptr<render_tree::Node>& source); + math::RectF GetBounds() const; // The source tree, which will be used as the input to the filters specified @@ -65,6 +69,11 @@ // If set, then a Gaussian blur will be applied to the source with a // Gaussian kernel of standard deviation |blur_sigma|. base::optional<BlurFilter> blur_filter; + + // If set, indicates that the rasterized output of the source content should + // be used as a texture which is then mapped onto a 3D mesh specified by the + // filter. + base::optional<MapToMeshFilter> map_to_mesh_filter; }; explicit FilterNode(const Builder& builder) : data_(builder) {} @@ -78,6 +87,9 @@ FilterNode(const BlurFilter& blur_filter, const scoped_refptr<render_tree::Node>& source); + FilterNode(const MapToMeshFilter& map_to_mesh_filter, + const scoped_refptr<render_tree::Node>& source); + void Accept(NodeVisitor* visitor) OVERRIDE; math::RectF GetBounds() const OVERRIDE { return data_.GetBounds(); }
diff --git a/src/cobalt/render_tree/map_to_mesh_filter.h b/src/cobalt/render_tree/map_to_mesh_filter.h new file mode 100644 index 0000000..5451c11 --- /dev/null +++ b/src/cobalt/render_tree/map_to_mesh_filter.h
@@ -0,0 +1,33 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COBALT_RENDER_TREE_MAP_TO_MESH_FILTER_H_ +#define COBALT_RENDER_TREE_MAP_TO_MESH_FILTER_H_ + +namespace cobalt { +namespace render_tree { + +// A MapToMeshFilter can be used to map source content onto a 3D mesh, within a +// specified well-defined viewport. +class MapToMeshFilter { + public: + MapToMeshFilter() {} +}; + +} // namespace render_tree +} // namespace cobalt + +#endif // COBALT_RENDER_TREE_MAP_TO_MESH_FILTER_H_
diff --git a/src/cobalt/render_tree/node_visitor_test.cc b/src/cobalt/render_tree/node_visitor_test.cc index 7a24cc4..ad7c3b2 100644 --- a/src/cobalt/render_tree/node_visitor_test.cc +++ b/src/cobalt/render_tree/node_visitor_test.cc
@@ -18,6 +18,7 @@ #include <string> +#include "base/bind.h" #include "cobalt/math/rect_f.h" #include "cobalt/math/size.h" #include "cobalt/render_tree/animations/animate_node.h" @@ -100,6 +101,8 @@ } }; +void SetBounds(const cobalt::math::Rect&) {} + } // namespace TEST(NodeVisitorTest, VisitsAnimate) { @@ -128,8 +131,10 @@ } TEST(NodeVisitorTest, VisitsPunchThroughVideo) { + PunchThroughVideoNode::Builder builder(cobalt::math::RectF(), + base::Bind(SetBounds)); scoped_refptr<PunchThroughVideoNode> punch_through_video_node( - new PunchThroughVideoNode(cobalt::math::RectF())); + new PunchThroughVideoNode(builder)); MockNodeVisitor mock_visitor; EXPECT_CALL(mock_visitor, Visit(punch_through_video_node.get())); punch_through_video_node->Accept(&mock_visitor);
diff --git a/src/cobalt/render_tree/punch_through_video_node.h b/src/cobalt/render_tree/punch_through_video_node.h index 99f02b0..7261ab7 100644 --- a/src/cobalt/render_tree/punch_through_video_node.h +++ b/src/cobalt/render_tree/punch_through_video_node.h
@@ -17,8 +17,10 @@ #ifndef COBALT_RENDER_TREE_PUNCH_THROUGH_VIDEO_NODE_H_ #define COBALT_RENDER_TREE_PUNCH_THROUGH_VIDEO_NODE_H_ +#include "base/callback.h" #include "base/compiler_specific.h" #include "cobalt/base/type_id.h" +#include "cobalt/math/rect.h" #include "cobalt/math/rect_f.h" #include "cobalt/render_tree/node.h" @@ -39,15 +41,17 @@ // support punch out video rendering. class PunchThroughVideoNode : public Node { public: - class Builder { - public: - explicit Builder(const math::RectF& rect) : rect(rect) {} + typedef base::Callback<void(const math::Rect&)> SetBoundsCB; + + struct Builder { + Builder(const math::RectF& rect, const SetBoundsCB& set_bounds_cb) + : rect(rect), set_bounds_cb(set_bounds_cb) {} // The destination rectangle (size includes border). math::RectF rect; + const SetBoundsCB set_bounds_cb; }; - explicit PunchThroughVideoNode(const math::RectF& rect) : data_(rect) {} explicit PunchThroughVideoNode(const Builder& builder) : data_(builder) {} void Accept(NodeVisitor* visitor) OVERRIDE;
diff --git a/src/cobalt/render_tree/render_tree.gyp b/src/cobalt/render_tree/render_tree.gyp index 6e7c6a7..5d6f1a6 100644 --- a/src/cobalt/render_tree/render_tree.gyp +++ b/src/cobalt/render_tree/render_tree.gyp
@@ -25,6 +25,7 @@ 'target_name': 'render_tree', 'type': 'static_library', 'sources': [ + 'blur_filter.h', 'border.cc', 'border.h', 'brush.cc', @@ -43,6 +44,7 @@ 'glyph_buffer.h', 'image_node.cc', 'image_node.h', + 'map_to_mesh_filter.h', 'matrix_transform_node.cc', 'matrix_transform_node.h', 'node.h',
diff --git a/src/cobalt/renderer/backend/egl/graphics_context.cc b/src/cobalt/renderer/backend/egl/graphics_context.cc index a418417..3482472 100644 --- a/src/cobalt/renderer/backend/egl/graphics_context.cc +++ b/src/cobalt/renderer/backend/egl/graphics_context.cc
@@ -14,12 +14,11 @@ * limitations under the License. */ -#include "cobalt/renderer/backend/egl/graphics_context.h" - #include <GLES2/gl2.h> - #include <algorithm> +#include "cobalt/renderer/backend/egl/graphics_context.h" + #include "base/debug/trace_event.h" #include "cobalt/base/polymorphic_downcast.h" #include "cobalt/renderer/backend/egl/graphics_system.h" @@ -155,16 +154,14 @@ blit_program_ = glCreateProgram(); blit_vertex_shader_ = glCreateShader(GL_VERTEX_SHADER); - const char* blit_vertex_shader_source = "\ - attribute vec2 a_position;\ - attribute vec2 a_tex_coord;\ - varying vec2 v_tex_coord;\ - \ - void main() {\ - gl_Position = vec4(a_position.x, a_position.y, 0, 1);\ - v_tex_coord = a_tex_coord;\ - }\ - "; + const char* blit_vertex_shader_source = + "attribute vec2 a_position;" + "attribute vec2 a_tex_coord;" + "varying vec2 v_tex_coord;" + "void main() {" + " gl_Position = vec4(a_position.x, a_position.y, 0, 1);" + " v_tex_coord = a_tex_coord;" + "}"; int blit_vertex_shader_source_length = strlen(blit_vertex_shader_source); GL_CALL(glShaderSource(blit_vertex_shader_, 1, &blit_vertex_shader_source, &blit_vertex_shader_source_length)); @@ -172,15 +169,13 @@ GL_CALL(glAttachShader(blit_program_, blit_vertex_shader_)); blit_fragment_shader_ = glCreateShader(GL_FRAGMENT_SHADER); - const char* blit_fragment_shader_source = "\ - precision mediump float;\ - varying vec2 v_tex_coord;\ - uniform sampler2D texture;\ - \ - void main() {\ - gl_FragColor = texture2D(texture, v_tex_coord);\ - }\ - "; + const char* blit_fragment_shader_source = + "precision mediump float;" + "varying vec2 v_tex_coord;" + "uniform sampler2D texture;" + "void main() {" + " gl_FragColor = texture2D(texture, v_tex_coord);" + "}"; int blit_fragment_shader_source_length = strlen(blit_fragment_shader_source); GL_CALL(glShaderSource(blit_fragment_shader_, 1, &blit_fragment_shader_source, &blit_fragment_shader_source_length)); @@ -202,7 +197,7 @@ float tex_coord_u; float tex_coord_v; }; - const QuadVertex kQuadVerts[4] = { + const QuadVertex kBlitQuadVerts[4] = { {-1.0f, -1.0f, 0.0f, 1.0f}, {-1.0f, 1.0f, 0.0f, 0.0f}, {1.0f, -1.0f, 1.0f, 1.0f}, @@ -210,13 +205,12 @@ }; GL_CALL(glGenBuffers(1, &blit_vertex_buffer_)); GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, blit_vertex_buffer_)); - GL_CALL(glBufferData( - GL_ARRAY_BUFFER, sizeof(kQuadVerts), kQuadVerts, GL_STATIC_DRAW)); + GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(kBlitQuadVerts), kBlitQuadVerts, + GL_STATIC_DRAW)); } GraphicsContextEGL::~GraphicsContextEGL() { MakeCurrent(); - GL_CALL(glDeleteBuffers(1, &blit_vertex_buffer_)); GL_CALL(glDeleteProgram(blit_program_)); GL_CALL(glDeleteShader(blit_fragment_shader_)); @@ -399,8 +393,8 @@ } void GraphicsContextEGL::SecurityClear() { - // Clear the screen to a color that is bright and gross to exagerate that this - // is a problem if it is witnessed. + // Clear the screen to a color that is bright and gross to exaggerate that + // this is a problem if it is witnessed. GL_CALL(glClearColor(1.0f, 0.4f, 1.0f, 1.0f)); GL_CALL(glClear(GL_COLOR_BUFFER_BIT)); }
diff --git a/src/cobalt/renderer/default_options_starboard.gyp b/src/cobalt/renderer/default_options_starboard.gyp new file mode 100644 index 0000000..990ffa6 --- /dev/null +++ b/src/cobalt/renderer/default_options_starboard.gyp
@@ -0,0 +1,28 @@ +# Copyright 2016 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +{ + 'targets': [ + { + 'target_name': 'default_options', + 'type': 'static_library', + 'includes': [ + 'renderer_parameters_setup.gypi', + ], + 'sources': [ + '<(DEPTH)/cobalt/renderer/renderer_module_default_options_starboard.cc', + ], + }, + ], +}
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc index a5f38e7..a1c5aba 100644 --- a/src/cobalt/renderer/pipeline.cc +++ b/src/cobalt/renderer/pipeline.cc
@@ -21,6 +21,7 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/path_service.h" +#include "cobalt/base/address_sanitizer.h" #include "cobalt/base/cobalt_paths.h" #include "cobalt/base/polymorphic_downcast.h" #include "cobalt/render_tree/dump_render_tree_to_string.h" @@ -45,7 +46,8 @@ // The stack size to be used for the renderer thread. This is must be large // enough to support recursing on the render tree. -const int kRendererThreadStackSize = 128 * 1024; +const int kRendererThreadStackSize = + 128 * 1024 + base::kAsanAdditionalStackSize; // How frequently the CVal stats for rasterize current tree timing should // update. The time interval is in milliseconds.
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc index 3f37aeb..12475b8 100644 --- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc +++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
@@ -242,9 +242,16 @@ render_tree::PunchThroughVideoNode* punch_through_video_node) { SbBlitterSetColor(context_, SbBlitterColorFromRGBA(0, 0, 0, 0)); SbBlitterSetBlending(context_, false); - SbBlitterFillRect(context_, - RectFToBlitterRect(render_state_.transform.TransformRect( - punch_through_video_node->data().rect))); + SbBlitterRect blitter_rect = + RectFToBlitterRect(render_state_.transform.TransformRect( + punch_through_video_node->data().rect)); + SbBlitterFillRect(context_, blitter_rect); + + if (!punch_through_video_node->data().set_bounds_cb.is_null()) { + punch_through_video_node->data().set_bounds_cb.Run( + math::Rect(blitter_rect.x, blitter_rect.y, blitter_rect.width, + blitter_rect.height)); + } } namespace {
diff --git a/src/cobalt/renderer/rasterizer/blitter/scratch_surface_cache.cc b/src/cobalt/renderer/rasterizer/blitter/scratch_surface_cache.cc index d9b3790..d1d4292 100644 --- a/src/cobalt/renderer/rasterizer/blitter/scratch_surface_cache.cc +++ b/src/cobalt/renderer/rasterizer/blitter/scratch_surface_cache.cc
@@ -55,10 +55,13 @@ common::ScratchSurfaceCache::Surface* ScratchSurfaceCache::Delegate::CreateSurface(const math::Size& size) { - return new BlitterSurface( - SbBlitterCreateRenderTargetSurface(device_, size.width(), size.height(), - kSbBlitterSurfaceFormatRGBA8), - size); + SbBlitterSurface blitter_surface = SbBlitterCreateRenderTargetSurface( + device_, size.width(), size.height(), kSbBlitterSurfaceFormatRGBA8); + if (SbBlitterIsSurfaceValid(blitter_surface)) { + return new BlitterSurface(blitter_surface, size); + } else { + return NULL; + } } void ScratchSurfaceCache::Delegate::DestroySurface( @@ -80,9 +83,13 @@ } SbBlitterSurface CachedScratchSurface::GetSurface() { - return base::polymorphic_downcast<BlitterSurface*>( - common_scratch_surface_.GetSurface()) - ->blitter_surface(); + if (common_scratch_surface_.GetSurface()) { + return base::polymorphic_downcast<BlitterSurface*>( + common_scratch_surface_.GetSurface()) + ->blitter_surface(); + } else { + return NULL; + } } } // namespace blitter
diff --git a/src/cobalt/renderer/rasterizer/common/scratch_surface_cache.cc b/src/cobalt/renderer/rasterizer/common/scratch_surface_cache.cc index dac975b..6c6b358 100644 --- a/src/cobalt/renderer/rasterizer/common/scratch_surface_cache.cc +++ b/src/cobalt/renderer/rasterizer/common/scratch_surface_cache.cc
@@ -54,6 +54,17 @@ TRACE_EVENT2("cobalt::renderer", "ScratchSurfaceCache::AcquireScratchSurface()", "width", size.width(), "height", size.height()); + if (cache_capacity_in_bytes_ == 0) { + // If scratch surface caching is disabled, just create and immediately + // return a surface. + Surface* surface = delegate_->CreateSurface(size); + if (surface) { + delegate_->PrepareForUse(surface, size); + return surface; + } else { + return NULL; + } + } // First check if we can find a suitable surface in our cache that is at // least the size requested. @@ -93,6 +104,12 @@ "ScratchSurfaceCache::ReleaseScratchSurface()", "width", surface->GetSize().width(), "height", surface->GetSize().height()); + if (cache_capacity_in_bytes_ == 0) { + // If scratch surface caching is disabled, immediately destroy the surface + // and return. + delegate_->DestroySurface(surface); + return; + } DCHECK_EQ(surface_stack_.back(), surface); surface_stack_.pop_back();
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc index 5fde5c2..6dc87b7 100644 --- a/src/cobalt/renderer/rasterizer/pixel_test.cc +++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -16,6 +16,7 @@ #include <cmath> +#include "base/bind.h" #include "cobalt/math/matrix3_f.h" #include "cobalt/math/rect_f.h" #include "cobalt/math/size_f.h" @@ -110,6 +111,12 @@ namespace renderer { namespace rasterizer { +namespace { + +void SetBounds(const math::Rect&) {} + +} // namespace + TEST_F(PixelTest, RedFillRectOnEntireSurface) { // Create a test render tree that will fill the entire output surface // with a solid color rectangle. @@ -2748,7 +2755,8 @@ scoped_ptr<Brush>(new SolidColorBrush( ColorRGBA(0.5f, 0.5f, 1.0f, 1.0f))))); - builder.AddChild(new PunchThroughVideoNode(RectF(50, 50, 100, 100))); + builder.AddChild(new PunchThroughVideoNode(PunchThroughVideoNode::Builder( + RectF(50, 50, 100, 100), base::Bind(SetBounds)))); TestTree(new CompositionNode(builder.Pass())); }
diff --git a/src/cobalt/renderer/rasterizer/skia/rasterizer.gyp b/src/cobalt/renderer/rasterizer/skia/rasterizer.gyp index 43555a0..cd5c0c0 100644 --- a/src/cobalt/renderer/rasterizer/skia/rasterizer.gyp +++ b/src/cobalt/renderer/rasterizer/skia/rasterizer.gyp
@@ -40,4 +40,4 @@ ], }, ], -} \ No newline at end of file +}
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc index 1f8996b..8a34aff 100644 --- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc +++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -74,7 +74,7 @@ const CreateScratchSurfaceFunction* create_scratch_surface_function, SurfaceCacheDelegate* surface_cache_delegate, common::SurfaceCache* surface_cache, Type visitor_type) - : render_target_(render_target), + : draw_state_(render_target), create_scratch_surface_function_(create_scratch_surface_function), surface_cache_delegate_(surface_cache_delegate), surface_cache_(surface_cache), @@ -82,11 +82,9 @@ DCHECK_EQ(surface_cache_delegate_ == NULL, surface_cache_ == NULL); if (surface_cache_delegate_) { // Update our surface cache delegate to point to this render tree node - // visitor and our canvas. - surface_cache_scoped_context_.emplace( - surface_cache_delegate_, render_target_, - base::Bind(&RenderTreeNodeVisitor::SetRenderTarget, - base::Unretained(this))); + // visitor and our draw state. + surface_cache_scoped_context_.emplace(surface_cache_delegate_, + &draw_state_); } } @@ -124,8 +122,8 @@ return; } - render_target_->translate(composition_node->data().offset().x(), - composition_node->data().offset().y()); + draw_state_.render_target->translate(composition_node->data().offset().x(), + composition_node->data().offset().y()); // If we have more than one child (there is little to be gained by performing // these calculations otherwise since our bounding rectangle is equal to @@ -136,9 +134,9 @@ base::optional<SkMatrix> total_matrix; if (children.size() > 1) { SkIRect canvas_boundsi; - render_target_->getClipDeviceBounds(&canvas_boundsi); + draw_state_.render_target->getClipDeviceBounds(&canvas_boundsi); canvas_bounds = SkRect::Make(canvas_boundsi); - total_matrix = render_target_->getTotalMatrix(); + total_matrix = draw_state_.render_target->getTotalMatrix(); } for (render_tree::CompositionNode::Children::const_iterator iter = @@ -152,11 +150,11 @@ } } - render_target_->translate(-composition_node->data().offset().x(), - -composition_node->data().offset().y()); + draw_state_.render_target->translate(-composition_node->data().offset().x(), + -composition_node->data().offset().y()); #if ENABLE_FLUSH_AFTER_EVERY_NODE - render_target_->flush(); + draw_state_.render_target->flush(); #endif } @@ -224,11 +222,12 @@ void RenderTreeNodeVisitor::RenderFilterViaOffscreenSurface( const render_tree::FilterNode::Builder& filter_node) { - const SkMatrix& total_matrix_skia = render_target_->getTotalMatrix(); + const SkMatrix& total_matrix_skia = + draw_state_.render_target->getTotalMatrix(); math::Matrix3F total_matrix = SkiaMatrixToCobalt(total_matrix_skia); SkIRect canvas_boundsi; - render_target_->getClipDeviceBounds(&canvas_boundsi); + draw_state_.render_target->getClipDeviceBounds(&canvas_boundsi); common::OffscreenRenderCoordinateMapping coord_mapping = common::GetOffscreenRenderCoordinateMapping( @@ -282,13 +281,13 @@ // destination rectangles should be exactly equal. paint.setFilterLevel(SkPaint::kNone_FilterLevel); - // We've already used the render_target_'s scale when rendering to the - // offscreen surface, so reset the scale for now. - render_target_->save(); + // We've already used the draw_state_.render_target's scale when rendering to + // the offscreen surface, so reset the scale for now. + draw_state_.render_target->save(); ApplyBlurFilterToPaint(&paint, filter_node.blur_filter); - ApplyViewportMask(render_target_, filter_node.viewport_filter); + ApplyViewportMask(draw_state_.render_target, filter_node.viewport_filter); - render_target_->setMatrix(CobaltMatrixToSkia( + draw_state_.render_target->setMatrix(CobaltMatrixToSkia( math::TranslateMatrix(coord_mapping.output_pre_translate) * total_matrix * math::ScaleMatrix(coord_mapping.output_post_scale))); @@ -304,11 +303,12 @@ SkRect source_rect = SkRect::MakeWH(coord_mapping.output_bounds.width(), coord_mapping.output_bounds.height()); - render_target_->drawImageRect(image, &source_rect, dest_rect, &paint); + draw_state_.render_target->drawImageRect(image, &source_rect, dest_rect, + &paint); // Finally restore our parent render target's original transform for the // next draw call. - render_target_->restore(); + draw_state_.render_target->restore(); } namespace { @@ -341,9 +341,33 @@ return false; } + +bool SourceCanRenderWithOpacity(render_tree::Node* source) { + if (source->GetTypeId() == base::GetTypeId<render_tree::ImageNode>() || + source->GetTypeId() == base::GetTypeId<render_tree::RectNode>()) { + return true; + } else if (source->GetTypeId() == + base::GetTypeId<render_tree::CompositionNode>()) { + // If we are a composition of valid sources, then we also allow + // rendering through a viewport here. + render_tree::CompositionNode* composition_node = + base::polymorphic_downcast<render_tree::CompositionNode*>(source); + typedef render_tree::CompositionNode::Children Children; + const Children& children = composition_node->data().children(); + if (children.size() == 1 && SourceCanRenderWithOpacity(children[0].get())) { + return true; + } + } + return false; +} } // namespace void RenderTreeNodeVisitor::Visit(render_tree::FilterNode* filter_node) { + if (filter_node->data().map_to_mesh_filter) { + // TODO: Implement support for MapToMeshFilter. + return; + } + #if ENABLE_RENDER_TREE_VISITOR_TRACING TRACE_EVENT0("cobalt::renderer", "Visit(FilterNode)"); @@ -379,7 +403,7 @@ return; } - const SkMatrix& total_matrix = render_target_->getTotalMatrix(); + const SkMatrix& total_matrix = draw_state_.render_target->getTotalMatrix(); bool has_rounded_corners = filter_node->data().viewport_filter && @@ -395,7 +419,8 @@ !filter_node->data().blur_filter && // If an opacity filter is being applied, we must render to a separate // texture first. - !filter_node->data().opacity_filter && + (!filter_node->data().opacity_filter || + SourceCanRenderWithOpacity(filter_node->data().source)) && // If transforms are applied to the viewport, then we will render to // a separate texture first. total_matrix.rectStaysRect() && @@ -407,10 +432,18 @@ SourceCanRenderWithRoundedCorners(filter_node->data().source)); if (can_render_with_clip_mask_directly) { - render_target_->save(); - ApplyViewportMask(render_target_, filter_node->data().viewport_filter); + RenderTreeNodeVisitorDrawState original_draw_state(draw_state_); + + draw_state_.render_target->save(); + ApplyViewportMask(draw_state_.render_target, + filter_node->data().viewport_filter); + + if (filter_node->data().opacity_filter) { + draw_state_.opacity *= filter_node->data().opacity_filter->opacity(); + } filter_node->data().source->Accept(this); - render_target_->restore(); + draw_state_.render_target->restore(); + draw_state_ = original_draw_state; } else { common::SurfaceCache::Block cache_block(surface_cache_, filter_node); if (cache_block.Cached()) return; @@ -418,7 +451,7 @@ RenderFilterViaOffscreenSurface(filter_node->data()); } #if ENABLE_FLUSH_AFTER_EVERY_NODE - render_target_->flush(); + draw_state_.render_target->flush(); #endif } @@ -454,18 +487,23 @@ mat.Get(1, 2) <= 0.0f && 1.0f - mat.Get(1, 2) <= mat.Get(1, 1); } -SkPaint CreateSkPaintForImageRendering() { +SkPaint CreateSkPaintForImageRendering( + const RenderTreeNodeVisitorDrawState& draw_state) { SkPaint paint; paint.setFilterLevel(SkPaint::kLow_FilterLevel); + if (draw_state.opacity < 1.0f) { + paint.setAlpha(draw_state.opacity * 255); + } + return paint; } void RenderSinglePlaneImage(SinglePlaneImage* single_plane_image, - SkCanvas* render_target, + RenderTreeNodeVisitorDrawState* draw_state, const math::RectF& destination_rect, const math::Matrix3F* local_transform) { - SkPaint paint = CreateSkPaintForImageRendering(); + SkPaint paint = CreateSkPaintForImageRendering(*draw_state); // In the most frequent by far case where the normalized transformed image // texture coordinates lie within the unit square, then we must ensure NOT @@ -486,9 +524,9 @@ SkRect src = SkRect::MakeXYWH(x, y, width, height); - render_target->drawBitmapRectToRect(single_plane_image->GetBitmap(), &src, - CobaltRectFToSkiaRect(destination_rect), - &paint); + draw_state->render_target->drawBitmapRectToRect( + single_plane_image->GetBitmap(), &src, + CobaltRectFToSkiaRect(destination_rect), &paint); } else { // Use the more general approach which allows arbitrary local texture // coordinate matrices. @@ -502,12 +540,13 @@ SkShader::kRepeat_TileMode, &skia_local_transform)); paint.setShader(image_shader); - render_target->drawRect(CobaltRectFToSkiaRect(destination_rect), paint); + draw_state->render_target->drawRect(CobaltRectFToSkiaRect(destination_rect), + paint); } } void RenderMultiPlaneImage(MultiPlaneImage* multi_plane_image, - SkCanvas* render_target, + RenderTreeNodeVisitorDrawState* draw_state, const math::RectF& destination_rect, const math::Matrix3F* local_transform) { SkMatrix skia_local_transform = CobaltMatrixToSkia(*local_transform); @@ -552,9 +591,10 @@ } } - SkPaint paint = CreateSkPaintForImageRendering(); + SkPaint paint = CreateSkPaintForImageRendering(*draw_state); paint.setShader(yuv2rgb_shader); - render_target->drawRect(CobaltRectFToSkiaRect(destination_rect), paint); + draw_state->render_target->drawRect(CobaltRectFToSkiaRect(destination_rect), + paint); } } // namespace @@ -588,18 +628,18 @@ // depending on whether it's single or multi planed. if (image->GetTypeId() == base::GetTypeId<SinglePlaneImage>()) { RenderSinglePlaneImage(base::polymorphic_downcast<SinglePlaneImage*>(image), - render_target_, image_node->data().destination_rect, + &draw_state_, image_node->data().destination_rect, &(image_node->data().local_transform)); } else if (image->GetTypeId() == base::GetTypeId<MultiPlaneImage>()) { RenderMultiPlaneImage(base::polymorphic_downcast<MultiPlaneImage*>(image), - render_target_, image_node->data().destination_rect, + &draw_state_, image_node->data().destination_rect, &(image_node->data().local_transform)); } else { NOTREACHED(); } #if ENABLE_FLUSH_AFTER_EVERY_NODE - render_target_->flush(); + draw_state_.render_target->flush(); #endif } @@ -615,9 +655,9 @@ // Concatenate the matrix transform to the render target and then continue // on with rendering our source. - render_target_->save(); + draw_state_.render_target->save(); - render_target_->concat( + draw_state_.render_target->concat( CobaltMatrixToSkia(matrix_transform_node->data().transform)); // Since our scale may have changed, inform the surface cache system to update @@ -628,10 +668,10 @@ matrix_transform_node->data().source->Accept(this); - render_target_->restore(); + draw_state_.render_target->restore(); #if ENABLE_FLUSH_AFTER_EVERY_NODE - render_target_->flush(); + draw_state_.render_target->flush(); #endif } @@ -651,16 +691,30 @@ // We proceed anyway, just in case things happen to work out. } - const math::RectF& rect = punch_through_video_node->data().rect; + const math::RectF& math_rect = punch_through_video_node->data().rect; SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); paint.setARGB(0, 0, 0, 0); - render_target_->drawRect( - SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()), paint); + SkRect sk_rect = SkRect::MakeXYWH(math_rect.x(), math_rect.y(), + math_rect.width(), math_rect.height()); + SkMatrix total_matrix = draw_state_.render_target->getTotalMatrix(); + + SkRect sk_rect_transformed; + total_matrix.mapRect(&sk_rect_transformed, sk_rect); + + if (!punch_through_video_node->data().set_bounds_cb.is_null()) { + punch_through_video_node->data().set_bounds_cb.Run( + math::Rect(static_cast<int>(sk_rect_transformed.x()), + static_cast<int>(sk_rect_transformed.y()), + static_cast<int>(sk_rect_transformed.width()), + static_cast<int>(sk_rect_transformed.height()))); + } + + draw_state_.render_target->drawRect(sk_rect, paint); #if ENABLE_FLUSH_AFTER_EVERY_NODE - render_target_->flush(); + draw_state_.render_target->flush(); #endif } @@ -668,7 +722,9 @@ class SkiaBrushVisitor : public render_tree::BrushVisitor { public: - explicit SkiaBrushVisitor(SkPaint* paint) : paint_(paint) {} + explicit SkiaBrushVisitor(SkPaint* paint, + const RenderTreeNodeVisitorDrawState& draw_state) + : paint_(paint), draw_state_(draw_state) {} void Visit( const cobalt::render_tree::SolidColorBrush* solid_color_brush) OVERRIDE; @@ -679,19 +735,21 @@ private: SkPaint* paint_; + const RenderTreeNodeVisitorDrawState& draw_state_; }; void SkiaBrushVisitor::Visit( const cobalt::render_tree::SolidColorBrush* solid_color_brush) { const cobalt::render_tree::ColorRGBA& color = solid_color_brush->color(); - if (color.a() == 1.0f) { + float alpha = color.a() * draw_state_.opacity; + if (alpha == 1.0f) { paint_->setXfermodeMode(SkXfermode::kSrc_Mode); } else { paint_->setXfermodeMode(SkXfermode::kSrcOver_Mode); } - paint_->setARGB(color.a() * 255, color.r() * 255, color.g() * 255, + paint_->setARGB(alpha * 255, color.r() * 255, color.g() * 255, color.b() * 255); } @@ -736,9 +794,12 @@ SkGradientShader::kInterpolateColorsInPremul_Flag, NULL)); paint_->setShader(shader); - if (!skia_color_stops.has_alpha) { + if (!skia_color_stops.has_alpha && draw_state_.opacity == 1.0f) { paint_->setXfermodeMode(SkXfermode::kSrc_Mode); } else { + if (draw_state_.opacity < 1.0f) { + paint_->setAlpha(255 * draw_state_.opacity); + } paint_->setXfermodeMode(SkXfermode::kSrcOver_Mode); } } @@ -771,22 +832,26 @@ SkGradientShader::kInterpolateColorsInPremul_Flag, &local_matrix)); paint_->setShader(shader); - if (!skia_color_stops.has_alpha) { + if (!skia_color_stops.has_alpha && draw_state_.opacity == 1.0f) { paint_->setXfermodeMode(SkXfermode::kSrc_Mode); } else { + if (draw_state_.opacity < 1.0f) { + paint_->setAlpha(255 * draw_state_.opacity); + } paint_->setXfermodeMode(SkXfermode::kSrcOver_Mode); } } -void DrawRectWithBrush(SkCanvas* render_target, +void DrawRectWithBrush(RenderTreeNodeVisitorDrawState* draw_state, cobalt::render_tree::Brush* brush, const math::RectF& rect) { // Setup our paint object according to the brush parameters set on the // rectangle. SkPaint paint; - SkiaBrushVisitor brush_visitor(&paint); + SkiaBrushVisitor brush_visitor(&paint, *draw_state); brush->Accept(&brush_visitor); - render_target->drawRect( + + draw_state->render_target->drawRect( SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()), paint); } @@ -820,7 +885,8 @@ } // namespace void DrawRoundedRectWithBrush( - SkCanvas* render_target, render_tree::Brush* brush, const math::RectF& rect, + RenderTreeNodeVisitorDrawState* draw_state, render_tree::Brush* brush, + const math::RectF& rect, const render_tree::RoundedCorners& rounded_corners) { if (!CheckForSolidBrush(brush)) { NOTREACHED() << "Only solid brushes are currently supported for this shape " @@ -829,23 +895,27 @@ } SkPaint paint; - SkiaBrushVisitor brush_visitor(&paint); + SkiaBrushVisitor brush_visitor(&paint, *draw_state); brush->Accept(&brush_visitor); paint.setAntiAlias(true); - render_target->drawPath(RoundedRectToSkiaPath(rect, rounded_corners), paint); + draw_state->render_target->drawPath( + RoundedRectToSkiaPath(rect, rounded_corners), paint); } -void DrawQuadWithColorIfBorderIsSolid(render_tree::BorderStyle border_style, - SkCanvas* render_target, - const render_tree::ColorRGBA& color, - const SkPoint* points, bool anti_alias) { +void DrawQuadWithColorIfBorderIsSolid( + render_tree::BorderStyle border_style, + RenderTreeNodeVisitorDrawState* draw_state, + const render_tree::ColorRGBA& color, const SkPoint* points, + bool anti_alias) { if (border_style == render_tree::kBorderStyleSolid) { SkPaint paint; - paint.setARGB(color.a() * 255, color.r() * 255, color.g() * 255, + float alpha = color.a(); + alpha *= draw_state->opacity; + paint.setARGB(alpha * 255, color.r() * 255, color.g() * 255, color.b() * 255); paint.setAntiAlias(anti_alias); - if (color.a() == 1.0f) { + if (alpha == 1.0f) { paint.setXfermodeMode(SkXfermode::kSrc_Mode); } else { paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); @@ -853,7 +923,7 @@ SkPath path; path.addPoly(points, 4, true); - render_target->drawPath(path, paint); + draw_state->render_target->drawPath(path, paint); } else { DCHECK_EQ(border_style, render_tree::kBorderStyleNone); } @@ -867,26 +937,33 @@ // ||G_______H|| // |/_________\| // C D -void DrawSolidNonRoundRectBorder(SkCanvas* render_target, +void DrawSolidNonRoundRectBorder(RenderTreeNodeVisitorDrawState* draw_state, const math::RectF& rect, const render_tree::Border& border) { - bool anti_alias = true; + // Check if the border colors are the same or not to determine whether we + // should be using antialiasing. If the border colors are different, then + // there will be visible diagonal edges in the output and so we would like to + // render with antialiasing enabled to smooth those diagonal edges. + bool border_colors_are_same = border.top.color == border.left.color && + border.top.color == border.bottom.color && + border.top.color == border.right.color; - if (border.top.color == border.left.color && - border.top.color == border.bottom.color && - border.top.color == border.right.color) { - // Disable the anti-alias when the borders have the same color. - anti_alias = false; - } + // If any of the border edges have width less than this threshold, they will + // use antialiasing as otherwise depending on the border's fractional + // position, it may have one extra pixel visible, which is a large percentage + // of its small width. + const float kAntiAliasWidthThreshold = 3.0f; // Top SkPoint top_points[4] = { - {rect.x(), rect.y()}, // A - {rect.x() + border.left.width, rect.y() + border.top.width}, // E - {rect.right() - border.right.width, rect.y() + border.top.width}, // F - {rect.right(), rect.y()}}; // B - DrawQuadWithColorIfBorderIsSolid(border.top.style, render_target, - border.top.color, top_points, anti_alias); + {rect.x(), rect.y()}, // A + {rect.x() + border.left.width, rect.y() + border.top.width}, // E + {rect.right() - border.right.width, rect.y() + border.top.width}, // F + {rect.right(), rect.y()}}; // B + DrawQuadWithColorIfBorderIsSolid( + border.top.style, draw_state, border.top.color, top_points, + border.top.width < kAntiAliasWidthThreshold ? true + : !border_colors_are_same); // Left SkPoint left_points[4] = { @@ -894,8 +971,10 @@ {rect.x(), rect.bottom()}, // C {rect.x() + border.left.width, rect.bottom() - border.bottom.width}, // G {rect.x() + border.left.width, rect.y() + border.top.width}}; // E - DrawQuadWithColorIfBorderIsSolid(border.left.style, render_target, - border.left.color, left_points, anti_alias); + DrawQuadWithColorIfBorderIsSolid( + border.left.style, draw_state, border.left.color, left_points, + border.left.width < kAntiAliasWidthThreshold ? true + : !border_colors_are_same); // Bottom SkPoint bottom_points[4] = { @@ -903,25 +982,27 @@ {rect.x(), rect.bottom()}, // C {rect.right(), rect.bottom()}, // D {rect.right() - border.right.width, - rect.bottom() - border.bottom.width}}; // H - DrawQuadWithColorIfBorderIsSolid(border.bottom.style, render_target, - border.bottom.color, bottom_points, - anti_alias); + rect.bottom() - border.bottom.width}}; // H + DrawQuadWithColorIfBorderIsSolid( + border.bottom.style, draw_state, border.bottom.color, bottom_points, + border.bottom.width < kAntiAliasWidthThreshold ? true + : !border_colors_are_same); // Right SkPoint right_points[4] = { - {rect.right() - border.right.width, rect.y() + border.top.width}, // F + {rect.right() - border.right.width, rect.y() + border.top.width}, // F {rect.right() - border.right.width, - rect.bottom() - border.bottom.width}, // H - {rect.right(), rect.bottom()}, // D - {rect.right(), rect.y()}}; // B - DrawQuadWithColorIfBorderIsSolid(border.right.style, render_target, - border.right.color, right_points, - anti_alias); + rect.bottom() - border.bottom.width}, // H + {rect.right(), rect.bottom()}, // D + {rect.right(), rect.y()}}; // B + DrawQuadWithColorIfBorderIsSolid( + border.right.style, draw_state, border.right.color, right_points, + border.right.width < kAntiAliasWidthThreshold ? true + : !border_colors_are_same); } void DrawSolidRoundedRectBorderToRenderTarget( - SkCanvas* render_target, const math::RectF& rect, + RenderTreeNodeVisitorDrawState* draw_state, const math::RectF& rect, const render_tree::RoundedCorners& rounded_corners, const math::RectF& content_rect, const render_tree::RoundedCorners& inner_rounded_corners, @@ -930,21 +1011,22 @@ paint.setAntiAlias(true); const render_tree::ColorRGBA& color = border.top.color; - paint.setARGB(color.a() * 255, color.r() * 255, color.g() * 255, - color.b() * 255); - if (color.a() == 1.0f) { + float alpha = color.a(); + alpha *= draw_state->opacity; + paint.setARGB(alpha * 255, color.r() * 255, color.g() * 255, color.b() * 255); + if (alpha == 1.0f) { paint.setXfermodeMode(SkXfermode::kSrc_Mode); } else { paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); } - render_target->drawDRRect( + draw_state->render_target->drawDRRect( RoundedRectToSkia(rect, rounded_corners), RoundedRectToSkia(content_rect, inner_rounded_corners), paint); } void DrawSolidRoundedRectBorderSoftware( - SkCanvas* render_target, const math::RectF& rect, + RenderTreeNodeVisitorDrawState* draw_state, const math::RectF& rect, const render_tree::RoundedCorners& rounded_corners, const math::RectF& content_rect, const render_tree::RoundedCorners& inner_rounded_corners, @@ -960,14 +1042,19 @@ math::RectF canvas_rect(rect.size()); math::RectF canvas_content_rect(content_rect); canvas_content_rect.Offset(-rect.OffsetFromOrigin()); - DrawSolidRoundedRectBorderToRenderTarget(&canvas, canvas_rect, + + RenderTreeNodeVisitorDrawState sub_draw_state(*draw_state); + sub_draw_state.render_target = &canvas; + + DrawSolidRoundedRectBorderToRenderTarget(&sub_draw_state, canvas_rect, rounded_corners, canvas_content_rect, inner_rounded_corners, border); canvas.flush(); SkPaint render_target_paint; render_target_paint.setFilterLevel(SkPaint::kNone_FilterLevel); - render_target->drawBitmap(bitmap, rect.x(), rect.y(), &render_target_paint); + draw_state->render_target->drawBitmap(bitmap, rect.x(), rect.y(), + &render_target_paint); } namespace { @@ -999,7 +1086,7 @@ } // namespace void DrawSolidRoundedRectBorder( - SkCanvas* render_target, const math::RectF& rect, + RenderTreeNodeVisitorDrawState* draw_state, const math::RectF& rect, const render_tree::RoundedCorners& rounded_corners, const math::RectF& content_rect, const render_tree::RoundedCorners& inner_rounded_corners, @@ -1011,8 +1098,8 @@ if (IsCircle(rect.size(), rounded_corners)) { // We are able to render circular borders using hardware, so introduce // a special case for them. - DrawSolidRoundedRectBorderToRenderTarget(render_target, rect, - rounded_corners, content_rect, + DrawSolidRoundedRectBorderToRenderTarget(draw_state, rect, rounded_corners, + content_rect, inner_rounded_corners, border); } else { // For now we fallback to software for drawing most rounded corner borders, @@ -1020,7 +1107,7 @@ // do this is to limit then number of shaders that need to be implemented. NOTIMPLEMENTED() << "Warning: Software rasterizing a solid rectangle " "border."; - DrawSolidRoundedRectBorderSoftware(render_target, rect, rounded_corners, + DrawSolidRoundedRectBorderSoftware(draw_state, rect, rounded_corners, content_rect, inner_rounded_corners, border); } @@ -1057,28 +1144,28 @@ if (rect_node->data().background_brush) { if (inner_rounded_corners && !inner_rounded_corners->AreSquares()) { - DrawRoundedRectWithBrush(render_target_, + DrawRoundedRectWithBrush(&draw_state_, rect_node->data().background_brush.get(), content_rect, *inner_rounded_corners); } else { - DrawRectWithBrush(render_target_, - rect_node->data().background_brush.get(), content_rect); + DrawRectWithBrush(&draw_state_, rect_node->data().background_brush.get(), + content_rect); } } if (rect_node->data().border) { if (rect_node->data().rounded_corners) { DrawSolidRoundedRectBorder( - render_target_, rect, *rect_node->data().rounded_corners, - content_rect, *inner_rounded_corners, *rect_node->data().border); + &draw_state_, rect, *rect_node->data().rounded_corners, content_rect, + *inner_rounded_corners, *rect_node->data().border); } else { - DrawSolidNonRoundRectBorder(render_target_, rect, + DrawSolidNonRoundRectBorder(&draw_state_, rect, *rect_node->data().border); } } #if ENABLE_FLUSH_AFTER_EVERY_NODE - render_target_->flush(); + draw_state_.render_target->flush(); #endif } @@ -1250,21 +1337,22 @@ #if ENABLE_RENDER_TREE_VISITOR_TRACING TRACE_EVENT0("cobalt::renderer", "Visit(RectShadowNode)"); #endif + DCHECK_EQ(1.0f, draw_state_.opacity); common::SurfaceCache::Block cache_block(surface_cache_, rect_shadow_node); if (cache_block.Cached()) return; if (rect_shadow_node->data().rounded_corners) { - DrawRoundedRectShadowNode(rect_shadow_node, render_target_); + DrawRoundedRectShadowNode(rect_shadow_node, draw_state_.render_target); } else { if (rect_shadow_node->data().inset) { - DrawInsetRectShadowNode(rect_shadow_node, render_target_); + DrawInsetRectShadowNode(rect_shadow_node, draw_state_.render_target); } else { - DrawOutsetRectShadowNode(rect_shadow_node, render_target_); + DrawOutsetRectShadowNode(rect_shadow_node, draw_state_.render_target); } } #if ENABLE_FLUSH_AFTER_EVERY_NODE - render_target_->flush(); + draw_state_.render_target->flush(); #endif } @@ -1312,6 +1400,7 @@ #if ENABLE_RENDER_TREE_VISITOR_TRACING TRACE_EVENT0("cobalt::renderer", "Visit(TextNode)"); #endif + DCHECK_EQ(1.0f, draw_state_.opacity); common::SurfaceCache::Block cache_block(surface_cache_, text_node); if (cache_block.Cached()) return; @@ -1341,20 +1430,21 @@ const render_tree::Shadow& shadow = shadows[i]; RenderText( - render_target_, text_node->data().glyph_buffer, shadow.color, - math::PointAtOffsetFromOrigin(text_node->data().offset + - shadow.offset), + draw_state_.render_target, text_node->data().glyph_buffer, + shadow.color, math::PointAtOffsetFromOrigin(text_node->data().offset + + shadow.offset), shadow.blur_sigma == 0.0f ? blur_zero_sigma : shadow.blur_sigma); } } // Finally render the main text. - RenderText( - render_target_, text_node->data().glyph_buffer, text_node->data().color, - math::PointAtOffsetFromOrigin(text_node->data().offset), blur_zero_sigma); + RenderText(draw_state_.render_target, text_node->data().glyph_buffer, + text_node->data().color, + math::PointAtOffsetFromOrigin(text_node->data().offset), + blur_zero_sigma); #if ENABLE_FLUSH_AFTER_EVERY_NODE - render_target_->flush(); + draw_state_.render_target->flush(); #endif }
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.h b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.h index b325107..3efa3c6 100644 --- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.h +++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.h
@@ -29,6 +29,7 @@ #include "cobalt/render_tree/rect_node.h" #include "cobalt/render_tree/text_node.h" #include "cobalt/renderer/rasterizer/common/surface_cache.h" +#include "cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h" #include "cobalt/renderer/rasterizer/skia/surface_cache_delegate.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -93,11 +94,8 @@ // then apply the filter to the offscreen surface. void RenderFilterViaOffscreenSurface( const render_tree::FilterNode::Builder& filter_node); - void SetRenderTarget(SkCanvas* render_target) { - render_target_ = render_target; - } - SkCanvas* render_target_; + RenderTreeNodeVisitorDrawState draw_state_; const CreateScratchSurfaceFunction* create_scratch_surface_function_; Type visitor_type_;
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h new file mode 100644 index 0000000..01a5d62 --- /dev/null +++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h
@@ -0,0 +1,40 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COBALT_RENDERER_RASTERIZER_SKIA_RENDER_TREE_NODE_VISITOR_DRAW_STATE_H_ +#define COBALT_RENDERER_RASTERIZER_SKIA_RENDER_TREE_NODE_VISITOR_DRAW_STATE_H_ + +#include "third_party/skia/include/core/SkCanvas.h" + +namespace cobalt { +namespace renderer { +namespace rasterizer { +namespace skia { + +struct RenderTreeNodeVisitorDrawState { + explicit RenderTreeNodeVisitorDrawState(SkCanvas* render_target) + : render_target(render_target), opacity(1.0f) {} + + SkCanvas* render_target; + float opacity; +}; + +} // namespace skia +} // namespace rasterizer +} // namespace renderer +} // namespace cobalt + +#endif // COBALT_RENDERER_RASTERIZER_SKIA_RENDER_TREE_NODE_VISITOR_DRAW_STATE_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/scratch_surface_cache.cc b/src/cobalt/renderer/rasterizer/skia/scratch_surface_cache.cc index f959c8b..7de5fdc 100644 --- a/src/cobalt/renderer/rasterizer/skia/scratch_surface_cache.cc +++ b/src/cobalt/renderer/rasterizer/skia/scratch_surface_cache.cc
@@ -53,7 +53,12 @@ common::ScratchSurfaceCache::Surface* ScratchSurfaceCache::Delegate::CreateSurface(const math::Size& size) { - return new SkiaSurface(create_sk_surface_function_.Run(size), size); + SkSurface* sk_surface = create_sk_surface_function_.Run(size); + if (sk_surface) { + return new SkiaSurface(sk_surface, size); + } else { + return NULL; + } } void ScratchSurfaceCache::Delegate::DestroySurface( @@ -89,9 +94,13 @@ } SkSurface* CachedScratchSurface::GetSurface() { - return base::polymorphic_downcast<SkiaSurface*>( - common_scratch_surface_.GetSurface()) - ->sk_surface(); + if (common_scratch_surface_.GetSurface()) { + return base::polymorphic_downcast<SkiaSurface*>( + common_scratch_surface_.GetSurface()) + ->sk_surface(); + } else { + return NULL; + } } } // namespace skia
diff --git a/src/cobalt/renderer/rasterizer/skia/software_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/software_rasterizer.cc index 208d179..d1cafc6 100644 --- a/src/cobalt/renderer/rasterizer/skia/software_rasterizer.cc +++ b/src/cobalt/renderer/rasterizer/skia/software_rasterizer.cc
@@ -39,8 +39,8 @@ class SoftwareScratchSurface : public RenderTreeNodeVisitor::ScratchSurface { public: - explicit SoftwareScratchSurface(const math::Size& size) - : surface_(CreateScratchSkSurface(size)) {} + explicit SoftwareScratchSurface(SkSurface* sk_surface) + : surface_(sk_surface) {} SkSurface* GetSurface() OVERRIDE { return surface_.get(); } private: @@ -51,8 +51,13 @@ const math::Size& size) { TRACE_EVENT2("cobalt::renderer", "CreateScratchSurface()", "width", size.width(), "height", size.height()); - return scoped_ptr<RenderTreeNodeVisitor::ScratchSurface>( - new SoftwareScratchSurface(size)); + SkSurface* sk_surface = CreateScratchSkSurface(size); + if (sk_surface) { + return scoped_ptr<RenderTreeNodeVisitor::ScratchSurface>( + new SoftwareScratchSurface(sk_surface)); + } else { + return scoped_ptr<RenderTreeNodeVisitor::ScratchSurface>(); + } } void ReturnScratchImage(SkSurface* surface) { surface->unref(); }
diff --git a/src/cobalt/renderer/rasterizer/skia/surface_cache_delegate.cc b/src/cobalt/renderer/rasterizer/skia/surface_cache_delegate.cc index 447ab7a..598de2c 100644 --- a/src/cobalt/renderer/rasterizer/skia/surface_cache_delegate.cc +++ b/src/cobalt/renderer/rasterizer/skia/surface_cache_delegate.cc
@@ -35,7 +35,7 @@ const math::Size& max_surface_size) : create_sk_surface_function_(create_sk_surface_function), max_surface_size_(max_surface_size), - canvas_(NULL) + draw_state_(NULL) #if defined(ENABLE_DEBUG_CONSOLE) , toggle_cache_highlights_(false), @@ -51,7 +51,8 @@ } void SurfaceCacheDelegate::UpdateCanvasScale() { - math::Matrix3F total_matrix = SkiaMatrixToCobalt(canvas_->getTotalMatrix()); + math::Matrix3F total_matrix = + SkiaMatrixToCobalt(draw_state_->render_target->getTotalMatrix()); math::Vector3dF column_0(total_matrix.column(0)); math::Vector3dF column_1(total_matrix.column(1)); scale_ = math::Vector2dF(column_0.Length(), column_1.Length()); @@ -73,8 +74,8 @@ return; } - canvas_->save(); - SkMatrix total_matrix = canvas_->getTotalMatrix(); + draw_state_->render_target->save(); + SkMatrix total_matrix = draw_state_->render_target->getTotalMatrix(); // We "preScale()" the "post scale" because skia uses "pre" to mean that the // transform will be applied before the other transforms, whereas // common::OffscreenRenderCoordinateMapping() uses "post" to mean that the @@ -83,10 +84,13 @@ skia_surface->output_post_scale().y()); total_matrix.postTranslate(skia_surface->output_pre_translate().x(), skia_surface->output_pre_translate().y()); - canvas_->setMatrix(total_matrix); + draw_state_->render_target->setMatrix(total_matrix); SkPaint paint; paint.setFilterLevel(SkPaint::kLow_FilterLevel); + if (draw_state_->opacity < 1.0f) { + paint.setAlpha(draw_state_->opacity * 255); + } SkRect dest_rect = SkRect::MakeXYWH(skia_surface->output_bounds().x(), skia_surface->output_bounds().y(), @@ -99,15 +103,15 @@ #if defined(ENABLE_DEBUG_CONSOLE) if (toggle_cache_highlights_) { paint.setARGB(128, 255, 128, 128); - canvas_->drawRect(dest_rect, paint); + draw_state_->render_target->drawRect(dest_rect, paint); } else // NOLINT(readability/braces) #endif { - canvas_->drawImageRect(skia_surface->image(), &source_rect, dest_rect, - &paint); + draw_state_->render_target->drawImageRect(skia_surface->image(), + &source_rect, dest_rect, &paint); } - canvas_->restore(); + draw_state_->render_target->restore(); } void SurfaceCacheDelegate::StartRecording(const math::RectF& local_bounds) { @@ -121,8 +125,9 @@ // suffer. math::Vector2dF inv_scale(scale_.x() < 1.0f ? 1.0f / scale_.x() : 1.0f, scale_.y() < 1.0f ? 1.0f / scale_.y() : 1.0f); - math::Matrix3F total_matrix = SkiaMatrixToCobalt(canvas_->getTotalMatrix()) * - math::ScaleMatrix(inv_scale); + math::Matrix3F total_matrix = + SkiaMatrixToCobalt(draw_state_->render_target->getTotalMatrix()) * + math::ScaleMatrix(inv_scale); // Figure where we should render to the offscreen surface and then how to map // that later to the onscreen surface when applying the cached surface. We @@ -133,7 +138,7 @@ // If the output has an area of 0 then there is nothing to cache. This should // not be common. if (coord_mapping.output_bounds.size().GetArea() == 0) { - recording_data_.emplace(canvas_, coord_mapping, + recording_data_.emplace(*draw_state_, coord_mapping, static_cast<SkSurface*>(NULL)); return; } @@ -145,17 +150,17 @@ coord_mapping.output_bounds.height())); CHECK(surface); - recording_data_.emplace(canvas_, coord_mapping, surface); + recording_data_.emplace(*draw_state_, coord_mapping, surface); SkCanvas* offscreen_canvas = surface->getCanvas(); - set_canvas_function_.Run(offscreen_canvas); - canvas_ = offscreen_canvas; + draw_state_->render_target = offscreen_canvas; + draw_state_->opacity = 1.0f; // Clear the draw area to RGBA(0, 0, 0, 0) for a fresh scratch surface before // returning. - canvas_->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); + draw_state_->render_target->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); - offscreen_canvas->setMatrix( + draw_state_->render_target->setMatrix( CobaltMatrixToSkia(coord_mapping.sub_render_transform)); } @@ -163,8 +168,7 @@ TRACE_EVENT0("cobalt::renderer", "SurfaceCacheDelegate::EndRecording()"); DCHECK(recording_data_); - set_canvas_function_.Run(recording_data_->original_canvas); - canvas_ = recording_data_->original_canvas; + *draw_state_ = recording_data_->original_draw_state; // Save the results as a CachedSurface and return them. CachedSurface* cached_surface =
diff --git a/src/cobalt/renderer/rasterizer/skia/surface_cache_delegate.h b/src/cobalt/renderer/rasterizer/skia/surface_cache_delegate.h index 9e5fb69..7d0a045 100644 --- a/src/cobalt/renderer/rasterizer/skia/surface_cache_delegate.h +++ b/src/cobalt/renderer/rasterizer/skia/surface_cache_delegate.h
@@ -26,7 +26,7 @@ #endif #include "cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h" #include "cobalt/renderer/rasterizer/common/surface_cache.h" -#include "third_party/skia/include/core/SkCanvas.h" +#include "cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h" #include "third_party/skia/include/core/SkSurface.h" namespace cobalt { @@ -76,7 +76,6 @@ public: typedef base::Callback<SkSurface*(const math::Size&)> CreateSkSurfaceFunction; - typedef base::Callback<void(SkCanvas*)> SetCanvasFunction; // Creating a ScopedContext object declares a render tree visitor context, // and so there should be one ScopedContext per render tree visitor. Since @@ -85,35 +84,28 @@ // SurfaceCacheDelegate object. class ScopedContext { public: - ScopedContext(SurfaceCacheDelegate* delegate, SkCanvas* canvas, - const SetCanvasFunction& set_canvas_function) - : delegate_(delegate), - old_canvas_(delegate_->canvas_), - old_set_canvas_function_(delegate_->set_canvas_function_) { - // Setup our new canvas to the provided one. Remember a new method - // to set the canvas for the current rendering context in case we need - // to start recording on a offscreen surface. - delegate_->canvas_ = canvas; - delegate_->set_canvas_function_ = set_canvas_function; + ScopedContext(SurfaceCacheDelegate* delegate, + RenderTreeNodeVisitorDrawState* draw_state) + : delegate_(delegate), old_draw_state_(delegate_->draw_state_) { + // Setup our new draw state (including render target) to the provided one. + delegate_->draw_state_ = draw_state; // A new canvas may mean a new total matrix, so inform our delegate to // refresh its scale. delegate_->UpdateCanvasScale(); } ~ScopedContext() { - // Restore the canvas to the old setting. - delegate_->set_canvas_function_ = old_set_canvas_function_; - delegate_->canvas_ = old_canvas_; + // Restore the draw state to the old setting. + delegate_->draw_state_ = old_draw_state_; - if (delegate_->canvas_) { + if (delegate_->draw_state_) { delegate_->UpdateCanvasScale(); } } private: SurfaceCacheDelegate* delegate_; - SkCanvas* old_canvas_; - SetCanvasFunction old_set_canvas_function_; + RenderTreeNodeVisitorDrawState* old_draw_state_; }; SurfaceCacheDelegate( @@ -134,16 +126,17 @@ private: // Defines a set of data that is relevant only while recording. struct RecordingData { - RecordingData(SkCanvas* original_canvas, + RecordingData(const RenderTreeNodeVisitorDrawState& original_draw_state, const common::OffscreenRenderCoordinateMapping& coord_mapping, SkSurface* surface) - : original_canvas(original_canvas), + : original_draw_state(original_draw_state), coord_mapping(coord_mapping), surface(surface) {} - // The original canvas that the recorded draw calls would have otherwise - // targeted if we were not recording. - SkCanvas* original_canvas; + // The original draw state (including render target/canvas) that the + // recorded draw calls would have otherwise targeted if we were not + // recording. + RenderTreeNodeVisitorDrawState original_draw_state; // Information about how to map the offscreen cached surface to the main // render target. @@ -161,25 +154,21 @@ // something new. CreateSkSurfaceFunction create_sk_surface_function_; - // A function that can be used to change the current canvas in order to - // redirect Skia rasterization commands to an offscreen cached surface. - SetCanvasFunction set_canvas_function_; - // The maximum size of an SkSurface. math::Size max_surface_size_; - // The current canvas that is being targeted, whether its the main onscreen - // surface or a cached surface. - SkCanvas* canvas_; + // The current draw state (including the SkCanvas) that is being targeted, + // whether its the main onscreen surface or a cached surface. + RenderTreeNodeVisitorDrawState* draw_state_; // If we are currently recording, this will contain all of the data relevant // to that recording. base::optional<RecordingData> recording_data_; - // The current scale of canvas_->getTotalMatrix(). This lets us quickly check - // if the scale of a render node changes from the last time we observed it, - // which may happen if an animation is targeting a MatrixTransformNode render - // tree node. + // The current scale of draw_state_->render_target->getTotalMatrix(). This + // lets us quickly check if the scale of a render node changes from the last + // time we observed it, which may happen if an animation is targeting a + // MatrixTransformNode render tree node. math::Vector2dF scale_; #if defined(ENABLE_DEBUG_CONSOLE)
diff --git a/src/cobalt/renderer/renderer.gyp b/src/cobalt/renderer/renderer.gyp index 657e39e..5a1253e 100644 --- a/src/cobalt/renderer/renderer.gyp +++ b/src/cobalt/renderer/renderer.gyp
@@ -36,13 +36,6 @@ 'includes': [ 'copy_font_data.gypi', ], - - 'defines': [ - 'COBALT_SKIA_CACHE_SIZE_IN_BYTES=<(skia_cache_size_in_bytes)', - 'COBALT_SCRATCH_SURFACE_CACHE_SIZE_IN_BYTES=<(scratch_surface_cache_size_in_bytes)', - 'COBALT_SURFACE_CACHE_SIZE_IN_BYTES=<(surface_cache_size_in_bytes)', - ], - 'dependencies': [ '<(DEPTH)/cobalt/base/base.gyp:base', '<(DEPTH)/cobalt/math/math.gyp:math', @@ -53,21 +46,14 @@ '<(DEPTH)/cobalt/system_window/system_window.gyp:system_window', ], 'conditions': [ - ['rasterizer_type == "software"', { - 'defines': [ - 'COBALT_FORCE_SOFTWARE_RASTERIZER', - ], - }], - ['rasterizer_type == "stub"', { - 'defines': [ - 'COBALT_FORCE_STUB_RASTERIZER', - ], - }], ['OS=="starboard"', { - 'sources': [ - 'renderer_module_default_options_starboard.cc', + 'dependencies': [ + '<(default_renderer_options_dependency)', ], }, { + 'includes': [ + 'renderer_parameters_setup.gypi', + ], 'sources': [ 'renderer_module_default_options_<(actual_target_arch).cc', ],
diff --git a/src/cobalt/renderer/renderer_parameters_setup.gypi b/src/cobalt/renderer/renderer_parameters_setup.gypi new file mode 100644 index 0000000..95724f6 --- /dev/null +++ b/src/cobalt/renderer/renderer_parameters_setup.gypi
@@ -0,0 +1,33 @@ +# Copyright 2016 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +{ + 'defines': [ + 'COBALT_SKIA_CACHE_SIZE_IN_BYTES=<(skia_cache_size_in_bytes)', + 'COBALT_SCRATCH_SURFACE_CACHE_SIZE_IN_BYTES=<(scratch_surface_cache_size_in_bytes)', + 'COBALT_SURFACE_CACHE_SIZE_IN_BYTES=<(surface_cache_size_in_bytes)', + ], + 'conditions': [ + ['rasterizer_type == "software"', { + 'defines': [ + 'COBALT_FORCE_SOFTWARE_RASTERIZER', + ], + }], + ['rasterizer_type == "stub"', { + 'defines': [ + 'COBALT_FORCE_STUB_RASTERIZER', + ], + }], + ], +}
diff --git a/src/cobalt/script/exception_message.cc b/src/cobalt/script/exception_message.cc new file mode 100644 index 0000000..73ac903 --- /dev/null +++ b/src/cobalt/script/exception_message.cc
@@ -0,0 +1,68 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cobalt/script/exception_message.h" + +namespace cobalt { +namespace script { + +ExceptionMessage kMessages[kNumMessageTypes] = { + {kSimpleError, kError, " "}, + {kSimpleTypeError, kTypeError, " "}, + {kSimpleRangeError, kRangeError, " "}, + {kSimpleReferenceError, kReferenceError, " "}, + {kNotNullableType, kTypeError, "Value is null but type is not nullable."}, + {kNotObjectType, kTypeError, "Value is not an object."}, + {kNotObjectOrFunction, kTypeError, "Value is not an object or function."}, + {kNotInt64Type, kTypeError, "Value is not int64."}, + {kNotUint64Type, kTypeError, "Value is not uint64."}, + {kNotNumberType, kTypeError, "Value is not a number."}, + {kDoesNotImplementInterface, kTypeError, + "Value does not implement the interface type."}, + {kConvertToStringFailed, kTypeError, + "JS value cannot be converted to string."}, + {kNotFinite, kTypeError, "Non-finite floating-point value."}, + {kNotSupportedType, kTypeError, "Value is null but type is not nullable."}, + {kConvertToUTF8Failed, kTypeError, "Failed to convert JS value to utf8."}, + {kConvertToEnumFailed, kTypeError, "Failed to convert JS value to enum."}, + {kStringifierProblem, kTypeError, "Stringifier problem."}, + {kNotFunctionValue, kTypeError, "Value is not a function."}, + {kInvalidNumberOfArguments, kRangeError, "Invalid number of arguments."}, + {kNotUnionType, kTypeError, "Value is not a member of the union type."}, + {kOutsideBounds, kRangeError, "Offset is outside the object's bounds."}, + {kInvalidLength, kRangeError, "Invalid length."}, + {kNotAnArrayBuffer, kTypeError, "Value is not an ArrayBuffer."}, + {kWrongByteOffsetMultiple, kRangeError, + "Byte offset should be a multiple of %d."}, + {kWrongByteLengthMultiple, kRangeError, + "Byte length should be a multiple of %d."}, + {kPropertySyntaxError, kSyntaxError, "%s."}, +}; + +const char* GetExceptionMessageFormat(MessageType message_type) { + DCHECK_GT(message_type, kNoError); + DCHECK_LT(message_type, kNumMessageTypes); + return kMessages[message_type].format; +} + +SimpleExceptionType GetSimpleExceptionType(MessageType message_type) { + DCHECK_GT(message_type, kNoError); + DCHECK_LT(message_type, kNumMessageTypes); + return kMessages[message_type].exception_type; +} + +} // namespace script +} // namespace cobalt
diff --git a/src/cobalt/script/exception_message.h b/src/cobalt/script/exception_message.h new file mode 100644 index 0000000..928c790 --- /dev/null +++ b/src/cobalt/script/exception_message.h
@@ -0,0 +1,87 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef COBALT_SCRIPT_EXCEPTION_MESSAGE_H_ +#define COBALT_SCRIPT_EXCEPTION_MESSAGE_H_ + +#include <string> + +#include "cobalt/script/script_exception.h" + +namespace cobalt { +namespace script { + +// Simple exceptions as defined in: +// http://heycam.github.io/webidl/#dfn-simple-exception +enum SimpleExceptionType { + kError, + kTypeError, + kRangeError, + kReferenceError, + kSyntaxError, + kURIError +}; + +// Custom exception message type. +enum MessageType { + kNoError = -1, + + kSimpleError, + kSimpleTypeError, + kSimpleRangeError, + kSimpleReferenceError, + kNotNullableType, + kNotObjectType, + kNotObjectOrFunction, + kNotInt64Type, + kNotUint64Type, + kNotNumberType, + kDoesNotImplementInterface, + kConvertToStringFailed, + kNotFinite, + kNotSupportedType, + kConvertToUTF8Failed, + kConvertToEnumFailed, + kStringifierProblem, + kNotFunctionValue, + kInvalidNumberOfArguments, + kNotUnionType, + kOutsideBounds, + kInvalidLength, + kNotAnArrayBuffer, + kWrongByteOffsetMultiple, + kWrongByteLengthMultiple, + kPropertySyntaxError, + + kNumMessageTypes, +}; + +// Exception message contains an exception information. It includes a +// |message_type| which is the index of each exception message, a +// |exception_type| which is based on the spec. of simple exception, and a +// message |format|. +struct ExceptionMessage { + MessageType message_type; + SimpleExceptionType exception_type; + const char* format; +}; + +const char* GetExceptionMessageFormat(MessageType message_type); +SimpleExceptionType GetSimpleExceptionType(MessageType message_type); + +} // namespace script +} // namespace cobalt + +#endif // COBALT_SCRIPT_EXCEPTION_MESSAGE_H_
diff --git a/src/cobalt/script/exception_state.h b/src/cobalt/script/exception_state.h index 897ac04..cd4e7f7 100644 --- a/src/cobalt/script/exception_state.h +++ b/src/cobalt/script/exception_state.h
@@ -18,6 +18,7 @@ #include <string> +#include "cobalt/script/exception_message.h" #include "cobalt/script/script_exception.h" namespace cobalt { @@ -25,21 +26,10 @@ class ExceptionState { public: - // Simple exceptions as defined in: - // http://heycam.github.io/webidl/#dfn-simple-exception - enum SimpleExceptionType { - kError, - kTypeError, - kRangeError, - kReferenceError, - kSyntaxError, - kURIError - }; // IDL for this object must be an exception interface. virtual void SetException( const scoped_refptr<ScriptException>& exception) = 0; - virtual void SetSimpleException(SimpleExceptionType simple_error, - const std::string& message) = 0; + virtual void SetSimpleException(MessageType message_type, ...) = 0; }; } // namespace script
diff --git a/src/cobalt/script/javascriptcore/conversion_helpers.h b/src/cobalt/script/javascriptcore/conversion_helpers.h index 4642fc9..3b30135 100644 --- a/src/cobalt/script/javascriptcore/conversion_helpers.h +++ b/src/cobalt/script/javascriptcore/conversion_helpers.h
@@ -45,14 +45,6 @@ namespace script { namespace javascriptcore { -const char kNotAnObject[] = "Value is not an object."; -const char kNotAnObjectOrFunction[] = "Value is not an object or function."; -const char kNotAFunction[] = "Value is not a function."; -const char kNotNullableType[] = "Value is null but type is not nullable."; -const char kNotObjectType[] = "Value is not an object."; -const char kDoesNotImplementInterface[] = - "Value does not implement the interface type."; - // Flags that can be used as a bitmask for special conversion behaviour. enum ConversionFlags { kNoConversionFlags = 0, @@ -128,8 +120,7 @@ global_object->wrapper_factory()->GetClassInfo(base::GetTypeId<T>()); } else { // This is not a platform object. Return a type error. - out_exception->SetSimpleException(ExceptionState::kTypeError, - kDoesNotImplementInterface); + out_exception->SetSimpleException(kDoesNotImplementInterface); return NULL; } @@ -137,8 +128,7 @@ if (js_object->inherits(class_info)) { return base::polymorphic_downcast<T*>(wrappable); } else { - out_exception->SetSimpleException(ExceptionState::kTypeError, - kDoesNotImplementInterface); + out_exception->SetSimpleException(kDoesNotImplementInterface); return NULL; } } @@ -388,8 +378,7 @@ double double_value = jsvalue.toNumber(exec_state); if (!isfinite(double_value) && (conversion_flags & kConversionFlagRestricted)) { - out_exception->SetSimpleException(ExceptionState::kTypeError, - "Non-finite floating-point value."); + out_exception->SetSimpleException(kNotFinite); return; } *out_number = double_value; @@ -423,16 +412,14 @@ JSC::JSObject* js_object = NULL; if (jsvalue.isNull()) { if (!(conversion_flags & kConversionFlagNullable)) { - out_exception->SetSimpleException(ExceptionState::kTypeError, - kNotNullableType); + out_exception->SetSimpleException(kNotNullableType); return; } } else { // Returns NULL if jsvalue is not an object. js_object = jsvalue.getObject(); if (!js_object) { - out_exception->SetSimpleException(ExceptionState::kTypeError, - kNotObjectType); + out_exception->SetSimpleException(kNotObjectType); return; } } @@ -485,8 +472,7 @@ << "No conversion flags supported."; if (jsvalue.isNull()) { if (!(conversion_flags & kConversionFlagNullable)) { - out_exception->SetSimpleException(ExceptionState::kTypeError, - kNotNullableType); + out_exception->SetSimpleException(kNotNullableType); } // If it is a nullable type, just return. return; @@ -498,8 +484,7 @@ // https://www.w3.org/TR/WebIDL/#es-callback-function // 1. If V is not a Function object, throw a TypeError if (!jsvalue.isFunction()) { - out_exception->SetSimpleException(ExceptionState::kTypeError, - kNotAFunction); + out_exception->SetSimpleException(kNotFunctionValue); return; } @@ -522,8 +507,7 @@ << "No conversion flags supported."; if (jsvalue.isNull()) { if (!(conversion_flags & kConversionFlagNullable)) { - out_exception->SetSimpleException(ExceptionState::kTypeError, - kNotNullableType); + out_exception->SetSimpleException(kNotNullableType); } // If it is a nullable type, just return. return; @@ -538,8 +522,7 @@ // on the callback interface is run. if (!jsvalue.isFunction() && !jsvalue.isObject()) { - out_exception->SetSimpleException(ExceptionState::kTypeError, - kNotAnObjectOrFunction); + out_exception->SetSimpleException(kNotObjectOrFunction); return; } @@ -559,8 +542,7 @@ JSC::JSObject* js_object = NULL; if (jsvalue.isNull()) { if (!(conversion_flags & kConversionFlagNullable)) { - out_exception->SetSimpleException(ExceptionState::kTypeError, - kNotNullableType); + out_exception->SetSimpleException(kNotNullableType); } // Return here whether an exception was set or not. return; @@ -569,8 +551,7 @@ // 1. If Type(V) is not Object, throw a TypeError js_object = jsvalue.getObject(); if (!js_object) { - out_exception->SetSimpleException(ExceptionState::kTypeError, - kNotObjectType); + out_exception->SetSimpleException(kNotObjectType); return; } }
diff --git a/src/cobalt/script/javascriptcore/javascriptcore.gyp b/src/cobalt/script/javascriptcore/javascriptcore.gyp index 0504492..9cee381 100644 --- a/src/cobalt/script/javascriptcore/javascriptcore.gyp +++ b/src/cobalt/script/javascriptcore/javascriptcore.gyp
@@ -70,6 +70,7 @@ 'all_dependent_settings': { 'defines': [ 'ENGINE_DEFINES_ATTRIBUTES_ON_OBJECT', + 'ENGINE_USES_CONSERVATIVE_ROOTING', ], }, 'msvs_disabled_warnings': [
diff --git a/src/cobalt/script/javascriptcore/jsc_exception_state.cc b/src/cobalt/script/javascriptcore/jsc_exception_state.cc index 657a11b..b1c63d2 100644 --- a/src/cobalt/script/javascriptcore/jsc_exception_state.cc +++ b/src/cobalt/script/javascriptcore/jsc_exception_state.cc
@@ -34,12 +34,17 @@ DCHECK(exception_->isErrorInstance()); } -void JSCExceptionState::SetSimpleException(SimpleExceptionType simple_exception, - const std::string& message) { +void JSCExceptionState::SetSimpleException(MessageType message_type, ...) { DCHECK(thread_checker_.CalledOnValidThread()); JSC::JSLockHolder lock(&global_object_->globalData()); - WTF::String error_string = ToWTFString(message); - switch (simple_exception) { + + va_list arguments; + va_start(arguments, message_type); + WTF::String error_string = ToWTFString( + base::StringPrintV(GetExceptionMessageFormat(message_type), arguments)); + va_end(arguments); + + switch (GetSimpleExceptionType(message_type)) { case kError: exception_ = JSC::createError(global_object_, error_string); break;
diff --git a/src/cobalt/script/javascriptcore/jsc_exception_state.h b/src/cobalt/script/javascriptcore/jsc_exception_state.h index e5e3a22..1dfd273 100644 --- a/src/cobalt/script/javascriptcore/jsc_exception_state.h +++ b/src/cobalt/script/javascriptcore/jsc_exception_state.h
@@ -31,8 +31,7 @@ : global_object_(global_object), exception_(NULL) {} // ExceptionState interface void SetException(const scoped_refptr<ScriptException>& exception) OVERRIDE; - void SetSimpleException(SimpleExceptionType simple_exception, - const std::string& message) OVERRIDE; + void SetSimpleException(MessageType message_type, ...) OVERRIDE; bool is_exception_set() const { return (exception_ != NULL); } JSC::JSObject* exception_object() { return exception_; }
diff --git a/src/cobalt/script/javascriptcore/numeric_conversion_test.cc b/src/cobalt/script/javascriptcore/numeric_conversion_test.cc index 66e6653..04c9481 100644 --- a/src/cobalt/script/javascriptcore/numeric_conversion_test.cc +++ b/src/cobalt/script/javascriptcore/numeric_conversion_test.cc
@@ -205,7 +205,6 @@ const double kInfinity = std::numeric_limits<double>::infinity(); const double kNegativeInfinity = -std::numeric_limits<double>::infinity(); - // Unrestricted non-finite floating point conversions EXPECT_EQ(kInfinity, JSValueToNumber<TypeParam>( this->exec_state_, JSC::jsNumber(kInfinity), @@ -220,8 +219,7 @@ // Restricted non-finite floating point conversions. These should throw a // TypeError. - EXPECT_CALL(this->exception_state_, - SetSimpleException(ExceptionState::kTypeError, _)) + EXPECT_CALL(this->exception_state_, SetSimpleExceptionVA(kNotFinite, _)) .Times(3); JSValueToNumber<TypeParam>(this->exec_state_, JSC::jsNumber(kInfinity), kConversionFlagRestricted, @@ -234,7 +232,6 @@ &this->exception_state_); } - // ToNumber (http://es5.github.io/#x9.3) calls the ToPrimitive operation: // http://es5.github.io/#x9.1 // ToPrimitive calls the [[DefaultValue]] method of the object:
diff --git a/src/cobalt/script/javascriptcore/union_type_conversion_impl.h b/src/cobalt/script/javascriptcore/union_type_conversion_impl.h index 7aa589d..9122056 100644 --- a/src/cobalt/script/javascriptcore/union_type_conversion_impl.h +++ b/src/cobalt/script/javascriptcore/union_type_conversion_impl.h
@@ -101,9 +101,9 @@ } } - // TODO: Support Date, RegExp, DOMException, Error, ArrayBuffer - // DataView, TypedArrayName, callback functions, dictionary, array type. - // and sequences if necessary. + // TODO: Support Date, RegExp, DOMException, Error, ArrayBuffer, DataView, + // TypedArrayName, callback functions, dictionary, array type. + // And sequences if necessary. // 14. If V is a Boolean value, then: // 1. If types includes a boolean, then return the result of converting V @@ -182,8 +182,7 @@ } // 19. Throw a TypeError. - exception_state->SetSimpleException( - ExceptionState::kTypeError, "Value is not a member of the union type."); + exception_state->SetSimpleException(kNotUnionType); } template <typename T1, typename T2, typename T3> @@ -270,9 +269,9 @@ } } - // TODO: Support Date, RegExp, DOMException, Error, ArrayBuffer - // DataView, TypedArrayName, callback functions, dictionary, array type. - // and sequences if necessary. + // TODO: Support Date, RegExp, DOMException, Error, ArrayBuffer, DataView, + // TypedArrayName, callback functions, dictionary, array type. + // And sequences if necessary. // 14. If V is a Boolean value, then: // 1. If types includes a boolean, then return the result of converting V @@ -379,8 +378,7 @@ } // 19. Throw a TypeError. - exception_state->SetSimpleException( - ExceptionState::kTypeError, "Value is not a member of the union type."); + exception_state->SetSimpleException(kNotUnionType); } template <typename T1, typename T2, typename T3, typename T4> @@ -480,9 +478,9 @@ } } - // TODO: Support Date, RegExp, DOMException, Error, ArrayBuffer - // DataView, TypedArrayName, callback functions, dictionary, array type. - // and sequences if necessary. + // TODO: Support Date, RegExp, DOMException, Error, ArrayBuffer, DataView, + // TypedArrayName, callback functions, dictionary, array type. + // And sequences if necessary. // 14. If V is a Boolean value, then: // 1. If types includes a boolean, then return the result of converting V @@ -617,8 +615,7 @@ } // 19. Throw a TypeError. - exception_state->SetSimpleException( - ExceptionState::kTypeError, "Value is not a member of the union type."); + exception_state->SetSimpleException(kNotUnionType); } } // namespace javascriptcore
diff --git a/src/cobalt/script/javascriptcore/union_type_conversion_impl.h.pump b/src/cobalt/script/javascriptcore/union_type_conversion_impl.h.pump index 34dcb30..54eae9a 100644 --- a/src/cobalt/script/javascriptcore/union_type_conversion_impl.h.pump +++ b/src/cobalt/script/javascriptcore/union_type_conversion_impl.h.pump
@@ -23,8 +23,8 @@ * limitations under the License. */ -#ifndef SCRIPT_JAVASCRIPTCORE_UNION_TYPE_CONVERSION_IMPL_H_ -#define SCRIPT_JAVASCRIPTCORE_UNION_TYPE_CONVERSION_IMPL_H_ +#ifndef COBALT_SCRIPT_JAVASCRIPTCORE_UNION_TYPE_CONVERSION_IMPL_H_ +#define COBALT_SCRIPT_JAVASCRIPTCORE_UNION_TYPE_CONVERSION_IMPL_H_ #include "cobalt/script/javascriptcore/jsc_global_object.h" #include "cobalt/script/union_type.h" @@ -187,8 +187,7 @@ // 19. Throw a TypeError. - exception_state->SetSimpleException( - ExceptionState::kTypeError, "Value is not a member of the union type."); + exception_state->SetSimpleException(kNotUnionType); } ]] $$ for NUM_MEMBERS @@ -197,4 +196,4 @@ } // namespace script } // namespace cobalt -#endif // SCRIPT_JAVASCRIPTCORE_UNION_TYPE_CONVERSION_IMPL_H_ +#endif // COBALT_SCRIPT_JAVASCRIPTCORE_UNION_TYPE_CONVERSION_IMPL_H_
diff --git a/src/cobalt/script/logging_exception_state.h b/src/cobalt/script/logging_exception_state.h index d72acfd..abe79d7 100644 --- a/src/cobalt/script/logging_exception_state.h +++ b/src/cobalt/script/logging_exception_state.h
@@ -19,6 +19,7 @@ #include <string> #include "base/logging.h" +#include "base/stringprintf.h" #include "cobalt/script/exception_state.h" namespace cobalt { @@ -30,9 +31,14 @@ void SetException(const scoped_refptr<ScriptException>& exception) OVERRIDE { LogException(exception->name(), exception->message()); } - void SetSimpleException(SimpleExceptionType simple_error, - const std::string& message) OVERRIDE { - LogException(SimpleExceptionToString(simple_error), message); + + void SetSimpleException(MessageType message_type, ...) OVERRIDE { + va_list arguments; + va_start(arguments, message_type); + LogException( + SimpleExceptionToString(GetSimpleExceptionType(message_type)), + base::StringPrintV(GetExceptionMessageFormat(message_type), arguments)); + va_end(arguments); } bool is_exception_set() const { return is_exception_set_; }
diff --git a/src/cobalt/script/mozjs/callback_function_conversion.h b/src/cobalt/script/mozjs/callback_function_conversion.h index 1e0966d..85fd73a 100644 --- a/src/cobalt/script/mozjs/callback_function_conversion.h +++ b/src/cobalt/script/mozjs/callback_function_conversion.h
@@ -64,8 +64,7 @@ if (value.isNull()) { if (!(conversion_flags & kConversionFlagNullable)) { - exception_state->SetSimpleException(ExceptionState::kTypeError, - kNotNullableType); + exception_state->SetSimpleException(kNotNullableType); } // If it is a nullable type, just return. return; @@ -78,8 +77,7 @@ object = JSVAL_TO_OBJECT(value); } if (!object || !JS_ObjectIsFunction(context, object)) { - exception_state->SetSimpleException(ExceptionState::kTypeError, - "Value is not a function."); + exception_state->SetSimpleException(kNotFunctionValue); return; }
diff --git a/src/cobalt/script/mozjs/conversion_helpers.cc b/src/cobalt/script/mozjs/conversion_helpers.cc index c469d26..5eb537d 100644 --- a/src/cobalt/script/mozjs/conversion_helpers.cc +++ b/src/cobalt/script/mozjs/conversion_helpers.cc
@@ -43,16 +43,14 @@ JS::RootedString string(context, JS_ValueToString(context, value)); if (!string) { - exception_state->SetSimpleException(ExceptionState::kTypeError, - "Not supported type."); + exception_state->SetSimpleException(kConvertToStringFailed); return; } JSAutoByteString auto_byte_string; char* utf8_chars = auto_byte_string.encodeUtf8(context, string); if (!utf8_chars) { - exception_state->SetSimpleException(ExceptionState::kTypeError, - "Failed to convert to utf8."); + exception_state->SetSimpleException(kConvertToUTF8Failed); return; } @@ -86,8 +84,7 @@ // 1. If Type(V) is not Object, throw a TypeError // We'll handle the null case below. if (!value.isObjectOrNull()) { - exception_state->SetSimpleException(ExceptionState::kTypeError, - kNotObjectType); + exception_state->SetSimpleException(kNotObjectType); return; } @@ -95,8 +92,7 @@ if (!js_object) { // Set an exception if this is not nullable. if (!(conversion_flags & kConversionFlagNullable)) { - exception_state->SetSimpleException(ExceptionState::kTypeError, - kNotNullableType); + exception_state->SetSimpleException(kNotNullableType); } // Return here even for the non-exception case. return;
diff --git a/src/cobalt/script/mozjs/conversion_helpers.h b/src/cobalt/script/mozjs/conversion_helpers.h index 5aa53ed..ef3c581 100644 --- a/src/cobalt/script/mozjs/conversion_helpers.h +++ b/src/cobalt/script/mozjs/conversion_helpers.h
@@ -40,11 +40,6 @@ namespace script { namespace mozjs { -const char kNotNullableType[] = "Value is null but type is not nullable."; -const char kNotObjectType[] = "Value is not an object."; -const char kDoesNotImplementInterface[] = - "Value does not implement the interface type."; - // Flags that can be used as a bitmask for special conversion behaviour. enum ConversionFlags { kNoConversionFlags = 0, @@ -177,9 +172,7 @@ JSBool success = JS_ValueToInt64(context, value, &out); DCHECK(success); if (!success) { - exception_state->SetSimpleException( - ExceptionState::kTypeError, - "Cannot convert a JavaScript value to int64_t."); + exception_state->SetSimpleException(kNotInt64Type); return; } *out_number = static_cast<T>(out); @@ -251,9 +244,7 @@ JSBool success = JS_ValueToUint64(context, value, &out); DCHECK(success); if (!success) { - exception_state->SetSimpleException( - ExceptionState::kTypeError, - "Cannot convert a JavaScript value to uint64_t."); + exception_state->SetSimpleException(kNotUint64Type); return; } *out_number = static_cast<T>(out); @@ -294,16 +285,13 @@ DCHECK(out_number); double double_value; if (!JS::ToNumber(context, value, &double_value)) { - exception_state->SetSimpleException( - ExceptionState::kError, - "Cannot convert a JavaScript value to a number."); + exception_state->SetSimpleException(kNotNumberType); return; } if (!mozilla::IsFinite(double_value) && (conversion_flags & kConversionFlagRestricted)) { - exception_state->SetSimpleException(ExceptionState::kTypeError, - "Non-finite floating-point value."); + exception_state->SetSimpleException(kNotFinite); return; } @@ -395,15 +383,12 @@ JS::RootedObject js_object(context); if (value.isNull() || value.isUndefined()) { if (!(conversion_flags & kConversionFlagNullable)) { - exception_state->SetSimpleException(ExceptionState::kTypeError, - kNotNullableType); + exception_state->SetSimpleException(kNotNullableType); } return; } if (!JS_ValueToObject(context, value, js_object.address())) { - exception_state->SetSimpleException( - ExceptionState::kTypeError, - "Cannot convert a JavaScript value to an object."); + exception_state->SetSimpleException(kNotObjectType); return; } DCHECK(js_object); @@ -418,8 +403,7 @@ wrapper_factory->DoesObjectImplementInterface(js_object, base::GetTypeId<T>()); if (!object_implements_interface) { - exception_state->SetSimpleException(ExceptionState::kTypeError, - kDoesNotImplementInterface); + exception_state->SetSimpleException(kDoesNotImplementInterface); return; } WrapperPrivate* wrapper_private = @@ -429,8 +413,7 @@ } } // This is not a platform object. Return a type error. - exception_state->SetSimpleException(ExceptionState::kTypeError, - kDoesNotImplementInterface); + exception_state->SetSimpleException(kDoesNotImplementInterface); } // CallbackInterface -> JSValue @@ -473,8 +456,7 @@ << "No conversion flags supported."; if (value.isNull()) { if (!(conversion_flags & kConversionFlagNullable)) { - out_exception->SetSimpleException(ExceptionState::kTypeError, - kNotNullableType); + out_exception->SetSimpleException(kNotNullableType); } // If it is a nullable type, just return. return; @@ -485,8 +467,7 @@ // checking if the correct properties exist will happen when the operation // on the callback interface is run. if (!value.isObject()) { - out_exception->SetSimpleException(ExceptionState::kTypeError, - kNotObjectType); + out_exception->SetSimpleException(kNotObjectType); return; }
diff --git a/src/cobalt/script/mozjs/mozjs.gyp b/src/cobalt/script/mozjs/mozjs.gyp index 23ec990..76f11c1 100644 --- a/src/cobalt/script/mozjs/mozjs.gyp +++ b/src/cobalt/script/mozjs/mozjs.gyp
@@ -43,10 +43,13 @@ 'defines': [ 'ENGINE_SUPPORTS_INT64', ], 'all_dependent_settings': { 'defines': [ - # SpiderMonkey bindings implements indexed deleters. - 'ENGINE_SUPPORTS_INDEXED_DELETERS', - 'ENGINE_SUPPORTS_INT64', - 'ENGINE_SUPPORTS_STACK_TRACE_COLUMNS', ], + # SpiderMonkey bindings implements indexed deleters. + 'ENGINE_SUPPORTS_INDEXED_DELETERS', + 'ENGINE_SUPPORTS_INT64', + 'ENGINE_SUPPORTS_STACK_TRACE_COLUMNS', + # TODO: Remove this when exact rooting and generational GC is enabled. + 'ENGINE_USES_CONSERVATIVE_ROOTING', + ], }, 'conditions' :[ ['cobalt_enable_jit == 1', {
diff --git a/src/cobalt/script/mozjs/mozjs_callback_function.h b/src/cobalt/script/mozjs/mozjs_callback_function.h index 2994166..a2714de 100644 --- a/src/cobalt/script/mozjs/mozjs_callback_function.h +++ b/src/cobalt/script/mozjs/mozjs_callback_function.h
@@ -57,12 +57,16 @@ CallbackResult<R> Run() const OVERRIDE { - JS::RootedObject function(context_, weak_function_.Get()); CallbackResult<R> callback_result; - DLOG_IF(WARNING, !function) << "Function was garbage collected."; - if (function) { + JS::RootedObject function(context_, weak_function_.Get()); + if (!function) { + DLOG(WARNING) << "Function was garbage collected."; + callback_result.exception = true; + } else { JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, function); + JSExceptionState* previous_exception_state = + JS_SaveExceptionState(context_); // https://www.w3.org/TR/WebIDL/#es-invoking-callback-functions // Callback 'this' is set to null, unless overridden by other @@ -79,6 +83,7 @@ } else { callback_result = ConvertCallbackReturnValue<R>(context_, return_value); } + JS_RestoreExceptionState(context_, previous_exception_state); } return callback_result; } @@ -105,12 +110,16 @@ CallbackResult<R> Run( typename base::internal::CallbackParamTraits<A1>::ForwardType a1) const OVERRIDE { - JS::RootedObject function(context_, weak_function_.Get()); CallbackResult<R> callback_result; - DLOG_IF(WARNING, !function) << "Function was garbage collected."; - if (function) { + JS::RootedObject function(context_, weak_function_.Get()); + if (!function) { + DLOG(WARNING) << "Function was garbage collected."; + callback_result.exception = true; + } else { JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, function); + JSExceptionState* previous_exception_state = + JS_SaveExceptionState(context_); // https://www.w3.org/TR/WebIDL/#es-invoking-callback-functions // Callback 'this' is set to null, unless overridden by other @@ -132,6 +141,7 @@ } else { callback_result = ConvertCallbackReturnValue<R>(context_, return_value); } + JS_RestoreExceptionState(context_, previous_exception_state); } return callback_result; } @@ -159,12 +169,16 @@ typename base::internal::CallbackParamTraits<A1>::ForwardType a1, typename base::internal::CallbackParamTraits<A2>::ForwardType a2) const OVERRIDE { - JS::RootedObject function(context_, weak_function_.Get()); CallbackResult<R> callback_result; - DLOG_IF(WARNING, !function) << "Function was garbage collected."; - if (function) { + JS::RootedObject function(context_, weak_function_.Get()); + if (!function) { + DLOG(WARNING) << "Function was garbage collected."; + callback_result.exception = true; + } else { JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, function); + JSExceptionState* previous_exception_state = + JS_SaveExceptionState(context_); // https://www.w3.org/TR/WebIDL/#es-invoking-callback-functions // Callback 'this' is set to null, unless overridden by other @@ -187,6 +201,7 @@ } else { callback_result = ConvertCallbackReturnValue<R>(context_, return_value); } + JS_RestoreExceptionState(context_, previous_exception_state); } return callback_result; } @@ -215,12 +230,16 @@ typename base::internal::CallbackParamTraits<A2>::ForwardType a2, typename base::internal::CallbackParamTraits<A3>::ForwardType a3) const OVERRIDE { - JS::RootedObject function(context_, weak_function_.Get()); CallbackResult<R> callback_result; - DLOG_IF(WARNING, !function) << "Function was garbage collected."; - if (function) { + JS::RootedObject function(context_, weak_function_.Get()); + if (!function) { + DLOG(WARNING) << "Function was garbage collected."; + callback_result.exception = true; + } else { JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, function); + JSExceptionState* previous_exception_state = + JS_SaveExceptionState(context_); // https://www.w3.org/TR/WebIDL/#es-invoking-callback-functions // Callback 'this' is set to null, unless overridden by other @@ -244,6 +263,7 @@ } else { callback_result = ConvertCallbackReturnValue<R>(context_, return_value); } + JS_RestoreExceptionState(context_, previous_exception_state); } return callback_result; } @@ -273,12 +293,16 @@ typename base::internal::CallbackParamTraits<A3>::ForwardType a3, typename base::internal::CallbackParamTraits<A4>::ForwardType a4) const OVERRIDE { - JS::RootedObject function(context_, weak_function_.Get()); CallbackResult<R> callback_result; - DLOG_IF(WARNING, !function) << "Function was garbage collected."; - if (function) { + JS::RootedObject function(context_, weak_function_.Get()); + if (!function) { + DLOG(WARNING) << "Function was garbage collected."; + callback_result.exception = true; + } else { JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, function); + JSExceptionState* previous_exception_state = + JS_SaveExceptionState(context_); // https://www.w3.org/TR/WebIDL/#es-invoking-callback-functions // Callback 'this' is set to null, unless overridden by other @@ -303,6 +327,7 @@ } else { callback_result = ConvertCallbackReturnValue<R>(context_, return_value); } + JS_RestoreExceptionState(context_, previous_exception_state); } return callback_result; } @@ -334,12 +359,16 @@ typename base::internal::CallbackParamTraits<A4>::ForwardType a4, typename base::internal::CallbackParamTraits<A5>::ForwardType a5) const OVERRIDE { - JS::RootedObject function(context_, weak_function_.Get()); CallbackResult<R> callback_result; - DLOG_IF(WARNING, !function) << "Function was garbage collected."; - if (function) { + JS::RootedObject function(context_, weak_function_.Get()); + if (!function) { + DLOG(WARNING) << "Function was garbage collected."; + callback_result.exception = true; + } else { JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, function); + JSExceptionState* previous_exception_state = + JS_SaveExceptionState(context_); // https://www.w3.org/TR/WebIDL/#es-invoking-callback-functions // Callback 'this' is set to null, unless overridden by other @@ -365,6 +394,7 @@ } else { callback_result = ConvertCallbackReturnValue<R>(context_, return_value); } + JS_RestoreExceptionState(context_, previous_exception_state); } return callback_result; } @@ -397,12 +427,16 @@ typename base::internal::CallbackParamTraits<A5>::ForwardType a5, typename base::internal::CallbackParamTraits<A6>::ForwardType a6) const OVERRIDE { - JS::RootedObject function(context_, weak_function_.Get()); CallbackResult<R> callback_result; - DLOG_IF(WARNING, !function) << "Function was garbage collected."; - if (function) { + JS::RootedObject function(context_, weak_function_.Get()); + if (!function) { + DLOG(WARNING) << "Function was garbage collected."; + callback_result.exception = true; + } else { JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, function); + JSExceptionState* previous_exception_state = + JS_SaveExceptionState(context_); // https://www.w3.org/TR/WebIDL/#es-invoking-callback-functions // Callback 'this' is set to null, unless overridden by other @@ -429,6 +463,7 @@ } else { callback_result = ConvertCallbackReturnValue<R>(context_, return_value); } + JS_RestoreExceptionState(context_, previous_exception_state); } return callback_result; } @@ -462,12 +497,16 @@ typename base::internal::CallbackParamTraits<A6>::ForwardType a6, typename base::internal::CallbackParamTraits<A7>::ForwardType a7) const OVERRIDE { - JS::RootedObject function(context_, weak_function_.Get()); CallbackResult<R> callback_result; - DLOG_IF(WARNING, !function) << "Function was garbage collected."; - if (function) { + JS::RootedObject function(context_, weak_function_.Get()); + if (!function) { + DLOG(WARNING) << "Function was garbage collected."; + callback_result.exception = true; + } else { JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, function); + JSExceptionState* previous_exception_state = + JS_SaveExceptionState(context_); // https://www.w3.org/TR/WebIDL/#es-invoking-callback-functions // Callback 'this' is set to null, unless overridden by other @@ -495,6 +534,7 @@ } else { callback_result = ConvertCallbackReturnValue<R>(context_, return_value); } + JS_RestoreExceptionState(context_, previous_exception_state); } return callback_result; }
diff --git a/src/cobalt/script/mozjs/mozjs_callback_function.h.pump b/src/cobalt/script/mozjs/mozjs_callback_function.h.pump index 6ea3efe..810dcc5 100644 --- a/src/cobalt/script/mozjs/mozjs_callback_function.h.pump +++ b/src/cobalt/script/mozjs/mozjs_callback_function.h.pump
@@ -84,6 +84,8 @@ } else { JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, function); + JSExceptionState* previous_exception_state = + JS_SaveExceptionState(context_); // https://www.w3.org/TR/WebIDL/#es-invoking-callback-functions // Callback 'this' is set to null, unless overridden by other specifications @@ -112,6 +114,7 @@ } else { callback_result = ConvertCallbackReturnValue<R>(context_, return_value); } + JS_RestoreExceptionState(context_, previous_exception_state); } return callback_result; }
diff --git a/src/cobalt/script/mozjs/mozjs_exception_state.cc b/src/cobalt/script/mozjs/mozjs_exception_state.cc index e7839c4..111b56e 100644 --- a/src/cobalt/script/mozjs/mozjs_exception_state.cc +++ b/src/cobalt/script/mozjs/mozjs_exception_state.cc
@@ -16,34 +16,42 @@ #include "cobalt/script/mozjs/mozjs_exception_state.h" #include <string> +#include <vector> #include "base/logging.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" #include "cobalt/script/mozjs/conversion_helpers.h" -#include "third_party/mozjs/js/src/jsapi.h" namespace cobalt { namespace script { namespace mozjs { namespace { -std::string SimpleExceptionToString(ExceptionState::SimpleExceptionType type) { + +JSExnType ConvertToMozjsExceptionType(SimpleExceptionType type) { switch (type) { - case ExceptionState::kError: - return "Error"; - case ExceptionState::kTypeError: - return "TypeError"; - case ExceptionState::kRangeError: - return "RangeError"; - case ExceptionState::kReferenceError: - return "ReferenceError"; - case ExceptionState::kSyntaxError: - return "SyntaxError"; - case ExceptionState::kURIError: - return "URIError"; + case kError: + return JSEXN_ERR; + case kTypeError: + return JSEXN_TYPEERR; + case kRangeError: + return JSEXN_RANGEERR; + case kReferenceError: + return JSEXN_REFERENCEERR; + case kSyntaxError: + return JSEXN_SYNTAXERR; + case kURIError: + return JSEXN_URIERR; } - NOTREACHED(); - return ""; } + +// JSErrorCallback. +const JSErrorFormatString* GetErrorMessage(void* user_ref, const char* locale, + const unsigned error_number) { + return static_cast<JSErrorFormatString*>(user_ref); +} + } // namespace void MozjsExceptionState::SetException( @@ -63,17 +71,28 @@ is_exception_set_ = true; } -void MozjsExceptionState::SetSimpleException( - SimpleExceptionType simple_exception, const std::string& message) { +void MozjsExceptionState::SetSimpleException(MessageType message_type, ...) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!is_exception_set_); - std::stringstream stream; - stream << SimpleExceptionToString(simple_exception) << ": " << message; - // JS_ReportError first builds an error message from the given sprintf-style - // format string and any additional arguments passed after it. The resulting - // error message is passed to the context's JSErrorReporter callback. - JS_ReportError(context_, stream.str().c_str()); + va_list arguments; + va_start(arguments, message_type); + std::string error_message = + base::StringPrintV(GetExceptionMessageFormat(message_type), arguments); + JSErrorFormatString format_string; + format_string.format = error_message.c_str(); + // Already fed arguments for format. + format_string.argCount = 0; + format_string.exnType = + ConvertToMozjsExceptionType(GetSimpleExceptionType(message_type)); + + // This function creates a JSErrorReport, populate it with an error message + // obtained from the given JSErrorCallback. The resulting error message is + // passed to the context's JSErrorReporter callback. + JS_ReportErrorNumber(context_, GetErrorMessage, + static_cast<void*>(&format_string), message_type); + va_end(arguments); + is_exception_set_ = true; }
diff --git a/src/cobalt/script/mozjs/mozjs_exception_state.h b/src/cobalt/script/mozjs/mozjs_exception_state.h index 4a3c11d..361dc70 100644 --- a/src/cobalt/script/mozjs/mozjs_exception_state.h +++ b/src/cobalt/script/mozjs/mozjs_exception_state.h
@@ -32,8 +32,7 @@ : is_exception_set_(false), context_(context) {} // ExceptionState interface void SetException(const scoped_refptr<ScriptException>& exception) OVERRIDE; - void SetSimpleException(SimpleExceptionType simple_exception, - const std::string& message) OVERRIDE; + void SetSimpleException(MessageType message_type, ...) OVERRIDE; bool is_exception_set() const { return is_exception_set_; }
diff --git a/src/cobalt/script/mozjs/mozjs_global_object_proxy.cc b/src/cobalt/script/mozjs/mozjs_global_object_proxy.cc index d25e1b7..273f328 100644 --- a/src/cobalt/script/mozjs/mozjs_global_object_proxy.cc +++ b/src/cobalt/script/mozjs/mozjs_global_object_proxy.cc
@@ -147,9 +147,6 @@ js::SetDOMProxyInformation(0 /*domProxyHandlerFamily*/, kJSProxySlotExpando, DOMProxyShadowsCheck); #endif -#if !defined(COBALT_BUILD_TYPE_GOLD) && !defined(COBALT_BUILD_TYPE_QA) - options |= JSOPTION_EXTRA_WARNINGS; -#endif JS_SetOptions(context_, options); JS_SetErrorReporter(context_, &MozjsGlobalObjectProxy::ReportErrorHandler); @@ -202,7 +199,8 @@ const base::SourceLocation location = mozjs_source_code->location(); JSAutoRequest auto_request(context_); - JSAutoCompartment auto_comparment(context_, global_object_proxy_); + JSAutoCompartment auto_compartment(context_, global_object_proxy_); + JSExceptionState* previous_exception_state = JS_SaveExceptionState(context_); JS::RootedValue result_value(context_); std::string error_message; last_error_message_ = &error_message; @@ -230,6 +228,7 @@ *out_result_utf8 = *last_error_message_; } } + JS_RestoreExceptionState(context_, previous_exception_state); last_error_message_ = NULL; return success; } @@ -239,6 +238,29 @@ return util::GetStackTrace(context_, max_frames); } +void MozjsGlobalObjectProxy::PreventGarbageCollection( + const scoped_refptr<Wrappable>& wrappable) { + DCHECK(thread_checker_.CalledOnValidThread()); + JSAutoRequest auto_request(context_); + JSAutoCompartment auto_compartment(context_, global_object_proxy_); + WrapperPrivate* wrapper_private = + WrapperPrivate::GetFromWrappable(wrappable, context_, wrapper_factory()); + JS::RootedObject proxy(context_, wrapper_private->js_object_proxy()); + kept_alive_objects_.insert(CachedWrapperMultiMap::value_type( + wrappable.get(), JS::Heap<JSObject*>(proxy))); +} + +void MozjsGlobalObjectProxy::AllowGarbageCollection( + const scoped_refptr<Wrappable>& wrappable) { + DCHECK(thread_checker_.CalledOnValidThread()); + CachedWrapperMultiMap::iterator it = + kept_alive_objects_.find(wrappable.get()); + DCHECK(it != kept_alive_objects_.end()); + if (it != kept_alive_objects_.end()) { + kept_alive_objects_.erase(it); + } +} + void MozjsGlobalObjectProxy::DisableEval(const std::string& message) { DCHECK(thread_checker_.CalledOnValidThread()); eval_disabled_message_.emplace(message); @@ -260,7 +282,7 @@ void MozjsGlobalObjectProxy::Bind(const std::string& identifier, const scoped_refptr<Wrappable>& impl) { JSAutoRequest auto_request(context_); - JSAutoCompartment auto_comparment(context_, global_object_proxy_); + JSAutoCompartment auto_compartment(context_, global_object_proxy_); JS::RootedObject wrapper_proxy(context_, wrapper_factory_->GetWrapperProxy(impl)); @@ -365,6 +387,11 @@ "MozjsGlobalObjectProxy"); } } + for (CachedWrapperMultiMap::iterator it = + global_object_environment->kept_alive_objects_.begin(); + it != global_object_environment->kept_alive_objects_.end(); ++it) { + JS_CallHeapObjectTracer(trace, &it->second, "MozjsGlobalObjectProxy"); + } } JSBool MozjsGlobalObjectProxy::CheckEval(JSContext* context) {
diff --git a/src/cobalt/script/mozjs/mozjs_global_object_proxy.h b/src/cobalt/script/mozjs/mozjs_global_object_proxy.h index fc0b760..de36ae6 100644 --- a/src/cobalt/script/mozjs/mozjs_global_object_proxy.h +++ b/src/cobalt/script/mozjs/mozjs_global_object_proxy.h
@@ -63,14 +63,10 @@ std::vector<StackFrame> GetStackTrace(int max_frames = 0) OVERRIDE; void PreventGarbageCollection( - const scoped_refptr<Wrappable>& wrappable) OVERRIDE { - NOTIMPLEMENTED(); - } + const scoped_refptr<Wrappable>& wrappable) OVERRIDE; void AllowGarbageCollection( - const scoped_refptr<Wrappable>& wrappable) OVERRIDE { - NOTIMPLEMENTED(); - } + const scoped_refptr<Wrappable>& wrappable) OVERRIDE; void DisableEval(const std::string& message) OVERRIDE; @@ -144,10 +140,13 @@ }; typedef base::hash_map<intptr_t, InterfaceData*> CachedInterfaceData; + typedef base::hash_multimap<Wrappable*, JS::Heap<JSObject*> > + CachedWrapperMultiMap; base::ThreadChecker thread_checker_; JSContext* context_; WeakHeapObjectManager weak_object_manager_; + CachedWrapperMultiMap kept_alive_objects_; scoped_ptr<ReferencedObjectMap> referenced_objects_; CachedInterfaceData cached_interface_data_; STLValueDeleter<CachedInterfaceData> cached_interface_data_deleter_;
diff --git a/src/cobalt/script/mozjs/union_type_conversion_impl.h b/src/cobalt/script/mozjs/union_type_conversion_impl.h index 5d0076f..a8f7e6f 100644 --- a/src/cobalt/script/mozjs/union_type_conversion_impl.h +++ b/src/cobalt/script/mozjs/union_type_conversion_impl.h
@@ -187,8 +187,7 @@ return; } // 19. Throw a TypeError. - exception_state->SetSimpleException( - ExceptionState::kTypeError, "Value is not a member of the union type."); + exception_state->SetSimpleException(kNotUnionType); } template <typename T1, typename T2, typename T3> @@ -385,8 +384,7 @@ return; } // 19. Throw a TypeError. - exception_state->SetSimpleException( - ExceptionState::kTypeError, "Value is not a member of the union type."); + exception_state->SetSimpleException(kNotUnionType); } template <typename T1, typename T2, typename T3, typename T4> @@ -624,8 +622,7 @@ return; } // 19. Throw a TypeError. - exception_state->SetSimpleException( - ExceptionState::kTypeError, "Value is not a member of the union type."); + exception_state->SetSimpleException(kNotUnionType); } } // namespace mozjs
diff --git a/src/cobalt/script/mozjs/union_type_conversion_impl.h.pump b/src/cobalt/script/mozjs/union_type_conversion_impl.h.pump index 046f822..d4103ca 100644 --- a/src/cobalt/script/mozjs/union_type_conversion_impl.h.pump +++ b/src/cobalt/script/mozjs/union_type_conversion_impl.h.pump
@@ -46,12 +46,15 @@ $range TYPE 1..NUM_MEMBERS template <$for TYPE , [[typename T$(TYPE)]]> -void ToJSValue(JSContext* context, const script::UnionType$(NUM_MEMBERS)<$for TYPE , [[T$(TYPE)]]>& in_union, JS::MutableHandleValue out_value) { +void ToJSValue( + JSContext* context, + const script::UnionType$(NUM_MEMBERS)<$for TYPE , [[T$(TYPE)]]>& in_union, + JS::MutableHandleValue out_value) { $for TYPE [[ if (in_union.template IsType<T$(TYPE)>()) { - ToJSValue(context, in_union.template AsType<T$(TYPE)>(), out_value); - return; + ToJSValue(context, in_union.template AsType<T$(TYPE)>(), out_value); + return; } ]] @@ -110,8 +113,8 @@ global_object_proxy->wrapper_factory(); $for TYPE [[ - if (UnionTypeTraitsT$(TYPE)::is_interface_type - && wrapper_factory->DoesObjectImplementInterface( + if (UnionTypeTraitsT$(TYPE)::is_interface_type && + wrapper_factory->DoesObjectImplementInterface( rooted_object, UnionTypeTraitsT$(TYPE)::GetTypeID())) { FromJSValue(context, value, conversion_flags, exception_state, &t$(TYPE)); *out_union = script::UnionType$(NUM_MEMBERS)<$for TYPE , [[T$(TYPE)]]>(t$(TYPE)); @@ -192,8 +195,7 @@ ]] // 19. Throw a TypeError. - exception_state->SetSimpleException( - ExceptionState::kTypeError, "Value is not a member of the union type."); + exception_state->SetSimpleException(kNotUnionType); } ]] $$ for NUM_MEMBERS
diff --git a/src/cobalt/script/script.gyp b/src/cobalt/script/script.gyp index dce7d46..c827de2 100644 --- a/src/cobalt/script/script.gyp +++ b/src/cobalt/script/script.gyp
@@ -21,6 +21,8 @@ 'type': 'static_library', 'sources': [ 'call_frame.h', + 'exception_message.cc', + 'exception_message.h', 'execution_state.cc', 'execution_state.h', 'global_object_proxy.h',
diff --git a/src/cobalt/script/testing/mock_exception_state.h b/src/cobalt/script/testing/mock_exception_state.h index 1d8516c..c99c946 100644 --- a/src/cobalt/script/testing/mock_exception_state.h +++ b/src/cobalt/script/testing/mock_exception_state.h
@@ -29,8 +29,14 @@ class MockExceptionState : public ExceptionState { public: MOCK_METHOD1(SetException, void(const scoped_refptr<ScriptException>&)); - MOCK_METHOD2(SetSimpleException, - void(SimpleExceptionType, const std::string&)); + MOCK_METHOD2(SetSimpleExceptionVA, void(MessageType, va_list)); + + void SetSimpleException(MessageType message_type, ...) { + va_list arguments; + va_start(arguments, message_type); + SetSimpleExceptionVA(message_type, arguments); + va_end(arguments); + } }; } // namespace testing
diff --git a/src/cobalt/storage/virtual_file_system.cc b/src/cobalt/storage/virtual_file_system.cc index 8b67f27..70e1556 100644 --- a/src/cobalt/storage/virtual_file_system.cc +++ b/src/cobalt/storage/virtual_file_system.cc
@@ -21,6 +21,8 @@ #include "base/synchronization/lock.h" #include "cobalt/storage/virtual_file.h" +#include "starboard/client_porting/poem/string_poem.h" + namespace cobalt { namespace storage { @@ -126,6 +128,21 @@ base::AutoLock lock(file_table_lock_); ClearFileTable(); + if (buffer_size < 0) { + DLOG(ERROR) << "Buffer size must be positive: " + << buffer_size << " < 0."; + return; + } + + // The size of the buffer must be checked before copying the beginning of it + // into a SerializedHeader. + if (static_cast<size_t>(buffer_size) < sizeof(SerializedHeader)) { + DLOG(ERROR) << "Buffer size " << buffer_size + << " is too small to contain a SerializedHeader of size " + << sizeof(SerializedHeader) << "; operation aborted."; + return; + } + // Read in expected number of files SerializedHeader header; memcpy(&header, buffer, sizeof(SerializedHeader));
diff --git a/src/cobalt/webdriver/web_driver_module.cc b/src/cobalt/webdriver/web_driver_module.cc index 8884ddb..46a77e8 100644 --- a/src/cobalt/webdriver/web_driver_module.cc +++ b/src/cobalt/webdriver/web_driver_module.cc
@@ -403,7 +403,12 @@ base::Unretained(this), server_port)); } -WebDriverModule::~WebDriverModule() { webdriver_thread_.Stop(); } +WebDriverModule::~WebDriverModule() { + webdriver_thread_.message_loop()->PostTask( + FROM_HERE, base::Bind(&WebDriverModule::StopServer, + base::Unretained(this))); + webdriver_thread_.Stop(); +} void WebDriverModule::OnWindowRecreated() { if (MessageLoop::current() != webdriver_thread_.message_loop()) { @@ -427,6 +432,10 @@ base::Unretained(webdriver_dispatcher_.get())))); } +void WebDriverModule::StopServer() { + webdriver_server_.reset(); +} + SessionDriver* WebDriverModule::GetSessionDriver( const protocol::SessionId& session_id) { DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/src/cobalt/webdriver/web_driver_module.h b/src/cobalt/webdriver/web_driver_module.h index c18e7df..2df30dd 100644 --- a/src/cobalt/webdriver/web_driver_module.h +++ b/src/cobalt/webdriver/web_driver_module.h
@@ -66,6 +66,7 @@ private: void StartServer(int server_port); + void StopServer(); void GetServerStatus( const base::Value* parameters, const WebDriverDispatcher::PathVariableMap* path_variables,
diff --git a/src/cobalt/xhr/xml_http_request.cc b/src/cobalt/xhr/xml_http_request.cc index 5db9c13..04b02e1 100644 --- a/src/cobalt/xhr/xml_http_request.cc +++ b/src/cobalt/xhr/xml_http_request.cc
@@ -911,7 +911,7 @@ // well-formedness error, etc.), return null. scoped_refptr<dom::XMLDocument> xml_document = new dom::XMLDocument(); dom_parser::XMLDecoder xml_decoder( - xml_document, xml_document, NULL, + xml_document, xml_document, NULL, settings_->max_dom_element_depth(), base::SourceLocation("[object XMLHttpRequest]", 1, 1), base::Closure(), base::Bind(&XMLHttpRequest::XMLDecoderErrorCallback, base::Unretained(this)));
diff --git a/src/cobalt/xhr/xml_http_request_test.cc b/src/cobalt/xhr/xml_http_request_test.cc index d944d42..ac3b5c1 100644 --- a/src/cobalt/xhr/xml_http_request_test.cc +++ b/src/cobalt/xhr/xml_http_request_test.cc
@@ -92,7 +92,7 @@ class FakeSettings : public dom::DOMSettings { public: - FakeSettings() : dom::DOMSettings(NULL, NULL, NULL, NULL, NULL, NULL) {} + FakeSettings() : dom::DOMSettings(0, NULL, NULL, NULL, NULL, NULL, NULL) {} GURL base_url() const OVERRIDE { return GURL("http://example.com"); } };
diff --git a/src/media/base/pipeline.h b/src/media/base/pipeline.h index 4315f75..3f5aaf6 100644 --- a/src/media/base/pipeline.h +++ b/src/media/base/pipeline.h
@@ -15,6 +15,7 @@ #ifndef MEDIA_BASE_PIPELINE_H_ #define MEDIA_BASE_PIPELINE_H_ +#include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/message_loop_proxy.h" #include "base/time.h" @@ -23,8 +24,24 @@ #include "media/base/media_export.h" #include "media/base/pipeline_status.h" #include "media/base/ranges.h" +#include "ui/gfx/rect.h" #include "ui/gfx/size.h" +#if defined(OS_STARBOARD) +#if SB_HAS(PLAYER) + +#define COBALT_USE_SBPLAYER_PIPELINE + +#endif // SB_HAS(PLAYER) +#endif // defined(OS_STARBOARD) + +#if defined(COBALT_USE_SBPLAYER_PIPELINE) +#include "starboard/window.h" +typedef SbWindow PipelineWindow; +#else // defined(COBALT_USE_SBPLAYER_PIPELINE) +typedef void* PipelineWindow; +#endif // defined(COBALT_USE_SBPLAYER_PIPELINE) + namespace media { class MediaLog; @@ -34,6 +51,8 @@ // playing. class MEDIA_EXPORT Pipeline : public base::RefCountedThreadSafe<Pipeline> { public: + typedef base::Callback<void(const gfx::Rect&)> SetBoundsCB; + // Buffering states the pipeline transitions between during playback. // kHaveMetadata: // Indicates that the following things are known: @@ -50,6 +69,7 @@ typedef base::Callback<void(BufferingState)> BufferingStateCB; static scoped_refptr<Pipeline> Create( + PipelineWindow window, const scoped_refptr<base::MessageLoopProxy>& message_loop, MediaLog* media_log); @@ -151,6 +171,9 @@ // Gets the current pipeline statistics. virtual PipelineStatistics GetStatistics() const = 0; + + // Get the SetBoundsCB used to set the bounds of the video frame. + virtual SetBoundsCB GetSetBoundsCB() { return SetBoundsCB(); } }; } // namespace media
diff --git a/src/media/base/pipeline_impl.cc b/src/media/base/pipeline_impl.cc index 6857cf6..b34ae13 100644 --- a/src/media/base/pipeline_impl.cc +++ b/src/media/base/pipeline_impl.cc
@@ -68,8 +68,10 @@ } scoped_refptr<Pipeline> Pipeline::Create( + PipelineWindow window, const scoped_refptr<base::MessageLoopProxy>& message_loop, MediaLog* media_log) { + UNREFERENCED_PARAMETER(window); return new PipelineImpl(message_loop, media_log); }
diff --git a/src/media/base/sbplayer_pipeline.cc b/src/media/base/sbplayer_pipeline.cc index edc8565..2424340 100644 --- a/src/media/base/sbplayer_pipeline.cc +++ b/src/media/base/sbplayer_pipeline.cc
@@ -120,12 +120,35 @@ } } +class SetBoundsCaller : public base::RefCountedThreadSafe<SetBoundsCaller> { + public: + SetBoundsCaller() : player_(kSbPlayerInvalid) {} + void SetPlayer(SbPlayer player) { + base::Lock lock_; + player_ = player; + } + void SetBounds(const gfx::Rect& rect) { + base::AutoLock auto_lock(lock_); + if (SbPlayerIsValid(player_)) { + SbPlayerSetBounds(player_, rect.x(), rect.y(), rect.width(), + rect.height()); + } + } + + private: + base::Lock lock_; + SbPlayer player_; + + DISALLOW_COPY_AND_ASSIGN(SetBoundsCaller); +}; + // SbPlayerPipeline is a PipelineBase implementation that uses the SbPlayer // interface internally. class MEDIA_EXPORT SbPlayerPipeline : public Pipeline, public DemuxerHost { public: // Constructs a media pipeline that will execute on |message_loop|. - SbPlayerPipeline(const scoped_refptr<base::MessageLoopProxy>& message_loop, + SbPlayerPipeline(PipelineWindow window, + const scoped_refptr<base::MessageLoopProxy>& message_loop, MediaLog* media_log); ~SbPlayerPipeline() OVERRIDE; @@ -155,8 +178,16 @@ bool DidLoadingProgress() const OVERRIDE; PipelineStatistics GetStatistics() const OVERRIDE; + SetBoundsCB GetSetBoundsCB() OVERRIDE; private: + // A map from raw data pointer returned by DecoderBuffer::GetData() to the + // DecoderBuffer and a reference count. The reference count indicates how + // many instances of the DecoderBuffer is currently being decoded in the + // pipeline. + typedef std::map<const void*, std::pair<scoped_refptr<DecoderBuffer>, int> > + DecodingBuffers; + void StartTask(); // DataSourceHost (by way of DemuxerHost) implementation. @@ -206,6 +237,9 @@ // Lock used to serialize access for the following data members. mutable base::Lock lock_; + // The window this player associates with. + PipelineWindow window_; + // Whether or not the pipeline is running. bool running_; @@ -276,15 +310,19 @@ SbPlayer player_; - std::map<const void*, scoped_refptr<DecoderBuffer> > decoding_buffers_; + DecodingBuffers decoding_buffers_; + + scoped_refptr<SetBoundsCaller> set_bounds_caller_; DISALLOW_COPY_AND_ASSIGN(SbPlayerPipeline); }; SbPlayerPipeline::SbPlayerPipeline( + PipelineWindow window, const scoped_refptr<base::MessageLoopProxy>& message_loop, MediaLog* media_log) - : message_loop_(message_loop), + : window_(window), + message_loop_(message_loop), media_log_(media_log), total_bytes_(0), natural_size_(0, 0), @@ -295,7 +333,8 @@ ticket_(SB_PLAYER_INITIAL_TICKET), audio_read_in_progress_(false), video_read_in_progress_(false), - player_(kSbPlayerInvalid) {} + player_(kSbPlayerInvalid), + set_bounds_caller_(new SetBoundsCaller) {} SbPlayerPipeline::~SbPlayerPipeline() { DCHECK(player_ == kSbPlayerInvalid); @@ -339,6 +378,7 @@ DCHECK(!stop_cb.is_null()); if (SbPlayerIsValid(player_)) { + set_bounds_caller_->SetPlayer(kSbPlayerInvalid); SbPlayerDestroy(player_); player_ = kSbPlayerInvalid; } @@ -480,6 +520,14 @@ return statistics_; } +Pipeline::SetBoundsCB SbPlayerPipeline::GetSetBoundsCB() { +#if SB_IS(PLAYER_PUNCHED_OUT) + return base::Bind(&SetBoundsCaller::SetBounds, set_bounds_caller_); +#else // SB_IS(PLAYER_PUNCHED_OUT) + return Pipeline::SetBoundsCB(); +#endif // SB_IS(PLAYER_PUNCHED_OUT) +} + void SbPlayerPipeline::StartTask() { DCHECK(message_loop_->BelongsToCurrentThread()); @@ -528,9 +576,10 @@ audio_header.bits_per_sample = audio_config.bits_per_channel(); audio_header.audio_specific_config_size = 0; player_ = - SbPlayerCreate(kSbMediaVideoCodecH264, kSbMediaAudioCodecAac, + SbPlayerCreate(window_, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac, SB_PLAYER_NO_DURATION, drm_system, &audio_header, DeallocateSampleCB, DecoderStatusCB, PlayerStatusCB, this); + set_bounds_caller_->SetPlayer(player_); } void SbPlayerPipeline::SetDecryptor(Decryptor* decryptor) { @@ -663,9 +712,12 @@ SbPlayerWriteEndOfStream(player_, kSbMediaTypeAudio); return; } - DCHECK(decoding_buffers_.find(buffer->GetData()) == - decoding_buffers_.end()); - decoding_buffers_[buffer->GetData()] = buffer; + DecodingBuffers::iterator iter = decoding_buffers_.find(buffer->GetData()); + if (iter == decoding_buffers_.end()) { + decoding_buffers_[buffer->GetData()] = std::make_pair(buffer, 1); + } else { + ++iter->second.second; + } SbPlayerWriteSample(player_, kSbMediaTypeAudio, buffer->GetData(), buffer->GetDataSize(), TimeDeltaToSbMediaTime(buffer->GetTimestamp()), NULL, @@ -683,8 +735,12 @@ video_info.is_key_frame = false; video_info.frame_width = 1; video_info.frame_height = 1; - DCHECK(decoding_buffers_.find(buffer->GetData()) == decoding_buffers_.end()); - decoding_buffers_[buffer->GetData()] = buffer; + DecodingBuffers::iterator iter = decoding_buffers_.find(buffer->GetData()); + if (iter == decoding_buffers_.end()) { + decoding_buffers_[buffer->GetData()] = std::make_pair(buffer, 1); + } else { + ++iter->second.second; + } SbPlayerWriteSample(player_, kSbMediaTypeVideo, buffer->GetData(), buffer->GetDataSize(), TimeDeltaToSbMediaTime(buffer->GetTimestamp()), @@ -768,8 +824,17 @@ void SbPlayerPipeline::OnDeallocateSample(const void* sample_buffer) { DCHECK(message_loop_->BelongsToCurrentThread()); - DCHECK(decoding_buffers_.find(sample_buffer) != decoding_buffers_.end()); - decoding_buffers_.erase(decoding_buffers_.find(sample_buffer)); + DecodingBuffers::iterator iter = decoding_buffers_.find(sample_buffer); + DCHECK(iter != decoding_buffers_.end()); + if (iter == decoding_buffers_.end()) { + LOG(ERROR) << "SbPlayerPipeline::OnDeallocateSample encounters unknown " + << "sample_buffer " << sample_buffer; + return; + } + --iter->second.second; + if (iter->second.second == 0) { + decoding_buffers_.erase(iter); + } } // static @@ -791,7 +856,6 @@ SbPlayerState state, int ticket) { SbPlayerPipeline* pipeline = reinterpret_cast<SbPlayerPipeline*>(context); - DCHECK_EQ(pipeline->player_, player); pipeline->message_loop_->PostTask( FROM_HERE, base::Bind(&SbPlayerPipeline::OnPlayerStatus, pipeline, state, ticket)); @@ -813,10 +877,11 @@ #endif // SB_HAS(PLAYER) scoped_refptr<Pipeline> Pipeline::Create( + PipelineWindow window, const scoped_refptr<base::MessageLoopProxy>& message_loop, MediaLog* media_log) { #if SB_HAS(PLAYER) - return new SbPlayerPipeline(message_loop, media_log); + return new SbPlayerPipeline(window, message_loop, media_log); #else return NULL; #endif
diff --git a/src/media/base/shell_video_frame_provider.cc b/src/media/base/shell_video_frame_provider.cc index 589f886..7a9ddb2 100644 --- a/src/media/base/shell_video_frame_provider.cc +++ b/src/media/base/shell_video_frame_provider.cc
@@ -28,21 +28,22 @@ #endif // !defined(__LB_SHELL__FOR_RELEASE__) } -void ShellVideoFrameProvider::RegisterMediaTimeCB( - const MediaTimeCB& media_time_cb) { - DCHECK(!media_time_cb.is_null()); +void ShellVideoFrameProvider::RegisterMediaTimeAndSeekingStateCB( + const MediaTimeAndSeekingStateCB& media_time_and_seeking_state_cb) { + DCHECK(!media_time_and_seeking_state_cb.is_null()); base::AutoLock auto_lock(frames_lock_); - media_time_cb_ = media_time_cb; + media_time_and_seeking_state_cb_ = media_time_and_seeking_state_cb; } -void ShellVideoFrameProvider::UnregisterMediaTimeCB( - const MediaTimeCB& media_time_cb) { +void ShellVideoFrameProvider::UnregisterMediaTimeAndSeekingStateCB( + const MediaTimeAndSeekingStateCB& media_time_and_seeking_state_cb) { base::AutoLock auto_lock(frames_lock_); // It is possible that the register of a new callback happens earlier than the // unregister of the previous callback. Always ensure that the callback // passed in is the current one before resetting. - if (media_time_cb_.Equals(media_time_cb)) { - media_time_cb_.Reset(); + if (media_time_and_seeking_state_cb_.Equals( + media_time_and_seeking_state_cb)) { + media_time_and_seeking_state_cb_.Reset(); } } @@ -57,7 +58,9 @@ base::AutoLock auto_lock(frames_lock_); - base::TimeDelta media_time = GetMediaTime_Locked(); + base::TimeDelta media_time; + bool is_seeking; + GetMediaTimeAndSeekingState_Locked(&media_time, &is_seeking); while (!frames_.empty()) { int64_t frame_time = frames_[0]->GetTimestamp().InMicroseconds(); if (frame_time >= media_time.InMicroseconds()) @@ -66,7 +69,7 @@ frame_time + kEpsilonInMicroseconds >= media_time.InMicroseconds()) break; - if (current_frame_ != frames_[0]) { + if (current_frame_ != frames_[0] && !is_seeking) { ++dropped_frames_; #if !defined(__LB_SHELL__FOR_RELEASE__) @@ -117,9 +120,18 @@ return frames_.size(); } -base::TimeDelta ShellVideoFrameProvider::GetMediaTime_Locked() const { +void ShellVideoFrameProvider::GetMediaTimeAndSeekingState_Locked( + base::TimeDelta* media_time, + bool* is_seeking) const { + DCHECK(media_time); + DCHECK(is_seeking); frames_lock_.AssertAcquired(); - return media_time_cb_.is_null() ? base::TimeDelta() : media_time_cb_.Run(); + if (media_time_and_seeking_state_cb_.is_null()) { + *media_time = base::TimeDelta(); + *is_seeking = false; + return; + } + media_time_and_seeking_state_cb_.Run(media_time, is_seeking); } bool ShellVideoFrameProvider::QueryAndResetHasConsumedFrames() {
diff --git a/src/media/base/shell_video_frame_provider.h b/src/media/base/shell_video_frame_provider.h index 47ab8da..7344e78 100644 --- a/src/media/base/shell_video_frame_provider.h +++ b/src/media/base/shell_video_frame_provider.h
@@ -38,15 +38,21 @@ public: explicit ShellVideoFrameProvider(scoped_refptr<VideoFrame> punch_out = NULL); - typedef base::Callback<base::TimeDelta()> MediaTimeCB; + // The calling back returns the current media time and a bool which is set to + // true when a seek is in progress. + typedef base::Callback<void(base::TimeDelta*, bool*)> + MediaTimeAndSeekingStateCB; // This class uses the media time to decide which frame is current. It // retrieves the media time from the registered media_time_cb. There can only // be one registered media_time_cb at a certain time, a call to - // RegisterMediaTimeCB() will overwrite the previously registered callback. - void RegisterMediaTimeCB(const MediaTimeCB& media_time_cb); + // RegisterMediaTimeAndSeekingStateCB() will overwrite the previously + // registered callback. + void RegisterMediaTimeAndSeekingStateCB( + const MediaTimeAndSeekingStateCB& media_time_and_seeking_state_cb); // This function unregisters the media time callback if it hasn't been // overwritten by another callback. - void UnregisterMediaTimeCB(const MediaTimeCB& media_time_cb); + void UnregisterMediaTimeAndSeekingStateCB( + const MediaTimeAndSeekingStateCB& media_time_and_seeking_state_cb); // Returns the current frame to be displayed if there is one. Otherwise it // returns NULL. @@ -68,12 +74,13 @@ int ResetAndReturnDroppedFrames(); private: - base::TimeDelta GetMediaTime_Locked() const; + void GetMediaTimeAndSeekingState_Locked(base::TimeDelta* media_time, + bool* is_seeking) const; scoped_refptr<VideoFrame> punch_out_; mutable base::Lock frames_lock_; - MediaTimeCB media_time_cb_; + MediaTimeAndSeekingStateCB media_time_and_seeking_state_cb_; std::vector<scoped_refptr<VideoFrame> > frames_; scoped_refptr<VideoFrame> current_frame_; bool has_consumed_frames_;
diff --git a/src/media/player/web_media_player.h b/src/media/player/web_media_player.h index 98a125f..77a9c0c 100644 --- a/src/media/player/web_media_player.h +++ b/src/media/player/web_media_player.h
@@ -10,6 +10,7 @@ #include <string> +#include "base/callback.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/memory/ref_counted.h" @@ -20,6 +21,7 @@ #include "media/base/shell_video_frame_provider.h" #include "media/base/video_frame.h" #include "media/player/buffered_data_source.h" +#include "ui/gfx/rect.h" #include "ui/gfx/size.h" // Disable `unreferenced formal parameter` as we have many stub functions in @@ -30,6 +32,8 @@ class WebMediaPlayer { public: + typedef base::Callback<void(const gfx::Rect&)> SetBoundsCB; + enum NetworkState { kNetworkStateEmpty, kNetworkStateIdle, @@ -184,6 +188,8 @@ return kMediaKeyExceptionKeySystemNotSupported; } + virtual SetBoundsCB GetSetBoundsCB() { return SetBoundsCB(); } + // Instruct WebMediaPlayer to enter/exit fullscreen. virtual void EnterFullscreen() {} virtual void ExitFullscreen() {}
diff --git a/src/media/player/web_media_player_impl.cc b/src/media/player/web_media_player_impl.cc index 34bd3ac..42a2f87 100644 --- a/src/media/player/web_media_player_impl.cc +++ b/src/media/player/web_media_player_impl.cc
@@ -18,13 +18,13 @@ #include "base/string_number_conversions.h" #include "base/synchronization/waitable_event.h" #include "media/audio/shell_audio_sink.h" -#include "media/filters/shell_audio_renderer.h" #include "media/base/bind_to_loop.h" #include "media/base/filter_collection.h" #include "media/base/limits.h" #include "media/base/media_log.h" #include "media/base/video_frame.h" #include "media/filters/chunk_demuxer.h" +#include "media/filters/shell_audio_renderer.h" #include "media/filters/shell_demuxer.h" #include "media/filters/video_renderer_base.h" #include "media/player/web_media_player_proxy.h" @@ -114,6 +114,7 @@ } WebMediaPlayerImpl::WebMediaPlayerImpl( + PipelineWindow window, WebMediaPlayerClient* client, WebMediaPlayerDelegate* delegate, const scoped_refptr<ShellVideoFrameProvider>& video_frame_provider, @@ -141,7 +142,7 @@ scoped_refptr<base::MessageLoopProxy> pipeline_message_loop = message_loop_factory_->GetMessageLoop(MessageLoopFactory::kPipeline); - pipeline_ = Pipeline::Create(pipeline_message_loop, media_log_); + pipeline_ = Pipeline::Create(window, pipeline_message_loop, media_log_); // Also we want to be notified of |main_loop_| destruction. main_loop_->AddDestructionObserver(this); @@ -173,8 +174,11 @@ #endif // defined(COBALT_USE_PUNCHOUT) if (video_frame_provider_) { - media_time_cb_ = base::Bind(&Pipeline::GetMediaTime, pipeline_); - video_frame_provider_->RegisterMediaTimeCB(media_time_cb_); + media_time_and_seeking_state_cb_ = + base::Bind(&WebMediaPlayerImpl::GetMediaTimeAndSeekingState, + base::Unretained(this)); + video_frame_provider_->RegisterMediaTimeAndSeekingStateCB( + media_time_and_seeking_state_cb_); } if (delegate_) { delegate_->RegisterPlayer(this); @@ -189,9 +193,10 @@ } if (video_frame_provider_) { - DCHECK(!media_time_cb_.is_null()); - video_frame_provider_->UnregisterMediaTimeCB(media_time_cb_); - media_time_cb_.Reset(); + DCHECK(!media_time_and_seeking_state_cb_.is_null()); + video_frame_provider_->UnregisterMediaTimeAndSeekingStateCB( + media_time_and_seeking_state_cb_); + media_time_and_seeking_state_cb_.Reset(); } #if defined(__LB_ANDROID__) @@ -842,6 +847,12 @@ return e; } +WebMediaPlayerImpl::SetBoundsCB WebMediaPlayerImpl::GetSetBoundsCB() { + // |pipeline_| is always valid during WebMediaPlayerImpl's life time. It is + // also reference counted so it lives after WebMediaPlayerImpl is destroyed. + return pipeline_->GetSetBoundsCB(); +} + WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::CancelKeyRequestInternal( const std::string& key_system, const std::string& session_id) { @@ -1164,6 +1175,15 @@ } } +void WebMediaPlayerImpl::GetMediaTimeAndSeekingState( + base::TimeDelta* media_time, + bool* is_seeking) const { + DCHECK(media_time); + DCHECK(is_seeking); + *media_time = pipeline_->GetMediaTime(); + *is_seeking = state_.seeking; +} + WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() { DCHECK_EQ(main_loop_, MessageLoop::current()); DCHECK(client_);
diff --git a/src/media/player/web_media_player_impl.h b/src/media/player/web_media_player_impl.h index 7ef4c56..fcc1030 100644 --- a/src/media/player/web_media_player_impl.h +++ b/src/media/player/web_media_player_impl.h
@@ -69,11 +69,12 @@ #include "ui/gfx/size.h" #if defined(OS_STARBOARD) + #if SB_HAS(PLAYER) -#define COBALT_USE_SBPLAYER_PIPELINE #define COBALT_USE_PUNCHOUT #define COBALT_SKIP_SEEK_REQUEST_NEAR_END #endif // SB_HAS(PLAYER) + #endif // defined(OS_STARBOARD) namespace media { @@ -106,6 +107,7 @@ // |audio_renderer_sink| arguments should be the same object. WebMediaPlayerImpl( + PipelineWindow window, WebMediaPlayerClient* client, WebMediaPlayerDelegate* delegate, const scoped_refptr<ShellVideoFrameProvider>& video_frame_provider, @@ -203,6 +205,8 @@ MediaKeyException CancelKeyRequest(const std::string& key_system, const std::string& session_id) OVERRIDE; + SetBoundsCB GetSetBoundsCB() OVERRIDE; + // As we are closing the tab or even the browser, |main_loop_| is destroyed // even before this object gets destructed, so we need to know when // |main_loop_| is being destroyed and we can stop posting repaint task @@ -247,6 +251,9 @@ // Destroy resources held. void Destroy(); + void GetMediaTimeAndSeekingState(base::TimeDelta* media_time, + bool* is_seeking) const; + // Getter method to |client_|. WebMediaPlayerClient* GetClient(); @@ -365,7 +372,8 @@ // unimportant. bool suppress_destruction_errors_; - base::Callback<base::TimeDelta()> media_time_cb_; + base::Callback<void(base::TimeDelta*, bool*)> + media_time_and_seeking_state_cb_; DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImpl); };
diff --git a/src/starboard/client_porting/poem/abs_tests.cc b/src/starboard/client_porting/poem/abs_tests.cc index cfcc8c1..c4f2289 100644 --- a/src/starboard/client_porting/poem/abs_tests.cc +++ b/src/starboard/client_porting/poem/abs_tests.cc
@@ -16,10 +16,7 @@ #include "testing/gtest/include/gtest/gtest.h" -#ifndef POEM_FULL_EMULATION -#define POEM_FULL_EMULATION (1) #include "starboard/client_porting/poem/stdlib_poem.h" -#endif namespace starboard { namespace nplb {
diff --git a/src/starboard/client_porting/poem/eztime_poem.h b/src/starboard/client_porting/poem/eztime_poem.h index 280b0c6..31d301a 100644 --- a/src/starboard/client_porting/poem/eztime_poem.h +++ b/src/starboard/client_porting/poem/eztime_poem.h
@@ -18,10 +18,12 @@ #ifndef STARBOARD_CLIENT_PORTING_POEM_EZTIME_POEM_H_ #define STARBOARD_CLIENT_PORTING_POEM_EZTIME_POEM_H_ -// #if defined(POEM_FULL_EMULATION) && (POEM_FULL_EMULATION) +#if defined(STARBOARD) #include "starboard/client_porting/eztime/eztime.h" +#if !defined(POEM_NO_EMULATION) + #undef time_t #define time_t EzTimeT @@ -39,6 +41,8 @@ #define timegm(x) EzTimeTImplodeUTC(x) #define timelocal(x) EzTimeTImplodeLocal(x) -// #endif // POEM_FULL_EMULATION +#endif // POEM_NO_EMULATION + +#endif // STARBOARD #endif // STARBOARD_CLIENT_PORTING_POEM_EZTIME_POEM_H_
diff --git a/src/starboard/client_porting/poem/stdio_poem.h b/src/starboard/client_porting/poem/stdio_poem.h index 761dff1..2500568 100644 --- a/src/starboard/client_porting/poem/stdio_poem.h +++ b/src/starboard/client_porting/poem/stdio_poem.h
@@ -17,9 +17,12 @@ #ifndef STARBOARD_CLIENT_PORTING_POEM_STDIO_POEM_H_ #define STARBOARD_CLIENT_PORTING_POEM_STDIO_POEM_H_ -#if defined(POEM_FULL_EMULATION) && (POEM_FULL_EMULATION) +#if defined(STARBOARD) + +#if !defined(POEM_NO_EMULATION) #include "starboard/string.h" +#include "starboard/memory.h" #define wcsncmp(s1, s2, c) SbStringCompareWide(s1, s2, c) @@ -31,7 +34,12 @@ #define sprintf SbStringFormatUnsafeF #define vsscanf SbStringScan #define sscanf SbStringScanF +#define malloc(sz) SbMemoryAllocateUnchecked(sz) +#define free(a) SbMemoryFree(a) +#define realloc(m, sz) SbMemoryReallocateUnchecked(m, sz) -#endif // POEM_FULL_EMULATION +#endif // POEM_NO_EMULATION + +#endif // STARBOARD #endif // STARBOARD_CLIENT_PORTING_POEM_STDIO_POEM_H_
diff --git a/src/starboard/client_porting/poem/stdlib_poem.h b/src/starboard/client_porting/poem/stdlib_poem.h index 54c028b..ac0a4ae 100644 --- a/src/starboard/client_porting/poem/stdlib_poem.h +++ b/src/starboard/client_porting/poem/stdlib_poem.h
@@ -17,15 +17,25 @@ #ifndef STARBOARD_CLIENT_PORTING_POEM_STDLIB_POEM_H_ #define STARBOARD_CLIENT_PORTING_POEM_STDLIB_POEM_H_ +#if defined(STARBOARD) + #include "starboard/configuration.h" -SB_C_INLINE int PoemAbs(int x) { +#ifdef __cplusplus +extern "C" { +#endif + +static SB_C_INLINE int PoemAbs(int x) { if (x < 0) return -x; return x; } -#if defined(POEM_FULL_EMULATION) && (POEM_FULL_EMULATION) +#ifdef __cplusplus +} // extern "C" +#endif + +#if !defined(POEM_NO_EMULATION) #include "starboard/string.h" #include "starboard/system.h" @@ -43,6 +53,8 @@ #define abs(x) PoemAbs(x) -#endif // POEM_FULL_EMULATION +#endif // POEM_NO_EMULATION + +#endif // STARBOARD #endif // STARBOARD_CLIENT_PORTING_POEM_STDLIB_POEM_H_
diff --git a/src/starboard/client_porting/poem/string_poem.h b/src/starboard/client_porting/poem/string_poem.h index 1d9ee57..97ef46d 100644 --- a/src/starboard/client_porting/poem/string_poem.h +++ b/src/starboard/client_porting/poem/string_poem.h
@@ -17,9 +17,17 @@ #ifndef STARBOARD_CLIENT_PORTING_POEM_STRING_POEM_H_ #define STARBOARD_CLIENT_PORTING_POEM_STRING_POEM_H_ +#if defined(STARBOARD) + #include "starboard/string.h" +#include "starboard/memory.h" #ifdef __cplusplus + +// declaring the following 4 functions static inline is not necessary in C++ +// see: +// http://stackoverflow.com/questions/10847176/should-i-define-static-inline-methods-in-header-file + // Finds the last occurrence of |character| in |str|, returning a pointer to // the found character in the given string, or NULL if not found. // Meant to be a drop-in replacement for strchr, C++ signature @@ -59,7 +67,7 @@ // Finds the first occurrence of |character| in |str|, returning a pointer to // the found character in the given string, or NULL if not found. // Meant to be a drop-in replacement for strchr -SB_C_INLINE char* PoemFindCharacter(const char* str, int character) { +static SB_C_INLINE char* PoemFindCharacter(const char* str, int character) { // C-style cast used for C code return (char*)(SbStringFindCharacter(str, character)); } @@ -67,18 +75,22 @@ // Finds the last occurrence of |character| in |str|, returning a pointer to // the found character in the given string, or NULL if not found. // Meant to be a drop-in replacement for strchr -SB_C_INLINE char* PoemFindLastCharacter(const char* str, int character) { +static SB_C_INLINE char* PoemFindLastCharacter(const char* str, int character) { // C-style cast used for C code return (char*)(SbStringFindLastCharacter(str, character)); } #endif +#ifdef __cplusplus +extern "C" { +#endif + // Concatenates |source| onto the end of |out_destination|, presuming it has // |destination_size| total characters of storage available. Returns // |out_destination|. This method is a drop-in replacement for strncat -SB_C_INLINE char* PoemConcat(char* out_destination, - const char* source, - int destination_size) { +static SB_C_INLINE char* PoemConcat(char* out_destination, + const char* source, + int destination_size) { SbStringConcat(out_destination, source, destination_size); return out_destination; } @@ -91,7 +103,11 @@ return PoemConcat(out_destination, source, INT_MAX); } -#if defined(POEM_FULL_EMULATION) && (POEM_FULL_EMULATION) +#ifdef __cplusplus +} // extern "C" +#endif + +#if !defined(POEM_NO_EMULATION) #define strlen(s) SbStringGetLength(s) #define strcpy(o, s) SbStringCopyUnsafe(o, s) @@ -105,6 +121,11 @@ #define strncmp(s1, s2, c) SbStringCompare(s1, s2, c) #define strcmp(s1, s2) SbStringCompareAll(s1, s2) +#define memset(s, c, n) SbMemorySet(s, c, n) +#define memcpy(d, s, c) SbMemoryCopy(d, s, c) +#define memcmp(s1, s2, n) SbMemoryCompare(s1, s2, n) +#define memmove(d, s, n) SbMemoryMove(d, s, n) + // number conversion functions #define strtol(s, o, b) SbStringParseSignedInteger(s, o, b) #define atoi(v) SbStringAToI(v) @@ -114,6 +135,8 @@ #define strtoull(s, o, b) SbStringParseUInt64(s, o, b) #define strtod(s, o) SbStringParseDouble(s, o) -#endif // POEM_FULL_EMULATION +#endif // POEM_NO_EMULATION + +#endif // STARBOARD #endif // STARBOARD_CLIENT_PORTING_POEM_STRING_POEM_H_
diff --git a/src/starboard/client_porting/poem/strings_poem.h b/src/starboard/client_porting/poem/strings_poem.h index 9c77d36..8f98a06 100644 --- a/src/starboard/client_porting/poem/strings_poem.h +++ b/src/starboard/client_porting/poem/strings_poem.h
@@ -17,13 +17,17 @@ #ifndef STARBOARD_CLIENT_PORTING_POEM_STRINGS_POEM_H_ #define STARBOARD_CLIENT_PORTING_POEM_STRINGS_POEM_H_ -#if defined(POEM_FULL_EMULATION) && (POEM_FULL_EMULATION) +#if defined(STARBOARD) + +#if !defined(POEM_NO_EMULATION) #include "starboard/string.h" #define strcasecmp(s1, s2) SbStringCompareNoCase(s1, s2) #define strncasecmp(s1, s2) SbStringCompareNoCaseN(s1, s2) -#endif // POEM_FULL_EMULATION +#endif // POEM_NO_EMULATION + +#endif // STARBOARD #endif // STARBOARD_CLIENT_PORTING_POEM_STRINGS_POEM_H_
diff --git a/src/starboard/client_porting/poem/wchar_poem.h b/src/starboard/client_porting/poem/wchar_poem.h index c72ec8d..68af73c 100644 --- a/src/starboard/client_porting/poem/wchar_poem.h +++ b/src/starboard/client_porting/poem/wchar_poem.h
@@ -17,13 +17,17 @@ #ifndef STARBOARD_CLIENT_PORTING_POEM_WCHAR_POEM_H_ #define STARBOARD_CLIENT_PORTING_POEM_WCHAR_POEM_H_ -#if defined(POEM_FULL_EMULATION) && (POEM_FULL_EMULATION) +#if defined(STARBOARD) + +#if !defined(POEM_NO_EMULATION) #include "starboard/string.h" #define vswprintf SbStringFormatWide #define wcsncmp(s1, s2, c) SbStringCompareWide(s1, s2, c) -#endif // POEM_FULL_EMULATION +#endif // POEM_NO_EMULATION + +#endif // STARBOARD #endif // STARBOARD_CLIENT_PORTING_POEM_WCHAR_POEM_H_
diff --git a/src/starboard/linux/x64directfb/starboard_platform.gyp b/src/starboard/linux/x64directfb/starboard_platform.gyp index 9d95ec2..25c0476 100644 --- a/src/starboard/linux/x64directfb/starboard_platform.gyp +++ b/src/starboard/linux/x64directfb/starboard_platform.gyp
@@ -276,6 +276,7 @@ '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc', '<(DEPTH)/starboard/shared/starboard/player/player_internal.h', '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc', '<(DEPTH)/starboard/shared/starboard/player/player_set_pause.cc', '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc', '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
diff --git a/src/starboard/linux/x64x11/starboard_platform.gyp b/src/starboard/linux/x64x11/starboard_platform.gyp index 932e00e..65712a4 100644 --- a/src/starboard/linux/x64x11/starboard_platform.gyp +++ b/src/starboard/linux/x64x11/starboard_platform.gyp
@@ -237,6 +237,7 @@ '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc', '<(DEPTH)/starboard/shared/starboard/player/player_internal.h', '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc', '<(DEPTH)/starboard/shared/starboard/player/player_set_pause.cc', '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc', '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
diff --git a/src/starboard/nplb/memory_map_test.cc b/src/starboard/nplb/memory_map_test.cc index 53ccd50..52cdc1c 100644 --- a/src/starboard/nplb/memory_map_test.cc +++ b/src/starboard/nplb/memory_map_test.cc
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <algorithm> + #include "starboard/memory.h" #include "testing/gtest/include/gtest/gtest.h" @@ -57,10 +59,21 @@ void* memory = SbMemoryMap(bytes_mapped, kSbMemoryMapProtectWrite, "test"); ASSERT_NE(kFailed, memory); - // Force page commit. + // If this is the last iteration of the loop, then force a page commit for + // every single page. For any other iteration, force a page commit for + // roughly 1000 of the pages. + bool last_iteration = + !(total_bytes_mapped + bytes_mapped < SbSystemGetTotalCPUMemory() * 4); uint8_t* first_page = static_cast<uint8_t*>(memory); + const size_t page_increment_factor = + (last_iteration) + ? size_t(1u) + : std::max(static_cast<size_t>(bytes_mapped / + (1000 * SB_MEMORY_PAGE_SIZE)), + size_t(1u)); + for (uint8_t* page = first_page; page < first_page + bytes_mapped; - page += SB_MEMORY_PAGE_SIZE) { + page += SB_MEMORY_PAGE_SIZE * page_increment_factor) { *page = 0x55; }
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc index a3ead76..d81c51c 100644 --- a/src/starboard/nplb/player_create_test.cc +++ b/src/starboard/nplb/player_create_test.cc
@@ -13,6 +13,8 @@ // limitations under the License. #include "starboard/player.h" + +#include "starboard/window.h" #include "testing/gtest/include/gtest/gtest.h" #if SB_HAS(PLAYER) @@ -21,6 +23,12 @@ namespace nplb { TEST(SbPlayerTest, SunnyDay) { + SbWindowOptions window_options; + SbWindowSetDefaultOptions(&window_options); + + SbWindow window = SbWindowCreate(&window_options); + EXPECT_TRUE(SbWindowIsValid(window)); + SbMediaAudioHeader audio_header; audio_header.format_tag = 0xff; @@ -33,11 +41,13 @@ audio_header.number_of_channels * audio_header.bits_per_sample / 8; - SbPlayer player = SbPlayerCreate( - kSbMediaVideoCodecH264, kSbMediaAudioCodecAac, SB_PLAYER_NO_DURATION, - kSbDrmSystemInvalid, &audio_header, NULL, NULL, NULL, NULL); + SbPlayer player = + SbPlayerCreate(window, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac, + SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, &audio_header, + NULL, NULL, NULL, NULL); EXPECT_TRUE(SbPlayerIsValid(player)); SbPlayerDestroy(player); + SbWindowDestroy(window); } } // namespace nplb
diff --git a/src/starboard/nplb/socket_is_connected_and_idle_test.cc b/src/starboard/nplb/socket_is_connected_and_idle_test.cc index ffe83f5..74a082c 100644 --- a/src/starboard/nplb/socket_is_connected_and_idle_test.cc +++ b/src/starboard/nplb/socket_is_connected_and_idle_test.cc
@@ -22,6 +22,16 @@ namespace nplb { namespace { +bool IsNonIdleWithin(SbSocket socket, SbTimeMonotonic timeout) { + SbTimeMonotonic deadline = SbTimeGetMonotonicNow() + timeout; + while (SbTimeGetMonotonicNow() < deadline) { + if (!SbSocketIsConnectedAndIdle(socket)) { + return true; + } + } + return false; +} + TEST(SbSocketIsConnectedAndIdleTest, RainyDayInvalidSocket) { EXPECT_FALSE(SbSocketIsConnectedAndIdle(kSbSocketInvalid)); } @@ -39,15 +49,14 @@ char buf[512] = {0}; SbSocketSendTo(trio.server_socket, buf, sizeof(buf), NULL); - // Because I haven't called ReceiveFrom yet, this should stay false. The wait - // before the checking is for the delay on some platforms. - SbThreadSleep(kSocketTimeout); - EXPECT_FALSE(SbSocketIsConnectedAndIdle(trio.client_socket)); + // It may take some time after this call for trio.client_socket to show up as + // having new data available, even though it is a loopback connection, so we + // give it a grace period. + EXPECT_TRUE(IsNonIdleWithin(trio.client_socket, kSocketTimeout)); // Should be the same in the other direction. SbSocketSendTo(trio.client_socket, buf, sizeof(buf), NULL); - SbThreadSleep(kSocketTimeout); - EXPECT_FALSE(SbSocketIsConnectedAndIdle(trio.server_socket)); + EXPECT_TRUE(IsNonIdleWithin(trio.server_socket, kSocketTimeout)); EXPECT_TRUE(SbSocketDestroy(trio.server_socket)); EXPECT_TRUE(SbSocketDestroy(trio.client_socket)); @@ -60,9 +69,7 @@ return; } - SbThreadSleep(kSocketTimeout); - EXPECT_FALSE(SbSocketIsConnectedAndIdle(socket)); - + EXPECT_TRUE(IsNonIdleWithin(socket, kSocketTimeout)); EXPECT_TRUE(SbSocketDestroy(socket)); }
diff --git a/src/starboard/nplb/socket_waiter_wake_up_test.cc b/src/starboard/nplb/socket_waiter_wake_up_test.cc index 3ab0f6b..4ae3a42 100644 --- a/src/starboard/nplb/socket_waiter_wake_up_test.cc +++ b/src/starboard/nplb/socket_waiter_wake_up_test.cc
@@ -13,6 +13,7 @@ // limitations under the License. #include "starboard/nplb/socket_helpers.h" +#include "starboard/nplb/thread_helpers.h" #include "starboard/socket_waiter.h" #include "starboard/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,6 +25,11 @@ const int kMultiple = 5; const int kTrials = 8; +struct WakeUpContext { + SbSocketWaiter waiter; + Semaphore semaphore; +}; + void* WakeUpEntryPoint(void* context) { SbSocketWaiter waiter = reinterpret_cast<SbSocketWaiter>(context); SbSocketWaiterWakeUp(waiter); @@ -31,7 +37,9 @@ } void* WakeUpSleepEntryPoint(void* context) { - SbSocketWaiter waiter = reinterpret_cast<SbSocketWaiter>(context); + WakeUpContext* wake_up_context = reinterpret_cast<WakeUpContext*>(context); + SbSocketWaiter waiter = wake_up_context->waiter; + wake_up_context->semaphore.Take(); SbThreadSleep(kSocketTimeout); SbSocketWaiterWakeUp(waiter); return NULL; @@ -113,9 +121,16 @@ for (int i = 0; i < kTrials; ++i) { SbSocketWaiter waiter = SbSocketWaiterCreate(); EXPECT_TRUE(SbSocketWaiterIsValid(waiter)); + WakeUpContext context; + context.waiter = waiter; - SbThread thread = Spawn(waiter, &WakeUpSleepEntryPoint); - WaitShouldBlockBetween(waiter, kSocketTimeout, kSocketTimeout * 2); + SbThread thread = Spawn(&context, &WakeUpSleepEntryPoint); + SbTimeMonotonic start = SbTimeGetMonotonicNow(); + context.semaphore.Put(); + TimedWait(waiter); + SbTimeMonotonic duration = SbTimeGetMonotonicNow() - start; + EXPECT_GT(kSocketTimeout * 2, duration); + EXPECT_LE(kSocketTimeout, duration); Join(thread); EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
diff --git a/src/starboard/player.h b/src/starboard/player.h index f340312..c897cc7 100644 --- a/src/starboard/player.h +++ b/src/starboard/player.h
@@ -25,6 +25,7 @@ #include "starboard/export.h" #include "starboard/media.h" #include "starboard/types.h" +#include "starboard/window.h" #ifdef __cplusplus extern "C" { @@ -159,12 +160,15 @@ // --- Functions ------------------------------------------------------------- -// Creates a player for the specified |video_codec| and |audio_codec|, acquiring -// all resources needed to operate it, and returning an opaque handle to it. If -// |video_codec| is kSbMediaVideoCodecNone, the player is an audio-only -// player. Otherwise, the player is an audio/video decoder. |audio_codec| should -// never be kSbMediaAudioCodecNone. The expectation is that a new player will be -// created and destroyed for every playback. +// Creates a player that will be displayed on |window| for the specified +// |video_codec| and |audio_codec|, acquiring all resources needed to operate +// it, and returning an opaque handle to it. |window| can be kSbWindowInvalid +// for platforms where video will be only displayed on a particular window +// which the underlying implementation already has access to. If |video_codec| +// is kSbMediaVideoCodecNone, the player is an audio-only player. Otherwise, the +// player is an audio/video decoder. |audio_codec| should never be +// kSbMediaAudioCodecNone. The expectation is that a new player will be created +// and destroyed for every playback. // // |duration_pts| is the expected media duration in 90KHz ticks (PTS). It may be // set to SB_PLAYER_NO_DURATION for live streams. @@ -202,7 +206,8 @@ // simultaneously, then calls made to this function that attempt to exceed that // limit will return kSbPlayerInvalid. SB_EXPORT SbPlayer -SbPlayerCreate(SbMediaVideoCodec video_codec, +SbPlayerCreate(SbWindow window, + SbMediaVideoCodec video_codec, SbMediaAudioCodec audio_codec, SbMediaTime duration_pts, SbDrmSystem drm_system, @@ -278,7 +283,9 @@ #if SB_IS(PLAYER_PUNCHED_OUT) // Sets the player bounds to the given graphics plane coordinates. Will not take // effect until the next graphics frame buffer swap. The default bounds for a -// player are the full screen. +// player are the full screen. This function should be expected to be called up +// to once per frame, so implementors should take care to avoid related +// performance concerns with such frequent calls. SB_EXPORT void SbPlayerSetBounds(SbPlayer player, int x, int y,
diff --git a/src/starboard/raspi/1/gyp_configuration.gypi b/src/starboard/raspi/1/gyp_configuration.gypi index 67a0ee9..12a59f3 100644 --- a/src/starboard/raspi/1/gyp_configuration.gypi +++ b/src/starboard/raspi/1/gyp_configuration.gypi
@@ -23,6 +23,13 @@ 'gl_type': 'system_gles2', 'image_cache_size_in_bytes': 32 * 1024 * 1024, + # VideoCore's tiled renderer will do a render for every tile of a render + # target even if only part of that target was rendered to. Since the + # scratch surface cache is designed to choose large offscreen surfaces so + # that they can be maximally reused, it is not a very good fit for a tiled + # renderer. + 'scratch_surface_cache_size_in_bytes' : 0, + # This should have a default value in cobalt/base.gypi. See the comment # there for acceptable values for this variable. 'javascript_engine': 'javascriptcore',
diff --git a/src/starboard/raspi/1/starboard_platform.gyp b/src/starboard/raspi/1/starboard_platform.gyp index d1b864c..d5b6b23 100644 --- a/src/starboard/raspi/1/starboard_platform.gyp +++ b/src/starboard/raspi/1/starboard_platform.gyp
@@ -244,6 +244,7 @@ '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc', '<(DEPTH)/starboard/shared/starboard/player/player_internal.h', '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc', '<(DEPTH)/starboard/shared/starboard/player/player_set_pause.cc', '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc', '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc index be5a4ba..00e98e6 100644 --- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc +++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
@@ -194,7 +194,13 @@ // static AudioDecoder* AudioDecoder::Create(SbMediaAudioCodec audio_codec, const SbMediaAudioHeader& audio_header) { - return new ffmpeg::AudioDecoder(audio_codec, audio_header); + ffmpeg::AudioDecoder* decoder = + new ffmpeg::AudioDecoder(audio_codec, audio_header); + if (decoder->is_valid()) { + return decoder; + } + delete decoder; + return NULL; } } // namespace player
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h index dc24f99..eac65a2 100644 --- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h +++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -40,6 +40,8 @@ void Reset() SB_OVERRIDE; int GetSamplesPerSecond() SB_OVERRIDE; + bool is_valid() const { return codec_context_ != NULL; } + private: void InitializeCodec(); void TeardownCodec();
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc index 5174f70..6c7aec8 100644 --- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc +++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -293,7 +293,12 @@ // static VideoDecoder* VideoDecoder::Create(SbMediaVideoCodec video_codec) { - return new ffmpeg::VideoDecoder(video_codec); + ffmpeg::VideoDecoder* decoder = new ffmpeg::VideoDecoder(video_codec); + if (decoder->is_valid()) { + return decoder; + } + delete decoder; + return NULL; } } // namespace player
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h index 1a78aba..6bcd6d3 100644 --- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h +++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
@@ -42,6 +42,8 @@ void WriteEndOfStream() SB_OVERRIDE; void Reset() SB_OVERRIDE; + bool is_valid() const { return codec_context_ != NULL; } + private: enum EventType { kInvalid,
diff --git a/src/starboard/shared/starboard/application.cc b/src/starboard/shared/starboard/application.cc index 1482b1a..b7ca314 100644 --- a/src/starboard/shared/starboard/application.cc +++ b/src/starboard/shared/starboard/application.cc
@@ -18,6 +18,8 @@ #include "starboard/condition_variable.h" #include "starboard/event.h" #include "starboard/log.h" +#include "starboard/memory.h" +#include "starboard/string.h" namespace starboard { namespace shared { @@ -39,11 +41,11 @@ } // Dispatches a Start event to the system event handler. -void DispatchStart(int argc, char** argv) { +void DispatchStart(int argc, char** argv, const char* link) { SbEventStartData start_data; start_data.argument_values = argv; start_data.argument_count = argc; - start_data.link = NULL; + start_data.link = link; Dispatch(kSbEventTypeStart, &start_data, NULL); } @@ -54,7 +56,8 @@ Application* Application::g_instance = NULL; -Application::Application() : error_level_(0), thread_(SbThreadGetCurrent()) { +Application::Application() + : error_level_(0), thread_(SbThreadGetCurrent()), start_link_(NULL) { Application* old_instance = reinterpret_cast<Application*>(SbAtomicAcquire_CompareAndSwapPtr( reinterpret_cast<SbAtomicPtr*>(&g_instance), @@ -71,11 +74,12 @@ reinterpret_cast<SbAtomicPtr>(NULL))); SB_DCHECK(old_instance); SB_DCHECK(old_instance == this); + SbMemoryFree(start_link_); } int Application::Run(int argc, char** argv) { Initialize(); - DispatchStart(argc, argv); + DispatchStart(argc, argv, start_link_); for (;;) { if (!DispatchAndDelete(GetNextEvent())) { @@ -105,8 +109,24 @@ CancelTimedEvent(id); } -void Application::HandleFrame(const player::VideoFrame& frame) { - AcceptFrame(frame); +#if SB_HAS(PLAYER) && SB_IS(PLAYER_PUNCHED_OUT) +void Application::HandleFrame(SbPlayer player, + const player::VideoFrame& frame, + int x, + int y, + int width, + int height) { + AcceptFrame(player, frame, x, y, width, height); +} +#endif // SB_HAS(PLAYER) && SB_IS(PLAYER_PUNCHED_OUT) + +void Application::SetStartLink(const char* start_link) { + SbMemoryFree(start_link_); + if (start_link) { + start_link_ = SbStringDuplicate(start_link); + } else { + start_link_ = NULL; + } } bool Application::DispatchAndDelete(Application::Event* event) {
diff --git a/src/starboard/shared/starboard/application.h b/src/starboard/shared/starboard/application.h index 730f8da..2c990a0 100644 --- a/src/starboard/shared/starboard/application.h +++ b/src/starboard/shared/starboard/application.h
@@ -22,11 +22,13 @@ #include "starboard/condition_variable.h" #include "starboard/event.h" #include "starboard/log.h" +#include "starboard/player.h" #include "starboard/shared/internal_only.h" #include "starboard/shared/starboard/player/video_frame_internal.h" #include "starboard/thread.h" #include "starboard/time.h" #include "starboard/types.h" +#include "starboard/window.h" namespace starboard { namespace shared { @@ -62,6 +64,13 @@ delete static_cast<T*>(value); } + // Destructor function that deletes the value as an array of the + // parameterized type. + template <typename T> + static void DeleteArrayDestructor(void* value) { + delete[] static_cast<T*>(value); + } + // A Starboard event and its destructor. Takes ownership of the event, thus // deleting the event and calling the destructor on its data when it is // deleted. @@ -125,10 +134,17 @@ // external thread. void Cancel(SbEventId id); - // Handles receiving a new video frame from the media system. Only used when - // the application needs to composite video frames with punch-out video - // manually (should be rare). Will be called from an external thread. - void HandleFrame(const player::VideoFrame& frame); +#if SB_HAS(PLAYER) && SB_IS(PLAYER_PUNCHED_OUT) + // Handles receiving a new video frame of |player| from the media system. Only + // used when the application needs to composite video frames with punch-out + // video manually (should be rare). Will be called from an external thread. + void HandleFrame(SbPlayer player, + const player::VideoFrame& frame, + int x, + int y, + int width, + int height); +#endif // SB_HAS(PLAYER) && SB_IS(PLAYER_PUNCHED_OUT) protected: // Initializes any systems that need initialization before application @@ -141,9 +157,16 @@ // must be run after the application stop event is handled. virtual void Teardown() {} +#if SB_HAS(PLAYER) && SB_IS(PLAYER_PUNCHED_OUT) // Subclasses may override this method to accept video frames from the media // system. Will be called from an external thread. - virtual void AcceptFrame(const player::VideoFrame& frame) {} + virtual void AcceptFrame(SbPlayer player, + const player::VideoFrame& frame, + int x, + int y, + int width, + int height) {} +#endif // SB_HAS(PLAYER) && SB_IS(PLAYER_PUNCHED_OUT) // Blocks until the next event is available. Subclasses must implement this // method to provide events for the platform. Gives ownership to the caller. @@ -172,7 +195,11 @@ // kSbTimeMax if there are no queued TimedEvents. virtual SbTimeMonotonic GetNextTimedEventTargetTime() = 0; - bool IsCurrentThread() { + // Sets the launch deep link string, if any, which is passed in the start + // event that initializes and starts Cobalt. + void SetStartLink(const char* start_link); + + bool IsCurrentThread() const { return SbThreadIsEqual(thread_, SbThreadGetCurrent()); } @@ -191,6 +218,10 @@ // The thread that this application was created on, which is assumed to be the // main thread. SbThread thread_; + + // The deep link included in the Start event sent to Cobalt. Initially NULL, + // derived classes may set it during initialization using |SetStartLink|. + char* start_link_; }; } // namespace starboard
diff --git a/src/starboard/shared/starboard/player/audio_renderer_internal.cc b/src/starboard/shared/starboard/player/audio_renderer_internal.cc index d7fae73..4648f66 100644 --- a/src/starboard/shared/starboard/player/audio_renderer_internal.cc +++ b/src/starboard/shared/starboard/player/audio_renderer_internal.cc
@@ -63,7 +63,10 @@ SbMediaTime input_pts = input_buffer.pts(); std::vector<float> decoded_audio; decoder_->Decode(input_buffer, &decoded_audio); - SB_DCHECK(!decoded_audio.empty()); + if (decoded_audio.empty()) { + SB_DLOG(ERROR) << "decoded_audio contains no frames."; + return; + } ScopedLock lock(mutex_); if (seeking_) {
diff --git a/src/starboard/shared/starboard/player/player_create.cc b/src/starboard/shared/starboard/player/player_create.cc index aaf0539..8489cce 100644 --- a/src/starboard/shared/starboard/player/player_create.cc +++ b/src/starboard/shared/starboard/player/player_create.cc
@@ -17,7 +17,8 @@ #include "starboard/log.h" #include "starboard/shared/starboard/player/player_internal.h" -SbPlayer SbPlayerCreate(SbMediaVideoCodec video_codec, +SbPlayer SbPlayerCreate(SbWindow window, + SbMediaVideoCodec video_codec, SbMediaAudioCodec audio_codec, SbMediaTime duration_pts, SbDrmSystem drm_system, @@ -41,7 +42,7 @@ return kSbPlayerInvalid; } - return new SbPlayerPrivate(video_codec, audio_codec, duration_pts, drm_system, - audio_header, sample_deallocate_func, + return new SbPlayerPrivate(window, video_codec, audio_codec, duration_pts, + drm_system, audio_header, sample_deallocate_func, decoder_status_func, player_status_func, context); }
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc index 5090a84..e14da96 100644 --- a/src/starboard/shared/starboard/player/player_internal.cc +++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -29,6 +29,7 @@ } SbPlayerPrivate::SbPlayerPrivate( + SbWindow window, SbMediaVideoCodec video_codec, SbMediaAudioCodec audio_codec, SbMediaTime duration_pts, @@ -49,6 +50,7 @@ is_paused_(true), volume_(1.0), worker_(this, + window, video_codec, audio_codec, drm_system, @@ -90,6 +92,14 @@ worker_.EnqueueEvent(data); } +#if SB_IS(PLAYER_PUNCHED_OUT) +void SbPlayerPrivate::SetBounds(int x, int y, int width, int height) { + PlayerWorker::SetBoundsEventData data = {x, y, width, height}; + worker_.EnqueueEvent(data); + // TODO: Wait until a frame is rendered with the updated bounds. +} +#endif + void SbPlayerPrivate::GetInfo(SbPlayerInfo* out_player_info) { SB_DCHECK(out_player_info != NULL);
diff --git a/src/starboard/shared/starboard/player/player_internal.h b/src/starboard/shared/starboard/player/player_internal.h index 54cd1f8..58b350d 100644 --- a/src/starboard/shared/starboard/player/player_internal.h +++ b/src/starboard/shared/starboard/player/player_internal.h
@@ -20,12 +20,14 @@ #include "starboard/shared/internal_only.h" #include "starboard/shared/starboard/player/player_worker.h" #include "starboard/time.h" +#include "starboard/window.h" // TODO: Implement DRM support struct SbPlayerPrivate : starboard::shared::starboard::player::PlayerWorker::Host { public: - SbPlayerPrivate(SbMediaVideoCodec video_codec, + SbPlayerPrivate(SbWindow window, + SbMediaVideoCodec video_codec, SbMediaAudioCodec audio_codec, SbMediaTime duration_pts, SbDrmSystem drm_system, @@ -43,6 +45,10 @@ const SbMediaVideoSampleInfo* video_sample_info, const SbDrmSampleInfo* sample_drm_info); void WriteEndOfStream(SbMediaType stream_type); +#if SB_IS(PLAYER_PUNCHED_OUT) + void SetBounds(int x, int y, int width, int height); +#endif + void GetInfo(SbPlayerInfo* out_player_info); void SetPause(bool pause); void SetVolume(double volume);
diff --git a/src/starboard/shared/starboard/player/player_set_bounds.cc b/src/starboard/shared/starboard/player/player_set_bounds.cc new file mode 100644 index 0000000..660a9da --- /dev/null +++ b/src/starboard/shared/starboard/player/player_set_bounds.cc
@@ -0,0 +1,30 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/player.h" + +#include "starboard/log.h" +#include "starboard/shared/starboard/player/player_internal.h" + +#if SB_IS(PLAYER_PUNCHED_OUT) + +void SbPlayerSetBounds(SbPlayer player, int x, int y, int width, int height) { + if (!SbPlayerIsValid(player)) { + SB_DLOG(WARNING) << "player is invalid."; + return; + } + player->SetBounds(x, y, width, height); +} + +#endif // SB_IS(PLAYER_PUNCHED_OUT)
diff --git a/src/starboard/shared/starboard/player/player_worker.cc b/src/starboard/shared/starboard/player/player_worker.cc index cce730e..5bab32b 100644 --- a/src/starboard/shared/starboard/player/player_worker.cc +++ b/src/starboard/shared/starboard/player/player_worker.cc
@@ -29,6 +29,7 @@ namespace player { PlayerWorker::PlayerWorker(Host* host, + SbWindow window, SbMediaVideoCodec video_codec, SbMediaAudioCodec audio_codec, SbDrmSystem drm_system, @@ -38,6 +39,7 @@ SbPlayer player, void* context) : host_(host), + window_(window), video_codec_(video_codec), audio_codec_(audio_codec), drm_system_(drm_system), @@ -85,10 +87,12 @@ delete event; bool running = ProcessInitEvent(); + SetBoundsEventData bounds = {0, 0, 0, 0}; + while (running) { Event* event = queue_.GetTimed(kUpdateInterval); if (event == NULL) { - running &= ProcessUpdateEvent(); + running &= ProcessUpdateEvent(bounds); continue; } @@ -108,6 +112,9 @@ running &= ProcessWriteEndOfStreamEvent(event->data.write_end_of_stream); } else if (event->type == Event::kSetPause) { running &= ProcessSetPauseEvent(event->data.set_pause); + } else if (event->type == Event::kSetBounds) { + bounds = event->data.set_bounds; + ProcessUpdateEvent(bounds); } else if (event->type == Event::kStop) { ProcessStopEvent(); running = false; @@ -119,9 +126,19 @@ } bool PlayerWorker::ProcessInitEvent() { - audio_renderer_ = new AudioRenderer( - AudioDecoder::Create(audio_codec_, audio_header_), audio_header_); - video_renderer_ = new VideoRenderer(VideoDecoder::Create(video_codec_)); + AudioDecoder* audio_decoder = + AudioDecoder::Create(audio_codec_, audio_header_); + VideoDecoder* video_decoder = VideoDecoder::Create(video_codec_); + + if (!audio_decoder || !video_decoder) { + delete audio_decoder; + delete video_decoder; + UpdatePlayerState(kSbPlayerStateError); + return false; + } + + audio_renderer_ = new AudioRenderer(audio_decoder, audio_header_); + video_renderer_ = new VideoRenderer(video_decoder); if (audio_renderer_->is_valid() && video_renderer_->is_valid()) { UpdatePlayerState(kSbPlayerStateInitialized); return true; @@ -306,7 +323,7 @@ return true; } -bool PlayerWorker::ProcessUpdateEvent() { +bool PlayerWorker::ProcessUpdateEvent(const SetBoundsEventData& bounds) { SB_DCHECK(player_state_ != kSbPlayerStateDestroyed); if (player_state_ == kSbPlayerStatePrerolling || @@ -318,9 +335,9 @@ const VideoFrame& frame = video_renderer_->GetCurrentFrame(audio_renderer_->GetCurrentTime()); - // TODO: Properly paint the video frame. #if SB_IS(PLAYER_PUNCHED_OUT) - shared::starboard::Application::Get()->HandleFrame(frame); + shared::starboard::Application::Get()->HandleFrame( + player_, frame, bounds.x, bounds.y, bounds.width, bounds.height); #endif // SB_IS(PLAYER_PUNCHED_OUT) if (audio_decoder_state_ == kSbPlayerDecoderStateBufferFull && @@ -347,7 +364,8 @@ video_renderer_ = NULL; #if SB_IS(PLAYER_PUNCHED_OUT) // Clear the video frame as we terminate. - shared::starboard::Application::Get()->HandleFrame(VideoFrame()); + shared::starboard::Application::Get()->HandleFrame(player_, VideoFrame(), 0, + 0, 0, 0); #endif // SB_IS(PLAYER_PUNCHED_OUT) UpdatePlayerState(kSbPlayerStateDestroyed); }
diff --git a/src/starboard/shared/starboard/player/player_worker.h b/src/starboard/shared/starboard/player/player_worker.h index 1dca71d..843806f 100644 --- a/src/starboard/shared/starboard/player/player_worker.h +++ b/src/starboard/shared/starboard/player/player_worker.h
@@ -24,8 +24,8 @@ #include "starboard/shared/starboard/player/video_renderer_internal.h" #include "starboard/thread.h" #include "starboard/time.h" +#include "starboard/window.h" -// TODO: Implement DRM support namespace starboard { namespace shared { namespace starboard { @@ -59,6 +59,13 @@ bool pause; }; + struct SetBoundsEventData { + int x; + int y; + int width; + int height; + }; + struct Event { public: enum Type { @@ -67,6 +74,7 @@ kWriteSample, kWriteEndOfStream, kSetPause, + kSetBounds, kStop, }; @@ -75,6 +83,7 @@ WriteSampleEventData write_sample; WriteEndOfStreamEventData write_end_of_stream; SetPauseEventData set_pause; + SetBoundsEventData set_bounds; }; explicit Event(const SeekEventData& seek) : type(kSeek) { @@ -95,6 +104,10 @@ data.set_pause = set_pause; } + explicit Event(const SetBoundsEventData& set_bounds) : type(kSetBounds) { + data.set_bounds = set_bounds; + } + explicit Event(Type type) : type(type) { SB_DCHECK(type == kInit || type == kStop); } @@ -106,6 +119,7 @@ static const SbTime kUpdateInterval = 5 * kSbTimeMillisecond; PlayerWorker(Host* host, + SbWindow window, SbMediaVideoCodec video_codec, SbMediaAudioCodec audio_codec, SbDrmSystem drm_system, @@ -130,7 +144,7 @@ bool ProcessWriteSampleEvent(const WriteSampleEventData& data, bool* retry); bool ProcessWriteEndOfStreamEvent(const WriteEndOfStreamEventData& data); bool ProcessSetPauseEvent(const SetPauseEventData& data); - bool ProcessUpdateEvent(); + bool ProcessUpdateEvent(const SetBoundsEventData& bounds); void ProcessStopEvent(); void UpdateDecoderState(SbMediaType type); @@ -141,6 +155,7 @@ Host* host_; + SbWindow window_; SbMediaVideoCodec video_codec_; SbMediaAudioCodec audio_codec_; SbDrmSystem drm_system_;
diff --git a/src/starboard/shared/starboard/player/video_renderer_internal.cc b/src/starboard/shared/starboard/player/video_renderer_internal.cc index 03f7aee..da9ccf8 100644 --- a/src/starboard/shared/starboard/player/video_renderer_internal.cc +++ b/src/starboard/shared/starboard/player/video_renderer_internal.cc
@@ -62,13 +62,14 @@ void VideoRenderer::Seek(SbMediaTime seek_to_pts) { SB_DCHECK(seek_to_pts >= 0); + decoder_->Reset(); + ScopedLock lock(mutex_); seeking_to_pts_ = std::max<SbMediaTime>(seek_to_pts, 0); seeking_ = true; end_of_stream_written_ = false; - decoder_->Reset(); frames_.clear(); } @@ -81,11 +82,11 @@ } // Remove any frames with timestamps earlier than |media_time|, but always // keep at least one of the frames. - while (frames_.size() > 1 && frames_.begin()->pts() < media_time) { - frames_.erase(frames_.begin()); + while (frames_.size() > 1 && frames_.front().pts() < media_time) { + frames_.pop_front(); } - return *frames_.begin(); + return frames_.front(); } bool VideoRenderer::IsEndOfStreamPlayed() const {
diff --git a/src/starboard/shared/starboard/player/video_renderer_internal.h b/src/starboard/shared/starboard/player/video_renderer_internal.h index ef56cd6..f62cfda 100644 --- a/src/starboard/shared/starboard/player/video_renderer_internal.h +++ b/src/starboard/shared/starboard/player/video_renderer_internal.h
@@ -15,7 +15,7 @@ #ifndef STARBOARD_SHARED_STARBOARD_PLAYER_VIDEO_RENDERER_INTERNAL_H_ #define STARBOARD_SHARED_STARBOARD_PLAYER_VIDEO_RENDERER_INTERNAL_H_ -#include <vector> +#include <list> #include "starboard/log.h" #include "starboard/media.h" @@ -50,7 +50,7 @@ bool IsSeekingInProgress() const; private: - typedef std::vector<VideoFrame> Frames; + typedef std::list<VideoFrame> Frames; // Preroll considered finished after either kPrerollFrames is cached or EOS // is reached.
diff --git a/src/starboard/shared/starboard/thread_checker.h b/src/starboard/shared/starboard/thread_checker.h new file mode 100644 index 0000000..b1842ad --- /dev/null +++ b/src/starboard/shared/starboard/thread_checker.h
@@ -0,0 +1,69 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef STARBOARD_SHARED_STARBOARD_THREAD_CHECKER_H_ +#define STARBOARD_SHARED_STARBOARD_THREAD_CHECKER_H_ + +#include "starboard/atomic.h" +#include "starboard/thread.h" + +namespace starboard { +namespace shared { +namespace starboard { + +#if defined(COBALT_BUILD_TYPE_GOLD) + +class ThreadChecker { + public: + enum Type { kSetThreadIdOnCreation, kSetThreadIdOnFirstCheck }; + + explicit ThreadChecker(Type type = kSetThreadIdOnCreation) { + SB_UNREFERENCED_PARAMETER(type); + } + + bool CalledOnValidThread() const { return true; } +}; + +#else // defined(COBALT_BUILD_TYPE_GOLD) + +class ThreadChecker { + public: + enum Type { kSetThreadIdOnCreation, kSetThreadIdOnFirstCheck }; + + explicit ThreadChecker(Type type = kSetThreadIdOnCreation) { + if (type == kSetThreadIdOnCreation) + thread_id_ = SbThreadGetId(); + else + thread_id_ = kSbThreadInvalidId; + } + + bool CalledOnValidThread() const { + SbThreadId current_thread_id = SbThreadGetId(); + SbThreadId stored_thread_id = SbAtomicNoBarrier_CompareAndSwap( + &thread_id_, kSbThreadInvalidId, current_thread_id); + return stored_thread_id == kSbThreadInvalidId || + stored_thread_id == current_thread_id; + } + + private: + mutable SbThreadId thread_id_; +}; + +#endif // defined(COBALT_BUILD_TYPE_GOLD) + +} // namespace starboard +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_SHARED_STARBOARD_THREAD_CHECKER_H_
diff --git a/src/starboard/shared/stub/player_create.cc b/src/starboard/shared/stub/player_create.cc index e6be979..c9cb1e8 100644 --- a/src/starboard/shared/stub/player_create.cc +++ b/src/starboard/shared/stub/player_create.cc
@@ -14,7 +14,8 @@ #include "starboard/player.h" -SbPlayer SbPlayerCreate(SbMediaVideoCodec /*video_codec*/, +SbPlayer SbPlayerCreate(SbWindow /*window*/, + SbMediaVideoCodec /*video_codec*/, SbMediaAudioCodec /*audio_codec*/, SbMediaTime /*duration_pts*/, SbDrmSystem /*drm_system*/,
diff --git a/src/starboard/shared/stub/player_set_bounds.cc b/src/starboard/shared/stub/player_set_bounds.cc new file mode 100644 index 0000000..68a3c25 --- /dev/null +++ b/src/starboard/shared/stub/player_set_bounds.cc
@@ -0,0 +1,25 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/player.h" + +#if SB_IS(PLAYER_PUNCHED_OUT) + +void SbPlayerSetBounds(SbPlayer /*player*/, + int /*x*/, + int /*y*/, + int /*width*/, + int /*height*/) {} + +#endif // SB_IS(PLAYER_PUNCHED_OUT)
diff --git a/src/starboard/shared/x11/application_x11.cc b/src/starboard/shared/x11/application_x11.cc index 6225362..3499bd8 100644 --- a/src/starboard/shared/x11/application_x11.cc +++ b/src/starboard/shared/x11/application_x11.cc
@@ -681,7 +681,7 @@ ScopedLock lock(frame_mutex_); if (frame_written_) { // Clear the old frame, now that we are done with it. - frames_[frame_read_index_] = VideoFrame(); + frame_infos_[frame_read_index_].frame = VideoFrame(); // Increment the index to the next frame, which has been written. frame_read_index_ = (frame_read_index_ + 1) % kNumFrames; @@ -692,18 +692,26 @@ } index = frame_read_index_; } - if (!frames_[index].IsEndOfStream() && - frames_[index].format() != VideoFrame::kBGRA32) { - frames_[index] = frames_[index].ConvertTo(VideoFrame::kBGRA32); + FrameInfo& frame_info = frame_infos_[frame_read_index_]; + + if (!frame_info.frame.IsEndOfStream() && + frame_info.frame.format() != VideoFrame::kBGRA32) { + frame_info.frame = frame_info.frame.ConvertTo(VideoFrame::kBGRA32); } - window->Composite(&frames_[index]); + window->Composite(frame_info.x, frame_info.y, frame_info.width, + frame_info.height, &frame_info.frame); } } composite_event_id_ = SbEventSchedule(&CompositeCallback, this, kSbTimeSecond / 60); } -void ApplicationX11::AcceptFrame(const VideoFrame& frame) { +void ApplicationX11::AcceptFrame(SbPlayer player, + const VideoFrame& frame, + int x, + int y, + int width, + int height) { int write_index = -1; { ScopedLock lock(frame_mutex_); @@ -717,7 +725,11 @@ } // Copy the frame. - frames_[write_index] = frame; + frame_infos_[write_index].frame = frame; + frame_infos_[write_index].x = x; + frame_infos_[write_index].y = y; + frame_infos_[write_index].width = width; + frame_infos_[write_index].height = height; { ScopedLock lock(frame_mutex_);
diff --git a/src/starboard/shared/x11/application_x11.h b/src/starboard/shared/x11/application_x11.h index 6d017ba..4be2cab 100644 --- a/src/starboard/shared/x11/application_x11.h +++ b/src/starboard/shared/x11/application_x11.h
@@ -20,6 +20,7 @@ #include <vector> #include "starboard/configuration.h" +#include "starboard/player.h" #include "starboard/shared/internal_only.h" #include "starboard/shared/starboard/application.h" #include "starboard/shared/starboard/queue_application.h" @@ -47,8 +48,12 @@ void Composite(); protected: - void AcceptFrame(const shared::starboard::player::VideoFrame& frame) - SB_OVERRIDE; + void AcceptFrame(SbPlayer player, + const shared::starboard::player::VideoFrame& frame, + int x, + int y, + int width, + int height) SB_OVERRIDE; #endif // SB_IS(PLAYER_PUNCHED_OUT) protected: @@ -65,6 +70,16 @@ private: typedef std::vector<SbWindow> SbWindowVector; +#if SB_IS(PLAYER_PUNCHED_OUT) + struct FrameInfo { + shared::starboard::player::VideoFrame frame; + int x; + int y; + int width; + int height; + }; +#endif // SB_IS(PLAYER_PUNCHED_OUT) + // Ensures that X is up, display is populated and connected. void EnsureX(); @@ -87,7 +102,7 @@ int frame_read_index_; bool frame_written_; static const int kNumFrames = 2; - shared::starboard::player::VideoFrame frames_[kNumFrames]; + FrameInfo frame_infos_[kNumFrames]; #endif // SB_IS(PLAYER_PUNCHED_OUT) Display* display_;
diff --git a/src/starboard/shared/x11/window_internal.cc b/src/starboard/shared/x11/window_internal.cc index c330c30..3885bf6 100644 --- a/src/starboard/shared/x11/window_internal.cc +++ b/src/starboard/shared/x11/window_internal.cc
@@ -147,7 +147,11 @@ } #if SB_IS(PLAYER_PUNCHED_OUT) -void SbWindowPrivate::Composite(VideoFrame* frame) { +void SbWindowPrivate::Composite(int bounds_x, + int bounds_y, + int bounds_width, + int bounds_height, + VideoFrame* frame) { XSynchronize(display, True); XWindowAttributes window_attributes; XGetWindowAttributes(display, window, &window_attributes); @@ -227,10 +231,12 @@ // Initially assume we don't have to center or scale. int video_width = frame->width(); int video_height = frame->height(); - if (frame->width() != width || frame->height() != height) { + if (bounds_width != width || bounds_height != height || + frame->width() != width || frame->height() != height) { // Scale to fit the smallest dimension of the frame into the window. - double scale = std::min(width / static_cast<double>(frame->width()), - height / static_cast<double>(frame->height())); + double scale = + std::min(bounds_width / static_cast<double>(frame->width()), + bounds_height / static_cast<double>(frame->height())); // Center the scaled frame within the window. video_width = scale * frame->width(); video_height = scale * frame->height(); @@ -242,8 +248,8 @@ XRenderSetPictureTransform(display, video_picture, &transform); } - int dest_x = (width - video_width) / 2; - int dest_y = (height - video_height) / 2; + int dest_x = bounds_x + (bounds_width - video_width) / 2; + int dest_y = bounds_y + (bounds_height - video_height) / 2; XRenderComposite(display, PictOpSrc, video_picture, NULL, composition_picture, 0, 0, 0, 0, dest_x, dest_y, video_width, video_height);
diff --git a/src/starboard/shared/x11/window_internal.h b/src/starboard/shared/x11/window_internal.h index 00d3c01..7c55cb3 100644 --- a/src/starboard/shared/x11/window_internal.h +++ b/src/starboard/shared/x11/window_internal.h
@@ -35,8 +35,13 @@ #if SB_IS(PLAYER_PUNCHED_OUT) // Composites graphics and the given video frame video for this window. In // PLAYER_PUNCHED_OUT mode, this is the only way any graphics or video is - // presented in the window. - void Composite(::starboard::shared::starboard::player::VideoFrame* frame); + // presented in the window. The video frame will be rendered according to + // boundaries specified by the parameters. + void Composite(int bounds_x, + int bounds_y, + int bounds_width, + int bounds_height, + ::starboard::shared::starboard::player::VideoFrame* frame); // The cached XRender Picture that represents the window that is the // destination of the composition.
diff --git a/src/starboard/stub/starboard_platform.gyp b/src/starboard/stub/starboard_platform.gyp index e07af31..1ac0c82 100644 --- a/src/starboard/stub/starboard_platform.gyp +++ b/src/starboard/stub/starboard_platform.gyp
@@ -107,6 +107,7 @@ '<(DEPTH)/starboard/shared/stub/player_destroy.cc', '<(DEPTH)/starboard/shared/stub/player_get_info.cc', '<(DEPTH)/starboard/shared/stub/player_seek.cc', + '<(DEPTH)/starboard/shared/stub/player_set_bounds.cc', '<(DEPTH)/starboard/shared/stub/player_set_pause.cc', '<(DEPTH)/starboard/shared/stub/player_write_end_of_stream.cc', '<(DEPTH)/starboard/shared/stub/player_write_sample.cc',