Skip to content

Commit d405c20

Browse files
committed
Merge branch 'main' into amidala-v0.7.2-staging
2 parents 183f47d + 72d5ff4 commit d405c20

File tree

10 files changed

+185
-72
lines changed

10 files changed

+185
-72
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ mainstream 3D engines like [Unity][], [UnrealEngine][], and RealityKit.
1818

1919
### Request Formats
2020

21-
Like the classic Web browser, it supports not only the HTML document, but also lots of formats, such as PDF, SVG, etc. The following formats are JSAR is expected to support:
21+
Like the classic Web browser, it supports not only the HTML document, but also lots of formats, such as PDF, SVG, etc. The following formats JSAR is expected to support:
2222

2323
| Format | Status | Recommended Use Case |
2424
| -------- | ----------- | ---------------------------- |
25-
| HTML | Partially | Browsering classic Web pages |
25+
| HTML | Partially | Browsing classic Web pages |
2626
| PDF | Not started | Reading PDF documents |
2727
| SVG | Not started | Displaying SVG images |
2828
| GLTF | Ok | Displaying 3D models |
@@ -61,7 +61,7 @@ Like the classic Web browser, it supports not only the HTML document, but also l
6161

6262
### Rendering Backends
6363

64-
The followings are supported renderer backend:
64+
The following are supported renderer backends:
6565

6666
| Backend | OS | Status | Test Suite |
6767
| ------------- | ------- | ----------- | ---------- |

fixtures/html/canvas-leaferjs.html

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,6 @@
99
<body>
1010
</body>
1111

12-
<script type="module">
13-
import { Leafer, Rect } from 'https://unpkg.com/[email protected]/dist/web.module.min.js';
14-
try {
15-
var leafer = new Leafer({ view: window });
16-
var rect = new Rect({
17-
x: 100,
18-
y: 100,
19-
width: 200,
20-
height: 200,
21-
fill: '#32cd79',
22-
cornerRadius: [50, 80, 0, 80],
23-
draggable: true,
24-
});
25-
leafer.add(rect);
26-
console.info(leafer);
27-
} catch (error) {
28-
console.error(error);
29-
}
30-
</script>
12+
<script type="module" src="./canvas-leaferjs.js"></script>
3113

3214
</html>

fixtures/html/canvas-leaferjs.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Leafer, Rect } from 'https://unpkg.com/[email protected]/dist/web.module.min.js';
2+
try {
3+
var leafer = new Leafer({ view: window });
4+
var rect = new Rect({
5+
x: 100,
6+
y: 100,
7+
width: 200,
8+
height: 200,
9+
fill: '#32cd79',
10+
cornerRadius: [50, 80, 0, 80],
11+
draggable: true,
12+
});
13+
leafer.add(rect);
14+
console.info(leafer);
15+
} catch (error) {
16+
console.error(error);
17+
}

fixtures/html/template.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,14 @@ setTimeout(() => {
4646
}
4747
}
4848
container?.appendChild(node);
49+
50+
// Test querySelector() when new elements are added or removed
51+
console.info(document.body.innerHTML);
52+
const h = document.querySelector('.header');
53+
console.info(h, 'should not be null');
54+
55+
// TODO: fix the issue related to the node removal and replacements
56+
document.body.innerHTML = '<div>New content</div>';
57+
console.info(document.body.innerHTML);
58+
console.info(document.querySelector('.header'), 'should be null');
4959
}, 1000);

src/bindings/dom/event_target-inl.hpp

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -164,15 +164,10 @@ namespace dombinding
164164
return env.Undefined();
165165
}
166166

167-
dom::DOMEventType eventType;
168-
try
167+
std::optional<dom::DOMEventType> eventType = dom::StringToEventType(typeString, eventTargetType());
168+
if (!eventType.has_value())
169169
{
170-
eventType = dom::StringToEventType(typeString, eventTargetType());
171-
}
172-
catch (const invalid_argument &e)
173-
{
174-
auto msg = "Failed to execute 'addEventListener' on 'EventTarget': " + string(e.what());
175-
Napi::TypeError::New(env, msg).ThrowAsJavaScriptException();
170+
cerr << "The event type '" << typeString << "' is not supported by the event target." << endl;
176171
return env.Undefined();
177172
}
178173

@@ -227,7 +222,7 @@ namespace dombinding
227222
}
228223
};
229224

230-
auto nativeListener = eventTarget->addEventListener(eventType, listenerCallback);
225+
auto nativeListener = eventTarget->addEventListener(eventType.value(), listenerCallback);
231226
listenerRefToNativeIdMap.insert({listenerRef, nativeListener->id});
232227
return env.Undefined();
233228
}
@@ -266,15 +261,10 @@ namespace dombinding
266261
return env.Undefined();
267262
}
268263

269-
dom::DOMEventType eventType;
270-
try
271-
{
272-
eventType = dom::StringToEventType(typeString, eventTargetType());
273-
}
274-
catch (const invalid_argument &e)
264+
std::optional<dom::DOMEventType> eventType = dom::StringToEventType(typeString, eventTargetType());
265+
if (!eventType.has_value())
275266
{
276-
auto msg = "Failed to execute 'removeEventListener' on 'EventTarget': " + string(e.what());
277-
Napi::TypeError::New(env, msg).ThrowAsJavaScriptException();
267+
cerr << "The event type '" << typeString << "' is not supported by the event target." << endl;
278268
return env.Undefined();
279269
}
280270

@@ -284,7 +274,7 @@ namespace dombinding
284274
if (listenerRef->Value() == listenerValue)
285275
{
286276
uint32_t listenerId = it->second;
287-
eventTarget->removeEventListener(eventType, listenerId);
277+
eventTarget->removeEventListener(eventType.value(), listenerId);
288278
it = listenerRefToNativeIdMap.erase(it);
289279
break;
290280
}
@@ -328,19 +318,14 @@ namespace dombinding
328318
}
329319

330320
auto eventTypeString = eventObject.Get("type").ToString().Utf8Value();
331-
dom::DOMEventType eventType;
332-
try
333-
{
334-
eventType = dom::StringToEventType(eventTypeString, eventTargetType());
335-
}
336-
catch (const invalid_argument &e)
321+
std::optional<dom::DOMEventType> eventType = dom::StringToEventType(eventTypeString, eventTargetType());
322+
if (!eventType.has_value())
337323
{
338-
auto msg = "Failed to execute 'dispatchEvent' on 'EventTarget': " + string(e.what());
339-
Napi::TypeError::New(env, msg).ThrowAsJavaScriptException();
324+
cerr << "The event type '" << eventTypeString << "' is not supported by the event target type." << endl;
340325
return env.Undefined();
341326
}
342327

343-
eventTarget->dispatchEvent(eventType, nullptr);
328+
eventTarget->dispatchEvent(eventType.value(), nullptr);
344329
return Napi::Boolean::New(env, true);
345330
}
346331

src/client/dom/document.cpp

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -111,21 +111,17 @@ namespace dom
111111
}
112112
}
113113

114+
// Clear list and maps.
115+
allElementsList.clear();
116+
elementMapById.clear();
117+
114118
// Update the element list and maps.
115-
auto updateElementListAndMaps = [this](shared_ptr<Node> childNode)
119+
auto initElementsCache = [this](shared_ptr<Node> childNode)
116120
{
117-
if (childNode->nodeType == NodeType::ELEMENT_NODE)
118-
{
119-
auto element = std::dynamic_pointer_cast<Element>(childNode);
120-
allElementsList.push_back(element);
121-
if (!element->id.empty())
122-
elementMapById[element->id] = element;
123-
}
121+
onNodeAdded(childNode, true, false);
124122
return true;
125123
};
126-
allElementsList.clear();
127-
elementMapById.clear();
128-
iterateChildNodes(updateElementListAndMaps);
124+
iterateChildNodes(initElementsCache);
129125

130126
if (shouldOpen)
131127
openInternal();
@@ -250,6 +246,72 @@ namespace dom
250246
onStyleSheetsDidChange();
251247
}
252248

249+
void Document::onNodeAdded(const std::shared_ptr<Node> node, bool fast_insert, bool recursive)
250+
{
251+
if (TR_UNLIKELY(node == nullptr))
252+
return;
253+
254+
if (node->isElement())
255+
{
256+
shared_ptr<Element> element = Node::As<Element>(node);
257+
258+
// Fast inserting elements into the list without checking for duplicates.
259+
// FIXME(yorkie): At initialization, we can ensure that the elements are unique.
260+
if (fast_insert)
261+
{
262+
allElementsList.push_back(element);
263+
}
264+
else
265+
{
266+
// Check if element is already in the list to avoid duplicates
267+
auto it = std::find(allElementsList.begin(), allElementsList.end(), element);
268+
if (it == allElementsList.end())
269+
{
270+
// If not, add it to the all elements list
271+
allElementsList.push_back(element);
272+
}
273+
}
274+
275+
// Add the element to the element map by id
276+
if (!element->id.empty())
277+
elementMapById[element->id] = element;
278+
}
279+
280+
if (recursive)
281+
{
282+
// Recursively add child nodes
283+
for (auto childNode : node->childNodes)
284+
onNodeAdded(childNode, fast_insert, true);
285+
}
286+
}
287+
288+
void Document::onNodeRemoved(const shared_ptr<Node> node, bool recursive)
289+
{
290+
if (TR_UNLIKELY(node == nullptr))
291+
return;
292+
293+
if (node->isElement())
294+
{
295+
shared_ptr<Element> element = Node::As<Element>(node);
296+
297+
// Remove the element from the all elements list
298+
auto it = std::remove(allElementsList.begin(), allElementsList.end(), element);
299+
if (it != allElementsList.end())
300+
allElementsList.erase(it, allElementsList.end());
301+
302+
// Remove the element from the element map by id
303+
if (!element->id.empty())
304+
elementMapById.erase(element->id);
305+
}
306+
307+
if (recursive)
308+
{
309+
// Recursively remove child nodes
310+
for (auto childNode : node->childNodes)
311+
onNodeRemoved(childNode, true);
312+
}
313+
}
314+
253315
void Document::openInternal()
254316
{
255317
// Connect the window and document before opening this document.

src/client/dom/document.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ namespace dom
9898
protected:
9999
virtual void onDocumentOpened() {};
100100
virtual void onStyleSheetsDidChange() {};
101+
void onNodeAdded(const std::shared_ptr<Node>, bool fast_insert, bool recursive);
102+
void onNodeRemoved(const std::shared_ptr<Node>, bool recursive);
101103

102104
private:
103105
bool isDocument() const override final { return true; }

src/client/dom/dom_event_target.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include <string>
44
#include <memory>
5+
#include <optional>
6+
57
#include "common/utility.hpp"
68
#include "common/events_v2/event_target.hpp"
79

@@ -220,7 +222,7 @@ namespace dom
220222
* @param fromEventTarget The event target type that the event is from.
221223
* @returns The `DOMEventType` enum.
222224
*/
223-
inline DOMEventType StringToEventType(std::string typeStr, DOMEventTargetType fromEventTarget)
225+
inline std::optional<DOMEventType> StringToEventType(std::string typeStr, DOMEventTargetType fromEventTarget)
224226
{
225227
std::string eventFullName = ToLowerCase(typeStr);
226228
if (fromEventTarget != DOMEventTargetType::kEventTarget)
@@ -233,7 +235,7 @@ namespace dom
233235
DOM_EVENT_TYPES_MAP(XX)
234236
#undef XX
235237

236-
throw std::invalid_argument("Invalid event type string: " + typeStr);
238+
return std::nullopt;
237239
}
238240

239241
/**

src/client/dom/node.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ namespace dom
118118
auto it = find(childNodes.begin(), childNodes.end(), aChild);
119119
if (it != childNodes.end())
120120
{
121-
childNodes.erase(it);
121+
it = childNodes.erase(it);
122122
childRemovedCallback(aChild);
123123
}
124124
}
@@ -441,7 +441,10 @@ namespace dom
441441
auto self = shared_from_this();
442442
child->parentNode = self;
443443
// Update all the child nodes' owner document when a new child is added.
444-
child->updateFieldsFromDocument(getOwnerDocumentReference(), true);
444+
auto ownerDocument = getOwnerDocumentReference();
445+
child->updateFieldsFromDocument(ownerDocument, true);
446+
if (TR_LIKELY(ownerDocument != nullptr))
447+
ownerDocument->onNodeAdded(child, false, true);
445448

446449
markAsDirty();
447450
notifyMutationObservers(MutationRecord::OnAddChild(self, child));
@@ -456,6 +459,10 @@ namespace dom
456459
auto self = shared_from_this();
457460
child->parentNode.reset();
458461

462+
auto ownerDocument = getOwnerDocumentReference();
463+
if (TR_LIKELY(ownerDocument != nullptr))
464+
ownerDocument->onNodeRemoved(child, true);
465+
459466
markAsDirty();
460467
notifyMutationObservers(MutationRecord::OnRemoveChild(self, child));
461468

@@ -563,6 +570,7 @@ namespace dom
563570
else
564571
ownerDocument = nullopt;
565572

573+
// Update the document for fields of child nodes recursively
566574
if (updateChildren && !childNodes.empty())
567575
{
568576
for (auto child : childNodes)

0 commit comments

Comments
 (0)