Add a basic sky-element custom element

After this CL, you can use <sky-element> to describe custom elements. The
current iteration is very basic and is hardcoded to "example", but its a start.

This CL renames the |init| function to |_init| to prevent importers from
calling it directly. Also, we now pass the <script> element to |_init| to give
some context.

R=ojan@chromium.org, eseidel@chromium.org

Review URL: https://codereview.chromium.org/950493003
This commit is contained in:
Adam Barth 2015-02-20 21:45:36 -08:00
parent 58bfcf3a8f
commit 16db84fde1
11 changed files with 157 additions and 15 deletions

View File

@ -115,8 +115,11 @@ void HTMLScriptRunner::executeLibrary(RefPtr<AbstractModule> module,
// or script failures, etc.
Microtask::performCheckpoint();
if (LocalFrame* frame = contextFrame(m_element.get()))
frame->dart().ExecuteLibraryInModule(module.get(), library->dart_value());
if (LocalFrame* frame = contextFrame(m_element.get())) {
frame->dart().ExecuteLibraryInModule(module.get(),
library->dart_value(),
m_element.get());
}
advanceTo(StateCompleted);
// We may be deleted at this point.

View File

@ -16,6 +16,7 @@
#include "sky/engine/core/app/Module.h"
#include "sky/engine/core/dom/Element.h"
#include "sky/engine/core/frame/LocalFrame.h"
#include "sky/engine/core/html/HTMLScriptElement.h"
#include "sky/engine/core/html/imports/HTMLImport.h"
#include "sky/engine/core/html/imports/HTMLImportChild.h"
#include "sky/engine/core/loader/FrameLoaderClient.h"
@ -29,6 +30,7 @@
#include "sky/engine/tonic/dart_gc_controller.h"
#include "sky/engine/tonic/dart_isolate_scope.h"
#include "sky/engine/tonic/dart_state.h"
#include "sky/engine/tonic/dart_wrappable.h"
#include "sky/engine/wtf/text/TextPosition.h"
namespace blink {
@ -122,7 +124,8 @@ void DartController::LoadScriptInModule(
}
void DartController::ExecuteLibraryInModule(AbstractModule* module,
Dart_Handle library) {
Dart_Handle library,
HTMLScriptElement* script) {
ASSERT(library);
DCHECK(Dart_CurrentIsolate() == dart_state()->isolate());
DartApiScope dart_api_scope;
@ -130,16 +133,23 @@ void DartController::ExecuteLibraryInModule(AbstractModule* module,
// Don't continue if we failed to load the module.
if (LogIfError(Dart_FinalizeLoading(true)))
return;
const char* name = module->isApplication() ? "main" : "init";
const char* name = module->isApplication() ? "main" : "_init";
// main() is required, but init() is not:
// TODO(rmacnak): Dart_LookupFunction won't find re-exports, etc.
Dart_Handle entry = Dart_LookupFunction(library, ToDart(name));
if (!Dart_IsFunction(entry) && !module->isApplication())
if (module->isApplication()) {
LogIfError(Dart_Invoke(library, ToDart(name), 0, nullptr));
return;
}
if (!Dart_IsFunction(entry))
return;
Dart_Handle result = Dart_Invoke(library, ToDart(name), 0, nullptr);
LogIfError(result);
Dart_Handle args[] = {
ToDart(script),
};
LogIfError(Dart_Invoke(library, ToDart(name), arraysize(args), args));
}
static void UnhandledExceptionCallback(Dart_Handle error) {

View File

@ -16,8 +16,9 @@ namespace blink {
class AbstractModule;
class BuiltinSky;
class DOMDartState;
class Document;
class DartValue;
class Document;
class HTMLScriptElement;
class DartController {
public:
@ -33,7 +34,9 @@ class DartController {
const String& source,
const TextPosition& textPosition,
const LoadFinishedCallback& load_finished_callback);
void ExecuteLibraryInModule(AbstractModule* module, Dart_Handle library);
void ExecuteLibraryInModule(AbstractModule* module,
Dart_Handle library,
HTMLScriptElement* script);
void ClearForClose();
void CreateIsolateFor(Document* document);

View File

@ -0,0 +1,25 @@
<!--
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-->
<import src="/sky/framework/sky-element.sky" />
<sky-element>
<template>
<style>
div {
height: 50px;
width: 50px;
background-color: green;
}
</style>
<div>Hi</div>
</template>
<script>
@Tagname('example')
class ExampleElement extends SkyElement {
}
_init(script) => register(script, ExampleElement);
</script>
</sky-element>

View File

@ -0,0 +1,16 @@
<!--
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-->
<sky>
<import src="example-element.sky" />
<style>
example {
border: 1px solid blue;
width: 100px;
height: 100px;
}
</style>
<example />
</sky>

85
framework/sky-element.sky Normal file
View File

@ -0,0 +1,85 @@
<!--
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-->
<script>
import "dart:mirrors";
import "dart:sky";
class _Registration {
Element template;
_Registration(this.template);
}
final Map<String, _Registration> _registery = new Map<String, _Registration>();
class Tagname {
final String name;
const Tagname(this.name);
}
String _getTagName(Type type) {
return reflectClass(type).metadata.firstWhere(
(i) => i.reflectee is Tagname).reflectee.name;
}
abstract class SkyElement extends Element {
// Override these functions to receive lifecycle notifications.
void created() {}
void attached() {}
void detached() {}
void attributeChanged(String attrName, String oldValue, String newValue) {}
void shadowRootReady() {}
String get tagName => _getTagName(runtimeType);
// TODO(abarth): Rather than hard-coding "example" here, we should make the
// bindings read the |tagName| property of this object during construction.
SkyElement() : super("example") {
created();
// Invoke attributeChanged callback when element is first created too.
for (Attr attribute in getAttributes())
attributeChangedCallback(attribute.name, null, attribute.value);
}
attachedCallback() {
if (shadowRoot == null) {
var registration = _registery[tagName];
if (registration.template != null) {
ShadowRoot shadow = ensureShadowRoot();
var tree = registration.template.content.cloneNode(deep:true);
shadow.appendChild(tree);
shadowRootReady();
}
}
attached();
}
detachedCallback() {
detached();
}
attributeChangedCallback(name, oldValue, newValue) {
attributeChanged(name, oldValue, newValue);
}
}
void register(Element script, Type type) {
Element definition = script.parentNode;
if (definition.tagName != 'sky-element')
throw new UnsupportedError('register() calls must be inside a <sky-element>.');
ClassMirror mirror = reflectClass(type);
if (!mirror.isSubclassOf(reflectClass(SkyElement)))
throw new UnsupportedError('@Tagname can only be used on descendants of SkyElement');
String tagName = _getTagName(type);
Element template = definition.querySelector('template');
document.registerElement(tagName, type);
_registery[tagName] = new _Registration(template);
}
</script>

View File

@ -2,7 +2,7 @@
<script>
final String one = 'one';
bool oneInit = false;
void init() {
void _init(_) {
oneInit = true;
}
final conflict = 'one';
@ -10,7 +10,7 @@ final conflict = 'one';
<script>
final String two = 'two';
bool twoInit = false;
void init() {
void _init(_) {
twoInit = true;
}
final conflict = 'two';

View File

@ -6,7 +6,7 @@ class TestClass extends base.BaseClass {
bool didInit = false;
void init() {
void _init(_) {
didInit = (new TestClass() != null);
}
</script>

View File

@ -3,7 +3,7 @@
import "dart:sky";
import "dart:sky.internals" as internals;
void init() {
void _init(_) {
window.addEventListener('load', (_) {
internals.notifyTestComplete(DomSerializer.serializeNode(document));
});

View File

@ -2,7 +2,7 @@
import "dart:sky" as sky;
import "dart:sky.internals" as internals;
void init() {
void _init(_) {
sky.window.addEventListener('load', (_) =>
internals.notifyTestComplete(internals.renderTreeAsText()));
}

View File

@ -6,7 +6,7 @@ void handleLoadEvent(event) {
internals.notifyTestComplete(internals.contentAsText());
}
void init() {
void _init(_) {
sky.window.addEventListener('load', handleLoadEvent);
}
</script>