Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
132 views
in Technique[技术] by (71.8m points)

javascript - Can I add a button or a link to my web page which lets me view the source of that page (like pre-pending the URL with view-source:)?

In Firefox 78, when visiting view-source:https://example.com/my-page/, it's possible to click on any link in the HTML Source and the link will take you through to view-source:https://example.com/my-other-page/ without issue.

But if a view-source:-prefixed link appears on a normal web page (ie. a page without a view-source: prefix in front of the URL), the link no longer works.

This makes it impossible to add a link or a button to a page which allows the user to see the HTML Source of that page.


HTML Links are blocked:

When clicked, any link on a normal web page such as this:

<a href="view-source:https://example.com/my-page/">View Source</a>

will produce the following error in the console:

Security Error: Content at https://example.com/my-page/ may not load or link to view-source:https://example.com/my-page/.


Javascript is blocked:

Alternatively, a <button> which, when clicked, fires the following script:

window.open('view-source:' + window.location.href, '_blank');

will produce the following error in the console:

Uncaught Error: Access to 'view-source:https://example.com/my-page/' from script denied


Can I add a button or a link to my web page which lets me view the source of that page?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Can I add a button or a link to my web page which lets me view the source of that page?

Yes. It turns out, this is possible with:

  • Javascript
  • CSS
  • a handful of Regular Expressions

N.B. Note that in the example below, the line

const HTMLSourceTab = window.open(window.location.href, '_blank');

does not execute, due to StackSnippet security constraints which prevent execution of window.open().

However, the script does work on GitHub Pages.


Example:

const viewHTMLSourceButton = document.getElementsByClassName('viewHTMLSource')[0];

const openHTMLSource = () => {

  const HTMLSourceTab = window.open(window.location.href, '_blank');

  let documentDOM = document.documentElement.cloneNode(true);
  let documentDOMBody = documentDOM.getElementsByTagName('body')[0];
  documentDOMBody.removeAttribute('style');

  let viewHTMLSourceButtonHelpers = [...documentDOM.getElementsByClassName('viewHTMLSourceButtonHelper')];
  for (helper of viewHTMLSourceButtonHelpers) {helper.remove();}
  
  let documentMarkup = documentDOM.outerHTML;
  documentMarkup = documentMarkup.replace(/><head>/g, '>
<head>');
  documentMarkup = documentMarkup.replace(/</body></html>/g, '</body>
</html>');
  documentMarkup = documentMarkup.replace(/<script>[sS]+?</scrip.>/g, '');
  documentMarkup = documentMarkup.replace(/><script/g, '>
<script');
  documentMarkup = documentMarkup.replace(/<([^>]+?)
([^>]+?)>/g, '<$1 $2>');
  documentMarkup = documentMarkup.split('
');


  for (let i = 0; i < documentMarkup.length; i++) {

    documentMarkup[i] = documentMarkup[i].replace(/</g, '&lt;');
    documentMarkup[i] = documentMarkup[i].replace(/"/g, '&quot;');
    documentMarkup[i] = documentMarkup[i].replace(/>/g, '&gt;');

    documentMarkup[i] = documentMarkup[i].replace(/(&lt;/?)([w-]+?)(s|&gt;)/g, '$1<span class="HTMLSourceElementName">$2</span>$3');
    documentMarkup[i] = documentMarkup[i].replace(/=&quot;(.+?)&quot;/g, '=&quot;<span class="HTMLSourceAttributeValue">$1</span><b>&quot;</b>');
    documentMarkup[i] = documentMarkup[i].replace(/([w-]+)=&quot;/g, '<span class="HTMLSourceAttributeName">$1</span><b>=&quot;</b>');
  }
  
  let lineCounterWidth;
  
  switch (true) {
  
    case (documentMarkup.length > 9999) : lineCounterWidth = '45'; break;
    case (documentMarkup.length > 999) : lineCounterWidth = '36'; break;
    case (documentMarkup.length > 99) : lineCounterWidth = '27'; break;
    case (documentMarkup.length > 9) : lineCounterWidth = '18'; break;
    default : lineCounterWidth = '9';
  }     

  let documentDoctype = '';
  documentDoctype += '<li class="HTMLSourceDoctype"><div class="line">';
  documentDoctype += '&lt;!DOCTYPE html&gt;';
  documentDoctype += '</div></li>';
  
  let HTMLSource = '';
  HTMLSource += '<ol class="HTMLSourceList">
';
  HTMLSource += documentDoctype + '
';
  HTMLSource += '<li><div class="line">';
  HTMLSource += documentMarkup.join('</div></li>
<li><div class="line">');
  HTMLSource += '</div></li>
';
  HTMLSource += '</ol>';
  

  let HTMLSourceStylesContent = '';
  HTMLSourceStylesContent += '.HTMLSource {position: absolute; top: 0; left: 0; z-index: 96; width: 100vw; height: 100vh; padding-bottom: 12px; font-family:monospace; color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); box-sizing: border-box; overflow: auto;}';
  HTMLSourceStylesContent += '.HTMLSourceList {list-style-type: none; margin: 8px 0 0; padding-left: 0; counter-reset: line;}';
  HTMLSourceStylesContent += '.HTMLSourceList li {position: relative; display: block; clear: both; width: 100%; font-size: 13px; line-height: 16px; white-space: pre-wrap;}';
  HTMLSourceStylesContent += '.HTMLSourceList li::before {content: counter(line); display: inline-block; float: left; width: ' + lineCounterWidth + 'px; margin-right: 6px; color: rgb(204, 204, 204); text-align: right; font-style: normal; counter-increment: line;}';
  HTMLSourceStylesContent += '.HTMLSourceList li .line {display: inline-block; float: right; width: calc(100%  - ' + lineCounterWidth + 'px - 6px);}';
  HTMLSourceStylesContent += '.HTMLSourceDoctype {color: rgb(70, 130, 180); font-style: italic;}';
  HTMLSourceStylesContent += '.HTMLSourceElementName {color: rgb(128, 0, 128); font-weight: bold;}';
  HTMLSourceStylesContent += '.HTMLSourceAttributeName {font-weight: bold;}';
  HTMLSourceStylesContent += '.HTMLSourceAttributeValue {color: rgb(0, 0, 255);}';
  
  let HTMLSourceScriptContent = '';
  HTMLSourceScriptContent += 'document.body.style.position = "fixed";';

  HTMLSourceScriptContent += 'let HTMLSourceStyles = document.createElement("style");';
  HTMLSourceScriptContent += 'HTMLSourceStyles.classList.add("viewHTMLSourceButtonHelper");';
  HTMLSourceScriptContent += 'HTMLSourceStyles.textContent = `' + HTMLSourceStylesContent + '`;';
  HTMLSourceScriptContent += 'document.head.appendChild(HTMLSourceStyles);';
  
  HTMLSourceScriptContent += 'let HTMLSourceMarkup = document.createElement("div");';
  HTMLSourceScriptContent += 'HTMLSourceMarkup.classList.add("HTMLSource");';
  HTMLSourceScriptContent += 'HTMLSourceMarkup.classList.add("viewHTMLSourceButtonHelper");';
  HTMLSourceScriptContent += 'HTMLSourceMarkup.innerHTML = `' + HTMLSource + '`;';
  HTMLSourceScriptContent += 'document.body.appendChild(HTMLSourceMarkup);';
  
  let HTMLSourceScript = document.createElement('script');
  HTMLSourceScript.classList.add('viewHTMLSourceButtonHelper');
  HTMLSourceScript.textContent = HTMLSourceScriptContent;
  
  HTMLSourceTab.addEventListener('DOMContentLoaded', () => HTMLSourceTab.document.body.appendChild(HTMLSourceScript));
  HTMLSourceTab.focus();
};

viewHTMLSourceButton.addEventListener('click', openHTMLSource, false);
hr {
  margin: 36px 0;
}
<button type="button" class="viewHTMLSource">View HTML Source</button>

<hr />

<p><strong>N.B.</strong> Note that in this example, <code>line 26</code>:</p>

<p><code>const HTMLSourceTab = window.open(window.location.href, '_blank');</code></p>

<p>does <em>not</em> execute, due to <strong>StackSnippet</strong> security constraints which prevent execution of <code>window.open()</code>.</p>

<p>See: <a href="https://meta.stackoverflow.com/questions/337916/why-is-window-open-blocked">https://meta.stackoverflow.com/questions/337916/why-is-window-open-blocked</a></p>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...