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',