JSValue JSInspectorFrontendHost::search(ExecState* exec, const ArgList& args) { if (args.size() < 2) return jsUndefined(); Node* node = toNode(args.at(0)); if (!node) return jsUndefined(); String target = args.at(1).toString(exec); if (exec->hadException()) return jsUndefined(); MarkedArgumentBuffer result; RefPtr<Range> searchRange(rangeOfContents(node)); ExceptionCode ec = 0; do { RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, false)); if (resultRange->collapsed(ec)) break; // A non-collapsed result range can in some funky whitespace cases still not // advance the range's start position (4509328). Break to avoid infinite loop. VisiblePosition newStart = endVisiblePosition(resultRange.get(), DOWNSTREAM); if (newStart == startVisiblePosition(searchRange.get(), DOWNSTREAM)) break; result.append(toJS(exec, resultRange.get())); setStart(searchRange.get(), newStart); } while (true); return constructArray(exec, result); }
static JSValueRef search(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) { InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); if (!controller) return JSValueMakeUndefined(ctx); if (argumentCount < 2 || !JSValueIsString(ctx, arguments[1])) return JSValueMakeUndefined(ctx); Node* node = toNode(toJS(arguments[0])); if (!node) return JSValueMakeUndefined(ctx); JSStringRef string = JSValueToStringCopy(ctx, arguments[1], 0); String target(JSStringGetCharactersPtr(string), JSStringGetLength(string)); JSStringRelease(string); JSObjectRef globalObject = JSContextGetGlobalObject(ctx); JSStringRef constructorString = JSStringCreateWithUTF8CString("Array"); JSObjectRef arrayConstructor = JSValueToObject(ctx, JSObjectGetProperty(ctx, globalObject, constructorString, 0), 0); JSStringRelease(constructorString); JSObjectRef array = JSObjectCallAsConstructor(ctx, arrayConstructor, 0, 0, 0); JSStringRef pushString = JSStringCreateWithUTF8CString("push"); JSValueRef pushValue = JSObjectGetProperty(ctx, array, pushString, 0); JSStringRelease(pushString); JSObjectRef push = JSValueToObject(ctx, pushValue, 0); RefPtr<Range> searchRange(rangeOfContents(node)); int exception = 0; do { RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, false)); if (resultRange->collapsed(exception)) break; // A non-collapsed result range can in some funky whitespace cases still not // advance the range's start position (4509328). Break to avoid infinite loop. VisiblePosition newStart = endVisiblePosition(resultRange.get(), DOWNSTREAM); if (newStart == startVisiblePosition(searchRange.get(), DOWNSTREAM)) break; KJS::JSLock lock; JSValueRef arg0 = toRef(toJS(toJS(ctx), resultRange.get())); JSObjectCallAsFunction(ctx, push, array, 1, &arg0, 0); setStart(searchRange.get(), newStart); } while (true); return array; }
void InPageSearchManager::scopeStringMatches(const String& text, bool reset, bool locateActiveMatchOnly, Frame* scopingFrame) { if (reset) { if (!locateActiveMatchOnly) { m_activeMatchCount = 0; m_scopingComplete = false; } m_resumeScopingFromRange = 0; m_locatingActiveMatch = true; m_activeMatchIndex = 0; // New search should always start from mainFrame. scopeStringMatchesSoon(m_webPage->mainFrame(), text, false /* reset */, locateActiveMatchOnly); return; } if (m_resumeScopingFromRange && scopingFrame != m_resumeScopingFromRange->ownerDocument().frame()) m_resumeScopingFromRange = 0; RefPtr<Range> searchRange(rangeOfContents(scopingFrame->document())); Node* originalEndContainer = searchRange->endContainer(); int originalEndOffset = searchRange->endOffset(); ExceptionCode ec = 0, ec2 = 0; if (m_resumeScopingFromRange) { searchRange->setStart(m_resumeScopingFromRange->startContainer(), m_resumeScopingFromRange->startOffset(ec2) + 1, ec); if (ec || ec2) { m_scopingComplete = true; // We should stop scoping because of some stale data. return; } } int matchCount = 0; bool timeout = false; double startTime = currentTime(); do { RefPtr<Range> resultRange(findPlainText(searchRange.get(), text, m_scopingCaseInsensitive ? CaseInsensitive : 0)); if (resultRange->collapsed(ec)) { if (!resultRange->startContainer()->isInShadowTree()) break; searchRange->setStartAfter(resultRange->startContainer()->deprecatedShadowAncestorNode(), ec); searchRange->setEnd(originalEndContainer, originalEndOffset, ec); continue; } ++matchCount; bool foundActiveMatch = false; if (m_locatingActiveMatch && areRangesEqual(resultRange.get(), m_activeMatch.get())) { foundActiveMatch = true; m_locatingActiveMatch = false; if (locateActiveMatchOnly) { m_activeMatchIndex += matchCount; m_webPage->m_client->updateFindStringResult(m_activeMatchCount, m_activeMatchIndex); return; } m_activeMatchIndex = m_activeMatchCount + matchCount; } if (!locateActiveMatchOnly && m_highlightAllMatches) resultRange->ownerDocument().markers().addTextMatchMarker(resultRange.get(), foundActiveMatch); searchRange->setStart(resultRange->endContainer(ec), resultRange->endOffset(ec), ec); ShadowRoot* shadowTreeRoot = searchRange->shadowRoot(); if (searchRange->collapsed(ec) && shadowTreeRoot) searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec); m_resumeScopingFromRange = resultRange; timeout = (currentTime() - startTime) >= MaxScopingDuration; } while (!timeout); if (matchCount > 0) { if (locateActiveMatchOnly) { // We have not found it yet. // m_activeMatchIndex now temporarily remember where we left over in this time slot. m_activeMatchIndex += matchCount; } else { if (m_highlightAllMatches) scopingFrame->editor().setMarkedTextMatchesAreHighlighted(true /* highlight */); m_activeMatchCount += matchCount; m_webPage->m_client->updateFindStringResult(m_activeMatchCount, m_activeMatchIndex); } } if (timeout) scopeStringMatchesSoon(scopingFrame, text, false /* reset */, locateActiveMatchOnly); else { // Scoping is done for this frame. Frame* nextFrame = DOMSupport::incrementFrame(scopingFrame, true /* forward */, false /* wrapFlag */); if (!nextFrame) { m_scopingComplete = true; return; // Scoping is done for all frames; } scopeStringMatchesSoon(nextFrame, text, false /* reset */, locateActiveMatchOnly); } }
void InPageSearchManager::scopeStringMatches(const String& text, bool reset, Frame* scopingFrame) { if (reset) { m_activeMatchCount = 0; m_resumeScopingFromRange = 0; m_scopingComplete = false; m_locatingActiveMatch = true; // New search should always start from mainFrame. scopeStringMatchesSoon(m_webPage->mainFrame(), text, false /* reset */); return; } if (m_resumeScopingFromRange && scopingFrame != m_resumeScopingFromRange->ownerDocument()->frame()) m_resumeScopingFromRange = 0; RefPtr<Range> searchRange(rangeOfContents(scopingFrame->document())); Node* originalEndContainer = searchRange->endContainer(); int originalEndOffset = searchRange->endOffset(); ExceptionCode ec = 0, ec2 = 0; if (m_resumeScopingFromRange) { searchRange->setStart(m_resumeScopingFromRange->startContainer(), m_resumeScopingFromRange->startOffset(ec2) + 1, ec); if (ec || ec2) { m_scopingComplete = true; // We should stop scoping because of some stale data. return; } } int matchCount = 0; bool timeout = false; double startTime = currentTime(); do { RefPtr<Range> resultRange(findPlainText(searchRange.get(), text, CaseInsensitive)); if (resultRange->collapsed(ec)) { if (!resultRange->startContainer()->isInShadowTree()) break; searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), ec); searchRange->setEnd(originalEndContainer, originalEndOffset, ec); continue; } if (scopingFrame->editor()->insideVisibleArea(resultRange.get())) { ++matchCount; bool foundActiveMatch = false; if (m_locatingActiveMatch && areRangesEqual(resultRange.get(), m_activeMatch.get())) { foundActiveMatch = true; m_locatingActiveMatch = false; m_activeMatchIndex = m_activeMatchCount + matchCount; // FIXME: We need to notify client with m_activeMatchIndex. } resultRange->ownerDocument()->markers()->addTextMatchMarker(resultRange.get(), foundActiveMatch); } searchRange->setStart(resultRange->endContainer(ec), resultRange->endOffset(ec), ec); Node* shadowTreeRoot = searchRange->shadowTreeRootNode(); if (searchRange->collapsed(ec) && shadowTreeRoot) searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec); m_resumeScopingFromRange = resultRange; timeout = (currentTime() - startTime) >= MaxScopingDuration; } while (!timeout); if (matchCount > 0) { scopingFrame->editor()->setMarkedTextMatchesAreHighlighted(true /* highlight */); m_activeMatchCount += matchCount; } if (timeout) scopeStringMatchesSoon(scopingFrame, text, false /* reset */); else { // Scoping is done for this frame. Frame* nextFrame = DOMSupport::incrementFrame(scopingFrame, true /* forward */, false /* wrapFlag */); if (!nextFrame) { m_scopingComplete = true; return; // Scoping is done for all frames; } scopeStringMatchesSoon(nextFrame, text, false /* reset */); } }