mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1067325 - MathML source in tab. r=mconley
This commit is contained in:
parent
fc405bc150
commit
937c90eb83
@ -1012,8 +1012,14 @@ nsContextMenu.prototype = {
|
||||
inBackground: false
|
||||
});
|
||||
let viewSourceBrowser = gBrowser.getBrowserForTab(tab);
|
||||
top.gViewSourceUtils.viewSourceFromSelectionInBrowser(reference,
|
||||
viewSourceBrowser);
|
||||
if (aContext == "selection") {
|
||||
top.gViewSourceUtils
|
||||
.viewSourceFromSelectionInBrowser(reference, viewSourceBrowser);
|
||||
} else {
|
||||
top.gViewSourceUtils
|
||||
.viewSourceFromFragmentInBrowser(reference, aContext,
|
||||
viewSourceBrowser);
|
||||
}
|
||||
} else {
|
||||
// unused (and play nice for fragments generated via XSLT too)
|
||||
var docUrl = null;
|
||||
|
@ -13,15 +13,17 @@ XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
|
||||
"resource://gre/modules/Deprecated.jsm");
|
||||
|
||||
const NS_XHTML = 'http://www.w3.org/1999/xhtml';
|
||||
const NS_XHTML = "http://www.w3.org/1999/xhtml";
|
||||
const VIEW_SOURCE_CSS = "resource://gre-resources/viewsource.css";
|
||||
const BUNDLE_URL = "chrome://global/locale/viewSource.properties";
|
||||
|
||||
// These are markers used to delimit the selection during processing. They
|
||||
// are removed from the final rendering.
|
||||
// We use noncharacter Unicode codepoints to minimize the risk of clashing
|
||||
// with anything that might legitimately be present in the document.
|
||||
// U+FDD0..FDEF <noncharacters>
|
||||
const MARK_SELECTION_START = '\uFDD0';
|
||||
const MARK_SELECTION_END = '\uFDEF';
|
||||
const MARK_SELECTION_START = "\uFDD0";
|
||||
const MARK_SELECTION_END = "\uFDEF";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ViewSourceBrowser"];
|
||||
|
||||
@ -115,6 +117,23 @@ ViewSourceBrowser.prototype = {
|
||||
return this.browser.webNavigation;
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for whether long lines should be wrapped.
|
||||
*/
|
||||
get wrapLongLines() {
|
||||
return Services.prefs.getBoolPref("view_source.wrap_long_lines");
|
||||
},
|
||||
|
||||
/**
|
||||
* A getter for the view source string bundle.
|
||||
*/
|
||||
get bundle() {
|
||||
if (this._bundle) {
|
||||
return this._bundle;
|
||||
}
|
||||
return this._bundle = Services.strings.createBundle(BUNDLE_URL);
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads the source for a URL while applying some optional features if
|
||||
* enabled.
|
||||
@ -339,4 +358,185 @@ ViewSourceBrowser.prototype = {
|
||||
} while (n != ancestor && p);
|
||||
return path;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the view source browser from a fragment of some document, as in
|
||||
* markups such as MathML where reformatting the output is helpful.
|
||||
*
|
||||
* @param aNode
|
||||
* Some element within the fragment of interest.
|
||||
* @param aContext
|
||||
* A string denoting the type of fragment. Currently, "mathml" is the
|
||||
* only accepted value.
|
||||
*/
|
||||
loadViewSourceFromFragment(node, context) {
|
||||
var Node = node.ownerDocument.defaultView.Node;
|
||||
this._lineCount = 0;
|
||||
this._startTargetLine = 0;
|
||||
this._endTargetLine = 0;
|
||||
this._targetNode = node;
|
||||
if (this._targetNode && this._targetNode.nodeType == Node.TEXT_NODE)
|
||||
this._targetNode = this._targetNode.parentNode;
|
||||
|
||||
// walk up the tree to the top-level element (e.g., <math>, <svg>)
|
||||
var topTag;
|
||||
if (context == "mathml")
|
||||
topTag = "math";
|
||||
else
|
||||
throw "not reached";
|
||||
var topNode = this._targetNode;
|
||||
while (topNode && topNode.localName != topTag)
|
||||
topNode = topNode.parentNode;
|
||||
if (!topNode)
|
||||
return;
|
||||
|
||||
// serialize
|
||||
var title = this.bundle.GetStringFromName("viewMathMLSourceTitle");
|
||||
var wrapClass = this.wrapLongLines ? ' class="wrap"' : '';
|
||||
var source =
|
||||
'<!DOCTYPE html>'
|
||||
+ '<html>'
|
||||
+ '<head><title>' + title + '</title>'
|
||||
+ '<link rel="stylesheet" type="text/css" href="' + VIEW_SOURCE_CSS + '">'
|
||||
+ '<style type="text/css">'
|
||||
+ '#target { border: dashed 1px; background-color: lightyellow; }'
|
||||
+ '</style>'
|
||||
+ '</head>'
|
||||
+ '<body id="viewsource"' + wrapClass
|
||||
+ ' onload="document.title=\''+title+'\'; document.getElementById(\'target\').scrollIntoView(true)">'
|
||||
+ '<pre>'
|
||||
+ this._getOuterMarkup(topNode, 0)
|
||||
+ '</pre></body></html>'
|
||||
; // end
|
||||
|
||||
// display
|
||||
this.browser.loadURI("data:text/html;charset=utf-8," +
|
||||
encodeURIComponent(source));
|
||||
},
|
||||
|
||||
_getInnerMarkup(node, indent) {
|
||||
var str = '';
|
||||
for (var i = 0; i < node.childNodes.length; i++) {
|
||||
str += this._getOuterMarkup(node.childNodes.item(i), indent);
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
_getOuterMarkup(node, indent) {
|
||||
var Node = node.ownerDocument.defaultView.Node;
|
||||
var newline = "";
|
||||
var padding = "";
|
||||
var str = "";
|
||||
if (node == this._targetNode) {
|
||||
this._startTargetLine = this._lineCount;
|
||||
str += '</pre><pre id="target">';
|
||||
}
|
||||
|
||||
switch (node.nodeType) {
|
||||
case Node.ELEMENT_NODE: // Element
|
||||
// to avoid the wide gap problem, '\n' is not emitted on the first
|
||||
// line and the lines before & after the <pre id="target">...</pre>
|
||||
if (this._lineCount > 0 &&
|
||||
this._lineCount != this._startTargetLine &&
|
||||
this._lineCount != this._endTargetLine) {
|
||||
newline = "\n";
|
||||
}
|
||||
this._lineCount++;
|
||||
for (var k = 0; k < indent; k++) {
|
||||
padding += " ";
|
||||
}
|
||||
str += newline + padding
|
||||
+ '<<span class="start-tag">' + node.nodeName + '</span>';
|
||||
for (var i = 0; i < node.attributes.length; i++) {
|
||||
var attr = node.attributes.item(i);
|
||||
if (attr.nodeName.match(/^[-_]moz/)) {
|
||||
continue;
|
||||
}
|
||||
str += ' <span class="attribute-name">'
|
||||
+ attr.nodeName
|
||||
+ '</span>=<span class="attribute-value">"'
|
||||
+ this._unicodeToEntity(attr.nodeValue)
|
||||
+ '"</span>';
|
||||
}
|
||||
if (!node.hasChildNodes()) {
|
||||
str += "/>";
|
||||
}
|
||||
else {
|
||||
str += ">";
|
||||
var oldLine = this._lineCount;
|
||||
str += this._getInnerMarkup(node, indent + 2);
|
||||
if (oldLine == this._lineCount) {
|
||||
newline = "";
|
||||
padding = "";
|
||||
}
|
||||
else {
|
||||
newline = (this._lineCount == this._endTargetLine) ? "" : "\n";
|
||||
this._lineCount++;
|
||||
}
|
||||
str += newline + padding
|
||||
+ '</<span class="end-tag">' + node.nodeName + '</span>>';
|
||||
}
|
||||
break;
|
||||
case Node.TEXT_NODE: // Text
|
||||
var tmp = node.nodeValue;
|
||||
tmp = tmp.replace(/(\n|\r|\t)+/g, " ");
|
||||
tmp = tmp.replace(/^ +/, "");
|
||||
tmp = tmp.replace(/ +$/, "");
|
||||
if (tmp.length != 0) {
|
||||
str += '<span class="text">' + this._unicodeToEntity(tmp) + '</span>';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (node == this._targetNode) {
|
||||
this._endTargetLine = this._lineCount;
|
||||
str += '</pre><pre>';
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
_unicodeToEntity(text) {
|
||||
const charTable = {
|
||||
'&': '&<span class="entity">amp;</span>',
|
||||
'<': '&<span class="entity">lt;</span>',
|
||||
'>': '&<span class="entity">gt;</span>',
|
||||
'"': '&<span class="entity">quot;</span>'
|
||||
};
|
||||
|
||||
function charTableLookup(letter) {
|
||||
return charTable[letter];
|
||||
}
|
||||
|
||||
function convertEntity(letter) {
|
||||
try {
|
||||
var unichar = this._entityConverter
|
||||
.ConvertToEntity(letter, entityVersion);
|
||||
var entity = unichar.substring(1); // extract '&'
|
||||
return '&<span class="entity">' + entity + '</span>';
|
||||
} catch (ex) {
|
||||
return letter;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._entityConverter) {
|
||||
try {
|
||||
this._entityConverter = Cc["@mozilla.org/intl/entityconverter;1"]
|
||||
.createInstance(Ci.nsIEntityConverter);
|
||||
} catch(e) { }
|
||||
}
|
||||
|
||||
const entityVersion = Ci.nsIEntityConverter.entityW3C;
|
||||
|
||||
var str = text;
|
||||
|
||||
// replace chars in our charTable
|
||||
str = str.replace(/[<>&"]/g, charTableLookup);
|
||||
|
||||
// replace chars > 0x7f via nsIEntityConverter
|
||||
str = str.replace(/[^\0-\u007f]/g, convertEntity);
|
||||
|
||||
return str;
|
||||
},
|
||||
};
|
||||
|
@ -6,22 +6,12 @@
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var gDebug = 0;
|
||||
var gLineCount = 0;
|
||||
var gStartTargetLine = 0;
|
||||
var gEndTargetLine = 0;
|
||||
var gTargetNode = null;
|
||||
|
||||
var gEntityConverter = null;
|
||||
var gWrapLongLines = false;
|
||||
const gViewSourceCSS = 'resource://gre-resources/viewsource.css';
|
||||
|
||||
function onLoadViewPartialSource()
|
||||
{
|
||||
function onLoadViewPartialSource() {
|
||||
// check the view_source.wrap_long_lines pref
|
||||
// and set the menuitem's checked attribute accordingly
|
||||
gWrapLongLines = Services.prefs.getBoolPref("view_source.wrap_long_lines");
|
||||
document.getElementById("menu_wrapLongLines").setAttribute("checked", gWrapLongLines);
|
||||
let wrapLongLines = Services.prefs.getBoolPref("view_source.wrap_long_lines");
|
||||
document.getElementById("menu_wrapLongLines")
|
||||
.setAttribute("checked", wrapLongLines);
|
||||
document.getElementById("menu_highlightSyntax")
|
||||
.setAttribute("checked",
|
||||
Services.prefs.getBoolPref("view_source.syntax_highlight"));
|
||||
@ -29,186 +19,7 @@ function onLoadViewPartialSource()
|
||||
if (window.arguments[3] == 'selection')
|
||||
viewSourceChrome.loadViewSourceFromSelection(window.arguments[2]);
|
||||
else
|
||||
viewPartialSourceForFragment(window.arguments[2], window.arguments[3]);
|
||||
viewSourceChrome.loadViewSourceFromFragment(window.arguments[2], window.arguments[3]);
|
||||
|
||||
window.content.focus();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// special handler for markups such as MathML where reformatting the output is
|
||||
// helpful
|
||||
function viewPartialSourceForFragment(node, context)
|
||||
{
|
||||
gTargetNode = node;
|
||||
if (gTargetNode && gTargetNode.nodeType == Node.TEXT_NODE)
|
||||
gTargetNode = gTargetNode.parentNode;
|
||||
|
||||
// walk up the tree to the top-level element (e.g., <math>, <svg>)
|
||||
var topTag;
|
||||
if (context == 'mathml')
|
||||
topTag = 'math';
|
||||
else
|
||||
throw 'not reached';
|
||||
var topNode = gTargetNode;
|
||||
while (topNode && topNode.localName != topTag)
|
||||
topNode = topNode.parentNode;
|
||||
if (!topNode)
|
||||
return;
|
||||
|
||||
// serialize
|
||||
var title = gViewSourceBundle.getString("viewMathMLSourceTitle");
|
||||
var wrapClass = gWrapLongLines ? ' class="wrap"' : '';
|
||||
var source =
|
||||
'<!DOCTYPE html>'
|
||||
+ '<html>'
|
||||
+ '<head><title>' + title + '</title>'
|
||||
+ '<link rel="stylesheet" type="text/css" href="' + gViewSourceCSS + '">'
|
||||
+ '<style type="text/css">'
|
||||
+ '#target { border: dashed 1px; background-color: lightyellow; }'
|
||||
+ '</style>'
|
||||
+ '</head>'
|
||||
+ '<body id="viewsource"' + wrapClass
|
||||
+ ' onload="document.title=\''+title+'\';document.getElementById(\'target\').scrollIntoView(true)">'
|
||||
+ '<pre>'
|
||||
+ getOuterMarkup(topNode, 0)
|
||||
+ '</pre></body></html>'
|
||||
; // end
|
||||
|
||||
// display
|
||||
gBrowser.loadURI("data:text/html;charset=utf-8," + encodeURIComponent(source));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
function getInnerMarkup(node, indent) {
|
||||
var str = '';
|
||||
for (var i = 0; i < node.childNodes.length; i++) {
|
||||
str += getOuterMarkup(node.childNodes.item(i), indent);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
function getOuterMarkup(node, indent) {
|
||||
var newline = '';
|
||||
var padding = '';
|
||||
var str = '';
|
||||
if (node == gTargetNode) {
|
||||
gStartTargetLine = gLineCount;
|
||||
str += '</pre><pre id="target">';
|
||||
}
|
||||
|
||||
switch (node.nodeType) {
|
||||
case Node.ELEMENT_NODE: // Element
|
||||
// to avoid the wide gap problem, '\n' is not emitted on the first
|
||||
// line and the lines before & after the <pre id="target">...</pre>
|
||||
if (gLineCount > 0 &&
|
||||
gLineCount != gStartTargetLine &&
|
||||
gLineCount != gEndTargetLine) {
|
||||
newline = '\n';
|
||||
}
|
||||
gLineCount++;
|
||||
if (gDebug) {
|
||||
newline += gLineCount;
|
||||
}
|
||||
for (var k = 0; k < indent; k++) {
|
||||
padding += ' ';
|
||||
}
|
||||
str += newline + padding
|
||||
+ '<<span class="start-tag">' + node.nodeName + '</span>';
|
||||
for (var i = 0; i < node.attributes.length; i++) {
|
||||
var attr = node.attributes.item(i);
|
||||
if (!gDebug && attr.nodeName.match(/^[-_]moz/)) {
|
||||
continue;
|
||||
}
|
||||
str += ' <span class="attribute-name">'
|
||||
+ attr.nodeName
|
||||
+ '</span>=<span class="attribute-value">"'
|
||||
+ unicodeTOentity(attr.nodeValue)
|
||||
+ '"</span>';
|
||||
}
|
||||
if (!node.hasChildNodes()) {
|
||||
str += '/>';
|
||||
}
|
||||
else {
|
||||
str += '>';
|
||||
var oldLine = gLineCount;
|
||||
str += getInnerMarkup(node, indent + 2);
|
||||
if (oldLine == gLineCount) {
|
||||
newline = '';
|
||||
padding = '';
|
||||
}
|
||||
else {
|
||||
newline = (gLineCount == gEndTargetLine) ? '' : '\n';
|
||||
gLineCount++;
|
||||
if (gDebug) {
|
||||
newline += gLineCount;
|
||||
}
|
||||
}
|
||||
str += newline + padding
|
||||
+ '</<span class="end-tag">' + node.nodeName + '</span>>';
|
||||
}
|
||||
break;
|
||||
case Node.TEXT_NODE: // Text
|
||||
var tmp = node.nodeValue;
|
||||
tmp = tmp.replace(/(\n|\r|\t)+/g, " ");
|
||||
tmp = tmp.replace(/^ +/, "");
|
||||
tmp = tmp.replace(/ +$/, "");
|
||||
if (tmp.length != 0) {
|
||||
str += '<span class="text">' + unicodeTOentity(tmp) + '</span>';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (node == gTargetNode) {
|
||||
gEndTargetLine = gLineCount;
|
||||
str += '</pre><pre>';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
function unicodeTOentity(text)
|
||||
{
|
||||
const charTable = {
|
||||
'&': '&<span class="entity">amp;</span>',
|
||||
'<': '&<span class="entity">lt;</span>',
|
||||
'>': '&<span class="entity">gt;</span>',
|
||||
'"': '&<span class="entity">quot;</span>'
|
||||
};
|
||||
|
||||
function charTableLookup(letter) {
|
||||
return charTable[letter];
|
||||
}
|
||||
|
||||
function convertEntity(letter) {
|
||||
try {
|
||||
var unichar = gEntityConverter.ConvertToEntity(letter, entityVersion);
|
||||
var entity = unichar.substring(1); // extract '&'
|
||||
return '&<span class="entity">' + entity + '</span>';
|
||||
} catch (ex) {
|
||||
return letter;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gEntityConverter) {
|
||||
try {
|
||||
gEntityConverter =
|
||||
Components.classes["@mozilla.org/intl/entityconverter;1"]
|
||||
.createInstance(Components.interfaces.nsIEntityConverter);
|
||||
} catch(e) { }
|
||||
}
|
||||
|
||||
const entityVersion = Components.interfaces.nsIEntityConverter.entityW3C;
|
||||
|
||||
var str = text;
|
||||
|
||||
// replace chars in our charTable
|
||||
str = str.replace(/[<>&"]/g, charTableLookup);
|
||||
|
||||
// replace chars > 0x7f via nsIEntityConverter
|
||||
str = str.replace(/[^\0-\u007f]/g, convertEntity);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ const BUNDLE_URL = "chrome://global/locale/viewSource.properties";
|
||||
// We use noncharacter Unicode codepoints to minimize the risk of clashing
|
||||
// with anything that might legitimately be present in the document.
|
||||
// U+FDD0..FDEF <noncharacters>
|
||||
const MARK_SELECTION_START = '\uFDD0';
|
||||
const MARK_SELECTION_END = '\uFDEF';
|
||||
const MARK_SELECTION_START = "\uFDD0";
|
||||
const MARK_SELECTION_END = "\uFDEF";
|
||||
|
||||
let global = this;
|
||||
|
||||
@ -69,7 +69,8 @@ let ViewSourceContent = {
|
||||
|
||||
get isViewSource() {
|
||||
let uri = content.document.documentURI;
|
||||
return uri == "about:blank" || uri.startsWith("view-source:");
|
||||
return uri == "about:blank" || uri.startsWith("view-source:") ||
|
||||
(uri.startsWith("data:") && uri.includes("MathML"));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -109,6 +109,27 @@ var gViewSourceUtils = {
|
||||
viewSourceBrowser.loadViewSourceFromSelection(aSelection);
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays view source for a MathML fragment from some document in the
|
||||
* provided <browser>. This allows for non-window display methods, such as a
|
||||
* tab from Firefox. The caller that manages the <browser> is responsible for
|
||||
* ensuring the companion frame script, viewSource-content.js, has been loaded
|
||||
* for the <browser>.
|
||||
*
|
||||
* @param aNode
|
||||
* Some element within the fragment of interest.
|
||||
* @param aContext
|
||||
* A string denoting the type of fragment. Currently, "mathml" is the
|
||||
* only accepted value.
|
||||
* @param aViewSourceInBrowser
|
||||
* The browser to display the view source in.
|
||||
*/
|
||||
viewSourceFromFragmentInBrowser: function(aNode, aContext,
|
||||
aViewSourceInBrowser) {
|
||||
let viewSourceBrowser = new ViewSourceBrowser(aViewSourceInBrowser);
|
||||
viewSourceBrowser.loadViewSourceFromFragment(aNode, aContext);
|
||||
},
|
||||
|
||||
// Opens the interval view source viewer
|
||||
_openInInternalViewer: function(aArgsOrURL, aPageDescriptor, aDocument, aLineNumber)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user