10

Is it possible to enable the "Reader View" of Chrome for Android, when Chrome doesn't offer it?


On iOS, Safari leaves the choice entirely to the user, who has to click a button – which is good, as Chrome's popup-bar is a bit distracting, and especially because it means that the user is free to enable it on any website.

In Firefox, the button isn't always offered, but display of the Reader mode can be forced with about:reader?url=URL, which can easily be turned into a bookmarklet.

But for Chrome, I couldn't find any option of forcing Reader View yet, and more often than not it isn't offered on the very articles, where it would help the most, such as those with bad color choices and overly obstrusive ads.

6
  • 1
    Enter the URL chrome://flags, search #reader-mode-heuristics and change it from default to whatever is convenient to you.
    – Firelord
    Nov 27, 2019 at 22:52
  • 1
    @Firelord While it works, it is overkill. As long as I don't set it to "always", some pages where it would be needed don't offer it. But on every other page it is a nuisance. Of course, if there is no other option, I have to live with it and occasionally open the articles in Firefox...
    – kdb
    Nov 28, 2019 at 8:57
  • I wonder, is it possible to enable flags states purely by invoking uri with respective headers? If so, you can create bookmarklets to toggle the states. If not, you might still be able to use a TamperMonkey script.
    – Arctiic
    May 13, 2023 at 16:35
  • @Arctiic: Tampermonkey won't help on mobile, where reader view is mostly relevant. Meanwhile, redirecting to such URLs also is increasingly limited by CORS rules -- for instance, you can't redirect to a data: URI from a bookmarklet, only from extensions. Meanwhile, I've mostly solved it though by writing a custom reader-mode bookmarklet, that removes all of the annoying elements and does some minor formatting changes.
    – kdb
    May 15, 2023 at 11:00
  • @kdb Speaking of which, I have an Android automotive head unit (runs Jellybean) that comes with an generic onboard web browser (not branded, literally just called "browser"). As it turned out, the URL box on that browser accepts a string entry that puts the browser into a "debug" mode of some sort, which then allows you to access, e.g., `file:\\` and other insecure URI directly from the browser. I suppose Chrome wouldn't have anything similar, would it? Likely not a good practice anyway, in retrospect.
    – Arctiic
    May 16, 2023 at 3:44

6 Answers 6

2

Technically, it is possible to request on-demand reader view (also known as "simplified view") on Chrome for Android.

Chrome uses DOM Distiller to determine if a page is worth to be simplified, then simplify and render it as a new page if requested.

There are a few ways to load a URL in reader view:

  1. (Manual) from chrome://dom-distiller, a debug page that allows a user to input a URL and force load it in simplified mode.

  2. (Possible idea for automation) a unique URL for every simplified page, which can be directly accessed from the address bar.

    chrome-distiller://<UUID>_<HASH>/?url=<URL>
    

    Where:

    • UUID: Universally unique identifier. By default, it uses UUIDv4 (random), but any UUID works, including nil UUID (00000000-0000-0000-0000-000000000000)
    • HASH: SHA-256 of URL
    • URL: target URL (optionally, percent-encoded for safety).

    Example URL for this page:

    chrome-distiller://00000000-0000-0000-0000-000000000000_07036109224c60335e35e3b4c22dd02cf775f69d4430245c4c454aff570d6787/?url=https://android.stackexchange.com/questions/218970
    

Additional reading:

1
  • 3
    "chrome://dom-distiller" doesn't seem to be a thing anymore (tested with Chrome for Android v92 stable).
    – Tom
    Aug 22, 2021 at 19:48
1

You can make it appear on every webpage by enabling it in chrome://flags/#reader-mode-heuristics

so, enable it when you need it, and disable when you're done as it would appear on every page.

1

#reader-mode-heuristics works but inconsistent.

Chrome Android version 101 with chrome://flags/#reader-mode-heuristics set to Always only works some of the time.

e.g. Opening the BBC News main page shows the Reader View Prompt. But after dismissing the prompt and following a link on the main page to a specific article there is no Reader View Prompt for that page.

But, with the article page open, closing Chrome and then restarting it does cause the prompt to appear.

1
  • YES! close and open chrome. That's what worked for me. Jun 15, 2022 at 1:50
1

It is possible if you have the Xposed framework installed.

I write the Xposed module ChromeXt, which adds the reader moder item to the page menu.

0
0

I have implemented a bookmarklet style solution described here:

https://gist.github.com/hughpearse/9454406ad43116dfaef8c43e7eac84f4

1. Original code and explanation (Hugh Pearse)

In chrome on android the "Simplified Page" feature does not have a button. If for some reason the browser decides not to display the layover toast message to access this feature, then the user is unable to enter "simplified view". The solution to this problem has been discussed before on Stack Overflow here, where they suggest opening a url to access a local resource on the browser cache. The local resource URL is formatted as follows:

chrome-distiller://<UUID>_<HASH>/?url=<URL>

It is not permitted to perform a javascript redirect to a local resource in the browser, however it is possible to display the URL as text.

javascript:function customModulo(r,o){const a=Math.floor(r/o);const t=r-o*a;return t}var sha256=function r(o){function a(r,o){return r>>>o|r<<32-o}var t=Math.pow;var n=t(2,32);var v="length";var f,e;var c="";var i=[];var u=o[v]*8;var l=r.h=r.h||[];var s=r.k=r.k||[];var h=s[v];var d={};for(var w=2;h<64;w++){if(!d[w]){for(f=0;f<313;f+=w){d[f]=w}l[h]=t(w,.5)*n|0;s[h++]=t(w,1/3)*n|0}}o+="\x80";while(customModulo(o[v],64)-56)o+="\0";for(f=0;f<o[v];f++){e=o.charCodeAt(f);if(e>>8)return;i[f>>2]|=e<<customModulo(3-f,4)*8}i[i[v]]=u/n|0;i[i[v]]=u;for(e=0;e<i[v];){var M=i.slice(e,e+=16);var g=l;l=l.slice(0,8);for(f=0;f<64;f++){var m=f+e;var k=M[f-15],S=M[f-2];var p=l[0],A=l[4];if(f<16){M[f]=M[f]}else{M[f]=M[f-16]+(a(k,7)^a(k,18)^k>>>3)+M[f-7]+(a(S,17)^a(S,19)^S>>>10)|0}var C=l[7]+(a(A,6)^a(A,11)^a(A,25))+(A&l[5]^~A&l[6])+s[f]+M[f];var b=(a(p,2)^a(p,13)^a(p,22))+(p&l[1]^p&l[2]^l[1]&l[2]);l=[C+b|0].concat(l);l[4]=l[4]+C|0}for(f=0;f<8;f++){l[f]=l[f]+g[f]|0}}for(f=0;f<8;f++){for(e=3;e+1;e--){var j=l[f]>>e*8&255;c+=function(){if(j<16){return"0"+j.toString(16)}else{return j.toString(16)}}()}}return c};var readerUrl="chrome-distiller://00000000-0000-0000-0000-000000000000_"+sha256(window.location.href)+decodeURIComponent("/%3Furl=")+window.location.href;window.location=readerUrl;

It is also tricky to open a bookmarklet in Chrome on Android. The process is described here.

To use:

Save this bookmark to Chrome in Android, then open a web page you want to read, then search in the address bar for the bookmarklet, and then open it.

2. Extended code and explanation (kdb)

Changing location.href is disallowed by security policies. I updated the script to use the clipboard instead.

2.1. Minified version for bookmarklet use

javascript:/* Chrome Reader View.js - 10:25:07 2023-07-31 */(function(){ main();function main(){document.head.innerHTML="";document.body.innerHTML="<input id='reader-url-input' style='width: 100%25'/>"+"<button id='reader-url-button' style='width: 100%25'>Copy to Clipboard</button>";let inp=document.getElementById("reader-url-input");let but=document.getElementById("reader-url-button");inp.value=readerViewUrl();but.addEventListener("click",copyReaderUrl);copyReaderUrl();function copyReaderUrl(){inp.select();inp.setSelectionRange(0,99999);navigator.clipboard.writeText(inp.value);}location.href=readerViewUrl();}function readerViewUrl(){return"chrome-distiller:/"+"/00000000-0000-0000-0000-000000000000_"+sha256(window.location.href)+"/?url="+window.location.href;}function customModulo(r,o){let a=Math.floor(r/o);let t=r-o*a;return t}function sha256(o){let r=sha256;function a(r,o){return r>>>o|r<<32-o}var t=Math.pow;var n=t(2,32);var v="length";var f,e;var c="";var i=[];var u=o[v]*8;var l=r.h=r.h||[];var s=r.k=r.k||[];var h=s[v];var d={};for(var w=2;h<64;w++){if(!d[w]){for(f=0;f<313;f+=w){d[f]=w}l[h]=t(w,.5)*n|0;s[h++]=t(w,1/3)*n|0}}o+="\x80";while(customModulo(o[v],64)-56)o+="\0";for(f=0;f<o[v];f++){e=o.charCodeAt(f);if(e>>8)return;i[f>>2]|=e<<customModulo(3-f,4)*8}i[i[v]]=u/n|0;i[i[v]]=u;for(e=0;e<i[v];){var M=i.slice(e,e+=16);var g=l;l=l.slice(0,8);for(f=0;f<64;f++){var m=f+e;var k=M[f-15],S=M[f-2];var p=l[0],A=l[4];if(f<16){M[f]=M[f]}else{M[f]=M[f-16]+(a(k,7)^ a(k,18)^ k>>>3)+M[f-7]+(a(S,17)^ a(S,19)^ S>>>10)|0}var C=l[7]+(a(A,6)^ a(A,11)^ a(A,25))+(A&l[5]^ ~A&l[6])+s[f]+M[f];var b=(a(p,2)^ a(p,13)^ a(p,22))+(p&l[1]^ p&l[2]^ l[1]&l[2]);l=[C+b|0].concat(l);l[4]=l[4]+C|0}for(f=0;f<8;f++){l[f]=l[f]+g[f]|0}}for(f=0;f<8;f++){for(e=3;e+1;e--){var j=l[f]>>e*8&255;c+=function(){if(j<16){return"0"+j.toString(16)}else{return j.toString(16)}}()}}return c};})();undefined;

2.2. Formatted source code

// Script overall based on [hp01]. Updated 2023/07/31 by kdb.
//
// Usage
//   1. Add minified version (one-liner starting with javascript:) as a bookmarklet.
//   2. Invoke the bookmarklet by typing the name into the address bar on mobile.
//   3. On mobile, the cliboard should now contain the chrome-distiller:// URL.
//      If it does not, click the copy-to-clipboard button.
//   4. Paste into the URL bar and press Enter. Beware: "the link you copied" does *not* work,
//      as that will extract the http[s]:// part out of the URL instead of using the chrome-distiller:// URL.
//
// References
//   [hp01] https://gist.github.com/hughpearse/9454406ad43116dfaef8c43e7eac84f4
//   [w301] https://www.w3schools.com/howto/howto_js_copy_clipboard.asp

main();

function main() {
    // We cannot directly write to the clipboard without a user interaction context,
    // so an <input/><button/> pattern is used.
    //
    // On Chrome for Android, invoking a bookmarklet already enables
    // permissions for writing to the clipboard, so the function is invoked immediately.

    document.head.innerHTML = "";
    document.body.innerHTML =
        "<input id='reader-url-input' style='width: 100%'/>" +
        "<button id='reader-url-button' style='width: 100%'>Copy to Clipboard</button>";
    const inp = document.getElementById("reader-url-input");
    const but = document.getElementById("reader-url-button");
    inp.value = readerViewUrl();
    but.addEventListener("click", copyReaderUrl);
    copyReaderUrl();
    function copyReaderUrl() {
        inp.select();
        inp.setSelectionRange(0, 99999); // For mobile according to [w301]
        navigator.clipboard.writeText(inp.value);
    }
    // Changing the href will most likely fail due to security
    // policies of Chrome, but is left in case it works at some point.
    location.href = readerViewUrl();
}

function readerViewUrl() {
    // Code from [hp01], but simplified/reformatted.
    // The pattern "/"+"/" is a hack due to my minification script stripping out "//..." patterns.
    return "chrome-distiller:/"+"/00000000-0000-0000-0000-000000000000_"
          + sha256(window.location.href)
          + "/?url=" + window.location.href;
}



function customModulo(r, o) {
    // Unchanged from [hp01].
    const a = Math.floor(r / o);
    const t = r - o * a;
    return t
}

function sha256(o) {
    // Unchanged from [hp01].
    const r = sha256;
    function a(r, o) {
    return r >>> o | r << 32 - o
    }
    var t = Math.pow;
    var n = t(2, 32);
    var v = "length";
    var f, e;
    var c = "";
    var i = [];
    var u = o[v] * 8;
    var l = r.h = r.h || [];
    var s = r.k = r.k || [];
    var h = s[v];
    var d = {};
    for (var w = 2; h < 64; w++) {
    if (!d[w]) {
        for (f = 0; f < 313; f += w) {
        d[f] = w
        }
        l[h] = t(w, .5) * n | 0;
        s[h++] = t(w, 1 / 3) * n | 0
    }
    }
    o += "\x80";
    while (customModulo(o[v], 64) - 56) o += "\0";
    for (f = 0; f < o[v]; f++) {
    e = o.charCodeAt(f);
    if (e >> 8) return;
    i[f >> 2] |= e << customModulo(3 - f, 4) * 8
    }
    i[i[v]] = u / n | 0;
    i[i[v]] = u;
    for (e = 0; e < i[v];) {
    var M = i.slice(e, e += 16);
    var g = l;
    l = l.slice(0, 8);
    for (f = 0; f < 64; f++) {
        var m = f + e;
        var k = M[f - 15],
        S = M[f - 2];
        var p = l[0],
        A = l[4];
        if (f < 16) {
        M[f] = M[f]
        } else {
        M[f] = M[f - 16] + (a(k, 7) ^ a(k, 18) ^ k >>> 3) + M[f - 7] + (a(S, 17) ^ a(S, 19) ^ S >>> 10) | 0
        }
        var C = l[7] + (a(A, 6) ^ a(A, 11) ^ a(A, 25)) + (A & l[5] ^ ~A & l[6]) + s[f] + M[f];
        var b = (a(p, 2) ^ a(p, 13) ^ a(p, 22)) + (p & l[1] ^ p & l[2] ^ l[1] & l[2]);
        l = [C + b | 0].concat(l);
        l[4] = l[4] + C | 0
    }
    for (f = 0; f < 8; f++) {
        l[f] = l[f] + g[f] | 0
    }
    }
    for (f = 0; f < 8; f++) {
    for (e = 3; e + 1; e--) {
        var j = l[f] >> e * 8 & 255;
        c += function() {
        if (j < 16) {
            return "0" + j.toString(16)
        } else {
            return j.toString(16)
        }
        }()
    }
    }
    return c
};
0
-1

They just seem to have moved and renamed it.

You can now find it under Settings | Accessibility | Simplified view for web pages

(thanks to enquirer at reddit for pointing me in that direction)

1
  • 2
    Welcome to Android Enthusiasts Stack Exchange! While this might help other readers who never see a prompt for simplified view anymore, I'm afraid this doesn't help the asker, since as stated by its description: "Offer to show articles in simplified view, when supported". However, as the asker has mentioned, some websites are not compliant with the requirement and thus the prompt will never appear...
    – Andrew T.
    Dec 20, 2019 at 17:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .