Created
September 28, 2024 07:03
-
-
Save mike-clark-8192/af6e0d2e579045ff3f0ecbe056c5544a to your computer and use it in GitHub Desktop.
JavaScript port of Chrome Dev Tools `$x`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function $x(selector, contextNode = document) { | |
// Ensure the selector is provided and is a string | |
if (!selector || typeof selector !== 'string') { | |
throw new Error('$x requires a non-empty string as its first argument.'); | |
} | |
// Ensure the context node is a valid DOM Node | |
if (!contextNode || !(contextNode instanceof Node)) { | |
throw new Error('The second argument to $x must be a DOM Node.'); | |
} | |
// Evaluate the XPath expression | |
let xpathResult; | |
try { | |
xpathResult = document.evaluate( | |
selector, | |
contextNode, | |
null, // No custom namespace resolver | |
XPathResult.ANY_TYPE, // We want any result type | |
null // No initial result | |
); | |
} catch (e) { | |
// Handle XPath evaluation errors | |
console.error('Error in XPath evaluation:', e); | |
return null; | |
} | |
// Handle different result types | |
switch (xpathResult.resultType) { | |
case XPathResult.NUMBER_TYPE: | |
return xpathResult.numberValue; | |
case XPathResult.STRING_TYPE: | |
return xpathResult.stringValue; | |
case XPathResult.BOOLEAN_TYPE: | |
return xpathResult.booleanValue; | |
case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: | |
case XPathResult.ORDERED_NODE_ITERATOR_TYPE: | |
// Iterate over the nodes and collect them in an array | |
const nodes = []; | |
let node = xpathResult.iterateNext(); | |
while (node) { | |
nodes.push(node); | |
node = xpathResult.iterateNext(); | |
} | |
return nodes; | |
case XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE: | |
case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE: | |
// Collect nodes from the snapshot result into an array | |
const snapshotNodes = []; | |
for (let i = 0; i < xpathResult.snapshotLength; i++) { | |
snapshotNodes.push(xpathResult.snapshotItem(i)); | |
} | |
return snapshotNodes; | |
case XPathResult.ANY_UNORDERED_NODE_TYPE: | |
case XPathResult.FIRST_ORDERED_NODE_TYPE: | |
// Return the single node if available | |
return xpathResult.singleNodeValue; | |
default: | |
return null; // Unexpected result type | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void MainThreadDebugger::XpathSelectorCallback( | |
const v8::FunctionCallbackInfo<v8::Value>& info) { | |
if (info.Length() < 1) | |
return; | |
const String& selector = | |
ToCoreStringWithUndefinedOrNullCheck(info.GetIsolate(), info[0]); | |
if (selector.empty()) | |
return; | |
Node* node = SecondArgumentAsNode(info); | |
if (!node || !node->IsContainerNode()) | |
return; | |
ScriptState* script_state = | |
ScriptState::ForRelevantRealm(info.GetIsolate(), info.This()); | |
ExceptionState exception_state(info.GetIsolate(), | |
v8::ExceptionContext::kOperation, | |
"CommandLineAPI", "$x"); | |
XPathResult* result = XPathEvaluator::Create()->evaluate( | |
nullptr, selector, node, nullptr, XPathResult::kAnyType, ScriptValue(), | |
exception_state); | |
if (exception_state.HadException()) { | |
if (exception_state.HadException()) { | |
ApplyContextToException(script_state, exception_state.GetException(), | |
exception_state.GetContext()); | |
} | |
return; | |
} | |
if (!result) { | |
return; | |
} | |
if (result->resultType() == XPathResult::kNumberType) { | |
bindings::V8SetReturnValue(info, result->numberValue(exception_state)); | |
} else if (result->resultType() == XPathResult::kStringType) { | |
bindings::V8SetReturnValue(info, result->stringValue(exception_state), | |
info.GetIsolate(), | |
bindings::V8ReturnValue::kNonNullable); | |
} else if (result->resultType() == XPathResult::kBooleanType) { | |
bindings::V8SetReturnValue(info, result->booleanValue(exception_state)); | |
} else { | |
v8::Isolate* isolate = info.GetIsolate(); | |
v8::Local<v8::Context> context = isolate->GetCurrentContext(); | |
v8::Local<v8::Array> nodes = v8::Array::New(isolate); | |
wtf_size_t index = 0; | |
while (Node* next_node = result->iterateNext(exception_state)) { | |
v8::Local<v8::Value> value = | |
ToV8Traits<Node>::ToV8(script_state, next_node); | |
if (!CreateDataPropertyInArray(context, nodes, index++, value) | |
.FromMaybe(false)) { | |
return; | |
} | |
} | |
if (exception_state.HadException()) { | |
ApplyContextToException(script_state, exception_state.GetException(), | |
exception_state.GetContext()); | |
return; | |
} | |
info.GetReturnValue().Set(nodes); | |
} | |
} | |
void MainThreadDebugger::installAdditionalCommandLineAPI( | |
v8::Local<v8::Context> context, | |
v8::Local<v8::Object> object) { | |
ThreadDebuggerCommonImpl::installAdditionalCommandLineAPI(context, object); | |
CreateFunctionProperty( | |
context, object, "$", MainThreadDebugger::QuerySelectorCallback, | |
"function $(selector, [startNode]) { [Command Line API] }", | |
v8::SideEffectType::kHasNoSideEffect); | |
CreateFunctionProperty( | |
context, object, "$$", MainThreadDebugger::QuerySelectorAllCallback, | |
"function $$(selector, [startNode]) { [Command Line API] }", | |
v8::SideEffectType::kHasNoSideEffect); | |
CreateFunctionProperty( | |
context, object, "$x", MainThreadDebugger::XpathSelectorCallback, | |
"function $x(xpath, [startNode]) { [Command Line API] }", | |
v8::SideEffectType::kHasNoSideEffect); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment