Skip to content

Commit 8b92b87

Browse files
authored
Custom queries manager improvements (#2234)
* Custom queries manager improvements * cr * Fix bad merge
1 parent 7d2ffef commit 8b92b87

File tree

1 file changed

+152
-156
lines changed

1 file changed

+152
-156
lines changed

lib/PuppeteerSharp/CustomQueriesManager.cs

Lines changed: 152 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,170 +1,30 @@
11
using System;
2-
using System.Collections;
32
using System.Collections.Generic;
43
using System.Linq;
54
using System.Text.RegularExpressions;
6-
using Newtonsoft.Json.Linq;
7-
using PuppeteerSharp.Helpers;
85

96
namespace PuppeteerSharp
107
{
118
internal class CustomQueriesManager
129
{
1310
private static readonly string[] _customQuerySeparators = new[] { "=", "/" };
14-
private readonly Dictionary<string, PuppeteerQueryHandler> _internalQueryHandlers;
15-
private readonly Dictionary<string, PuppeteerQueryHandler> _queryHandlers = new();
16-
private readonly PuppeteerQueryHandler _pierceHandler = CreatePuppeteerQueryHandler(new CustomQueryHandler
11+
private readonly Dictionary<string, PuppeteerQueryHandler> _internalQueryHandlers = new()
1712
{
18-
QueryOne = @"(element, selector) => {
19-
let found = null;
20-
const search = (root) => {
21-
const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
22-
do {
23-
const currentNode = iter.currentNode;
24-
if (currentNode.shadowRoot) {
25-
search(currentNode.shadowRoot);
26-
}
27-
if (currentNode instanceof ShadowRoot) {
28-
continue;
29-
}
30-
if (currentNode !== root && !found && currentNode.matches(selector)) {
31-
found = currentNode;
32-
}
33-
} while (!found && iter.nextNode());
34-
};
35-
if (element instanceof Document) {
36-
element = element.documentElement;
37-
}
38-
search(element);
39-
return found;
40-
}",
41-
QueryAll = @"(element, selector) => {
42-
const result = [];
43-
const collect = (root) => {
44-
const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
45-
do {
46-
const currentNode = iter.currentNode;
47-
if (currentNode.shadowRoot) {
48-
collect(currentNode.shadowRoot);
49-
}
50-
if (currentNode instanceof ShadowRoot) {
51-
continue;
52-
}
53-
if (currentNode !== root && currentNode.matches(selector)) {
54-
result.push(currentNode);
55-
}
56-
} while (iter.nextNode());
57-
};
58-
if (element instanceof Document) {
59-
element = element.documentElement;
60-
}
61-
collect(element);
62-
return result;
63-
}",
64-
});
13+
["aria"] = AriaQueryHandlerFactory.Create(),
14+
["pierce"] = CreatePierceHandler(),
15+
["text"] = CreateTextQueryHandler(),
16+
["xpath"] = CreateXpathHandler(),
17+
};
18+
19+
private readonly Dictionary<string, PuppeteerQueryHandler> _queryHandlers = new();
6520

66-
private readonly PuppeteerQueryHandler _ariaHandler = AriaQueryHandlerFactory.Create();
6721
private readonly Regex _customQueryHandlerNameRegex = new("[a-zA-Z]+$", RegexOptions.Compiled);
6822
private readonly PuppeteerQueryHandler _defaultHandler = CreatePuppeteerQueryHandler(new CustomQueryHandler
6923
{
7024
QueryOne = "(element, selector) => element.querySelector(selector)",
7125
QueryAll = "(element, selector) => element.querySelectorAll(selector)",
7226
});
7327

74-
private readonly PuppeteerQueryHandler _textQueryHandler = CreatePuppeteerQueryHandler(new CustomQueryHandler
75-
{
76-
QueryOne = @"(element, selector, {createTextContent}) => {
77-
const search = (root)=> {
78-
for (const node of root.childNodes) {
79-
if (node instanceof Element) {
80-
let matchedNode;
81-
if (node.shadowRoot) {
82-
matchedNode = search(node.shadowRoot);
83-
} else {
84-
matchedNode = search(node);
85-
}
86-
if (matchedNode) {
87-
return matchedNode;
88-
}
89-
}
90-
}
91-
const textContent = createTextContent(root);
92-
if (textContent.full.includes(selector)) {
93-
return root;
94-
}
95-
return null;
96-
};
97-
return search(element);
98-
}",
99-
QueryAll = @"(element, selector, {createTextContent}) => {
100-
const search = (root) => {
101-
let results = [];
102-
for (const node of root.childNodes) {
103-
if (node instanceof Element) {
104-
let matchedNodes;
105-
if (node.shadowRoot) {
106-
matchedNodes = search(node.shadowRoot);
107-
} else {
108-
matchedNodes = search(node);
109-
}
110-
results = results.concat(matchedNodes);
111-
}
112-
}
113-
if (results.length > 0) {
114-
return results;
115-
}
116-
117-
const textContent = createTextContent(root);
118-
if (textContent.full.includes(selector)) {
119-
return [root];
120-
}
121-
return [];
122-
};
123-
return search(element);
124-
}",
125-
});
126-
127-
private readonly PuppeteerQueryHandler _xpathHandler = CreatePuppeteerQueryHandler(new CustomQueryHandler
128-
{
129-
QueryOne = @"(element, selector) => {
130-
const doc = element.ownerDocument || document;
131-
const result = doc.evaluate(
132-
selector,
133-
element,
134-
null,
135-
XPathResult.FIRST_ORDERED_NODE_TYPE
136-
);
137-
return result.singleNodeValue;
138-
}",
139-
QueryAll = @"(element, selector) => {
140-
const doc = element.ownerDocument || document;
141-
const iterator = doc.evaluate(
142-
selector,
143-
element,
144-
null,
145-
XPathResult.ORDERED_NODE_ITERATOR_TYPE
146-
);
147-
const array = [];
148-
let item;
149-
while ((item = iterator.iterateNext())) {
150-
array.push(item);
151-
}
152-
return array;
153-
},
154-
})",
155-
});
156-
157-
public CustomQueriesManager()
158-
{
159-
_internalQueryHandlers = new()
160-
{
161-
["aria"] = _ariaHandler,
162-
["pierce"] = _pierceHandler,
163-
["text"] = _textQueryHandler,
164-
["xpath"] = _xpathHandler,
165-
};
166-
}
167-
16828
internal void RegisterCustomQueryHandler(string name, CustomQueryHandler queryHandler)
16929
{
17030
if (_internalQueryHandlers.ContainsKey(name))
@@ -194,16 +54,16 @@ internal void RegisterCustomQueryHandler(string name, CustomQueryHandler queryHa
19454

19555
foreach (var kv in handlers)
19656
{
197-
foreach (var separator in _customQuerySeparators)
198-
{
199-
var prefix = $"{kv.Key}{separator}";
200-
201-
if (selector.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
57+
foreach (var separator in _customQuerySeparators)
20258
{
203-
selector = selector.Substring(prefix.Length);
204-
return (selector, kv.Value);
59+
var prefix = $"{kv.Key}{separator}";
60+
61+
if (selector.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
62+
{
63+
selector = selector.Substring(prefix.Length);
64+
return (selector, kv.Value);
65+
}
20566
}
206-
}
20767
}
20868

20969
return (selector, _defaultHandler);
@@ -223,6 +83,142 @@ internal void ClearCustomQueryHandlers()
22383
}
22484
}
22585

86+
private static PuppeteerQueryHandler CreatePierceHandler() =>
87+
CreatePuppeteerQueryHandler(new CustomQueryHandler
88+
{
89+
QueryOne = @"(element, selector) => {
90+
let found = null;
91+
const search = (root) => {
92+
const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
93+
do {
94+
const currentNode = iter.currentNode;
95+
if (currentNode.shadowRoot) {
96+
search(currentNode.shadowRoot);
97+
}
98+
if (currentNode instanceof ShadowRoot) {
99+
continue;
100+
}
101+
if (currentNode !== root && !found && currentNode.matches(selector)) {
102+
found = currentNode;
103+
}
104+
} while (!found && iter.nextNode());
105+
};
106+
if (element instanceof Document) {
107+
element = element.documentElement;
108+
}
109+
search(element);
110+
return found;
111+
}",
112+
QueryAll = @"(element, selector) => {
113+
const result = [];
114+
const collect = (root) => {
115+
const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
116+
do {
117+
const currentNode = iter.currentNode;
118+
if (currentNode.shadowRoot) {
119+
collect(currentNode.shadowRoot);
120+
}
121+
if (currentNode instanceof ShadowRoot) {
122+
continue;
123+
}
124+
if (currentNode !== root && currentNode.matches(selector)) {
125+
result.push(currentNode);
126+
}
127+
} while (iter.nextNode());
128+
};
129+
if (element instanceof Document) {
130+
element = element.documentElement;
131+
}
132+
collect(element);
133+
return result;
134+
}",
135+
});
136+
137+
private static PuppeteerQueryHandler CreateTextQueryHandler() =>
138+
CreatePuppeteerQueryHandler(new CustomQueryHandler
139+
{
140+
QueryOne = @"(element, selector, {createTextContent}) => {
141+
const search = (root)=> {
142+
for (const node of root.childNodes) {
143+
if (node instanceof Element) {
144+
let matchedNode;
145+
if (node.shadowRoot) {
146+
matchedNode = search(node.shadowRoot);
147+
} else {
148+
matchedNode = search(node);
149+
}
150+
if (matchedNode) {
151+
return matchedNode;
152+
}
153+
}
154+
}
155+
const textContent = createTextContent(root);
156+
if (textContent.full.includes(selector)) {
157+
return root;
158+
}
159+
return null;
160+
};
161+
return search(element);
162+
}",
163+
QueryAll = @"(element, selector, {createTextContent}) => {
164+
const search = (root) => {
165+
let results = [];
166+
for (const node of root.childNodes) {
167+
if (node instanceof Element) {
168+
let matchedNodes;
169+
if (node.shadowRoot) {
170+
matchedNodes = search(node.shadowRoot);
171+
} else {
172+
matchedNodes = search(node);
173+
}
174+
results = results.concat(matchedNodes);
175+
}
176+
}
177+
if (results.length > 0) {
178+
return results;
179+
}
180+
181+
const textContent = createTextContent(root);
182+
if (textContent.full.includes(selector)) {
183+
return [root];
184+
}
185+
return [];
186+
};
187+
return search(element);
188+
}",
189+
});
190+
191+
private static PuppeteerQueryHandler CreateXpathHandler() =>
192+
CreatePuppeteerQueryHandler(new CustomQueryHandler
193+
{
194+
QueryOne = @"(element, selector) => {
195+
const doc = element.ownerDocument || document;
196+
const result = doc.evaluate(
197+
selector,
198+
element,
199+
null,
200+
XPathResult.FIRST_ORDERED_NODE_TYPE
201+
);
202+
return result.singleNodeValue;
203+
}",
204+
QueryAll = @"(element, selector) => {
205+
const doc = element.ownerDocument || document;
206+
const iterator = doc.evaluate(
207+
selector,
208+
element,
209+
null,
210+
XPathResult.ORDERED_NODE_ITERATOR_TYPE
211+
);
212+
const array = [];
213+
let item;
214+
while ((item = iterator.iterateNext())) {
215+
array.push(item);
216+
}
217+
return array;
218+
},
219+
})",
220+
});
221+
226222
private static PuppeteerQueryHandler CreatePuppeteerQueryHandler(CustomQueryHandler handler)
227223
{
228224
var internalHandler = new PuppeteerQueryHandler();

0 commit comments

Comments
 (0)