summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brüning <michael.bruning@qt.io>2018-08-20 16:26:03 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-21 15:18:19 +0200
commitbe18c1dcfa68328acdf11e0a534749d9a5d75f8b (patch)
tree53a7b347d2771f4502338ac973a80a37edacdee7
parentb74e93a9b07243da4f3d2784cde49916de455364 (diff)
parente56dd7e37ea96c4ba1354e319bdaa9d01c401747 (diff)
Merge remote-tracking branch 'origin/5.11' into 5.12
Conflicts: .qmake.conf configure.json examples/webenginewidgets/markdowneditor/resources/3rdparty/marked.js examples/webenginewidgets/markdowneditor/resources/3rdparty/qt_attribution.json examples/webenginewidgets/markdowneditor/resources/markdowneditor.qrc mkspecs/features/platform.prf src/3rdparty src/core/media_capture_devices_dispatcher.cpp src/core/net/url_request_context_getter_qt.cpp src/core/net/url_request_context_getter_qt.h src/core/web_contents_adapter.cpp Change-Id: I467133ba455b1f85f6bb61793794c31cb1094541
-rw-r--r--config.tests/libvpx/libvpx.cpp2
-rw-r--r--config.tests/libvpx/libvpx.pro3
-rw-r--r--examples/webengine/quicknanobrowser/BrowserWindow.qml5
-rw-r--r--examples/webengine/recipebrowser/resources/pages/assets/3rdparty/MARKED-LICENSE.txt2
-rw-r--r--examples/webengine/recipebrowser/resources/pages/assets/3rdparty/marked.js1514
-rw-r--r--examples/webengine/recipebrowser/resources/pages/assets/3rdparty/marked.min.js6
-rw-r--r--examples/webengine/recipebrowser/resources/pages/assets/3rdparty/qt_attribution.json8
-rw-r--r--examples/webengine/recipebrowser/resources/pages/burger.html2
-rw-r--r--examples/webengine/recipebrowser/resources/pages/cupcakes.html2
-rw-r--r--examples/webengine/recipebrowser/resources/pages/pasta.html2
-rw-r--r--examples/webengine/recipebrowser/resources/pages/pizza.html2
-rw-r--r--examples/webengine/recipebrowser/resources/pages/skewers.html2
-rw-r--r--examples/webengine/recipebrowser/resources/pages/soup.html2
-rw-r--r--examples/webengine/recipebrowser/resources/pages/steak.html2
-rw-r--r--examples/webengine/recipebrowser/resources/resources.qrc2
-rw-r--r--examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc2
-rw-r--r--examples/webenginewidgets/markdowneditor/resources/3rdparty/marked.js374
-rw-r--r--examples/webenginewidgets/markdowneditor/resources/3rdparty/qt_attribution.json4
-rw-r--r--mkspecs/features/platform.prf21
m---------src/3rdparty0
-rw-r--r--src/core/api/qwebengineurlrequestjob.cpp26
-rw-r--r--src/core/config/windows.pri4
-rw-r--r--src/core/devtools_frontend_qt.cpp11
-rw-r--r--src/core/doc/src/qtwebenginecore-module.qdoc2
-rw-r--r--src/core/media_capture_devices_dispatcher.cpp11
-rw-r--r--src/core/net/url_request_custom_job_proxy.cpp2
-rw-r--r--src/core/render_widget_host_view_qt.cpp19
-rw-r--r--src/core/render_widget_host_view_qt.h3
-rw-r--r--src/core/render_widget_host_view_qt_delegate.h1
-rw-r--r--src/core/web_contents_adapter.cpp42
-rw-r--r--src/core/web_contents_adapter.h3
-rw-r--r--src/core/web_engine_context.cpp4
-rw-r--r--src/core/web_event_factory.cpp6
-rw-r--r--src/core/web_event_factory.h4
-rw-r--r--src/webengine/api/qquickwebengineview.cpp2
-rw-r--r--src/webengine/doc/src/qtwebengine-module.qdoc2
-rw-r--r--src/webengine/doc/src/qtwebengine-platform-notes.qdoc2
-rw-r--r--src/webenginewidgets/api/qwebenginepage.cpp5
-rw-r--r--src/webenginewidgets/api/qwebengineview.cpp2
-rw-r--r--src/webenginewidgets/doc/src/qtwebenginewidgets-module.qdoc2
-rw-r--r--src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp22
-rw-r--r--src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h7
-rw-r--r--tests/auto/widgets/faviconmanager/tst_faviconmanager.cpp7
-rw-r--r--tests/auto/widgets/qwebenginepage/BLACKLIST2
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp41
-rw-r--r--tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp40
-rw-r--r--tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp2
47 files changed, 2034 insertions, 197 deletions
diff --git a/config.tests/libvpx/libvpx.cpp b/config.tests/libvpx/libvpx.cpp
index ec261cd5a..d4b34c6b5 100644
--- a/config.tests/libvpx/libvpx.cpp
+++ b/config.tests/libvpx/libvpx.cpp
@@ -26,8 +26,8 @@
**
****************************************************************************/
-#include <vpx/svc_context.h>
#include <vpx/vpx_frame_buffer.h>
+#include <vpx/vp8cx.h>
#include <vpx/vp8dx.h>
#ifndef VPX_CTRL_VPXD_GET_LAST_QUANTIZER
diff --git a/config.tests/libvpx/libvpx.pro b/config.tests/libvpx/libvpx.pro
index aff6d1857..13ce13647 100644
--- a/config.tests/libvpx/libvpx.pro
+++ b/config.tests/libvpx/libvpx.pro
@@ -1,3 +1,4 @@
SOURCES += libvpx.cpp
-PKGCONFIG += libvpx
+CONFIG += link_pkgconfig
+PKGCONFIG += vpx
CONFIG -= qt
diff --git a/examples/webengine/quicknanobrowser/BrowserWindow.qml b/examples/webengine/quicknanobrowser/BrowserWindow.qml
index f60d6ad1f..7b8767b8d 100644
--- a/examples/webengine/quicknanobrowser/BrowserWindow.qml
+++ b/examples/webengine/quicknanobrowser/BrowserWindow.qml
@@ -505,6 +505,11 @@ ApplicationWindow {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
+ onNewViewRequested: function(request) {
+ var tab = tabs.createEmptyTab(currentWebView.profile);
+ tabs.currentIndex = tabs.count - 1;
+ request.openIn(tab.item);
+ }
}
MessageDialog {
id: sslDialog
diff --git a/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/MARKED-LICENSE.txt b/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/MARKED-LICENSE.txt
index a7b812ed6..8e3ba0e0a 100644
--- a/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/MARKED-LICENSE.txt
+++ b/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/MARKED-LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2011-2014, Christopher Jeffrey (https://github.com/chjj/)
+Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/marked.js b/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/marked.js
new file mode 100644
index 000000000..33c02d9cf
--- /dev/null
+++ b/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/marked.js
@@ -0,0 +1,1514 @@
+/**
+ * marked - a markdown parser
+ * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
+ * https://github.com/markedjs/marked
+ */
+
+;(function(root) {
+'use strict';
+
+/**
+ * Block-Level Grammar
+ */
+
+var block = {
+ newline: /^\n+/,
+ code: /^( {4}[^\n]+\n*)+/,
+ fences: noop,
+ hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
+ heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
+ nptable: noop,
+ blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
+ list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
+ html: '^ {0,3}(?:' // optional indentation
+ + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
+ + '|comment[^\\n]*(\\n+|$)' // (2)
+ + '|<\\?[\\s\\S]*?\\?>\\n*' // (3)
+ + '|<![A-Z][\\s\\S]*?>\\n*' // (4)
+ + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*' // (5)
+ + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
+ + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
+ + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
+ + ')',
+ def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
+ table: noop,
+ lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
+ paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)*)/,
+ text: /^[^\n]+/
+};
+
+block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
+block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
+block.def = edit(block.def)
+ .replace('label', block._label)
+ .replace('title', block._title)
+ .getRegex();
+
+block.bullet = /(?:[*+-]|\d+\.)/;
+block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
+block.item = edit(block.item, 'gm')
+ .replace(/bull/g, block.bullet)
+ .getRegex();
+
+block.list = edit(block.list)
+ .replace(/bull/g, block.bullet)
+ .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
+ .replace('def', '\\n+(?=' + block.def.source + ')')
+ .getRegex();
+
+block._tag = 'address|article|aside|base|basefont|blockquote|body|caption'
+ + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
+ + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
+ + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
+ + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'
+ + '|track|ul';
+block._comment = /<!--(?!-?>)[\s\S]*?-->/;
+block.html = edit(block.html, 'i')
+ .replace('comment', block._comment)
+ .replace('tag', block._tag)
+ .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
+ .getRegex();
+
+block.paragraph = edit(block.paragraph)
+ .replace('hr', block.hr)
+ .replace('heading', block.heading)
+ .replace('lheading', block.lheading)
+ .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
+ .getRegex();
+
+block.blockquote = edit(block.blockquote)
+ .replace('paragraph', block.paragraph)
+ .getRegex();
+
+/**
+ * Normal Block Grammar
+ */
+
+block.normal = merge({}, block);
+
+/**
+ * GFM Block Grammar
+ */
+
+block.gfm = merge({}, block.normal, {
+ fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\n? *\1 *(?:\n+|$)/,
+ paragraph: /^/,
+ heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
+});
+
+block.gfm.paragraph = edit(block.paragraph)
+ .replace('(?!', '(?!'
+ + block.gfm.fences.source.replace('\\1', '\\2') + '|'
+ + block.list.source.replace('\\1', '\\3') + '|')
+ .getRegex();
+
+/**
+ * GFM + Tables Block Grammar
+ */
+
+block.tables = merge({}, block.gfm, {
+ nptable: /^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,
+ table: /^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/
+});
+
+/**
+ * Pedantic grammar
+ */
+
+block.pedantic = merge({}, block.normal, {
+ html: edit(
+ '^ *(?:comment *(?:\\n|\\s*$)'
+ + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
+ + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
+ .replace('comment', block._comment)
+ .replace(/tag/g, '(?!(?:'
+ + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
+ + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
+ + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
+ .getRegex(),
+ def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/
+});
+
+/**
+ * Block Lexer
+ */
+
+function Lexer(options) {
+ this.tokens = [];
+ this.tokens.links = {};
+ this.options = options || marked.defaults;
+ this.rules = block.normal;
+
+ if (this.options.pedantic) {
+ this.rules = block.pedantic;
+ } else if (this.options.gfm) {
+ if (this.options.tables) {
+ this.rules = block.tables;
+ } else {
+ this.rules = block.gfm;
+ }
+ }
+}
+
+/**
+ * Expose Block Rules
+ */
+
+Lexer.rules = block;
+
+/**
+ * Static Lex Method
+ */
+
+Lexer.lex = function(src, options) {
+ var lexer = new Lexer(options);
+ return lexer.lex(src);
+};
+
+/**
+ * Preprocessing
+ */
+
+Lexer.prototype.lex = function(src) {
+ src = src
+ .replace(/\r\n|\r/g, '\n')
+ .replace(/\t/g, ' ')
+ .replace(/\u00a0/g, ' ')
+ .replace(/\u2424/g, '\n');
+
+ return this.token(src, true);
+};
+
+/**
+ * Lexing
+ */
+
+Lexer.prototype.token = function(src, top) {
+ src = src.replace(/^ +$/gm, '');
+ var next,
+ loose,
+ cap,
+ bull,
+ b,
+ item,
+ space,
+ i,
+ tag,
+ l,
+ isordered,
+ istask,
+ ischecked;
+
+ while (src) {
+ // newline
+ if (cap = this.rules.newline.exec(src)) {
+ src = src.substring(cap[0].length);
+ if (cap[0].length > 1) {
+ this.tokens.push({
+ type: 'space'
+ });
+ }
+ }
+
+ // code
+ if (cap = this.rules.code.exec(src)) {
+ src = src.substring(cap[0].length);
+ cap = cap[0].replace(/^ {4}/gm, '');
+ this.tokens.push({
+ type: 'code',
+ text: !this.options.pedantic
+ ? cap.replace(/\n+$/, '')
+ : cap
+ });
+ continue;
+ }
+
+ // fences (gfm)
+ if (cap = this.rules.fences.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'code',
+ lang: cap[2],
+ text: cap[3] || ''
+ });
+ continue;
+ }
+
+ // heading
+ if (cap = this.rules.heading.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'heading',
+ depth: cap[1].length,
+ text: cap[2]
+ });
+ continue;
+ }
+
+ // table no leading pipe (gfm)
+ if (top && (cap = this.rules.nptable.exec(src))) {
+ item = {
+ type: 'table',
+ header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
+ align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+ cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
+ };
+
+ if (item.header.length === item.align.length) {
+ src = src.substring(cap[0].length);
+
+ for (i = 0; i < item.align.length; i++) {
+ if (/^ *-+: *$/.test(item.align[i])) {
+ item.align[i] = 'right';
+ } else if (/^ *:-+: *$/.test(item.align[i])) {
+ item.align[i] = 'center';
+ } else if (/^ *:-+ *$/.test(item.align[i])) {
+ item.align[i] = 'left';
+ } else {
+ item.align[i] = null;
+ }
+ }
+
+ for (i = 0; i < item.cells.length; i++) {
+ item.cells[i] = splitCells(item.cells[i], item.header.length);
+ }
+
+ this.tokens.push(item);
+
+ continue;
+ }
+ }
+
+ // hr
+ if (cap = this.rules.hr.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'hr'
+ });
+ continue;
+ }
+
+ // blockquote
+ if (cap = this.rules.blockquote.exec(src)) {
+ src = src.substring(cap[0].length);
+
+ this.tokens.push({
+ type: 'blockquote_start'
+ });
+
+ cap = cap[0].replace(/^ *> ?/gm, '');
+
+ // Pass `top` to keep the current
+ // "toplevel" state. This is exactly
+ // how markdown.pl works.
+ this.token(cap, top);
+
+ this.tokens.push({
+ type: 'blockquote_end'
+ });
+
+ continue;
+ }
+
+ // list
+ if (cap = this.rules.list.exec(src)) {
+ src = src.substring(cap[0].length);
+ bull = cap[2];
+ isordered = bull.length > 1;
+
+ this.tokens.push({
+ type: 'list_start',
+ ordered: isordered,
+ start: isordered ? +bull : ''
+ });
+
+ // Get each top-level item.
+ cap = cap[0].match(this.rules.item);
+
+ next = false;
+ l = cap.length;
+ i = 0;
+
+ for (; i < l; i++) {
+ item = cap[i];
+
+ // Remove the list item's bullet
+ // so it is seen as the next token.
+ space = item.length;
+ item = item.replace(/^ *([*+-]|\d+\.) +/, '');
+
+ // Outdent whatever the
+ // list item contains. Hacky.
+ if (~item.indexOf('\n ')) {
+ space -= item.length;
+ item = !this.options.pedantic
+ ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
+ : item.replace(/^ {1,4}/gm, '');
+ }
+
+ // Determine whether the next list item belongs here.
+ // Backpedal if it does not belong in this list.
+ if (this.options.smartLists && i !== l - 1) {
+ b = block.bullet.exec(cap[i + 1])[0];
+ if (bull !== b && !(bull.length > 1 && b.length > 1)) {
+ src = cap.slice(i + 1).join('\n') + src;
+ i = l - 1;
+ }
+ }
+
+ // Determine whether item is loose or not.
+ // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
+ // for discount behavior.
+ loose = next || /\n\n(?!\s*$)/.test(item);
+ if (i !== l - 1) {
+ next = item.charAt(item.length - 1) === '\n';
+ if (!loose) loose = next;
+ }
+
+ // Check for task list items
+ istask = /^\[[ xX]\] /.test(item);
+ ischecked = undefined;
+ if (istask) {
+ ischecked = item[1] !== ' ';
+ item = item.replace(/^\[[ xX]\] +/, '');
+ }
+
+ this.tokens.push({
+ type: loose
+ ? 'loose_item_start'
+ : 'list_item_start',
+ task: istask,
+ checked: ischecked
+ });
+
+ // Recurse.
+ this.token(item, false);
+
+ this.tokens.push({
+ type: 'list_item_end'
+ });
+ }
+
+ this.tokens.push({
+ type: 'list_end'
+ });
+
+ continue;
+ }
+
+ // html
+ if (cap = this.rules.html.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: this.options.sanitize
+ ? 'paragraph'
+ : 'html',
+ pre: !this.options.sanitizer
+ && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
+ text: cap[0]
+ });
+ continue;
+ }
+
+ // def
+ if (top && (cap = this.rules.def.exec(src))) {
+ src = src.substring(cap[0].length);
+ if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
+ tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
+ if (!this.tokens.links[tag]) {
+ this.tokens.links[tag] = {
+ href: cap[2],
+ title: cap[3]
+ };
+ }
+ continue;
+ }
+
+ // table (gfm)
+ if (top && (cap = this.rules.table.exec(src))) {
+ item = {
+ type: 'table',
+ header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
+ align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+ cells: cap[3] ? cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') : []
+ };
+
+ if (item.header.length === item.align.length) {
+ src = src.substring(cap[0].length);
+
+ for (i = 0; i < item.align.length; i++) {
+ if (/^ *-+: *$/.test(item.align[i])) {
+ item.align[i] = 'right';
+ } else if (/^ *:-+: *$/.test(item.align[i])) {
+ item.align[i] = 'center';
+ } else if (/^ *:-+ *$/.test(item.align[i])) {
+ item.align[i] = 'left';
+ } else {
+ item.align[i] = null;
+ }
+ }
+
+ for (i = 0; i < item.cells.length; i++) {
+ item.cells[i] = splitCells(
+ item.cells[i].replace(/^ *\| *| *\| *$/g, ''),
+ item.header.length);
+ }
+
+ this.tokens.push(item);
+
+ continue;
+ }
+ }
+
+ // lheading
+ if (cap = this.rules.lheading.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'heading',
+ depth: cap[2] === '=' ? 1 : 2,
+ text: cap[1]
+ });
+ continue;
+ }
+
+ // top-level paragraph
+ if (top && (cap = this.rules.paragraph.exec(src))) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'paragraph',
+ text: cap[1].charAt(cap[1].length - 1) === '\n'
+ ? cap[1].slice(0, -1)
+ : cap[1]
+ });
+ continue;
+ }
+
+ // text
+ if (cap = this.rules.text.exec(src)) {
+ // Top-level should never reach here.
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'text',
+ text: cap[0]
+ });
+ continue;
+ }
+
+ if (src) {
+ throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
+ }
+ }
+
+ return this.tokens;
+};
+
+/**
+ * Inline-Level Grammar
+ */
+
+var inline = {
+ escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
+ autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
+ url: noop,
+ tag: '^comment'
+ + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
+ + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
+ + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
+ + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
+ + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
+ link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,
+ reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
+ nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
+ strong: /^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)|^__([^\s])__(?!_)|^\*\*([^\s])\*\*(?!\*)/,
+ em: /^_([^\s][\s\S]*?[^\s_])_(?!_)|^_([^\s_][\s\S]*?[^\s])_(?!_)|^\*([^\s][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*][\s\S]*?[^\s])\*(?!\*)|^_([^\s_])_(?!_)|^\*([^\s*])\*(?!\*)/,
+ code: /^(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/,
+ br: /^ {2,}\n(?!\s*$)/,
+ del: noop,
+ text: /^[\s\S]+?(?=[\\<!\[`*]|\b_| {2,}\n|$)/
+};
+
+inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
+
+inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
+inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;
+inline.autolink = edit(inline.autolink)
+ .replace('scheme', inline._scheme)
+ .replace('email', inline._email)
+ .getRegex();
+
+inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
+
+inline.tag = edit(inline.tag)
+ .replace('comment', block._comment)
+ .replace('attribute', inline._attribute)
+ .getRegex();
+
+inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/;
+inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?)/;
+inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
+
+inline.link = edit(inline.link)
+ .replace('label', inline._label)
+ .replace('href', inline._href)
+ .replace('title', inline._title)
+ .getRegex();
+
+inline.reflink = edit(inline.reflink)
+ .replace('label', inline._label)
+ .getRegex();
+
+/**
+ * Normal Inline Grammar
+ */
+
+inline.normal = merge({}, inline);
+
+/**
+ * Pedantic Inline Grammar
+ */
+
+inline.pedantic = merge({}, inline.normal, {
+ strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
+ em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
+ link: edit(/^!?\[(label)\]\((.*?)\)/)
+ .replace('label', inline._label)
+ .getRegex(),
+ reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
+ .replace('label', inline._label)
+ .getRegex()
+});
+
+/**
+ * GFM Inline Grammar
+ */
+
+inline.gfm = merge({}, inline.normal, {
+ escape: edit(inline.escape).replace('])', '~|])').getRegex(),
+ url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/)
+ .replace('email', inline._email)
+ .getRegex(),
+ _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
+ del: /^~~(?=\S)([\s\S]*?\S)~~/,
+ text: edit(inline.text)
+ .replace(']|', '~]|')
+ .replace('|', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|')
+ .getRegex()
+});
+
+/**
+ * GFM + Line Breaks Inline Grammar
+ */
+
+inline.breaks = merge({}, inline.gfm, {
+ br: edit(inline.br).replace('{2,}', '*').getRegex(),
+ text: edit(inline.gfm.text).replace('{2,}', '*').getRegex()
+});
+
+/**
+ * Inline Lexer & Compiler
+ */
+
+function InlineLexer(links, options) {
+ this.options = options || marked.defaults;
+ this.links = links;
+ this.rules = inline.normal;
+ this.renderer = this.options.renderer || new Renderer();
+ this.renderer.options = this.options;
+
+ if (!this.links) {
+ throw new Error('Tokens array requires a `links` property.');
+ }
+
+ if (this.options.pedantic) {
+ this.rules = inline.pedantic;
+ } else if (this.options.gfm) {
+ if (this.options.breaks) {
+ this.rules = inline.breaks;
+ } else {
+ this.rules = inline.gfm;
+ }
+ }
+}
+
+/**
+ * Expose Inline Rules
+ */
+
+InlineLexer.rules = inline;
+
+/**
+ * Static Lexing/Compiling Method
+ */
+
+InlineLexer.output = function(src, links, options) {
+ var inline = new InlineLexer(links, options);
+ return inline.output(src);
+};
+
+/**
+ * Lexing/Compiling
+ */
+
+InlineLexer.prototype.output = function(src) {
+ var out = '',
+ link,
+ text,
+ href,
+ title,
+ cap;
+
+ while (src) {
+ // escape
+ if (cap = this.rules.escape.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += cap[1];
+ continue;
+ }
+
+ // autolink
+ if (cap = this.rules.autolink.exec(src)) {
+ src = src.substring(cap[0].length);
+ if (cap[2] === '@') {
+ text = escape(this.mangle(cap[1]));
+ href = 'mailto:' + text;
+ } else {
+ text = escape(cap[1]);
+ href = text;
+ }
+ out += this.renderer.link(href, null, text);
+ continue;
+ }
+
+ // url (gfm)
+ if (!this.inLink && (cap = this.rules.url.exec(src))) {
+ cap[0] = this.rules._backpedal.exec(cap[0])[0];
+ src = src.substring(cap[0].length);
+ if (cap[2] === '@') {
+ text = escape(cap[0]);
+ href = 'mailto:' + text;
+ } else {
+ text = escape(cap[0]);
+ if (cap[1] === 'www.') {
+ href = 'http://' + text;
+ } else {
+ href = text;
+ }
+ }
+ out += this.renderer.link(href, null, text);
+ continue;
+ }
+
+ // tag
+ if (cap = this.rules.tag.exec(src)) {
+ if (!this.inLink && /^<a /i.test(cap[0])) {
+ this.inLink = true;
+ } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
+ this.inLink = false;
+ }
+ src = src.substring(cap[0].length);
+ out += this.options.sanitize
+ ? this.options.sanitizer
+ ? this.options.sanitizer(cap[0])
+ : escape(cap[0])
+ : cap[0]
+ continue;
+ }
+
+ // link
+ if (cap = this.rules.link.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.inLink = true;
+ href = cap[2];
+ if (this.options.pedantic) {
+ link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
+
+ if (link) {
+ href = link[1];
+ title = link[3];
+ } else {
+ title = '';
+ }
+ } else {
+ title = cap[3] ? cap[3].slice(1, -1) : '';
+ }
+ href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
+ out += this.outputLink(cap, {
+ href: InlineLexer.escapes(href),
+ title: InlineLexer.escapes(title)
+ });
+ this.inLink = false;
+ continue;
+ }
+
+ // reflink, nolink
+ if ((cap = this.rules.reflink.exec(src))
+ || (cap = this.rules.nolink.exec(src))) {
+ src = src.substring(cap[0].length);
+ link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
+ link = this.links[link.toLowerCase()];
+ if (!link || !link.href) {
+ out += cap[0].charAt(0);
+ src = cap[0].substring(1) + src;
+ continue;
+ }
+ this.inLink = true;
+ out += this.outputLink(cap, link);
+ this.inLink = false;
+ continue;
+ }
+
+ // strong
+ if (cap = this.rules.strong.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1]));
+ continue;
+ }
+
+ // em
+ if (cap = this.rules.em.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]));
+ continue;
+ }
+
+ // code
+ if (cap = this.rules.code.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.codespan(escape(cap[2].trim(), true));
+ continue;
+ }
+
+ // br
+ if (cap = this.rules.br.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.br();
+ continue;
+ }
+
+ // del (gfm)
+ if (cap = this.rules.del.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.del(this.output(cap[1]));
+ continue;
+ }
+
+ // text
+ if (cap = this.rules.text.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.text(escape(this.smartypants(cap[0])));
+ continue;
+ }
+
+ if (src) {
+ throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
+ }
+ }
+
+ return out;
+};
+
+InlineLexer.escapes = function(text) {
+ return text ? text.replace(InlineLexer.rules._escapes, '$1') : text;
+}
+
+/**
+ * Compile Link
+ */
+
+InlineLexer.prototype.outputLink = function(cap, link) {
+ var href = link.href,
+ title = link.title ? escape(link.title) : null;
+
+ return cap[0].charAt(0) !== '!'
+ ? this.renderer.link(href, title, this.output(cap[1]))
+ : this.renderer.image(href, title, escape(cap[1]));
+};
+
+/**
+ * Smartypants Transformations
+ */
+
+InlineLexer.prototype.smartypants = function(text) {
+ if (!this.options.smartypants) return text;
+ return text
+ // em-dashes
+ .replace(/---/g, '\u2014')
+ // en-dashes
+ .replace(/--/g, '\u2013')
+ // opening singles
+ .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
+ // closing singles & apostrophes
+ .replace(/'/g, '\u2019')
+ // opening doubles
+ .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
+ // closing doubles
+ .replace(/"/g, '\u201d')
+ // ellipses
+ .replace(/\.{3}/g, '\u2026');
+};
+
+/**
+ * Mangle Links
+ */
+
+InlineLexer.prototype.mangle = function(text) {
+ if (!this.options.mangle) return text;
+ var out = '',
+ l = text.length,
+ i = 0,
+ ch;
+
+ for (; i < l; i++) {
+ ch = text.charCodeAt(i);
+ if (Math.random() > 0.5) {
+ ch = 'x' + ch.toString(16);
+ }
+ out += '&#' + ch + ';';
+ }
+
+ return out;
+};
+
+/**
+ * Renderer
+ */
+
+function Renderer(options) {
+ this.options = options || marked.defaults;
+}
+
+Renderer.prototype.code = function(code, lang, escaped) {
+ if (this.options.highlight) {
+ var out = this.options.highlight(code, lang);
+ if (out != null && out !== code) {
+ escaped = true;
+ code = out;
+ }
+ }
+
+ if (!lang) {
+ return '<pre><code>'
+ + (escaped ? code : escape(code, true))
+ + '</code></pre>';
+ }
+
+ return '<pre><code class="'
+ + this.options.langPrefix
+ + escape(lang, true)
+ + '">'
+ + (escaped ? code : escape(code, true))
+ + '</code></pre>\n';
+};
+
+Renderer.prototype.blockquote = function(quote) {
+ return '<blockquote>\n' + quote + '</blockquote>\n';
+};
+
+Renderer.prototype.html = function(html) {
+ return html;
+};
+
+Renderer.prototype.heading = function(text, level, raw) {
+ if (this.options.headerIds) {
+ return '<h'
+ + level
+ + ' id="'
+ + this.options.headerPrefix
+ + raw.toLowerCase().replace(/[^\w]+/g, '-')
+ + '">'
+ + text
+ + '</h'
+ + level
+ + '>\n';
+ }
+ // ignore IDs
+ return '<h' + level + '>' + text + '</h' + level + '>\n';
+};
+
+Renderer.prototype.hr = function() {
+ return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
+};
+
+Renderer.prototype.list = function(body, ordered, start) {
+ var type = ordered ? 'ol' : 'ul',
+ startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
+ return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
+};
+
+Renderer.prototype.listitem = function(text) {
+ return '<li>' + text + '</li>\n';
+};
+
+Renderer.prototype.checkbox = function(checked) {
+ return '<input '
+ + (checked ? 'checked="" ' : '')
+ + 'disabled="" type="checkbox"'
+ + (this.options.xhtml ? ' /' : '')
+ + '> ';
+}
+
+Renderer.prototype.paragraph = function(text) {
+ return '<p>' + text + '</p>\n';
+};
+
+Renderer.prototype.table = function(header, body) {
+ if (body) body = '<tbody>' + body + '</tbody>';
+
+ return '<table>\n'
+ + '<thead>\n'
+ + header
+ + '</thead>\n'
+ + body
+ + '</table>\n';
+};
+
+Renderer.prototype.tablerow = function(content) {
+ return '<tr>\n' + content + '</tr>\n';
+};
+
+Renderer.prototype.tablecell = function(content, flags) {
+ var type = flags.header ? 'th' : 'td';
+ var tag = flags.align
+ ? '<' + type + ' align="' + flags.align + '">'
+ : '<' + type + '>';
+ return tag + content + '</' + type + '>\n';
+};
+
+// span level renderer
+Renderer.prototype.strong = function(text) {
+ return '<strong>' + text + '</strong>';
+};
+
+Renderer.prototype.em = function(text) {
+ return '<em>' + text + '</em>';
+};
+
+Renderer.prototype.codespan = function(text) {
+ return '<code>' + text + '</code>';
+};
+
+Renderer.prototype.br = function() {
+ return this.options.xhtml ? '<br/>' : '<br>';
+};
+
+Renderer.prototype.del = function(text) {
+ return '<del>' + text + '</del>';
+};
+
+Renderer.prototype.link = function(href, title, text) {
+ if (this.options.sanitize) {
+ try {
+ var prot = decodeURIComponent(unescape(href))
+ .replace(/[^\w:]/g, '')
+ .toLowerCase();
+ } catch (e) {
+ return text;
+ }
+ if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
+ return text;
+ }
+ }
+ if (this.options.baseUrl && !originIndependentUrl.test(href)) {
+ href = resolveUrl(this.options.baseUrl, href);
+ }
+ try {
+ href = encodeURI(href).replace(/%25/g, '%');
+ } catch (e) {
+ return text;
+ }
+ var out = '<a href="' + escape(href) + '"';
+ if (title) {
+ out += ' title="' + title + '"';
+ }
+ out += '>' + text + '</a>';
+ return out;
+};
+
+Renderer.prototype.image = function(href, title, text) {
+ if (this.options.baseUrl && !originIndependentUrl.test(href)) {
+ href = resolveUrl(this.options.baseUrl, href);
+ }
+ var out = '<img src="' + href + '" alt="' + text + '"';
+ if (title) {
+ out += ' title="' + title + '"';
+ }
+ out += this.options.xhtml ? '/>' : '>';
+ return out;
+};
+
+Renderer.prototype.text = function(text) {
+ return text;
+};
+
+/**
+ * TextRenderer
+ * returns only the textual part of the token
+ */
+
+function TextRenderer() {}
+
+// no need for block level renderers
+
+TextRenderer.prototype.strong =
+TextRenderer.prototype.em =
+TextRenderer.prototype.codespan =
+TextRenderer.prototype.del =
+TextRenderer.prototype.text = function (text) {
+ return text;
+}
+
+TextRenderer.prototype.link =
+TextRenderer.prototype.image = function(href, title, text) {
+ return '' + text;
+}
+
+TextRenderer.prototype.br = function() {
+ return '';
+}
+
+/**
+ * Parsing & Compiling
+ */
+
+function Parser(options) {
+ this.tokens = [];
+ this.token = null;
+ this.options = options || marked.defaults;
+ this.options.renderer = this.options.renderer || new Renderer();
+ this.renderer = this.options.renderer;
+ this.renderer.options = this.options;
+}
+
+/**
+ * Static Parse Method
+ */
+
+Parser.parse = function(src, options) {
+ var parser = new Parser(options);
+ return parser.parse(src);
+};
+
+/**
+ * Parse Loop
+ */
+
+Parser.prototype.parse = function(src) {
+ this.inline = new InlineLexer(src.links, this.options);
+ // use an InlineLexer with a TextRenderer to extract pure text
+ this.inlineText = new InlineLexer(
+ src.links,
+ merge({}, this.options, {renderer: new TextRenderer()})
+ );
+ this.tokens = src.reverse();
+
+ var out = '';
+ while (this.next()) {
+ out += this.tok();
+ }
+
+ return out;
+};
+
+/**
+ * Next Token
+ */
+
+Parser.prototype.next = function() {
+ return this.token = this.tokens.pop();
+};
+
+/**
+ * Preview Next Token
+ */
+
+Parser.prototype.peek = function() {
+ return this.tokens[this.tokens.length - 1] || 0;
+};
+
+/**
+ * Parse Text Tokens
+ */
+
+Parser.prototype.parseText = function() {
+ var body = this.token.text;
+
+ while (this.peek().type === 'text') {
+ body += '\n' + this.next().text;
+ }
+
+ return this.inline.output(body);
+};
+
+/**
+ * Parse Current Token
+ */
+
+Parser.prototype.tok = function() {
+ switch (this.token.type) {
+ case 'space': {
+ return '';
+ }
+ case 'hr': {
+ return this.renderer.hr();
+ }
+ case 'heading': {
+ return this.renderer.heading(
+ this.inline.output(this.token.text),
+ this.token.depth,
+ unescape(this.inlineText.output(this.token.text)));
+ }
+ case 'code': {
+ return this.renderer.code(this.token.text,
+ this.token.lang,
+ this.token.escaped);
+ }
+ case 'table': {
+ var header = '',
+ body = '',
+ i,
+ row,
+ cell,
+ j;
+
+ // header
+ cell = '';
+ for (i = 0; i < this.token.header.length; i++) {
+ cell += this.renderer.tablecell(
+ this.inline.output(this.token.header[i]),
+ { header: true, align: this.token.align[i] }
+ );
+ }
+ header += this.renderer.tablerow(cell);
+
+ for (i = 0; i < this.token.cells.length; i++) {
+ row = this.token.cells[i];
+
+ cell = '';
+ for (j = 0; j < row.length; j++) {
+ cell += this.renderer.tablecell(
+ this.inline.output(row[j]),
+ { header: false, align: this.token.align[j] }
+ );
+ }
+
+ body += this.renderer.tablerow(cell);
+ }
+ return this.renderer.table(header, body);
+ }
+ case 'blockquote_start': {
+ body = '';
+
+ while (this.next().type !== 'blockquote_end') {
+ body += this.tok();
+ }
+
+ return this.renderer.blockquote(body);
+ }
+ case 'list_start': {
+ body = '';
+ var ordered = this.token.ordered,
+ start = this.token.start;
+
+ while (this.next().type !== 'list_end') {
+ body += this.tok();
+ }
+
+ return this.renderer.list(body, ordered, start);
+ }
+ case 'list_item_start': {
+ body = '';
+
+ if (this.token.task) {
+ body += this.renderer.checkbox(this.token.checked);
+ }
+
+ while (this.next().type !== 'list_item_end') {
+ body += this.token.type === 'text'
+ ? this.parseText()
+ : this.tok();
+ }
+
+ return this.renderer.listitem(body);
+ }
+ case 'loose_item_start': {
+ body = '';
+
+ while (this.next().type !== 'list_item_end') {
+ body += this.tok();
+ }
+
+ return this.renderer.listitem(body);
+ }
+ case 'html': {
+ // TODO parse inline content if parameter markdown=1
+ return this.renderer.html(this.token.text);
+ }
+ case 'paragraph': {
+ return this.renderer.paragraph(this.inline.output(this.token.text));
+ }
+ case 'text': {
+ return this.renderer.paragraph(this.parseText());
+ }
+ }
+};
+
+/**
+ * Helpers
+ */
+
+function escape(html, encode) {
+ return html
+ .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/"/g, '&quot;')
+ .replace(/'/g, '&#39;');
+}
+
+function unescape(html) {
+ // explicitly match decimal, hex, and named HTML entities
+ return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
+ n = n.toLowerCase();
+ if (n === 'colon') return ':';
+ if (n.charAt(0) === '#') {
+ return n.charAt(1) === 'x'
+ ? String.fromCharCode(parseInt(n.substring(2), 16))
+ : String.fromCharCode(+n.substring(1));
+ }
+ return '';
+ });
+}
+
+function edit(regex, opt) {
+ regex = regex.source || regex;
+ opt = opt || '';
+ return {
+ replace: function(name, val) {
+ val = val.source || val;
+ val = val.replace(/(^|[^\[])\^/g, '$1');
+ regex = regex.replace(name, val);
+ return this;
+ },
+ getRegex: function() {
+ return new RegExp(regex, opt);
+ }
+ };
+}
+
+function resolveUrl(base, href) {
+ if (!baseUrls[' ' + base]) {
+ // we can ignore everything in base after the last slash of its path component,
+ // but we might need to add _that_
+ // https://tools.ietf.org/html/rfc3986#section-3
+ if (/^[^:]+:\/*[^/]*$/.test(base)) {
+ baseUrls[' ' + base] = base + '/';
+ } else {
+ baseUrls[' ' + base] = base.replace(/[^/]*$/, '');
+ }
+ }
+ base = baseUrls[' ' + base];
+
+ if (href.slice(0, 2) === '//') {
+ return base.replace(/:[\s\S]*/, ':') + href;
+ } else if (href.charAt(0) === '/') {
+ return base.replace(/(:\/*[^/]*)[\s\S]*/, '$1') + href;
+ } else {
+ return base + href;
+ }
+}
+var baseUrls = {};
+var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
+
+function noop() {}
+noop.exec = noop;
+
+function merge(obj) {
+ var i = 1,
+ target,
+ key;
+
+ for (; i < arguments.length; i++) {
+ target = arguments[i];
+ for (key in target) {
+ if (Object.prototype.hasOwnProperty.call(target, key)) {
+ obj[key] = target[key];
+ }
+ }
+ }
+
+ return obj;
+}
+
+function splitCells(tableRow, count) {
+ var cells = tableRow.replace(/([^\\])\|/g, '$1 |').split(/ +\| */),
+ i = 0;
+
+ if (cells.length > count) {
+ cells.splice(count);
+ } else {
+ while (cells.length < count) cells.push('');
+ }
+
+ for (; i < cells.length; i++) {
+ cells[i] = cells[i].replace(/\\\|/g, '|');
+ }
+ return cells;
+}
+
+/**
+ * Marked
+ */
+
+function marked(src, opt, callback) {
+ // throw error in case of non string input
+ if (typeof src === 'undefined' || src === null) {
+ throw new Error('marked(): input parameter is undefined or null');
+ }
+ if (typeof src !== 'string') {
+ throw new Error('marked(): input parameter is of type '
+ + Object.prototype.toString.call(src) + ', string expected');
+ }
+
+ if (callback || typeof opt === 'function') {
+ if (!callback) {
+ callback = opt;
+ opt = null;
+ }
+
+ opt = merge({}, marked.defaults, opt || {});
+
+ var highlight = opt.highlight,
+ tokens,
+ pending,
+ i = 0;
+
+ try {
+ tokens = Lexer.lex(src, opt)
+ } catch (e) {
+ return callback(e);
+ }
+
+ pending = tokens.length;
+
+ var done = function(err) {
+ if (err) {
+ opt.highlight = highlight;
+ return callback(err);
+ }
+
+ var out;
+
+ try {
+ out = Parser.parse(tokens, opt);
+ } catch (e) {
+ err = e;
+ }
+
+ opt.highlight = highlight;
+
+ return err
+ ? callback(err)
+ : callback(null, out);
+ };
+
+ if (!highlight || highlight.length < 3) {
+ return done();
+ }
+
+ delete opt.highlight;
+
+ if (!pending) return done();
+
+ for (; i < tokens.length; i++) {
+ (function(token) {
+ if (token.type !== 'code') {
+ return --pending || done();
+ }
+ return highlight(token.text, token.lang, function(err, code) {
+ if (err) return done(err);
+ if (code == null || code === token.text) {
+ return --pending || done();
+ }
+ token.text = code;
+ token.escaped = true;
+ --pending || done();
+ });
+ })(tokens[i]);
+ }
+
+ return;
+ }
+ try {
+ if (opt) opt = merge({}, marked.defaults, opt);
+ return Parser.parse(Lexer.lex(src, opt), opt);
+ } catch (e) {
+ e.message += '\nPlease report this to https://github.com/markedjs/marked.';
+ if ((opt || marked.defaults).silent) {
+ return '<p>An error occurred:</p><pre>'
+ + escape(e.message + '', true)
+ + '</pre>';
+ }
+ throw e;
+ }
+}
+
+/**
+ * Options
+ */
+
+marked.options =
+marked.setOptions = function(opt) {
+ merge(marked.defaults, opt);
+ return marked;
+};
+
+marked.getDefaults = function () {
+ return {
+ baseUrl: null,
+ breaks: false,
+ gfm: true,
+ headerIds: true,
+ headerPrefix: '',
+ highlight: null,
+ langPrefix: 'language-',
+ mangle: true,
+ pedantic: false,
+ renderer: new Renderer(),
+ sanitize: false,
+ sanitizer: null,
+ silent: false,
+ smartLists: false,
+ smartypants: false,
+ tables: true,
+ xhtml: false
+ };
+}
+
+marked.defaults = marked.getDefaults();
+
+/**
+ * Expose
+ */
+
+marked.Parser = Parser;
+marked.parser = Parser.parse;
+
+marked.Renderer = Renderer;
+marked.TextRenderer = TextRenderer;
+
+marked.Lexer = Lexer;
+marked.lexer = Lexer.lex;
+
+marked.InlineLexer = InlineLexer;
+marked.inlineLexer = InlineLexer.output;
+
+marked.parse = marked;
+
+if (typeof module !== 'undefined' && typeof exports === 'object') {
+ module.exports = marked;
+} else if (typeof define === 'function' && define.amd) {
+ define(function() { return marked; });
+} else {
+ root.marked = marked;
+}
+})(this || (typeof window !== 'undefined' ? window : global));
diff --git a/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/marked.min.js b/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/marked.min.js
deleted file mode 100644
index f679a4776..000000000
--- a/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/marked.min.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * marked - a markdown parser
- * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
- * https://github.com/chjj/marked
- */
-(function(){var block={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:noop,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};block.bullet=/(?:[*+-]|\d+\.)/;block.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+block.def.source+")")();block.blockquote=replace(block.blockquote)("def",block.def)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b";block.html=replace(block.html)("comment",/<!--[\s\S]*?-->/)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("\\1","\\2")+"|"+block.list.source.replace("\\1","\\3")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm){if(this.options.tables){this.rules=block.tables}else{this.rules=block.gfm}}}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)};Lexer.prototype.lex=function(src){src=src.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n");return this.token(src,true)};Lexer.prototype.token=function(src,top,bq){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1){this.tokens.push({type:"space"})}}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm,"");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/\n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]||""});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/\n$/,"").split("\n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].split(/ *\| */)}this.tokens.push(item);continue}if(cap=this.rules.lheading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[2]==="="?1:2,text:cap[1]});continue}if(cap=this.rules.hr.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"hr"});continue}if(cap=this.rules.blockquote.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"blockquote_start"});cap=cap[0].replace(/^ *> ?/gm,"");this.token(cap,top,true);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i<l;i++){item=cap[i];space=item.length;item=item.replace(/^ *([*+-]|\d+\.) +/,"");if(~item.indexOf("\n ")){space-=item.length;item=!this.options.pedantic?item.replace(new RegExp("^ {1,"+space+"}","gm"),""):item.replace(/^ {1,4}/gm,"")}if(this.options.smartLists&&i!==l-1){b=block.bullet.exec(cap[i+1])[0];if(bull!==b&&!(bull.length>1&&b.length>1)){src=cap.slice(i+1).join("\n")+src;i=l-1}}loose=next||/\n\n(?!\s*$)/.test(item);if(i!==l-1){next=item.charAt(item.length-1)==="\n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false,bq);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&(cap[1]==="pre"||cap[1]==="script"||cap[1]==="style"),text:cap[0]});continue}if(!bq&&top&&(cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/(?: *\| *)?\n$/,"").split("\n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].replace(/^ *\| *| *\| *$/g,"").split(/ *\| */)}this.tokens.push(item);continue}if(top&&(cap=this.rules.paragraph.exec(src))){src=src.substring(cap[0].length);this.tokens.push({type:"paragraph",text:cap[1].charAt(cap[1].length-1)==="\n"?cap[1].slice(0,-1):cap[1]});continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"text",text:cap[0]});continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return this.tokens};var inline={escape:/^\\([\\`*{}\[\]()#+\-.!_>])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:noop,tag:/^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:noop,text:/^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/};inline._inside=/(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;inline._href=/\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal;this.renderer=this.options.renderer||new Renderer;this.renderer.options=this.options;if(!this.links){throw new Error("Tokens array requires a `links` property.")}if(this.options.gfm){if(this.options.breaks){this.rules=inline.breaks}else{this.rules=inline.gfm}}else if(this.options.pedantic){this.rules=inline.pedantic}}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length);out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1].charAt(6)===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=this.renderer.link(href,null,text);continue}if(!this.inLink&&(cap=this.rules.url.exec(src))){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=this.renderer.link(href,null,text);continue}if(cap=this.rules.tag.exec(src)){if(!this.inLink&&/^<a /i.test(cap[0])){this.inLink=true}else if(this.inLink&&/^<\/a>/i.test(cap[0])){this.inLink=false}src=src.substring(cap[0].length);out+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(cap[0]):escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);this.inLink=true;out+=this.outputLink(cap,{href:cap[2],title:cap[3]});this.inLink=false;continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/\s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0].charAt(0);src=cap[0].substring(1)+src;continue}this.inLink=true;out+=this.outputLink(cap,link);this.inLink=false;continue}if(cap=this.rules.strong.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.strong(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.em(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.codespan(escape(cap[2],true));continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.br();continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.del(this.output(cap[1]));continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.text(escape(this.smartypants(cap[0])));continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return out};InlineLexer.prototype.outputLink=function(cap,link){var href=escape(link.href),title=link.title?escape(link.title):null;return cap[0].charAt(0)!=="!"?this.renderer.link(href,title,this.output(cap[1])):this.renderer.image(href,title,escape(cap[1]))};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…")};InlineLexer.prototype.mangle=function(text){if(!this.options.mangle)return text;var out="",l=text.length,i=0,ch;for(;i<l;i++){ch=text.charCodeAt(i);if(Math.random()>.5){ch="x"+ch.toString(16)}out+="&#"+ch+";"}return out};function Renderer(options){this.options=options||{}}Renderer.prototype.code=function(code,lang,escaped){if(this.options.highlight){var out=this.options.highlight(code,lang);if(out!=null&&out!==code){escaped=true;code=out}}if(!lang){return"<pre><code>"+(escaped?code:escape(code,true))+"\n</code></pre>"}return'<pre><code class="'+this.options.langPrefix+escape(lang,true)+'">'+(escaped?code:escape(code,true))+"\n</code></pre>\n"};Renderer.prototype.blockquote=function(quote){return"<blockquote>\n"+quote+"</blockquote>\n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"<h"+level+' id="'+this.options.headerPrefix+raw.toLowerCase().replace(/[^\w]+/g,"-")+'">'+text+"</h"+level+">\n"};Renderer.prototype.hr=function(){return this.options.xhtml?"<hr/>\n":"<hr>\n"};Renderer.prototype.list=function(body,ordered){var type=ordered?"ol":"ul";return"<"+type+">\n"+body+"</"+type+">\n"};Renderer.prototype.listitem=function(text){return"<li>"+text+"</li>\n"};Renderer.prototype.paragraph=function(text){return"<p>"+text+"</p>\n"};Renderer.prototype.table=function(header,body){return"<table>\n"+"<thead>\n"+header+"</thead>\n"+"<tbody>\n"+body+"</tbody>\n"+"</table>\n"};Renderer.prototype.tablerow=function(content){return"<tr>\n"+content+"</tr>\n"};Renderer.prototype.tablecell=function(content,flags){var type=flags.header?"th":"td";var tag=flags.align?"<"+type+' style="text-align:'+flags.align+'">':"<"+type+">";return tag+content+"</"+type+">\n"};Renderer.prototype.strong=function(text){return"<strong>"+text+"</strong>"};Renderer.prototype.em=function(text){return"<em>"+text+"</em>"};Renderer.prototype.codespan=function(text){return"<code>"+text+"</code>"};Renderer.prototype.br=function(){return this.options.xhtml?"<br/>":"<br>"};Renderer.prototype.del=function(text){return"<del>"+text+"</del>"};Renderer.prototype.link=function(href,title,text){if(this.options.sanitize){try{var prot=decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return""}if(prot.indexOf("javascript:")===0||prot.indexOf("vbscript:")===0){return""}}var out='<a href="'+href+'"';if(title){out+=' title="'+title+'"'}out+=">"+text+"</a>";return out};Renderer.prototype.image=function(href,title,text){var out='<img src="'+href+'" alt="'+text+'"';if(title){out+=' title="'+title+'"'}out+=this.options.xhtml?"/>":">";return out};Renderer.prototype.text=function(text){return text};function Parser(options){this.tokens=[];this.token=null;this.options=options||marked.defaults;this.options.renderer=this.options.renderer||new Renderer;this.renderer=this.options.renderer;this.renderer.options=this.options}Parser.parse=function(src,options,renderer){var parser=new Parser(options,renderer);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options,this.renderer);this.tokens=src.reverse();var out="";while(this.next()){out+=this.tok()}return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;while(this.peek().type==="text"){body+="\n"+this.next().text}return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case"space":{return""}case"hr":{return this.renderer.hr()}case"heading":{return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text)}case"code":{return this.renderer.code(this.token.text,this.token.lang,this.token.escaped)}case"table":{var header="",body="",i,row,cell,flags,j;cell="";for(i=0;i<this.token.header.length;i++){flags={header:true,align:this.token.align[i]};cell+=this.renderer.tablecell(this.inline.output(this.token.header[i]),{header:true,align:this.token.align[i]})}header+=this.renderer.tablerow(cell);for(i=0;i<this.token.cells.length;i++){row=this.token.cells[i];cell="";for(j=0;j<row.length;j++){cell+=this.renderer.tablecell(this.inline.output(row[j]),{header:false,align:this.token.align[j]})}body+=this.renderer.tablerow(cell)}return this.renderer.table(header,body)}case"blockquote_start":{var body="";while(this.next().type!=="blockquote_end"){body+=this.tok()}return this.renderer.blockquote(body)}case"list_start":{var body="",ordered=this.token.ordered;while(this.next().type!=="list_end"){body+=this.tok()}return this.renderer.list(body,ordered)}case"list_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.token.type==="text"?this.parseText():this.tok()}return this.renderer.listitem(body)}case"loose_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.tok()}return this.renderer.listitem(body)}case"html":{var html=!this.token.pre&&!this.options.pedantic?this.inline.output(this.token.text):this.token.text;return this.renderer.html(html)}case"paragraph":{return this.renderer.paragraph(this.inline.output(this.token.text))}case"text":{return this.renderer.paragraph(this.parseText())}}};function escape(html,encode){return html.replace(!encode?/&(?!#?\w+;)/g:/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function unescape(html){return html.replace(/&([#\w]+);/g,function(_,n){n=n.toLowerCase();if(n==="colon")return":";if(n.charAt(0)==="#"){return n.charAt(1)==="x"?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1))}return""})}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^\[])\^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i=1,target,key;for(;i<arguments.length;i++){target=arguments[i];for(key in target){if(Object.prototype.hasOwnProperty.call(target,key)){obj[key]=target[key]}}}return obj}function marked(src,opt,callback){if(callback||typeof opt==="function"){if(!callback){callback=opt;opt=null}opt=merge({},marked.defaults,opt||{});var highlight=opt.highlight,tokens,pending,i=0;try{tokens=Lexer.lex(src,opt)}catch(e){return callback(e)}pending=tokens.length;var done=function(err){if(err){opt.highlight=highlight;return callback(err)}var out;try{out=Parser.parse(tokens,opt)}catch(e){err=e}opt.highlight=highlight;return err?callback(err):callback(null,out)};if(!highlight||highlight.length<3){return done()}delete opt.highlight;if(!pending)return done();for(;i<tokens.length;i++){(function(token){if(token.type!=="code"){return--pending||done()}return highlight(token.text,token.lang,function(err,code){if(err)return done(err);if(code==null||code===token.text){return--pending||done()}token.text=code;token.escaped=true;--pending||done()})})(tokens[i])}return}try{if(opt)opt=merge({},marked.defaults,opt);return Parser.parse(Lexer.lex(src,opt),opt)}catch(e){e.message+="\nPlease report this to https://github.com/chjj/marked.";if((opt||marked.defaults).silent){return"<p>An error occurred:</p><pre>"+escape(e.message+"",true)+"</pre>"}throw e}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,sanitizer:null,mangle:true,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof module!=="undefined"&&typeof exports==="object"){module.exports=marked}else if(typeof define==="function"&&define.amd){define(function(){return marked})}else{this.marked=marked}}).call(function(){return this||(typeof window!=="undefined"?window:global)}());
diff --git a/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/qt_attribution.json b/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/qt_attribution.json
index 42947ca0b..a6c68f46f 100644
--- a/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/qt_attribution.json
+++ b/examples/webengine/recipebrowser/resources/pages/assets/3rdparty/qt_attribution.json
@@ -5,12 +5,12 @@
"QDocModule": "qtwebengine",
"QtUsage": "Marked is used in the WebEngine RecipeBrowser example",
"QtParts": [ "examples" ],
- "Files": "marked.min.js",
+ "Files": "marked.js",
"Description": "A full-featured markdown parser and compiler, written in JavaScript. Built for speed.",
"Homepage": "https://github.com/chjj/marked",
- "Version": "0.3.6",
- "DownloadLocation": "https://github.com/chjj/marked/blob/v0.3.6/marked.min.js",
- "Copyright": "Copyright (c) 2011-2014, Christopher Jeffrey",
+ "Version": "0.4.0",
+ "DownloadLocation": "https://github.com/markedjs/marked/blob/0.4.0/lib/marked.js",
+ "Copyright": "Copyright (c) 2011-2018, Christopher Jeffrey",
"License": "MIT License",
"LicenseId": "MIT",
"LicenseFile": "MARKED-LICENSE.txt"
diff --git a/examples/webengine/recipebrowser/resources/pages/burger.html b/examples/webengine/recipebrowser/resources/pages/burger.html
index 315c0a866..b10170cbd 100644
--- a/examples/webengine/recipebrowser/resources/pages/burger.html
+++ b/examples/webengine/recipebrowser/resources/pages/burger.html
@@ -67,7 +67,7 @@ give it a minute to go gorgeous and sloppy.
</div><!--End of content-->
- <script src="assets/3rdparty/marked.min.js"></script>
+ <script src="assets/3rdparty/marked.js"></script>
<script src="assets/custom.js"></script>
</body>
</html>
diff --git a/examples/webengine/recipebrowser/resources/pages/cupcakes.html b/examples/webengine/recipebrowser/resources/pages/cupcakes.html
index c169196f2..e21247825 100644
--- a/examples/webengine/recipebrowser/resources/pages/cupcakes.html
+++ b/examples/webengine/recipebrowser/resources/pages/cupcakes.html
@@ -45,7 +45,7 @@ Cupcakes
**Enjoy!**
</div><!--End of content-->
- <script src="assets/3rdparty/marked.min.js"></script>
+ <script src="assets/3rdparty/marked.js"></script>
<script src="assets/custom.js"></script>
</body>
</html>
diff --git a/examples/webengine/recipebrowser/resources/pages/pasta.html b/examples/webengine/recipebrowser/resources/pages/pasta.html
index c94b0df0a..4de65292e 100644
--- a/examples/webengine/recipebrowser/resources/pages/pasta.html
+++ b/examples/webengine/recipebrowser/resources/pages/pasta.html
@@ -48,7 +48,7 @@ Pasta
**Enjoy!**
</div><!--End of content-->
- <script src="assets/3rdparty/marked.min.js"></script>
+ <script src="assets/3rdparty/marked.js"></script>
<script src="assets/custom.js"></script>
</body>
</html>
diff --git a/examples/webengine/recipebrowser/resources/pages/pizza.html b/examples/webengine/recipebrowser/resources/pages/pizza.html
index a1ebfa18e..c63102934 100644
--- a/examples/webengine/recipebrowser/resources/pages/pizza.html
+++ b/examples/webengine/recipebrowser/resources/pages/pizza.html
@@ -39,7 +39,7 @@ Pizza Diavola
**Enjoy!**
</div><!--End of content-->
- <script src="assets/3rdparty/marked.min.js"></script>
+ <script src="assets/3rdparty/marked.js"></script>
<script src="assets/custom.js"></script>
</body>
</html>
diff --git a/examples/webengine/recipebrowser/resources/pages/skewers.html b/examples/webengine/recipebrowser/resources/pages/skewers.html
index 63d85f7e1..6fbf90cb4 100644
--- a/examples/webengine/recipebrowser/resources/pages/skewers.html
+++ b/examples/webengine/recipebrowser/resources/pages/skewers.html
@@ -45,7 +45,7 @@ Grilled skewers
</div><!--End of content-->
- <script src="assets/3rdparty/marked.min.js"></script>
+ <script src="assets/3rdparty/marked.js"></script>
<script src="assets/custom.js"></script>
</body>
</html>
diff --git a/examples/webengine/recipebrowser/resources/pages/soup.html b/examples/webengine/recipebrowser/resources/pages/soup.html
index c7537d94c..e99f016cf 100644
--- a/examples/webengine/recipebrowser/resources/pages/soup.html
+++ b/examples/webengine/recipebrowser/resources/pages/soup.html
@@ -33,7 +33,7 @@ Soup
**Enjoy!**
</div><!--End of content-->
- <script src="assets/3rdparty/marked.min.js"></script>
+ <script src="assets/3rdparty/marked.js"></script>
<script src="assets/custom.js"></script>
</body>
</html>
diff --git a/examples/webengine/recipebrowser/resources/pages/steak.html b/examples/webengine/recipebrowser/resources/pages/steak.html
index 1871f0fe8..c58395d10 100644
--- a/examples/webengine/recipebrowser/resources/pages/steak.html
+++ b/examples/webengine/recipebrowser/resources/pages/steak.html
@@ -59,7 +59,7 @@ Grilled steak and rice
</div><!--End of content-->
- <script src="assets/3rdparty/marked.min.js"></script>
+ <script src="assets/3rdparty/marked.js"></script>
<script src="assets/custom.js"></script>
</body>
</html>
diff --git a/examples/webengine/recipebrowser/resources/resources.qrc b/examples/webengine/recipebrowser/resources/resources.qrc
index 88919159b..bd13dcfae 100644
--- a/examples/webengine/recipebrowser/resources/resources.qrc
+++ b/examples/webengine/recipebrowser/resources/resources.qrc
@@ -11,7 +11,7 @@
<file>pages/skewers.html</file>
<file>pages/cupcakes.html</file>
- <file>pages/assets/3rdparty/marked.min.js</file>
+ <file>pages/assets/3rdparty/marked.js</file>
<file>pages/assets/3rdparty/markdown.css</file>
<file>pages/assets/custom.css</file>
<file>pages/assets/custom.js</file>
diff --git a/examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc b/examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc
index b06d4ee84..9ff8f0615 100644
--- a/examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc
+++ b/examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc
@@ -154,7 +154,7 @@
In the \e index.html, we load a custom stylesheet and two JavaScript
libraries. \l{http://kevinburke.bitbucket.org/markdowncss/}{markdown.css} is
a markdown-friendly stylesheet created by Kevin Burke.
- \l{https://github.com/chjj/marked}{marked.min.js} is a markdown parser and
+ \l{https://github.com/chjj/marked}{marked.js} is a markdown parser and
compiler designed for speed written by Christopher Jeffrey and
\e qwebchannel.js is part of the \l{QWebChannel} module.
diff --git a/examples/webenginewidgets/markdowneditor/resources/3rdparty/marked.js b/examples/webenginewidgets/markdowneditor/resources/3rdparty/marked.js
index 5552616ea..33c02d9cf 100644
--- a/examples/webenginewidgets/markdowneditor/resources/3rdparty/marked.js
+++ b/examples/webenginewidgets/markdowneditor/resources/3rdparty/marked.js
@@ -16,20 +16,29 @@ var block = {
code: /^( {4}[^\n]+\n*)+/,
fences: noop,
hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
- heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
+ heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
nptable: noop,
blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
- html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
+ html: '^ {0,3}(?:' // optional indentation
+ + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
+ + '|comment[^\\n]*(\\n+|$)' // (2)
+ + '|<\\?[\\s\\S]*?\\?>\\n*' // (3)
+ + '|<![A-Z][\\s\\S]*?>\\n*' // (4)
+ + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*' // (5)
+ + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
+ + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
+ + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
+ + ')',
def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
table: noop,
lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
- paragraph: /^([^\n]+(?:\n?(?!hr|heading|lheading| {0,3}>|tag)[^\n]+)+)/,
+ paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)*)/,
text: /^[^\n]+/
};
-block._label = /(?:\\[\[\]]|[^\[\]])+/;
-block._title = /(?:"(?:\\"|[^"]|"[^"\n]*")*"|'\n?(?:[^'\n]+\n?)*'|\([^()]*\))/;
+block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
+block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
block.def = edit(block.def)
.replace('label', block._label)
.replace('title', block._title)
@@ -47,23 +56,24 @@ block.list = edit(block.list)
.replace('def', '\\n+(?=' + block.def.source + ')')
.getRegex();
-block._tag = '(?!(?:'
- + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
- + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
- + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b';
-
-block.html = edit(block.html)
- .replace('comment', /<!--[\s\S]*?-->/)
- .replace('closed', /<(tag)[\s\S]+?<\/\1>/)
- .replace('closing', /<tag(?:"[^"]*"|'[^']*'|\s[^'"\/>\s]*)*?\/?>/)
- .replace(/tag/g, block._tag)
+block._tag = 'address|article|aside|base|basefont|blockquote|body|caption'
+ + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
+ + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
+ + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
+ + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'
+ + '|track|ul';
+block._comment = /<!--(?!-?>)[\s\S]*?-->/;
+block.html = edit(block.html, 'i')
+ .replace('comment', block._comment)
+ .replace('tag', block._tag)
+ .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
.getRegex();
block.paragraph = edit(block.paragraph)
.replace('hr', block.hr)
.replace('heading', block.heading)
.replace('lheading', block.lheading)
- .replace('tag', '<' + block._tag)
+ .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
.getRegex();
block.blockquote = edit(block.blockquote)
@@ -97,8 +107,26 @@ block.gfm.paragraph = edit(block.paragraph)
*/
block.tables = merge({}, block.gfm, {
- nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
- table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
+ nptable: /^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,
+ table: /^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/
+});
+
+/**
+ * Pedantic grammar
+ */
+
+block.pedantic = merge({}, block.normal, {
+ html: edit(
+ '^ *(?:comment *(?:\\n|\\s*$)'
+ + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
+ + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
+ .replace('comment', block._comment)
+ .replace(/tag/g, '(?!(?:'
+ + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
+ + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
+ + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
+ .getRegex(),
+ def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/
});
/**
@@ -111,7 +139,9 @@ function Lexer(options) {
this.options = options || marked.defaults;
this.rules = block.normal;
- if (this.options.gfm) {
+ if (this.options.pedantic) {
+ this.rules = block.pedantic;
+ } else if (this.options.gfm) {
if (this.options.tables) {
this.rules = block.tables;
} else {
@@ -165,7 +195,9 @@ Lexer.prototype.token = function(src, top) {
i,
tag,
l,
- isordered;
+ isordered,
+ istask,
+ ischecked;
while (src) {
// newline
@@ -215,34 +247,36 @@ Lexer.prototype.token = function(src, top) {
// table no leading pipe (gfm)
if (top && (cap = this.rules.nptable.exec(src))) {
- src = src.substring(cap[0].length);
-
item = {
type: 'table',
- header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+ header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
- cells: cap[3].replace(/\n$/, '').split('\n')
+ cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
};
- for (i = 0; i < item.align.length; i++) {
- if (/^ *-+: *$/.test(item.align[i])) {
- item.align[i] = 'right';
- } else if (/^ *:-+: *$/.test(item.align[i])) {
- item.align[i] = 'center';
- } else if (/^ *:-+ *$/.test(item.align[i])) {
- item.align[i] = 'left';
- } else {
- item.align[i] = null;
+ if (item.header.length === item.align.length) {
+ src = src.substring(cap[0].length);
+
+ for (i = 0; i < item.align.length; i++) {
+ if (/^ *-+: *$/.test(item.align[i])) {
+ item.align[i] = 'right';
+ } else if (/^ *:-+: *$/.test(item.align[i])) {
+ item.align[i] = 'center';
+ } else if (/^ *:-+ *$/.test(item.align[i])) {
+ item.align[i] = 'left';
+ } else {
+ item.align[i] = null;
+ }
}
- }
- for (i = 0; i < item.cells.length; i++) {
- item.cells[i] = item.cells[i].split(/ *\| */);
- }
+ for (i = 0; i < item.cells.length; i++) {
+ item.cells[i] = splitCells(item.cells[i], item.header.length);
+ }
- this.tokens.push(item);
+ this.tokens.push(item);
- continue;
+ continue;
+ }
}
// hr
@@ -331,10 +365,20 @@ Lexer.prototype.token = function(src, top) {
if (!loose) loose = next;
}
+ // Check for task list items
+ istask = /^\[[ xX]\] /.test(item);
+ ischecked = undefined;
+ if (istask) {
+ ischecked = item[1] !== ' ';
+ item = item.replace(/^\[[ xX]\] +/, '');
+ }
+
this.tokens.push({
type: loose
? 'loose_item_start'
- : 'list_item_start'
+ : 'list_item_start',
+ task: istask,
+ checked: ischecked
});
// Recurse.
@@ -370,7 +414,7 @@ Lexer.prototype.token = function(src, top) {
if (top && (cap = this.rules.def.exec(src))) {
src = src.substring(cap[0].length);
if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
- tag = cap[1].toLowerCase();
+ tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
if (!this.tokens.links[tag]) {
this.tokens.links[tag] = {
href: cap[2],
@@ -382,36 +426,38 @@ Lexer.prototype.token = function(src, top) {
// table (gfm)
if (top && (cap = this.rules.table.exec(src))) {
- src = src.substring(cap[0].length);
-
item = {
type: 'table',
- header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+ header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
- cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
+ cells: cap[3] ? cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') : []
};
- for (i = 0; i < item.align.length; i++) {
- if (/^ *-+: *$/.test(item.align[i])) {
- item.align[i] = 'right';
- } else if (/^ *:-+: *$/.test(item.align[i])) {
- item.align[i] = 'center';
- } else if (/^ *:-+ *$/.test(item.align[i])) {
- item.align[i] = 'left';
- } else {
- item.align[i] = null;
+ if (item.header.length === item.align.length) {
+ src = src.substring(cap[0].length);
+
+ for (i = 0; i < item.align.length; i++) {
+ if (/^ *-+: *$/.test(item.align[i])) {
+ item.align[i] = 'right';
+ } else if (/^ *:-+: *$/.test(item.align[i])) {
+ item.align[i] = 'center';
+ } else if (/^ *:-+ *$/.test(item.align[i])) {
+ item.align[i] = 'left';
+ } else {
+ item.align[i] = null;
+ }
}
- }
- for (i = 0; i < item.cells.length; i++) {
- item.cells[i] = item.cells[i]
- .replace(/^ *\| *| *\| *$/g, '')
- .split(/ *\| */);
- }
+ for (i = 0; i < item.cells.length; i++) {
+ item.cells[i] = splitCells(
+ item.cells[i].replace(/^ *\| *| *\| *$/g, ''),
+ item.header.length);
+ }
- this.tokens.push(item);
+ this.tokens.push(item);
- continue;
+ continue;
+ }
}
// lheading
@@ -461,39 +507,54 @@ Lexer.prototype.token = function(src, top) {
*/
var inline = {
- escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
+ escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
url: noop,
- tag: /^<!--[\s\S]*?-->|^<\/?[a-zA-Z0-9\-]+(?:"[^"]*"|'[^']*'|\s[^<'">\/\s]*)*?\/?>/,
- link: /^!?\[(inside)\]\(href\)/,
- reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
- nolink: /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\]/,
- strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
- em: /^_([^\s_](?:[^_]|__)+?[^\s_])_\b|^\*((?:\*\*|[^*])+?)\*(?!\*)/,
+ tag: '^comment'
+ + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
+ + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
+ + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
+ + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
+ + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
+ link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,
+ reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
+ nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
+ strong: /^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)|^__([^\s])__(?!_)|^\*\*([^\s])\*\*(?!\*)/,
+ em: /^_([^\s][\s\S]*?[^\s_])_(?!_)|^_([^\s_][\s\S]*?[^\s])_(?!_)|^\*([^\s][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*][\s\S]*?[^\s])\*(?!\*)|^_([^\s_])_(?!_)|^\*([^\s*])\*(?!\*)/,
code: /^(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/,
br: /^ {2,}\n(?!\s*$)/,
del: noop,
text: /^[\s\S]+?(?=[\\<!\[`*]|\b_| {2,}\n|$)/
};
+inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
+
inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;
-
inline.autolink = edit(inline.autolink)
.replace('scheme', inline._scheme)
.replace('email', inline._email)
- .getRegex()
+ .getRegex();
+
+inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
+
+inline.tag = edit(inline.tag)
+ .replace('comment', block._comment)
+ .replace('attribute', inline._attribute)
+ .getRegex();
-inline._inside = /(?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]]|\](?=[^\[]*\]))*/;
-inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
+inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/;
+inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?)/;
+inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
inline.link = edit(inline.link)
- .replace('inside', inline._inside)
+ .replace('label', inline._label)
.replace('href', inline._href)
+ .replace('title', inline._title)
.getRegex();
inline.reflink = edit(inline.reflink)
- .replace('inside', inline._inside)
+ .replace('label', inline._label)
.getRegex();
/**
@@ -508,7 +569,13 @@ inline.normal = merge({}, inline);
inline.pedantic = merge({}, inline.normal, {
strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
- em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
+ em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
+ link: edit(/^!?\[(label)\]\((.*?)\)/)
+ .replace('label', inline._label)
+ .getRegex(),
+ reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
+ .replace('label', inline._label)
+ .getRegex()
});
/**
@@ -552,14 +619,14 @@ function InlineLexer(links, options) {
throw new Error('Tokens array requires a `links` property.');
}
- if (this.options.gfm) {
+ if (this.options.pedantic) {
+ this.rules = inline.pedantic;
+ } else if (this.options.gfm) {
if (this.options.breaks) {
this.rules = inline.breaks;
} else {
this.rules = inline.gfm;
}
- } else if (this.options.pedantic) {
- this.rules = inline.pedantic;
}
}
@@ -587,6 +654,7 @@ InlineLexer.prototype.output = function(src) {
link,
text,
href,
+ title,
cap;
while (src) {
@@ -650,9 +718,23 @@ InlineLexer.prototype.output = function(src) {
if (cap = this.rules.link.exec(src)) {
src = src.substring(cap[0].length);
this.inLink = true;
+ href = cap[2];
+ if (this.options.pedantic) {
+ link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
+
+ if (link) {
+ href = link[1];
+ title = link[3];
+ } else {
+ title = '';
+ }
+ } else {
+ title = cap[3] ? cap[3].slice(1, -1) : '';
+ }
+ href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
out += this.outputLink(cap, {
- href: cap[2],
- title: cap[3]
+ href: InlineLexer.escapes(href),
+ title: InlineLexer.escapes(title)
});
this.inLink = false;
continue;
@@ -678,14 +760,14 @@ InlineLexer.prototype.output = function(src) {
// strong
if (cap = this.rules.strong.exec(src)) {
src = src.substring(cap[0].length);
- out += this.renderer.strong(this.output(cap[2] || cap[1]));
+ out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1]));
continue;
}
// em
if (cap = this.rules.em.exec(src)) {
src = src.substring(cap[0].length);
- out += this.renderer.em(this.output(cap[2] || cap[1]));
+ out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]));
continue;
}
@@ -725,12 +807,16 @@ InlineLexer.prototype.output = function(src) {
return out;
};
+InlineLexer.escapes = function(text) {
+ return text ? text.replace(InlineLexer.rules._escapes, '$1') : text;
+}
+
/**
* Compile Link
*/
InlineLexer.prototype.outputLink = function(cap, link) {
- var href = escape(link.href),
+ var href = link.href,
title = link.title ? escape(link.title) : null;
return cap[0].charAt(0) !== '!'
@@ -788,7 +874,7 @@ InlineLexer.prototype.mangle = function(text) {
*/
function Renderer(options) {
- this.options = options || {};
+ this.options = options || marked.defaults;
}
Renderer.prototype.code = function(code, lang, escaped) {
@@ -803,7 +889,7 @@ Renderer.prototype.code = function(code, lang, escaped) {
if (!lang) {
return '<pre><code>'
+ (escaped ? code : escape(code, true))
- + '\n</code></pre>';
+ + '</code></pre>';
}
return '<pre><code class="'
@@ -811,7 +897,7 @@ Renderer.prototype.code = function(code, lang, escaped) {
+ escape(lang, true)
+ '">'
+ (escaped ? code : escape(code, true))
- + '\n</code></pre>\n';
+ + '</code></pre>\n';
};
Renderer.prototype.blockquote = function(quote) {
@@ -823,16 +909,20 @@ Renderer.prototype.html = function(html) {
};
Renderer.prototype.heading = function(text, level, raw) {
- return '<h'
- + level
- + ' id="'
- + this.options.headerPrefix
- + raw.toLowerCase().replace(/[^\w]+/g, '-')
- + '">'
- + text
- + '</h'
- + level
- + '>\n';
+ if (this.options.headerIds) {
+ return '<h'
+ + level
+ + ' id="'
+ + this.options.headerPrefix
+ + raw.toLowerCase().replace(/[^\w]+/g, '-')
+ + '">'
+ + text
+ + '</h'
+ + level
+ + '>\n';
+ }
+ // ignore IDs
+ return '<h' + level + '>' + text + '</h' + level + '>\n';
};
Renderer.prototype.hr = function() {
@@ -849,18 +939,26 @@ Renderer.prototype.listitem = function(text) {
return '<li>' + text + '</li>\n';
};
+Renderer.prototype.checkbox = function(checked) {
+ return '<input '
+ + (checked ? 'checked="" ' : '')
+ + 'disabled="" type="checkbox"'
+ + (this.options.xhtml ? ' /' : '')
+ + '> ';
+}
+
Renderer.prototype.paragraph = function(text) {
return '<p>' + text + '</p>\n';
};
Renderer.prototype.table = function(header, body) {
+ if (body) body = '<tbody>' + body + '</tbody>';
+
return '<table>\n'
+ '<thead>\n'
+ header
+ '</thead>\n'
- + '<tbody>\n'
+ body
- + '</tbody>\n'
+ '</table>\n';
};
@@ -871,7 +969,7 @@ Renderer.prototype.tablerow = function(content) {
Renderer.prototype.tablecell = function(content, flags) {
var type = flags.header ? 'th' : 'td';
var tag = flags.align
- ? '<' + type + ' style="text-align:' + flags.align + '">'
+ ? '<' + type + ' align="' + flags.align + '">'
: '<' + type + '>';
return tag + content + '</' + type + '>\n';
};
@@ -913,7 +1011,12 @@ Renderer.prototype.link = function(href, title, text) {
if (this.options.baseUrl && !originIndependentUrl.test(href)) {
href = resolveUrl(this.options.baseUrl, href);
}
- var out = '<a href="' + href + '"';
+ try {
+ href = encodeURI(href).replace(/%25/g, '%');
+ } catch (e) {
+ return text;
+ }
+ var out = '<a href="' + escape(href) + '"';
if (title) {
out += ' title="' + title + '"';
}
@@ -1115,6 +1218,10 @@ Parser.prototype.tok = function() {
case 'list_item_start': {
body = '';
+ if (this.token.task) {
+ body += this.renderer.checkbox(this.token.checked);
+ }
+
while (this.next().type !== 'list_item_end') {
body += this.token.type === 'text'
? this.parseText()
@@ -1133,10 +1240,8 @@ Parser.prototype.tok = function() {
return this.renderer.listitem(body);
}
case 'html': {
- var html = !this.token.pre && !this.options.pedantic
- ? this.inline.output(this.token.text)
- : this.token.text;
- return this.renderer.html(html);
+ // TODO parse inline content if parameter markdown=1
+ return this.renderer.html(this.token.text);
}
case 'paragraph': {
return this.renderer.paragraph(this.inline.output(this.token.text));
@@ -1175,7 +1280,7 @@ function unescape(html) {
}
function edit(regex, opt) {
- regex = regex.source;
+ regex = regex.source || regex;
opt = opt || '';
return {
replace: function(name, val) {
@@ -1234,6 +1339,22 @@ function merge(obj) {
return obj;
}
+function splitCells(tableRow, count) {
+ var cells = tableRow.replace(/([^\\])\|/g, '$1 |').split(/ +\| */),
+ i = 0;
+
+ if (cells.length > count) {
+ cells.splice(count);
+ } else {
+ while (cells.length < count) cells.push('');
+ }
+
+ for (; i < cells.length; i++) {
+ cells[i] = cells[i].replace(/\\\|/g, '|');
+ }
+ return cells;
+}
+
/**
* Marked
*/
@@ -1341,24 +1462,29 @@ marked.setOptions = function(opt) {
return marked;
};
-marked.defaults = {
- gfm: true,
- tables: true,
- breaks: false,
- pedantic: false,
- sanitize: false,
- sanitizer: null,
- mangle: true,
- smartLists: false,
- silent: false,
- highlight: null,
- langPrefix: 'lang-',
- smartypants: false,
- headerPrefix: '',
- renderer: new Renderer(),
- xhtml: false,
- baseUrl: null
-};
+marked.getDefaults = function () {
+ return {
+ baseUrl: null,
+ breaks: false,
+ gfm: true,
+ headerIds: true,
+ headerPrefix: '',
+ highlight: null,
+ langPrefix: 'language-',
+ mangle: true,
+ pedantic: false,
+ renderer: new Renderer(),
+ sanitize: false,
+ sanitizer: null,
+ silent: false,
+ smartLists: false,
+ smartypants: false,
+ tables: true,
+ xhtml: false
+ };
+}
+
+marked.defaults = marked.getDefaults();
/**
* Expose
diff --git a/examples/webenginewidgets/markdowneditor/resources/3rdparty/qt_attribution.json b/examples/webenginewidgets/markdowneditor/resources/3rdparty/qt_attribution.json
index 70f84b993..de5458eff 100644
--- a/examples/webenginewidgets/markdowneditor/resources/3rdparty/qt_attribution.json
+++ b/examples/webenginewidgets/markdowneditor/resources/3rdparty/qt_attribution.json
@@ -8,8 +8,8 @@
"Files": "marked.js",
"Description": "A full-featured markdown parser and compiler, written in JavaScript. Built for speed.",
"Homepage": "https://github.com/chjj/marked",
- "Version": "0.3.19",
- "DownloadLocation": "https://github.com/chjj/marked/blob/v0.3.19/lib/marked.js",
+ "Version": "0.4.0",
+ "DownloadLocation": "https://github.com/markedjs/marked/blob/0.4.0/lib/marked.js",
"Copyright": "Copyright (c) 2011-2018, Christopher Jeffrey",
"License": "MIT License",
"LicenseId": "MIT",
diff --git a/mkspecs/features/platform.prf b/mkspecs/features/platform.prf
index bf235a9aa..f36b506d8 100644
--- a/mkspecs/features/platform.prf
+++ b/mkspecs/features/platform.prf
@@ -89,6 +89,7 @@ defineTest(isArchSupported) {
}
defineTest(isGCCVersionSupported) {
+ # Keep in sync with src/webengine/doc/src/qtwebengine-platform-notes.qdoc
greaterThan(QT_GCC_MAJOR_VERSION, 4):return(true)
skipBuild("Using gcc version "$$QT_GCC_MAJOR_VERSION"."$$QT_GCC_MINOR_VERSION", but at least gcc version 5 is required to build Qt WebEngine.")
@@ -127,6 +128,26 @@ defineTest(isMinOSXSDKVersion) {
return(false)
}
+defineTest(isMinXcodeVersion) {
+ requested_major = $$1
+ requested_minor = $$2
+ requested_patch = $$3
+ isEmpty(requested_minor): requested_minor = 0
+ isEmpty(requested_patch): requested_patch = 0
+ target_var = QMAKE_XCODE_VERSION
+ major_version = $$section($$target_var, ., 0, 0)
+ minor_version = $$section($$target_var, ., 1, 1)
+ patch_version = $$section($$target_var, ., 2, 2)
+ isEmpty(minor_version): minor_version = 0
+ isEmpty(patch_version): patch_version = 0
+
+ greaterThan(major_version, $$requested_major):return(true)
+ equals(major_version, $$requested_major):greaterThan(minor_version, $$requested_minor):return(true)
+ equals(major_version, $$requested_major):equals(minor_version, $$requested_minor):!lessThan(patch_version, $$requested_patch):return(true)
+
+ return(false)
+}
+
defineTest(isMinWinSDKVersion) {
requested_major = $$1
requested_minor = $$2
diff --git a/src/3rdparty b/src/3rdparty
-Subproject d4ae420c54b0e8b4660b5c41ec0c38049115f9a
+Subproject 922a17dfc0045e5ca565faf446c8ce09557e92c
diff --git a/src/core/api/qwebengineurlrequestjob.cpp b/src/core/api/qwebengineurlrequestjob.cpp
index c028a1167..c3541598b 100644
--- a/src/core/api/qwebengineurlrequestjob.cpp
+++ b/src/core/api/qwebengineurlrequestjob.cpp
@@ -115,9 +115,24 @@ QByteArray QWebEngineUrlRequestJob::requestMethod() const
/*!
\since 5.11
- Returns the origin URL of the content that initiated the request. If the
- request was not initiated by web content the function will return an
- empty QUrl.
+ Returns the serialized origin of the content that initiated the request.
+
+ Generally, the origin consists of a scheme, hostname, and port. For example,
+ \c "http://localhost:8080" would be a valid origin. The port is omitted if
+ it is the scheme's default port (80 for \c http, 443 for \c https). The
+ hostname is omitted for non-network schemes such as \c file and \c qrc.
+
+ However, there is also the special value \c "null" representing a unique
+ origin. It is, for example, the origin of a sandboxed iframe. The purpose of
+ this special origin is to be always different from all other origins in the
+ same-origin check. In other words, content with a unique origin should never
+ have privileged access to any other content.
+
+ Finally, if the request was not initiated by web content, the function will
+ return an empty QUrl. This happens, for example, when you call \l
+ QWebEnginePage::setUrl().
+
+ This value can be used for implementing secure cross-origin checks.
*/
QUrl QWebEngineUrlRequestJob::initiator() const
{
@@ -136,9 +151,10 @@ QUrl QWebEngineUrlRequestJob::initiator() const
The device should remain available at least as long as the job exists.
When calling this method with a newly constructed device, one solution is to
- make the device delete itself when closed, like this:
+ make the device as a child of the job or delete itself when job is deleted,
+ like this:
\code
- connect(device, &QIODevice::aboutToClose, device, &QObject::deleteLater);
+ connect(job, &QObject::destroyed, device, &QObject::deleteLater);
\endcode
*/
void QWebEngineUrlRequestJob::reply(const QByteArray &contentType, QIODevice *device)
diff --git a/src/core/config/windows.pri b/src/core/config/windows.pri
index b3e4cf77d..5aa511da3 100644
--- a/src/core/config/windows.pri
+++ b/src/core/config/windows.pri
@@ -57,7 +57,7 @@ msvc {
equals(MSVC_VER, 15.0) {
MSVS_VERSION = 2017
} else {
- fatal("Visual Studio compiler version \"$$MSVC_VER\" is not supported by Qt WebEngine")
+ error("Visual Studio compiler version \"$$MSVC_VER\" is not supported by Qt WebEngine")
}
gn_args += visual_studio_version=$$MSVS_VERSION
@@ -71,5 +71,5 @@ msvc {
gn_args += target_cpu=\"$$GN_TARGET_CPU\"
} else {
- fatal("Qt WebEngine for Windows can only be built with the Microsoft Visual Studio C++ compiler")
+ error("Qt WebEngine for Windows can only be built with the Microsoft Visual Studio C++ compiler")
}
diff --git a/src/core/devtools_frontend_qt.cpp b/src/core/devtools_frontend_qt.cpp
index 154b275b5..bd9e0ebe7 100644
--- a/src/core/devtools_frontend_qt.cpp
+++ b/src/core/devtools_frontend_qt.cpp
@@ -446,7 +446,16 @@ void DevToolsFrontendQt::HandleMessageFromDevToolsFrontend(const std::string &me
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK,
false);
- m_frontendDelegate->OpenURLFromTab(nullptr, openParams);
+ // OpenURL will (via WebContentsDelegateQt::OpenURLFromTab) call
+ // application code, which may decide to close this devtools view (see
+ // quicknanobrowser for example).
+ //
+ // Chromium always calls SendMessageAck through a callback bound to a
+ // WeakPtr, we do the same here, except without the callback.
+ base::WeakPtr<DevToolsFrontendQt> weakThis = m_weakFactory.GetWeakPtr();
+ web_contents()->OpenURL(openParams);
+ if (!weakThis)
+ return;
} else if (method == "bringToFront") {
Activate();
} else {
diff --git a/src/core/doc/src/qtwebenginecore-module.qdoc b/src/core/doc/src/qtwebenginecore-module.qdoc
index 2ed0a4c06..65e0766de 100644
--- a/src/core/doc/src/qtwebenginecore-module.qdoc
+++ b/src/core/doc/src/qtwebenginecore-module.qdoc
@@ -43,9 +43,11 @@
indirectly included through the \l{Qt WebEngine QML Types}{Qt WebEngine} or
\l{Qt WebEngine Widgets C++ Classes}{Qt WebEngine Widgets} modules.
+ \if !defined(qtforpython)
To link against the module, add this line to your qmake project file:
\snippet qtwebenginecore_build_snippet.qdoc 0
However, \c webenginecore is implied by adding \c webengine or \c webenginewidgets.
+ \endif
*/
diff --git a/src/core/media_capture_devices_dispatcher.cpp b/src/core/media_capture_devices_dispatcher.cpp
index 5298e29cf..b6e59f3bb 100644
--- a/src/core/media_capture_devices_dispatcher.cpp
+++ b/src/core/media_capture_devices_dispatcher.cpp
@@ -114,7 +114,16 @@ void getDevicesForDesktopCapture(
content::DesktopMediaID getDefaultScreenId()
{
-#if QT_CONFIG(webengine_webrtc)
+ // While this function is executing another thread may also want to create a
+ // DesktopCapturer [1]. Unfortunately, creating a DesktopCapturer is not
+ // thread safe on X11 due to the use of webrtc::XErrorTrap. It's safe to
+ // disable this code on X11 since we don't actually need to create a
+ // DesktopCapturer to get the screen id anyway
+ // (ScreenCapturerLinux::GetSourceList always returns 0 as the id).
+ //
+ // [1]: webrtc::InProcessVideoCaptureDeviceLauncher::DoStartDesktopCaptureOnDeviceThread
+
+#if QT_CONFIG(webengine_webrtc) && !defined(USE_X11)
// Source id patterns are different across platforms.
// On Linux, the hardcoded value "0" is used.
// On Windows, the screens are enumerated consecutively in increasing order from 0.
diff --git a/src/core/net/url_request_custom_job_proxy.cpp b/src/core/net/url_request_custom_job_proxy.cpp
index 5280318ad..b5f10388c 100644
--- a/src/core/net/url_request_custom_job_proxy.cpp
+++ b/src/core/net/url_request_custom_job_proxy.cpp
@@ -159,7 +159,7 @@ void URLRequestCustomJobProxy::initialize(GURL url, std::string method, base::Op
QUrl initiatorOrigin;
if (initiator.has_value())
- initiatorOrigin = toQt(initiator.value().GetURL());
+ initiatorOrigin = QUrl::fromEncoded(QByteArray::fromStdString(initiator.value().Serialize()));
QWebEngineUrlSchemeHandler *schemeHandler = nullptr;
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp
index 3e5d9bc4b..e706c5869 100644
--- a/src/core/render_widget_host_view_qt.cpp
+++ b/src/core/render_widget_host_view_qt.cpp
@@ -1073,7 +1073,11 @@ bool RenderWidgetHostViewQt::forwardEvent(QEvent *event)
case QEvent::MouseMove:
// Skip second MouseMove event when a window is being adopted, so that Chromium
// can properly handle further move events.
- if (m_adapterClient->isBeingAdopted())
+ // Also make sure the adapter client exists to prevent a null pointer dereference,
+ // because it's possible for a QWebEnginePagePrivate (adapter) instance to be destroyed,
+ // and then the OS (observed on Windows) might still send mouse move events to a still
+ // existing popup RWHVQDW instance.
+ if (m_adapterClient && m_adapterClient->isBeingAdopted())
return false;
handleMouseEvent(static_cast<QMouseEvent*>(event));
break;
@@ -1091,12 +1095,14 @@ bool RenderWidgetHostViewQt::forwardEvent(QEvent *event)
case QEvent::TouchCancel:
handleTouchEvent(static_cast<QTouchEvent*>(event));
break;
+#if QT_CONFIG(tabletevent)
case QEvent::TabletPress:
Focus(); // Fall through.
case QEvent::TabletRelease:
case QEvent::TabletMove:
handleTabletEvent(static_cast<QTabletEvent*>(event));
break;
+#endif
#ifndef QT_NO_GESTURES
case QEvent::NativeGesture:
handleGestureEvent(static_cast<QNativeGestureEvent *>(event));
@@ -1172,6 +1178,15 @@ QVariant RenderWidgetHostViewQt::inputMethodQuery(Qt::InputMethodQuery query)
}
}
+void RenderWidgetHostViewQt::closePopup()
+{
+ // We notify the popup to be closed by telling it that it lost focus. WebKit does the rest
+ // (hiding the widget and automatic memory cleanup via
+ // RenderWidget::CloseWidgetSoon() -> RenderWidgetHostImpl::ShutdownAndDestroyWidget(true).
+ m_host->SetActive(false);
+ m_host->Blur();
+}
+
void RenderWidgetHostViewQt::ProcessAckedTouchEvent(const content::TouchEventWithLatencyInfo &touch, content::InputEventAckState ack_result) {
Q_UNUSED(touch);
const bool eventConsumed = ack_result == content::INPUT_EVENT_ACK_STATE_CONSUMED;
@@ -1617,10 +1632,12 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev)
}
}
+#if QT_CONFIG(tabletevent)
void RenderWidgetHostViewQt::handleTabletEvent(QTabletEvent *event)
{
handlePointerEvent<QTabletEvent>(event);
}
+#endif
template<class T>
void RenderWidgetHostViewQt::handlePointerEvent(T *event)
diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h
index 0779136f0..7b270e2b3 100644
--- a/src/core/render_widget_host_view_qt.h
+++ b/src/core/render_widget_host_view_qt.h
@@ -173,6 +173,7 @@ public:
void windowChanged() override;
bool forwardEvent(QEvent *) override;
QVariant inputMethodQuery(Qt::InputMethodQuery query) override;
+ void closePopup() override;
// Overridden from content::TextInputManager::Observer
void OnUpdateTextInputStateCalled(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view, bool did_update_state) override;
@@ -187,7 +188,9 @@ public:
void handleKeyEvent(QKeyEvent*);
void handleWheelEvent(QWheelEvent*);
void handleTouchEvent(QTouchEvent*);
+#if QT_CONFIG(tabletevent)
void handleTabletEvent(QTabletEvent *ev);
+#endif
#ifndef QT_NO_GESTURES
void handleGestureEvent(QNativeGestureEvent *);
#endif
diff --git a/src/core/render_widget_host_view_qt_delegate.h b/src/core/render_widget_host_view_qt_delegate.h
index 55dd1923a..051e3f9cc 100644
--- a/src/core/render_widget_host_view_qt_delegate.h
+++ b/src/core/render_widget_host_view_qt_delegate.h
@@ -89,6 +89,7 @@ public:
virtual void windowChanged() = 0;
virtual bool forwardEvent(QEvent *) = 0;
virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) = 0;
+ virtual void closePopup() = 0;
};
class QWEBENGINECORE_PRIVATE_EXPORT RenderWidgetHostViewQtDelegate {
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index 84b97412b..c7c20c23e 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -1450,23 +1450,6 @@ static void fillDropDataFromMimeData(content::DropData *dropData, const QMimeDat
}
}
-void WebContentsAdapter::enterDrag(QDragEnterEvent *e, const QPointF &screenPos)
-{
- CHECK_INITIALIZED();
-
- if (!m_currentDropData) {
- // The drag originated outside the WebEngineView.
- m_currentDropData.reset(new content::DropData);
- fillDropDataFromMimeData(m_currentDropData.get(), e->mimeData());
- }
-
- content::RenderViewHost *rvh = m_webContents->GetRenderViewHost();
- rvh->GetWidget()->FilterDropData(m_currentDropData.get());
- rvh->GetWidget()->DragTargetDragEnter(*m_currentDropData, toGfx(e->posF()), toGfx(screenPos),
- toWeb(e->possibleActions()),
- flagsFromModifiers(e->keyboardModifiers()));
-}
-
Qt::DropAction toQt(blink::WebDragOperation op)
{
if (op & blink::kWebDragOperationCopy)
@@ -1504,6 +1487,23 @@ static int toWeb(Qt::KeyboardModifiers modifiers)
return result;
}
+void WebContentsAdapter::enterDrag(QDragEnterEvent *e, const QPointF &screenPos)
+{
+ CHECK_INITIALIZED();
+
+ if (!m_currentDropData) {
+ // The drag originated outside the WebEngineView.
+ m_currentDropData.reset(new content::DropData);
+ fillDropDataFromMimeData(m_currentDropData.get(), e->mimeData());
+ }
+
+ content::RenderViewHost *rvh = m_webContents->GetRenderViewHost();
+ rvh->GetWidget()->FilterDropData(m_currentDropData.get());
+ rvh->GetWidget()->DragTargetDragEnter(*m_currentDropData, toGfx(e->posF()), toGfx(screenPos),
+ toWeb(e->possibleActions()),
+ toWeb(e->mouseButtons()) | toWeb(e->keyboardModifiers()));
+}
+
Qt::DropAction WebContentsAdapter::updateDragPosition(QDragMoveEvent *e, const QPointF &screenPos)
{
CHECK_INITIALIZED(Qt::DropAction());
@@ -1545,14 +1545,16 @@ void WebContentsAdapter::updateDragAction(int action)
m_currentDropAction = static_cast<blink::WebDragOperation>(action);
}
-void WebContentsAdapter::endDragging(const QPointF &clientPos, const QPointF &screenPos)
+void WebContentsAdapter::endDragging(QDropEvent *e, const QPointF &screenPos)
{
CHECK_INITIALIZED();
content::RenderViewHost *rvh = m_webContents->GetRenderViewHost();
rvh->GetWidget()->FilterDropData(m_currentDropData.get());
- m_lastDragClientPos = clientPos;
+ m_lastDragClientPos = e->posF();
m_lastDragScreenPos = screenPos;
- rvh->GetWidget()->DragTargetDrop(*m_currentDropData, toGfx(m_lastDragClientPos), toGfx(m_lastDragScreenPos), 0);
+ rvh->GetWidget()->DragTargetDrop(*m_currentDropData, toGfx(m_lastDragClientPos), toGfx(m_lastDragScreenPos),
+ toWeb(e->mouseButtons()) | toWeb(e->keyboardModifiers()));
+
m_currentDropData.reset();
}
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index bd2ca4b23..99114546d 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -73,6 +73,7 @@ QT_BEGIN_NAMESPACE
class QAccessibleInterface;
class QDragEnterEvent;
class QDragMoveEvent;
+class QDropEvent;
class QMimeData;
class QPageLayout;
class QString;
@@ -205,7 +206,7 @@ public:
void enterDrag(QDragEnterEvent *e, const QPointF &screenPos);
Qt::DropAction updateDragPosition(QDragMoveEvent *e, const QPointF &screenPos);
void updateDragAction(int action);
- void endDragging(const QPointF &clientPos, const QPointF &screenPos);
+ void endDragging(QDropEvent *e, const QPointF &screenPos);
void leaveDrag();
#endif // QT_CONFIG(draganddrop)
void printToPDF(const QPageLayout&, const QString&);
diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp
index a7301e02d..899e50e16 100644
--- a/src/core/web_engine_context.cpp
+++ b/src/core/web_engine_context.cpp
@@ -516,8 +516,10 @@ WebEngineContext::WebEngineContext()
parsedCommandLine->AppendSwitchASCII(switches::kUseGL, glType);
parsedCommandLine->AppendSwitch(switches::kInProcessGPU);
#ifdef Q_OS_WIN
- if (enableWebGLSoftwareRendering)
+ if (enableWebGLSoftwareRendering) {
parsedCommandLine->AppendSwitch(switches::kDisableGpuRasterization);
+ parsedCommandLine->AppendSwitch(switches::kIgnoreGpuBlacklist);
+ }
#endif
} else {
parsedCommandLine->AppendSwitch(switches::kDisableGpu);
diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp
index b8e8b1689..a1fd3e55b 100644
--- a/src/core/web_event_factory.cpp
+++ b/src/core/web_event_factory.cpp
@@ -76,7 +76,9 @@
#include <QKeyEvent>
#include <QMouseEvent>
#include <QStyleHints>
+#if QT_CONFIG(tabletevent)
#include <QTabletEvent>
+#endif
#include <QWheelEvent>
using namespace blink;
@@ -1186,6 +1188,7 @@ static WebInputEvent::Type webEventTypeForEvent(const QEvent* event)
}
}
+#if QT_CONFIG(tabletevent)
static WebPointerProperties::PointerType pointerTypeForTabletEvent(const QTabletEvent *ev)
{
switch (ev->pointerType()) {
@@ -1199,6 +1202,7 @@ static WebPointerProperties::PointerType pointerTypeForTabletEvent(const QTablet
return WebPointerProperties::PointerType::kMouse;
}
}
+#endif
WebMouseEvent WebEventFactory::toWebMouseEvent(QMouseEvent *ev, double dpiScale)
{
@@ -1230,6 +1234,7 @@ WebMouseEvent WebEventFactory::toWebMouseEvent(QHoverEvent *ev, double dpiScale)
return webKitEvent;
}
+#if QT_CONFIG(tabletevent)
WebMouseEvent WebEventFactory::toWebMouseEvent(QTabletEvent *ev, double dpiScale)
{
WebMouseEvent webKitEvent(webEventTypeForEvent(ev),
@@ -1248,6 +1253,7 @@ WebMouseEvent WebEventFactory::toWebMouseEvent(QTabletEvent *ev, double dpiScale
webKitEvent.pointer_type = pointerTypeForTabletEvent(ev);
return webKitEvent;
}
+#endif
WebMouseEvent WebEventFactory::toWebMouseEvent(QEvent *ev)
{
diff --git a/src/core/web_event_factory.h b/src/core/web_event_factory.h
index dc29970a7..4b22de7d7 100644
--- a/src/core/web_event_factory.h
+++ b/src/core/web_event_factory.h
@@ -54,7 +54,9 @@ class QEvent;
class QHoverEvent;
class QKeyEvent;
class QMouseEvent;
+#ifndef QT_NO_TABLETEVENT
class QTabletEvent;
+#endif
class QWheelEvent;
#ifndef QT_NO_GESTURES
class QNativeGestureEvent;
@@ -66,7 +68,9 @@ class WebEventFactory {
public:
static blink::WebMouseEvent toWebMouseEvent(QMouseEvent*, double dpiScale);
static blink::WebMouseEvent toWebMouseEvent(QHoverEvent*, double dpiScale);
+#ifndef QT_NO_TABLETEVENT
static blink::WebMouseEvent toWebMouseEvent(QTabletEvent*, double dpiScale);
+#endif
static blink::WebMouseEvent toWebMouseEvent(QEvent *);
#ifndef QT_NO_GESTURES
static blink::WebGestureEvent toWebGestureEvent(QNativeGestureEvent *, double dpiScale);
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index 02666d363..f0c071161 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -1497,7 +1497,7 @@ void QQuickWebEngineView::dropEvent(QDropEvent *e)
{
Q_D(QQuickWebEngineView);
e->accept();
- d->adapter->endDragging(e->pos(), mapToScreen(this, e->pos()));
+ d->adapter->endDragging(e, mapToScreen(this, e->pos()));
}
#endif // QT_CONFIG(draganddrop)
diff --git a/src/webengine/doc/src/qtwebengine-module.qdoc b/src/webengine/doc/src/qtwebengine-module.qdoc
index 97657f6a9..2d4d1b8da 100644
--- a/src/webengine/doc/src/qtwebengine-module.qdoc
+++ b/src/webengine/doc/src/qtwebengine-module.qdoc
@@ -38,7 +38,9 @@
\snippet qtwebengine_build_snippet.qdoc 1
+ \if !defined(qtforpython)
To link against the module, add the following to your qmake project file:
\snippet qtwebengine_build_snippet.qdoc 0
+ \endif
*/
diff --git a/src/webengine/doc/src/qtwebengine-platform-notes.qdoc b/src/webengine/doc/src/qtwebengine-platform-notes.qdoc
index 403cdf330..6c8703fe4 100644
--- a/src/webengine/doc/src/qtwebengine-platform-notes.qdoc
+++ b/src/webengine/doc/src/qtwebengine-platform-notes.qdoc
@@ -73,7 +73,7 @@
\section2 Linux
- On Linux, Clang or GCC version 4.7 or later is required.
+ On Linux, Clang or GCC version 5 or later is required.
Supported configurations are \c linux-g++ and \c{linux-clang}.
Qt WebEngine requires \c pkg-config to detect most of its dependencies. The
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index 6a4554b57..96468f173 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -2365,6 +2365,11 @@ void QWebEnginePage::printToPdf(const QWebEngineCallback<const QByteArray&> &res
It is the users responsibility to ensure the \a printer remains valid until \a resultCallback
has been called.
+ \note The rendering of the current content into a temporary PDF document is asynchronous and does
+ not block the main thread. However, the subsequent rendering of PDF into \a printer runs on the
+ main thread and will therefore block the event loop. Moreover, printing runs on the browser
+ process, which is by default not sandboxed.
+
The \a resultCallback must take a boolean as parameter. If printing was successful, this
boolean will have the value \c true, otherwise, its value will be \c false.
\since 5.8
diff --git a/src/webenginewidgets/api/qwebengineview.cpp b/src/webenginewidgets/api/qwebengineview.cpp
index 369c6b02b..a207af392 100644
--- a/src/webenginewidgets/api/qwebengineview.cpp
+++ b/src/webenginewidgets/api/qwebengineview.cpp
@@ -423,7 +423,7 @@ void QWebEngineView::dropEvent(QDropEvent *e)
if (!d->m_dragEntered)
return;
e->accept();
- d->page->d_ptr->adapter->endDragging(e->pos(), mapToGlobal(e->pos()));
+ d->page->d_ptr->adapter->endDragging(e, mapToGlobal(e->pos()));
d->m_dragEntered = false;
}
#endif // QT_CONFIG(draganddrop)
diff --git a/src/webenginewidgets/doc/src/qtwebenginewidgets-module.qdoc b/src/webenginewidgets/doc/src/qtwebenginewidgets-module.qdoc
index e4f259882..35fed802c 100644
--- a/src/webenginewidgets/doc/src/qtwebenginewidgets-module.qdoc
+++ b/src/webenginewidgets/doc/src/qtwebenginewidgets-module.qdoc
@@ -41,7 +41,9 @@
\snippet qtwebenginewidgets_build_snippet.qdoc 1
+ \if !defined(qtforpython)
To link against the module, add the following to your qmake project file:
\snippet qtwebenginewidgets_build_snippet.qdoc 0
+ \endif
*/
diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
index 9497ba0fa..d7bcd0ae3 100644
--- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
+++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
@@ -218,20 +218,24 @@ void RenderWidgetHostViewQtDelegateWidget::initAsPopup(const QRect& screenRect)
// to be destroyed.
setAttribute(Qt::WA_ShowWithoutActivating);
setFocusPolicy(Qt::NoFocus);
-
-#ifdef Q_OS_MACOS
- // macOS doesn't like Qt::ToolTip when QWebEngineView is inside a modal dialog, specifically by
- // not forwarding click events to the popup. So we use Qt::Tool which behaves the same way, but
- // works on macOS too.
- setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus);
-#else
- setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus);
-#endif
+ setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus);
setGeometry(screenRect);
show();
}
+void RenderWidgetHostViewQtDelegateWidget::closeEvent(QCloseEvent *event)
+{
+ Q_UNUSED(event);
+
+ // If a close event was received from the window manager (e.g. when moving the parent window,
+ // clicking outside the popup area)
+ // make sure to notify the Chromium WebUI popup and its underlying
+ // RenderWidgetHostViewQtDelegate instance to be closed.
+ if (m_isPopup)
+ m_client->closePopup();
+}
+
QRectF RenderWidgetHostViewQtDelegateWidget::screenRect() const
{
return QRectF(x(), y(), width(), height());
diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h
index 79958132c..42b454bc5 100644
--- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h
+++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h
@@ -48,6 +48,12 @@
namespace QtWebEngineCore {
+// Useful information keyboard and mouse QEvent propagation.
+// A RenderWidgetHostViewQtDelegateWidget instance initialized as a popup will receive
+// no keyboard focus (so all keyboard QEvents will be sent to the parent RWHVQD instance),
+// but will still receive mouse input (all mouse QEvent moves and clicks will be given to the popup
+// RWHVQD instance, and the mouse interaction area covers the surface of the whole parent
+// QWebEngineView, and not only the smaller surface that an HTML select popup would occupy).
class RenderWidgetHostViewQtDelegateWidget : public QQuickWidget, public RenderWidgetHostViewQtDelegate {
Q_OBJECT
public:
@@ -83,6 +89,7 @@ protected:
void resizeEvent(QResizeEvent *resizeEvent) override;
void showEvent(QShowEvent *) override;
void hideEvent(QHideEvent *) override;
+ void closeEvent(QCloseEvent *event) override;
QVariant inputMethodQuery(Qt::InputMethodQuery query) const override;
diff --git a/tests/auto/widgets/faviconmanager/tst_faviconmanager.cpp b/tests/auto/widgets/faviconmanager/tst_faviconmanager.cpp
index 14786e5b0..606d05d9e 100644
--- a/tests/auto/widgets/faviconmanager/tst_faviconmanager.cpp
+++ b/tests/auto/widgets/faviconmanager/tst_faviconmanager.cpp
@@ -30,6 +30,7 @@
#include "../util.h"
#include <qwebenginepage.h>
+#include <qwebengineprofile.h>
#include <qwebenginesettings.h>
#include <qwebengineview.h>
@@ -65,13 +66,16 @@ private Q_SLOTS:
private:
QWebEngineView *m_view;
QWebEnginePage *m_page;
+ QWebEngineProfile *m_profile;
};
void tst_FaviconManager::init()
{
+ m_profile = new QWebEngineProfile(this);
m_view = new QWebEngineView();
- m_page = m_view->page();
+ m_page = new QWebEnginePage(m_profile, m_view);
+ m_view->setPage(m_page);
}
@@ -87,6 +91,7 @@ void tst_FaviconManager::cleanupTestCase()
void tst_FaviconManager::cleanup()
{
delete m_view;
+ delete m_profile;
}
void tst_FaviconManager::faviconLoad()
diff --git a/tests/auto/widgets/qwebenginepage/BLACKLIST b/tests/auto/widgets/qwebenginepage/BLACKLIST
index 50f275eb4..9c7572b40 100644
--- a/tests/auto/widgets/qwebenginepage/BLACKLIST
+++ b/tests/auto/widgets/qwebenginepage/BLACKLIST
@@ -9,5 +9,3 @@ osx
[mouseMovementProperties]
windows
-osx-10.11
-osx-10.12
diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
index 95a46fbc2..8b36d5a6f 100644
--- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
+++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -128,6 +128,8 @@ private Q_SLOTS:
void getUserMediaRequest();
void getUserMediaRequestDesktopAudio();
void getUserMediaRequestSettingDisabled();
+ void getUserMediaRequestDesktopVideoManyPages();
+ void getUserMediaRequestDesktopVideoManyRequests();
void savePage();
void crashTests_LazyInitializationOfMainFrame();
@@ -2470,6 +2472,44 @@ void tst_QWebEnginePage::getUserMediaRequestSettingDisabled()
QTRY_VERIFY(!page.jsPromiseFulfilled() && page.jsPromiseRejected());
}
+// Try to trigger any possible race condition between the UI thread (permission
+// management) and the audio/device thread (desktop capture initialization).
+void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyPages()
+{
+ const QString constraints = QStringLiteral("{video: { mandatory: { chromeMediaSource: 'desktop' }}}");
+ const QWebEnginePage::Feature feature = QWebEnginePage::DesktopVideoCapture;
+ std::vector<GetUserMediaTestPage> pages(10);
+ for (GetUserMediaTestPage &page : pages)
+ QTRY_VERIFY_WITH_TIMEOUT(page.loadSucceeded(), 20000);
+ for (GetUserMediaTestPage &page : pages)
+ page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
+ for (GetUserMediaTestPage &page : pages)
+ page.jsGetUserMedia(constraints);
+ for (GetUserMediaTestPage &page : pages)
+ QTRY_VERIFY(page.gotFeatureRequest(feature));
+ for (GetUserMediaTestPage &page : pages)
+ page.acceptPendingRequest();
+ for (GetUserMediaTestPage &page : pages)
+ QTRY_VERIFY(page.jsPromiseFulfilled() || page.jsPromiseRejected());
+}
+
+// Try to trigger any possible race condition between the UI or audio/device
+// threads and the desktop capture thread, where the capture actually happens.
+void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyRequests()
+{
+ const QString constraints = QStringLiteral("{video: { mandatory: { chromeMediaSource: 'desktop' }}}");
+ const QWebEnginePage::Feature feature = QWebEnginePage::DesktopVideoCapture;
+ GetUserMediaTestPage page;
+ QTRY_VERIFY_WITH_TIMEOUT(page.loadSucceeded(), 20000);
+ page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
+ for (int i = 0; i != 100; ++i) {
+ page.jsGetUserMedia(constraints);
+ QTRY_VERIFY(page.gotFeatureRequest(feature));
+ page.acceptPendingRequest();
+ QTRY_VERIFY(page.jsPromiseFulfilled() || page.jsPromiseRejected());
+ }
+}
+
void tst_QWebEnginePage::savePage()
{
QWebEngineView view;
@@ -4078,6 +4118,7 @@ void tst_QWebEnginePage::mouseMovementProperties()
ConsolePage page;
view.setPage(&page);
view.resize(640, 480);
+ QTest::mouseMove(&view, QPoint(10, 10));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
index a04a1ac6f..1fe0f0304 100644
--- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
+++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
@@ -56,6 +56,7 @@ private Q_SLOTS:
void httpAcceptLanguage();
void downloadItem();
void changePersistentPath();
+ void initiator();
};
void tst_QWebEngineProfile::init()
@@ -528,5 +529,44 @@ void tst_QWebEngineProfile::changePersistentPath()
QVERIFY(newPath.endsWith(QStringLiteral("Test2")));
}
+class InitiatorSpy : public QWebEngineUrlSchemeHandler
+{
+public:
+ QUrl initiator;
+ void requestStarted(QWebEngineUrlRequestJob *job) override
+ {
+ initiator = job->initiator();
+ job->fail(QWebEngineUrlRequestJob::RequestDenied);
+ }
+};
+
+void tst_QWebEngineProfile::initiator()
+{
+ InitiatorSpy handler;
+ QWebEngineProfile profile;
+ profile.installUrlSchemeHandler("foo", &handler);
+ QWebEnginePage page(&profile);
+ QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool)));
+
+ // about:blank has a unique origin, so initiator should be QUrl("null")
+ evaluateJavaScriptSync(&page, "window.location = 'foo:bar'");
+ QVERIFY(loadFinishedSpy.wait());
+ QCOMPARE(handler.initiator, QUrl("null"));
+
+ page.setHtml("", QUrl("http://test:123/foo%20bar"));
+ QVERIFY(loadFinishedSpy.wait());
+
+ // baseUrl determines the origin, so QUrl("http://test:123")
+ evaluateJavaScriptSync(&page, "window.location = 'foo:bar'");
+ QVERIFY(loadFinishedSpy.wait());
+ QCOMPARE(handler.initiator, QUrl("http://test:123"));
+
+ // Directly calling load/setUrl should have initiator QUrl(), meaning
+ // browser-initiated, trusted.
+ page.load(QUrl("foo:bar"));
+ QVERIFY(loadFinishedSpy.wait());
+ QCOMPARE(handler.initiator, QUrl());
+}
+
QTEST_MAIN(tst_QWebEngineProfile)
#include "tst_qwebengineprofile.moc"
diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
index 286efca56..d01a5365f 100644
--- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
+++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
@@ -375,7 +375,7 @@ void tst_QWebEngineView::microFocusCoordinates()
QVariant initialMicroFocus = webView.focusProxy()->inputMethodQuery(Qt::ImMicroFocus);
evaluateJavaScriptSync(webView.page(), "window.scrollBy(0, 50)");
- QVERIFY(scrollSpy.wait());
+ QTRY_VERIFY(scrollSpy.count() > 0);
QTRY_VERIFY(webView.focusProxy()->inputMethodQuery(Qt::ImMicroFocus).isValid());
QVariant currentMicroFocus = webView.focusProxy()->inputMethodQuery(Qt::ImMicroFocus);