Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bridge/core/dom/events/event_target.cc
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ NativeValue EventTarget::HandleDispatchEventFromDart(int32_t argc, const NativeV
assert(event->currentTarget() != nullptr);

auto* window = DynamicTo<Window>(event->target());
if (window != nullptr && event->type() == event_type_names::kload) {
if (window != nullptr && (event->type() == event_type_names::kload || event->type() == event_type_names::kgcopen)) {
window->OnLoadEventFired();
}

Expand Down
3 changes: 2 additions & 1 deletion bridge/core/events/event_type_names.json5
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@
"webglcontextrestored",
"wheel",
"zoom",
"intersectionchange"
"intersectionchange",
"gcopen"
]
}
2 changes: 1 addition & 1 deletion bridge/third_party/quickjs/src/core/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -3006,7 +3006,7 @@ JSRuntime* JS_NewRuntime2(const JSMallocFunctions* mf, void* opaque) {
rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;
}
rt->malloc_state = ms;
rt->malloc_gc_threshold = 64 * 1024 * 1024; // 64 MB as a start
rt->malloc_gc_threshold = 2 * 1024 * 1024; // 2 MB as a start
rt->gc_off = FALSE;

#ifdef CONFIG_BIGNUM
Expand Down
5 changes: 5 additions & 0 deletions webf/lib/src/dom/comment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,9 @@ class Comment extends CharacterData {

@override
int get length => data.length;

@override
String toString() {
return 'Comment()';
}
}
8 changes: 4 additions & 4 deletions webf/lib/src/dom/container_node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ bool collectChildrenAndRemoveFromOldParent(Node node, List<Node> nodes) {
}
nodes.add(node);
ContainerNode? oldParent = node.parentNode;
if (oldParent != null) {
if (oldParent != null && node.isConnected) {
oldParent.removeChild(node);
}
return nodes.isNotEmpty;
Expand Down Expand Up @@ -92,7 +92,7 @@ abstract class ContainerNode extends Node {

// 6. Mark this element to dirty elements.
if (this is Element) {
ownerDocument.styleDirtyElements.add(this as Element);
ownerDocument.markElementStyleDirty(this as Element);
}

// 7. Trigger connected callback
Expand Down Expand Up @@ -180,7 +180,7 @@ abstract class ContainerNode extends Node {
}

if (this is Element) {
ownerDocument.styleDirtyElements.add(this as Element);
ownerDocument.markElementStyleDirty(this as Element);
}

bool isOldChildConnected = child.isConnected;
Expand Down Expand Up @@ -222,7 +222,7 @@ abstract class ContainerNode extends Node {
_insertNode(targets, null, _adoptAndAppendChild);

if (this is Element) {
ownerDocument.styleDirtyElements.add(this as Element);
ownerDocument.markElementStyleDirty(this as Element);
}

if (newChild.isConnected) {
Expand Down
64 changes: 44 additions & 20 deletions webf/lib/src/dom/document.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Copyright (C) 2022-present The WebF authors. All rights reserved.
*/
import 'dart:collection';
import 'dart:ffi';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
Expand Down Expand Up @@ -31,6 +32,7 @@ class _InactiveRenderObjects {

void _scheduleFrameToFinalizeRenderObjects() {
_isScheduled = true;

/// We needs to wait at least 2 frames to dispose all webf managed renderObjects.
/// All renderObjects managed by WebF should be disposed after Flutter managed renderObjects dispose.
RendererBinding.instance.addPostFrameCallback((timeStamp) {
Expand Down Expand Up @@ -70,13 +72,15 @@ class _InactiveRenderObjects {
}

void finalizeInactiveRenderObjects() {
for(RenderObject object in _renderObjects) {
for (RenderObject object in _renderObjects) {
object.dispose();
}
_renderObjects.clear();
}
}

enum DocumentReadyState { loading, interactive, complete }

enum VisibilityState { visible, hidden }

class Document extends ContainerNode {
Expand All @@ -92,9 +96,17 @@ class Document extends ContainerNode {

final List<AsyncCallback> pendingPreloadingScriptCallbacks = [];

Set<Element> styleDirtyElements = {};
final Set<int> _styleDirtyElements = {};

void markElementStyleDirty(Element element) {
_styleDirtyElements.add(element.pointer!.address);
}
void clearElementStyleDirty(Element element) {
_styleDirtyElements.remove(element.pointer!.address);
}

final NthIndexCache _nthIndexCache = NthIndexCache();

NthIndexCache get nthIndexCache => _nthIndexCache;

StyleNodeManager get styleNodeManager => _styleNodeManager;
Expand All @@ -113,12 +125,8 @@ class Document extends ContainerNode {
@override
bool get isConnected => true;

Document(
BindingContext context, {
required this.controller,
this.gestureListener,
List<Cookie>? initialCookies
}) : super(NodeType.DOCUMENT_NODE, context) {
Document(BindingContext context, {required this.controller, this.gestureListener, List<Cookie>? initialCookies})
: super(NodeType.DOCUMENT_NODE, context) {
cookie_ = CookieJar(controller.url, initialCookies: initialCookies);
_styleNodeManager = StyleNodeManager(this);
_scriptRunner = ScriptRunner(this, context.contextId);
Expand All @@ -127,6 +135,7 @@ class Document extends ContainerNode {

// https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Document.h#L1898
late ScriptRunner _scriptRunner;

ScriptRunner get scriptRunner => _scriptRunner;

_InactiveRenderObjects inactiveRenderObjects = _InactiveRenderObjects();
Expand All @@ -142,6 +151,7 @@ class Document extends ContainerNode {
Element? focusedElement;

late CookieJar cookie_;

CookieJar get cookie => cookie_;

// Returns the Window object of the active document.
Expand All @@ -158,7 +168,9 @@ class Document extends ContainerNode {
bool parsing = false;

int _requestCount = 0;

bool get hasPendingRequest => _requestCount > 0;

void incrementRequestCount() {
_requestCount++;
}
Expand All @@ -171,7 +183,9 @@ class Document extends ContainerNode {
// https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Document.h#L2091
// Counters that currently need to delay load event, such as parsing a script.
int _loadEventDelayCount = 0;

bool get isDelayingLoadEvent => _loadEventDelayCount > 0;

void incrementLoadEventDelayCount() {
_loadEventDelayCount++;
}
Expand All @@ -186,7 +200,9 @@ class Document extends ContainerNode {
}

int _domContentLoadedEventDelayCount = 0;

bool get isDelayingDOMContentLoadedEvent => _domContentLoadedEventDelayCount > 0;

void incrementDOMContentLoadedEventDelayCount() {
_domContentLoadedEventDelayCount++;
}
Expand All @@ -202,7 +218,8 @@ class Document extends ContainerNode {

@override
void initializeProperties(Map<String, BindingObjectProperty> properties) {
properties['cookie'] = BindingObjectProperty(getter: () => cookie.cookie(), setter: (value) => cookie.setCookieString(value));
properties['cookie'] =
BindingObjectProperty(getter: () => cookie.cookie(), setter: (value) => cookie.setCookieString(value));
properties['compatMode'] = BindingObjectProperty(getter: () => compatMode);
properties['domain'] = BindingObjectProperty(getter: () => domain, setter: (value) => domain = value);
properties['readyState'] = BindingObjectProperty(getter: () => readyState);
Expand All @@ -225,7 +242,8 @@ class Document extends ContainerNode {
methods['getElementsByClassName'] = BindingObjectMethodSync(call: (args) => getElementsByClassName(args));
methods['getElementsByTagName'] = BindingObjectMethodSync(call: (args) => getElementsByTagName(args));
methods['getElementsByName'] = BindingObjectMethodSync(call: (args) => getElementsByName(args));
methods['elementFromPoint'] = BindingObjectMethodSync(call: (args) => elementFromPoint(castToType<double>(args[0]), castToType<double>(args[1])));
methods['elementFromPoint'] = BindingObjectMethodSync(
call: (args) => elementFromPoint(castToType<double>(args[0]), castToType<double>(args[1])));
if (kDebugMode || kProfileMode) {
methods['___clear_cookies__'] = BindingObjectMethodSync(call: (args) => debugClearCookies(args));
}
Expand Down Expand Up @@ -298,6 +316,7 @@ class Document extends ContainerNode {
if (args[0].runtimeType == String && (args[0] as String).isEmpty) return null;
return QuerySelector.querySelector(this, args.first);
}

dynamic elementFromPoint(double x, double y) {
documentElement?.flushLayout();
return HitTestPoint(x, y);
Expand Down Expand Up @@ -369,7 +388,9 @@ class Document extends ContainerNode {
}

Element? _documentElement;

Element? get documentElement => _documentElement;

set documentElement(Element? element) {
if (_documentElement == element) {
return;
Expand Down Expand Up @@ -475,6 +496,7 @@ class Document extends ContainerNode {

// TODO: https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets
List<CSSStyleSheet> adoptedStyleSheets = [];

// The styleSheets attribute is readonly attribute.
final List<CSSStyleSheet> styleSheets = [];

Expand All @@ -488,6 +510,7 @@ class Document extends ContainerNode {
}

bool _recalculating = false;

void updateStyleIfNeeded() {
if (!styleNodeManager.hasPendingStyleSheet && !styleNodeManager.isStyleSheetCandidateNodeChanged) {
return;
Expand All @@ -504,31 +527,33 @@ class Document extends ContainerNode {
}

void flushStyle({bool rebuild = false}) {
if (styleDirtyElements.isEmpty) {
if (_styleDirtyElements.isEmpty) {
_recalculating = false;
return;
}
if (!styleNodeManager.updateActiveStyleSheets(rebuild: rebuild)) {
_recalculating = false;
styleDirtyElements.clear();
_styleDirtyElements.clear();
return;
}
if (styleDirtyElements.any((element) {
return element is HeadElement || element is HTMLElement;
if (_styleDirtyElements.any((address) {
BindingObject bindingObject = ownerView.getBindingObject(Pointer.fromAddress(address));
return bindingObject is HeadElement || bindingObject is HTMLElement;
}) ||
rebuild) {
documentElement?.recalculateStyle(rebuildNested: true);
} else {
for (Element element in styleDirtyElements) {
element.recalculateStyle();
for (int address in _styleDirtyElements) {
Element? element = ownerView.getBindingObject(Pointer.fromAddress(address)) as Element?;
element?.recalculateStyle();
}
}
styleDirtyElements.clear();
_styleDirtyElements.clear();
_recalculating = false;
}

void reactiveWidgetElements() {
for(WidgetElement widgetElement in aliveWidgetElements) {
for (WidgetElement widgetElement in aliveWidgetElements) {
widgetElement.reactiveRenderer();
}
}
Expand All @@ -540,10 +565,9 @@ class Document extends ContainerNode {
nthIndexCache.clearAll();
adoptedStyleSheets.clear();
cookie.clearCookie();
styleDirtyElements.clear();
_styleDirtyElements.clear();
fixedChildren.clear();
pendingPreloadingScriptCallbacks.clear();
super.dispose();
}

}
1 change: 1 addition & 0 deletions webf/lib/src/dom/element.dart
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,7 @@ abstract class Element extends ContainerNode with ElementBase, ElementEventMixin
flutterWidget = null;
flutterWidgetElement = null;
ownerDocument.inactiveRenderObjects.add(renderer);
ownerDocument.clearElementStyleDirty(this);
_beforeElement?.dispose();
_beforeElement = null;
_afterElement?.dispose();
Expand Down
4 changes: 3 additions & 1 deletion webf/lib/src/dom/node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,9 @@ abstract class Node extends EventTarget implements RenderObjectNode, LifecycleCa
/// Release any resources held by this node.
@override
void dispose() async {
parentNode?.removeChild(this);
if (isConnected) {
parentNode?.removeChild(this);
}
if (this is! Document) {
assert(!isRendererAttachedToSegmentTree, 'Should unmount $this before calling dispose.');
}
Expand Down
4 changes: 2 additions & 2 deletions webf/lib/src/dom/style_node_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class StyleNodeManager {
} else {
final root = document.documentElement;
if (root != null) {
document.styleDirtyElements.add(root);
document.markElementStyleDirty(root);
}
}
document.handleStyleSheets(newSheets);
Expand Down Expand Up @@ -113,7 +113,7 @@ class StyleNodeManager {
}
final rules = collector.matchedRules(changedRuleSet, node);
if (rules.isNotEmpty) {
document.styleDirtyElements.add(node);
document.markElementStyleDirty(node);
}
if (node.childNodes.isNotEmpty) {
stack.addAll(node.childNodes);
Expand Down
2 changes: 1 addition & 1 deletion webf/lib/src/dom/text_node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class TextNode extends CharacterData {

@override
String toString() {
return 'TextNode($hashCode)';
return 'TextNode($data)';
}

// Detach renderObject of current node from parent
Expand Down
1 change: 1 addition & 0 deletions webf/lib/src/dom/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Window extends EventTarget {
: screen = Screen(context!.contextId, document.controller.ownerFlutterView, document.controller.view),
super(context) {
BindingBridge.listenEvent(this, 'load');
BindingBridge.listenEvent(this, 'gcopen');
}

@override
Expand Down
4 changes: 2 additions & 2 deletions webf/lib/src/html/head.dart
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class LinkElement extends Element {
final String cssString = await resolveStringFromData(bundle.data!);
_styleSheet = CSSParser(cssString, href: href).parse();
_styleSheet?.href = href;
ownerDocument.styleDirtyElements.add(ownerDocument.documentElement!);
ownerDocument.markElementStyleDirty(ownerDocument.documentElement!);
ownerDocument.styleNodeManager.appendPendingStyleSheet(_styleSheet!);
ownerDocument.updateStyleIfNeeded();

Expand Down Expand Up @@ -271,7 +271,7 @@ mixin StyleElementMixin on Element {
_styleSheet = CSSParser(text).parse();
}
if (_styleSheet != null) {
ownerDocument.styleDirtyElements.add(ownerDocument.documentElement!);
ownerDocument.markElementStyleDirty(ownerDocument.documentElement!);
ownerDocument.styleNodeManager.appendPendingStyleSheet(_styleSheet!);
ownerDocument.updateStyleIfNeeded();
}
Expand Down
5 changes: 5 additions & 0 deletions webf/lib/src/launcher/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,11 @@ class WebFViewController implements WidgetsBindingObserver {
window = Window(BindingContext(view, _contextId, pointer), document);
_registerPlatformBrightnessChange();

// 3 seconds should be enough for page loading, make sure the JavaScript GC was opened.
Timer(Duration(seconds: 3), () {
window.dispatchEvent(Event('gcopen'));
});

// Blur input element when new input focused.
window.addEventListener(EVENT_CLICK, (event) async {
if (event.target is Element) {
Expand Down