quteprocess: properly escape xpath expression

Since XPath doesn't have a way to escape quotes (or any other
character), we have to use a workaround by using concat() and switching
between quoting styles.
This commit is contained in:
Daniel Schadt 2016-03-29 20:34:40 +02:00
parent b6c5ff25fd
commit f82d0f0c94

View File

@ -432,13 +432,13 @@ class QuteProc(testprocess.Process):
# Use Javascript and XPath to find the right element, use console.log to # Use Javascript and XPath to find the right element, use console.log to
# return an error (no element found, ambiguous element) # return an error (no element found, ambiguous element)
script = ( script = (
'var _es = document.evaluate(\'//*[text()="{text}"]\', document, ' 'var _es = document.evaluate(\'//*[text()={text}]\', document, '
'null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);' 'null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);'
'if (_es.snapshotLength == 0) {{ console.log("qute:no elems"); }} ' 'if (_es.snapshotLength == 0) {{ console.log("qute:no elems"); }} '
'else if (_es.snapshotLength > 1) {{ console.log("qute:ambiguous ' 'else if (_es.snapshotLength > 1) {{ console.log("qute:ambiguous '
'elems") }} ' 'elems") }} '
'else {{ console.log("qute:okay"); _es.snapshotItem(0).click() }}' 'else {{ console.log("qute:okay"); _es.snapshotItem(0).click() }}'
).format(text=webelem.javascript_escape(text)) ).format(text=webelem.javascript_escape(_xpath_escape(text)))
self.send_cmd(':jseval ' + script) self.send_cmd(':jseval ' + script)
message = self.wait_for_js('qute:*').message message = self.wait_for_js('qute:*').message
if message.endswith('qute:no elems'): if message.endswith('qute:no elems'):
@ -450,6 +450,35 @@ class QuteProc(testprocess.Process):
.format(message)) .format(message))
def _xpath_escape(text):
"""Escape a string to be used in an XPath expression.
The resulting string should still be escaped with javascript_escape, to
prevent javascript from interpreting the quotes.
This function is needed because XPath does not provide any character
escaping mechanisms, so to get the string
"I'm back", he said
you have to use concat like
concat('"I', "'m back", '", he said')
Args:
text: Text to escape
Return:
The string "escaped" as a concat() call.
"""
# Shortcut if at most a single quoting style is used
if not "'" in text or not '"' in text:
return repr(text)
parts = re.split('([\'"])', text)
# Python's repr() of strings will automatically choose the right quote type.
# Since each part only contains one "type" of quote, no escaping should be
# necessary.
parts = [repr(part) for part in parts if part]
return 'concat({})'.format(', '.join(parts))
@pytest.yield_fixture(scope='module') @pytest.yield_fixture(scope='module')
def quteproc_process(qapp, httpbin, request): def quteproc_process(qapp, httpbin, request):
"""Fixture for qutebrowser process which is started once per file.""" """Fixture for qutebrowser process which is started once per file."""