From f82d0f0c94434c46112239fd966f204550b649c8 Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Tue, 29 Mar 2016 20:34:40 +0200 Subject: [PATCH] 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. --- tests/integration/quteprocess.py | 33 ++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/integration/quteprocess.py b/tests/integration/quteprocess.py index 1cce4fb1a..e3f7ec882 100644 --- a/tests/integration/quteprocess.py +++ b/tests/integration/quteprocess.py @@ -432,13 +432,13 @@ class QuteProc(testprocess.Process): # Use Javascript and XPath to find the right element, use console.log to # return an error (no element found, ambiguous element) script = ( - 'var _es = document.evaluate(\'//*[text()="{text}"]\', document, ' + 'var _es = document.evaluate(\'//*[text()={text}]\', document, ' 'null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);' 'if (_es.snapshotLength == 0) {{ console.log("qute:no elems"); }} ' 'else if (_es.snapshotLength > 1) {{ console.log("qute:ambiguous ' 'elems") }} ' '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) message = self.wait_for_js('qute:*').message if message.endswith('qute:no elems'): @@ -450,6 +450,35 @@ class QuteProc(testprocess.Process): .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') def quteproc_process(qapp, httpbin, request): """Fixture for qutebrowser process which is started once per file."""