diff options
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, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}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); |