From ba22901e9774cfdc652c1c718ec921b8d647d4e8 Mon Sep 17 00:00:00 2001
From: Soren Stoutner <soren@stoutner.com>
Date: Wed, 10 Jan 2018 12:52:12 -0700
Subject: [PATCH] Add a View Source activity. 
 https://redmine.stoutner.com/issues/64

---
 .idea/dictionaries/soren.xml                  |   1 +
 COPYING                                       |   2 +-
 app/build.gradle                              |   4 +-
 app/src/main/AndroidManifest.xml              |  12 +
 .../main/assets/de/about_licenses_dark.html   |   6 +-
 .../main/assets/de/about_licenses_light.html  |   6 +-
 .../main/assets/en/about_licenses_dark.html   |   6 +-
 .../main/assets/en/about_licenses_light.html  |   6 +-
 .../main/assets/es/about_licenses_dark.html   |   8 +-
 .../main/assets/es/about_licenses_light.html  |   8 +-
 .../main/assets/it/about_licenses_dark.html   |   8 +-
 .../main/assets/it/about_licenses_light.html  |   8 +-
 .../activities/AboutActivity.java             |  12 +-
 .../activities/MainWebViewActivity.java       |  57 +-
 .../activities/ViewSourceActivity.java        | 588 ++++++++++++++++++
 .../dialogs/AboutViewSourceDialog.java        |  56 ++
 .../dialogs/ViewSslCertificateDialog.java     |  36 +-
 app/src/main/res/drawable/about_dark.xml      |  13 +
 .../drawable/{about.xml => about_light.xml}   |   4 +-
 .../res/layout/domain_settings_fragment.xml   |   6 +-
 app/src/main/res/layout/main_drawerlayout.xml |   9 +-
 app/src/main/res/layout/url_app_bar.xml       |   8 +-
 .../main/res/layout/view_source_app_bar.xml   |  51 ++
 .../layout/view_source_coordinatorlayout.xml  | 151 +++++
 .../main/res/menu/bookmarks_options_menu.xml  |   3 +-
 .../res/menu/view_source_options_menu.xml     |  32 +
 .../main/res/menu/webview_navigation_menu.xml |   2 +-
 .../main/res/menu/webview_options_menu.xml    |  10 +-
 app/src/main/res/values/attrs.xml             |   6 +-
 app/src/main/res/values/strings.xml           |  11 +
 app/src/main/res/values/styles.xml            |  12 +-
 ....png => 02-FullScreen-TranslucentBars.png} | Bin
 32 files changed, 1047 insertions(+), 95 deletions(-)
 create mode 100644 app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java
 create mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/AboutViewSourceDialog.java
 create mode 100644 app/src/main/res/drawable/about_dark.xml
 rename app/src/main/res/drawable/{about.xml => about_light.xml} (64%)
 create mode 100644 app/src/main/res/layout/view_source_app_bar.xml
 create mode 100644 app/src/main/res/layout/view_source_coordinatorlayout.xml
 create mode 100644 app/src/main/res/menu/view_source_options_menu.xml
 rename fastlane/metadata/android/en/images/phoneScreenshots/{02-Full Screen-TranslucentBars.png => 02-FullScreen-TranslucentBars.png} (100%)

diff --git a/.idea/dictionaries/soren.xml b/.idea/dictionaries/soren.xml
index c6d78c7e..b34ca1a2 100644
--- a/.idea/dictionaries/soren.xml
+++ b/.idea/dictionaries/soren.xml
@@ -9,6 +9,7 @@
       <w>amoled</w>
       <w>androidversion</w>
       <w>anonymized</w>
+      <w>apng</w>
       <w>appbarlayout</w>
       <w>aren</w>
       <w>autoselected</w>
diff --git a/COPYING b/COPYING
index 94bb603c..a0d34973 100644
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
-Privacy Browser copyright © 2015-2017 Soren Stoutner <soren@stoutner.com>.
+Privacy Browser copyright © 2015-2018 Soren Stoutner <soren@stoutner.com>.
 
 This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
diff --git a/app/build.gradle b/app/build.gradle
index 75255675..8644364d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
  *
@@ -67,7 +67,7 @@ dependencies {
     implementation fileTree(include: ['*.jar'], dir: 'libs')
     implementation 'com.android.support:design:26.1.0'
     // Only compile `com.google.firebase:firebase-ads` for the free flavor.
-    freeImplementation 'com.google.firebase:firebase-ads:11.6.2'
+    freeImplementation 'com.google.firebase:firebase-ads:11.8.0'
 }
 
 // Google's documentation says the following line is required for `firebase-ads` but things work correctly without it.  I have no interest in applying the Google Mobile Services plugin in the standard flavor if I don't have to.
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ad0e5305..9f260e6e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -153,5 +153,17 @@
             android:screenOrientation="fullUser"
             android:persistableMode="persistNever"
             tools:ignore="UnusedAttribute" />
+
+        <!-- `android:configChanges="orientation|screenSize"` makes the activity not reload when the orientation changes.
+             `android:persistableMode="persistNever"` removes Privacy Browser from the recents screen on a device reboot.
+             `tools:ignore="unusedAttribute"` removes the lint warning that `persistableMode` does not apply to API < 21. -->
+        <activity
+            android:name=".activities.ViewSourceActivity"
+            android:label="@string/view_source"
+            android:parentActivityName=".activities.MainWebViewActivity"
+            android:configChanges="orientation|screenSize"
+            android:screenOrientation="fullUser"
+            android:persistableMode="persistNever"
+            tools:ignore="UnusedAttribute" />
     </application>
 </manifest>
diff --git a/app/src/main/assets/de/about_licenses_dark.html b/app/src/main/assets/de/about_licenses_dark.html
index 9e15f881..3565a512 100644
--- a/app/src/main/assets/de/about_licenses_dark.html
+++ b/app/src/main/assets/de/about_licenses_dark.html
@@ -1,5 +1,5 @@
 <!--
-  Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
 
   Translation 2016 Aaron Gerlach <aaron@gerlach.com>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
@@ -30,7 +30,7 @@
 
     <body>
         <h3>Copyright</h3>
-        <p>Privacy Browser ist copyright © 2015-2017 von <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+        <p>Privacy Browser ist copyright © 2015-2018 von <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
 
         <h3>Lizenz</h3>
         <p>Privacy Browser ist veröffentlicht unter der <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ Lizenz</a>. The full text of the license is below.
@@ -39,6 +39,8 @@
         <h3>Attribute</h3>
         <p>The list of ad servers used by the ad blocker comes from <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
             Because a list of domain names is a list of facts, it <a href="https://www.copyright.gov/help/faq/faq-protect.html">cannot be copyrighted</a>.</p>
+        <p>Privacy Browser is built with the <a href="https://developer.android.com/topic/libraries/support-library/index.html">Android Support Library</a>,
+            which is released under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.</p>
         <p><img class="left" src="../en/images/privacy_browser.png"> <img class="left" src="../en/images/privacy_browser_free.png"> <img class="left" src="../en/images/warning.png"> <img class="left" src="../en/images/javascript_enabled.png">
             are derived from ic_security and ic_language, which are part of the <a href="https://material.io/icons/">Android Material icon set</a> and are released under the <a href ="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.
             The full text of the license is below. Modifications copyright © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting images are released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
diff --git a/app/src/main/assets/de/about_licenses_light.html b/app/src/main/assets/de/about_licenses_light.html
index 8d8851f8..00f0acb9 100644
--- a/app/src/main/assets/de/about_licenses_light.html
+++ b/app/src/main/assets/de/about_licenses_light.html
@@ -1,5 +1,5 @@
 <!--
-  Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
 
   Translation 2016 Aaron Gerlach <aaron@gerlach.com>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
@@ -30,7 +30,7 @@
 
     <body>
         <h3>Copyright</h3>
-        <p>Privacy Browser ist copyright © 2015-2017 von <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+        <p>Privacy Browser ist copyright © 2015-2018 von <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
 
         <h3>Lizenz</h3>
         <p>Privacy Browser ist veröffentlicht unter der <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ Lizenz</a>. The full text of the license is below.
@@ -39,6 +39,8 @@
         <h3>Attribute</h3>
         <p>The list of ad servers used by the ad blocker comes from <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
             Because a list of domain names is a list of facts, it <a href="https://www.copyright.gov/help/faq/faq-protect.html">cannot be copyrighted</a>.</p>
+        <p>Privacy Browser is built with the <a href="https://developer.android.com/topic/libraries/support-library/index.html">Android Support Library</a>,
+            which is released under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.</p>
         <p><img class="left" src="../en/images/privacy_browser.png"> <img class="left" src="../en/images/privacy_browser_free.png"> <img class="left" src="../en/images/warning.png"> <img class="left" src="../en/images/javascript_enabled.png">
             are derived from ic_security and ic_language, which are part of the <a href="https://material.io/icons/">Android Material icon set</a> and are released under the <a href ="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.
             The full text of the license is below. Modifications copyright © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting images are released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
diff --git a/app/src/main/assets/en/about_licenses_dark.html b/app/src/main/assets/en/about_licenses_dark.html
index aa52ae20..3474713e 100644
--- a/app/src/main/assets/en/about_licenses_dark.html
+++ b/app/src/main/assets/en/about_licenses_dark.html
@@ -1,5 +1,5 @@
 <!--
-  Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -25,7 +25,7 @@
 
     <body>
         <h3>Copyright</h3>
-        <p>Privacy Browser copyright © 2015-2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+        <p>Privacy Browser copyright © 2015-2018 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
 
         <h3>License</h3>
         <p>Privacy Browser is released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>. The full text of the license is below.
@@ -34,6 +34,8 @@
         <h3>Attributions</h3>
         <p>The list of ad servers used by the ad blocker comes from <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
             Because a list of domain names is a list of facts, it <a href="https://www.copyright.gov/help/faq/faq-protect.html">cannot be copyrighted</a>.</p>
+        <p>Privacy Browser is built with the <a href="https://developer.android.com/topic/libraries/support-library/index.html">Android Support Library</a>,
+            which is released under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.</p>
         <p><img class="left" src="images/privacy_browser.png"> <img class="left" src="images/privacy_browser_free.png"> <img class="left" src="images/warning.png"> <img class="left" src="images/javascript_enabled.png">
             are derived from ic_security and ic_language, which are part of the <a href="https://material.io/icons/">Android Material icon set</a> and are released under the <a href ="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.
             The full text of the license is below. Modifications copyright © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting images are released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
diff --git a/app/src/main/assets/en/about_licenses_light.html b/app/src/main/assets/en/about_licenses_light.html
index 1cebdb2c..500cb335 100644
--- a/app/src/main/assets/en/about_licenses_light.html
+++ b/app/src/main/assets/en/about_licenses_light.html
@@ -1,5 +1,5 @@
 <!--
-  Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -25,7 +25,7 @@
 
     <body>
         <h3>Copyright</h3>
-        <p>Privacy Browser copyright © 2015-2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+        <p>Privacy Browser copyright © 2015-2018 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
 
         <h3>License</h3>
         <p>Privacy Browser is released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>. The full text of the license is below.
@@ -34,6 +34,8 @@
         <h3>Attributions</h3>
         <p>The list of ad servers used by the ad blocker comes from <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
             Because a list of domain names is a list of facts, it <a href="https://www.copyright.gov/help/faq/faq-protect.html">cannot be copyrighted</a>.</p>
+        <p>Privacy Browser is built with the <a href="https://developer.android.com/topic/libraries/support-library/index.html">Android Support Library</a>,
+            which is released under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.</p>
         <p><img class="left" src="images/privacy_browser.png"> <img class="left" src="images/privacy_browser_free.png"> <img class="left" src="images/warning.png"> <img class="left" src="images/javascript_enabled.png">
             are derived from ic_security and ic_language, which are part of the <a href="https://material.io/icons/">Android Material icon set</a> and are released under the <a href ="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.
             The full text of the license is below. Modifications copyright © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting images are released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
diff --git a/app/src/main/assets/es/about_licenses_dark.html b/app/src/main/assets/es/about_licenses_dark.html
index 4feb1e0b..24e38fcc 100644
--- a/app/src/main/assets/es/about_licenses_dark.html
+++ b/app/src/main/assets/es/about_licenses_dark.html
@@ -1,7 +1,7 @@
 <!--
-  Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
 
-  Translation 2017 Jose A. León Becerra <emails@joseleon.me>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+  Translation 2017-2018 Jose A. León Becerra <emails@joseleon.me>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -27,7 +27,7 @@
 
     <body>
         <h3>Derechos de autor</h3>
-        <p>Navegador Privado tiene derechos de autor © 2015-2017 por <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+        <p>Navegador Privado tiene derechos de autor © 2015-2018 por <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
 
         <h3>Licencia</h3>
         <p>Navegador Privado está liberado bajo la licencia <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+</a>. El texto completo de la licencia se encuentra en la parte inferior de este documento (se deja en el idioma original).
@@ -36,6 +36,8 @@
         <h3>Atribuciones</h3>
         <p>La lista de servidores publicitarios usados por el bloqueador de anuncios procede de <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
             Ya que una lista de nombres de dominio es una lista de hechos, no puede tener <a href="https://www.copyright.gov/help/faq/faq-protect.html">derechos de autor</a>.</p>
+        <p>Navegador privado está construido con la <a href="https://developer.android.com/topic/libraries/support-library/index.html">librería de soporte de android</a>,
+            que se libera bajo la <a href="https://www.apache.org/licenses/LICENSE-2.0">Licencia Apache 2.0</a>.</p>
         <p><img class="left" src="../en/images/privacy_browser.png"> <img class="left" src="../en/images/privacy_browser_free.png"> <img class="left" src="../en/images/warning.png"> <img class="left" src="../en/images/javascript_enabled.png">
             derivan de ic_security y de ic_language, que son parte del <a href="https://material.io/icons/">conjunto de iconos Android Material</a> y son liberados bajo la <a href ="https://www.apache.org/licenses/LICENSE-2.0">Licencia Apache 2.0</a>.
             El texto completo de la licencia se encuentra debajo. Copyright de modificaciones © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. Las imágenes resultantes se liberan bajo la
diff --git a/app/src/main/assets/es/about_licenses_light.html b/app/src/main/assets/es/about_licenses_light.html
index be90b67e..b5eaec78 100644
--- a/app/src/main/assets/es/about_licenses_light.html
+++ b/app/src/main/assets/es/about_licenses_light.html
@@ -1,7 +1,7 @@
 <!--
-  Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
 
-  Translation 2017 Jose A. León Becerra <emails@joseleon.me>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+  Translation 2017-2018 Jose A. León Becerra <emails@joseleon.me>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -27,7 +27,7 @@
 
     <body>
         <h3>Derechos de autor</h3>
-        <p>Navegador Privado tiene derechos de autor © 2015-2017 por <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+        <p>Navegador Privado tiene derechos de autor © 2015-2018 por <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
 
         <h3>Licencia</h3>
         <p>Navegador Privado está liberado bajo la licencia <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+</a>. El texto completo de la licencia se encuentra en la parte inferior de este documento (se deja en el idioma original).
@@ -36,6 +36,8 @@
         <h3>Atribuciones</h3>
         <p>La lista de servidores publicitarios usados por el bloqueador de anuncios procede de <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
             Ya que una lista de nombres de dominio es una lista de hechos, no puede tener <a href="https://www.copyright.gov/help/faq/faq-protect.html">derechos de autor</a>.</p>
+        <p>Navegador privado está construido con la <a href="https://developer.android.com/topic/libraries/support-library/index.html">librería de soporte de android</a>,
+            que se libera bajo la <a href="https://www.apache.org/licenses/LICENSE-2.0">Licencia Apache 2.0</a>.</p>
         <p><img class="left" src="../en/images/privacy_browser.png"> <img class="left" src="../en/images/privacy_browser_free.png"> <img class="left" src="../en/images/warning.png"> <img class="left" src="../en/images/javascript_enabled.png">
             derivan de ic_security y de ic_language, que son parte del <a href="https://material.io/icons/">conjunto de iconos Android Material</a> y son liberados bajo la <a href ="https://www.apache.org/licenses/LICENSE-2.0">Licencia Apache 2.0</a>.
             El texto completo de la licencia se encuentra debajo. Copyright de modificaciones © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.
diff --git a/app/src/main/assets/it/about_licenses_dark.html b/app/src/main/assets/it/about_licenses_dark.html
index 7727f949..ef941af1 100644
--- a/app/src/main/assets/it/about_licenses_dark.html
+++ b/app/src/main/assets/it/about_licenses_dark.html
@@ -1,7 +1,7 @@
 <!--
-  Copyright © 2017 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2017-2018 Soren Stoutner <soren@stoutner.com>.
 
-  Translation 2017 Francesco Buratti.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+  Translation 2017-2018 Francesco Buratti.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
   Privacy Browser is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@
 
     <body>
         <h3>Copyright</h3>
-        <p>Privacy Browser copyright © 2015-2017: <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+        <p>Privacy Browser copyright © 2015-2018: <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
 
         <h3>Licenza</h3>
         <p>Privacy Browser è rilasciato con <a href="https://www.gnu.org/licenses/gpl-3.0.html">Licenza GPLv3+ </a>.
@@ -40,6 +40,8 @@
         <h3>Attribuzioni</h3>
         <p>La lista dei server utilizzata dalla funzionalità di blocco degli annunci è tratta da <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
             Dal momento che si tratta di una lista di domini <a href="https://www.copyright.gov/help/faq/faq-protect.html">non può essere coperta da Copyright</a>.</p>
+        <p>Privacy Browser è sviluppato con la <a href="https://developer.android.com/topic/libraries/support-library/index.html">Android Support Library</a>,
+            che è rilasciata con <a href="https://www.apache.org/licenses/LICENSE-2.0">Licenza Apache 2.0</a>.</p>
         <p><img class="left" src="../en/images/privacy_browser.png"> <img class="left" src="../en/images/privacy_browser_free.png"> <img class="left" src="../en/images/warning.png"> <img class="left" src="../en/images/javascript_enabled.png">
             sono state derivate da ic_security e ic_language, che fanno parte dell'<a href="https://material.io/icons/">Android Material icon set</a> e sono state rilasciate sotto <a href ="https://www.apache.org/licenses/LICENSE-2.0">Licenza Apache 2.0</a>.
             Il testo completo della Licenza è riportato di seguito. Copyright delle modifiche © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.
diff --git a/app/src/main/assets/it/about_licenses_light.html b/app/src/main/assets/it/about_licenses_light.html
index f93e858d..6936277f 100644
--- a/app/src/main/assets/it/about_licenses_light.html
+++ b/app/src/main/assets/it/about_licenses_light.html
@@ -1,7 +1,7 @@
 <!--
-  Copyright © 2017 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2017-2018 Soren Stoutner <soren@stoutner.com>.
 
-  Translation 2017 Francesco Buratti.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+  Translation 2017-2018 Francesco Buratti.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
   Privacy Browser is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@
 
     <body>
         <h3>Copyright</h3>
-        <p>Privacy Browser copyright © 2015-2017: <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+        <p>Privacy Browser copyright © 2015-2018: <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
 
         <h3>Licenza</h3>
         <p>Privacy Browser è rilasciato con <a href="https://www.gnu.org/licenses/gpl-3.0.html">Licenza GPLv3+ </a>.
@@ -40,6 +40,8 @@
         <h3>Attribuzioni</h3>
         <p>La lista dei server utilizzata dalla funzionalità di blocco degli annunci è tratta da <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
             Dal momento che si tratta di una lista di domini <a href="https://www.copyright.gov/help/faq/faq-protect.html">non può essere coperta da Copyright</a>.</p>
+        <p>Privacy Browser è sviluppato con la <a href="https://developer.android.com/topic/libraries/support-library/index.html">Android Support Library</a>,
+            che è rilasciata con <a href="https://www.apache.org/licenses/LICENSE-2.0">Licenza Apache 2.0</a>.</p>
         <p><img class="left" src="../en/images/privacy_browser.png"> <img class="left" src="../en/images/privacy_browser_free.png"> <img class="left" src="../en/images/warning.png"> <img class="left" src="../en/images/javascript_enabled.png">
             sono state derivate da ic_security e ic_language, che fanno parte dell'<a href="https://material.io/icons/">Android Material icon set</a> e sono state rilasciate sotto <a href ="https://www.apache.org/licenses/LICENSE-2.0">Licenza Apache 2.0</a>.
             Il testo completo della Licenza è riportato di seguito. Copyright delle modifiche © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/AboutActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/AboutActivity.java
index c2045215..0f6df3df 100644
--- a/app/src/main/java/com/stoutner/privacybrowser/activities/AboutActivity.java
+++ b/app/src/main/java/com/stoutner/privacybrowser/activities/AboutActivity.java
@@ -48,21 +48,21 @@ public class AboutActivity extends AppCompatActivity {
         // Set the content view.
         setContentView(R.layout.about_coordinatorlayout);
 
-        // We need to use the SupportActionBar from android.support.v7.app.ActionBar until the minimum API is >= 21.
-        Toolbar aboutAppBar = (Toolbar) findViewById(R.id.about_toolbar);
+        // `SupportActionBar` from `android.support.v7.app.ActionBar` must be used until the minimum API is >= 21.
+        Toolbar aboutAppBar = findViewById(R.id.about_toolbar);
         setSupportActionBar(aboutAppBar);
 
-        // Display the home arrow on supportAppBar.
+        // Display the home arrow on `supportAppBar`.
         final ActionBar appBar = getSupportActionBar();
-        assert appBar != null;// This assert removes the incorrect warning in Android Studio on the following line that appBar might be null.
+        assert appBar != null;  // This assert removes the incorrect warning in Android Studio on the following line that appBar might be null.
         appBar.setDisplayHomeAsUpEnabled(true);
 
         //  Setup the ViewPager.
-        ViewPager aboutViewPager = (ViewPager) findViewById(R.id.about_viewpager);
+        ViewPager aboutViewPager = findViewById(R.id.about_viewpager);
         aboutViewPager.setAdapter(new aboutPagerAdapter(getSupportFragmentManager()));
 
         // Setup the TabLayout and connect it to the ViewPager.
-        TabLayout aboutTabLayout = (TabLayout) findViewById(R.id.about_tablayout);
+        TabLayout aboutTabLayout = findViewById(R.id.about_tablayout);
         aboutTabLayout.setupWithViewPager(aboutViewPager);
     }
 
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
index 63225a08..27d56930 100644
--- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
+++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2015-2017 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2015-2018 Soren Stoutner <soren@stoutner.com>.
  *
  * Download cookie code contributed 2017 Hendrik Knackstedt.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
  *
@@ -167,6 +167,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     // `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog` and `CreateHomeScreenShortcutDialog`.  It is also used in `onCreate()`.
     public static String webViewTitle;
 
+    // `appliedUserAgentString` is public static so it can be accessed from `ViewSourceActivity`.  It is also used in `applyDomainSettings()`.
+    public static String appliedUserAgentString;
+
     // `displayWebpageImagesBoolean` is public static so it can be accessed from `DomainSettingsFragment`.  It is also used in `applyAppSettings()` and `applyDomainSettings()`.
     public static boolean displayWebpageImagesBoolean;
 
@@ -327,13 +330,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     // `urlTextBox` is used in `onCreate()`, `onOptionsItemSelected()`, `loadUrlFromTextBox()`, `loadUrl()`, and `highlightUrlText()`.
     private EditText urlTextBox;
 
-    // `redColorSpan` is used in `onCreate()` and `highlightUrlText()`.
+    // The color spans are used in `onCreate()` and `highlightUrlText()`.
     private ForegroundColorSpan redColorSpan;
-
-    // `initialGrayColorSpan` is sued in `onCreate()` and `highlightUrlText()`.
     private ForegroundColorSpan initialGrayColorSpan;
-
-    // `finalGrayColorSpam` is used in `onCreate()` and `highlightUrlText()`.
     private ForegroundColorSpan finalGrayColorSpan;
 
     // `adView` is used in `onCreate()` and `onConfigurationChanged()`.
@@ -415,16 +414,16 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         appBar.setCustomView(R.layout.url_app_bar);
         appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
 
-        // Initialize the `ForegroundColorSpans` and `StyleSpan` for highlighting `urlTextBox`.  We have to use the deprecated `getColor()` until API >= 23.
+        // Initialize the foreground color spans for highlighting the URLs.  We have to use the deprecated `getColor()` until API >= 23.
         redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
         initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
         finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
 
         // Get a handle for `urlTextBox`.
-        urlTextBox = appBar.getCustomView().findViewById(R.id.url_edittext);
+        urlTextBox = findViewById(R.id.url_edittext);
 
         // Remove the formatting from `urlTextBar` when the user is editing the text.
-        urlTextBox.setOnFocusChangeListener((v, hasFocus) -> {
+        urlTextBox.setOnFocusChangeListener((View v, boolean hasFocus) -> {
             if (hasFocus) {  // The user is editing `urlTextBox`.
                 // Remove the highlighting.
                 urlTextBox.getText().removeSpan(redColorSpan);
@@ -436,8 +435,8 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
             }
         });
 
-        // Set the `Go` button on the keyboard to load the URL in `urlTextBox`.
-        urlTextBox.setOnKeyListener((v, keyCode, event) -> {
+        // Set the go button on the keyboard to load the URL in `urlTextBox`.
+        urlTextBox.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
             // If the event is a key-down event on the `enter` button, load the URL.
             if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
                 // Load the URL into the mainWebView and consume the event.
@@ -812,8 +811,8 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
 
             // Close `bufferedReader`.
             bufferedReader.close();
-        } catch (IOException ioException) {
-            // We're pretty sure the asset exists, so we don't need to worry about the `IOException` ever being thrown.
+        } catch (IOException e) {
+            // The asset exists, so the `IOException` will never be thrown.
         }
 
         mainWebView.setWebViewClient(new WebViewClient() {
@@ -1127,7 +1126,10 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                             });
                 }
 
+                // Update the progress bar.
                 progressBar.setProgress(progress);
+
+                // Set the visibility of the progress bar.
                 if (progress < 100) {
                     // Show the progress bar.
                     progressBar.setVisibility(View.VISIBLE);
@@ -1221,7 +1223,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         registerForContextMenu(mainWebView);
 
         // Allow the downloading of files.
-        mainWebView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {
+        mainWebView.setDownloadListener((String url, String userAgent, String contentDisposition, String mimetype, long contentLength) -> {
             // Show the `DownloadFileDialog` `AlertDialog` and name this instance `@string/download`.
             AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength);
             downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
@@ -1578,7 +1580,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         // Run all the other default commands.
         super.onPrepareOptionsMenu(menu);
 
-        // `return true` displays the menu.
+        // Display the menu.
         return true;
     }
 
@@ -1588,6 +1590,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     // removeAllCookies is deprecated, but it is required for API < 21.
     @SuppressWarnings("deprecation")
     public boolean onOptionsItemSelected(MenuItem menuItem) {
+        // Get the selected menu item ID.
         int menuItemId = menuItem.getItemId();
 
         // Set the commands that relate to the menu entries.
@@ -1915,6 +1918,12 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                 printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
                 return true;
 
+            case R.id.view_source:
+                // Launch the Vew Source activity.
+                Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
+                startActivity(viewSourceIntent);
+                return true;
+
             case R.id.add_to_homescreen:
                 // Show the `CreateHomeScreenShortcutDialog` `AlertDialog` and name this instance `R.string.create_shortcut`.
                 AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog();
@@ -2259,7 +2268,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                 });
 
                 // Add a `Download Image` entry.
-                menu.add(R.string.download_image).setOnMenuItemClickListener(item -> {
+                menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
                     // Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
                     AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
                     downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
@@ -2296,7 +2305,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                 });
 
                 // Add a `Download Image` entry.
-                menu.add(R.string.download_image).setOnMenuItemClickListener(item -> {
+                menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
                     // Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
                     AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
                     downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
@@ -2824,10 +2833,10 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     }
 
     private void applyAppSettings() {
-        // Get a handle for `sharedPreferences`.  `this` references the current context.
+        // Get a handle for the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
-        // Store the values from `sharedPreferences` in variables.
+        // Store the values from the shared preferences in variables.
         String homepageString = sharedPreferences.getString("homepage", "https://start.duckduckgo.com");
         String torHomepageString = sharedPreferences.getString("tor_homepage", "https://3g2upl4pq6kufc4m.onion");
         String torSearchString = sharedPreferences.getString("tor_search", "https://3g2upl4pq6kufc4m.onion/html/?q=");
@@ -3179,6 +3188,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                             // Use the selected user agent.
                             mainWebView.getSettings().setUserAgentString(userAgentString);
                     }
+
+                    // Store the applied user agent string.
+                    appliedUserAgentString = mainWebView.getSettings().getUserAgentString();
                 }
 
                 // Set a green background on `urlTextBox` to indicate that custom domain settings are being used.  We have to use the deprecated `.getDrawable()` until the minimum API >= 21.
@@ -3241,6 +3253,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                             // Use the selected user agent.
                             mainWebView.getSettings().setUserAgentString(defaultUserAgentString);
                     }
+
+                    // Store the applied user agent string.
+                    appliedUserAgentString = mainWebView.getSettings().getUserAgentString();
                 }
 
                 // Set a transparent background on `urlTextBox`.  We have to use the deprecated `.getDrawable()` until the minimum API >= 21.
@@ -3347,9 +3362,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     private void highlightUrlText() {
         String urlString = urlTextBox.getText().toString();
 
-        if (urlString.startsWith("http://")) {  // Highlight connections that are not encrypted.
+        if (urlString.startsWith("http://")) {  // Highlight the protocol of connections that are not encrypted.
             urlTextBox.getText().setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-        } else if (urlString.startsWith("https://")) {  // Highlight connections that are encrypted.
+        } else if (urlString.startsWith("https://")) {  // De-emphasize the protocol of connections that are encrypted.
             urlTextBox.getText().setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
         }
 
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java
new file mode 100644
index 00000000..583e3c6a
--- /dev/null
+++ b/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java
@@ -0,0 +1,588 @@
+/*
+ * Copyright © 2017-2018 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.activities;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Typeface;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.LocaleList;
+import android.preference.PreferenceManager;
+import android.support.v4.app.NavUtils;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.webkit.CookieManager;
+import android.widget.EditText;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.dialogs.AboutViewSourceDialog;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Locale;
+
+public class ViewSourceActivity extends AppCompatActivity {
+    // `activity` is used in `onCreate()` and `goBack()`.
+    Activity activity;
+
+    // The color spans are used in `onCreate()` and `highlightUrlText()`.
+    private ForegroundColorSpan redColorSpan;
+    private ForegroundColorSpan initialGrayColorSpan;
+    private ForegroundColorSpan finalGrayColorSpan;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        // Set the theme.
+        if (MainWebViewActivity.darkTheme) {
+            setTheme(R.style.PrivacyBrowserDark);
+        } else {
+            setTheme(R.style.PrivacyBrowserLight);
+        }
+
+        // Run the default commands.
+        super.onCreate(savedInstanceState);
+
+        // Store a handle for the current activity.
+        activity = this;
+
+        // Set the content view.
+        setContentView(R.layout.view_source_coordinatorlayout);
+
+        // `SupportActionBar` from `android.support.v7.app.ActionBar` must be used until the minimum API is >= 21.
+        Toolbar viewSourceAppBar = findViewById(R.id.view_source_toolbar);
+        setSupportActionBar(viewSourceAppBar);
+
+        // Setup the app bar.
+        final ActionBar appBar = getSupportActionBar();
+
+        // Remove the incorrect warning in Android Studio that appBar might be null.
+        assert appBar != null;
+
+        // Add the custom layout to the app bar.
+        appBar.setCustomView(R.layout.view_source_app_bar);
+        appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
+
+        // Get a handle for the url text box.
+        EditText urlEditText = findViewById(R.id.url_edittext);
+
+        // Get the formatted URL string from the main activity.
+        String formattedUrlString = MainWebViewActivity.formattedUrlString;
+
+        // Populate the URL text box.
+        urlEditText.setText(formattedUrlString);
+
+        // Initialize the foreground color spans for highlighting the URLs.  We have to use the deprecated `getColor()` until API >= 23.
+        redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+        initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
+        finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
+
+        // Apply text highlighting to the URL.
+        highlightUrlText();
+
+        // Get a handle for the input method manager, which is used to hide the keyboard.
+        InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+
+        // Let Android Studio know that we aren't worried about the input method manager being null.
+        assert inputMethodManager != null;
+
+        // Remove the formatting from the URL when the user is editing the text.
+        urlEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> {
+            if (hasFocus) {  // The user is editing `urlTextBox`.
+                // Remove the highlighting.
+                urlEditText.getText().removeSpan(redColorSpan);
+                urlEditText.getText().removeSpan(initialGrayColorSpan);
+                urlEditText.getText().removeSpan(finalGrayColorSpan);
+            } else {  // The user has stopped editing `urlTextBox`.
+                // Hide the soft keyboard.
+                inputMethodManager.hideSoftInputFromWindow(urlEditText.getWindowToken(), 0);
+
+                // Reapply the highlighting.
+                highlightUrlText();
+
+
+            }
+        });
+
+        // Set the go button on the keyboard to request new source data.
+        urlEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
+            // Request new source data if the enter key was pressed.
+            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
+                // Hide the soft keyboard.
+                inputMethodManager.hideSoftInputFromWindow(urlEditText.getWindowToken(), 0);
+
+                // Remove the focus from the URL box.
+                urlEditText.clearFocus();
+
+                // Get new source data for the current URL.
+                new GetSource().execute(urlEditText.getText().toString());
+
+                // Consume the key press.
+                return true;
+            } else {
+                // Do not consume the key press.
+                return false;
+            }
+        });
+
+        // Get the source as an `AsyncTask`.
+        new GetSource().execute(formattedUrlString);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        getMenuInflater().inflate(R.menu.view_source_options_menu, menu);
+
+        // Display the menu.
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem menuItem) {
+        // Get a handle for the about alert dialog.
+        DialogFragment aboutDialogFragment = new AboutViewSourceDialog();
+
+        // Show the about alert dialog.
+        aboutDialogFragment.show(getFragmentManager(), getString(R.string.about));
+
+        // Consume the event.
+        return true;
+    }
+
+    public void goBack(View view) {
+        // Go home.
+        NavUtils.navigateUpFromSameTask(activity);
+    }
+
+    private void highlightUrlText() {
+        // Get a handle for the URL EditText.
+        EditText urlEditText = findViewById(R.id.url_edittext);
+
+        // Get the URL.
+        String urlString = urlEditText.getText().toString();
+
+        // Highlight the beginning of the URL.
+        if (urlString.startsWith("http://")) {  // Highlight the protocol of connections that are not encrypted.
+            urlEditText.getText().setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        } else if (urlString.startsWith("https://")) {  // De-emphasize the protocol of connections that are encrypted.
+            urlEditText.getText().setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        }
+
+        // Get the index of the `/` immediately after the domain name.
+        int endOfDomainName = urlString.indexOf("/", (urlString.indexOf("//") + 2));
+
+        // De-emphasize the text after the domain name.
+        if (endOfDomainName > 0) {
+            urlEditText.getText().setSpan(finalGrayColorSpan, endOfDomainName, urlString.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        }
+    }
+
+    // The first `String` declares the parameters.  The `Void` does not declare progress units.  The last `String` contains the results.
+    // `StaticFieldLeaks` are suppressed so that Android Studio doesn't complain about running an AsyncTask in a non-static context.
+    @SuppressLint("StaticFieldLeak")
+    private class GetSource extends AsyncTask<String, Void, String> {
+        // The class variables pass information from `doInBackground()` to `onPostExecute()`.
+        SpannableStringBuilder responseMessageBuilder;
+        SpannableStringBuilder requestHeadersBuilder;
+        SpannableStringBuilder responseHeadersBuilder;
+
+        // `onPreExecute()` operates on the UI thread.
+        @Override
+        protected void onPreExecute() {
+            // Get a handle for the progress bar.
+            ProgressBar progressBar = findViewById(R.id.progress_bar);
+
+            // Make the progress bar visible.
+            progressBar.setVisibility(View.VISIBLE);
+
+            // Set the progress bar to be indeterminate.
+            progressBar.setIndeterminate(true);
+        }
+
+        @Override
+        protected String doInBackground(String... formattedUrlString) {
+            // Initialize the response body `String`.
+            String responseBodyString = "";
+
+            // Because everything relating to requesting data from a webserver can throw errors, the entire section must catch `IOExceptions`.
+            try {
+                // Get the current URL from the main activity.
+                URL url = new URL(formattedUrlString[0]);
+
+                // Open a connection to the URL.  No data is actually sent at this point.
+                HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection();
+
+                // Instantiate the variables necessary to build the request headers.
+                requestHeadersBuilder = new SpannableStringBuilder();
+                int oldRequestHeadersBuilderLength;
+                int newRequestHeadersBuilderLength;
+
+
+                // Set the `Host` header property.
+                httpUrlConnection.setRequestProperty("Host", url.getHost());
+
+                // Add the `Host` header to the string builder and format the text.
+                if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
+                    requestHeadersBuilder.append("Host", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                } else {  // Older versions not so much.
+                    oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.append("Host");
+                    newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+                requestHeadersBuilder.append(":  ");
+                requestHeadersBuilder.append(url.getHost());
+
+
+                // Set the `Connection` header property.
+                httpUrlConnection.setRequestProperty("Connection", "keep-alive");
+
+                // Add the `Connection` header to the string builder and format the text.
+                requestHeadersBuilder.append(System.getProperty("line.separator"));
+                if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
+                    requestHeadersBuilder.append("Connection", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                } else {  // Older versions not so much.
+                    oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.append("Connection");
+                    newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+                requestHeadersBuilder.append(":  keep-alive");
+
+
+                // Get the current `User-Agent` string.
+                String userAgentString = MainWebViewActivity.appliedUserAgentString;
+
+                // Set the `User-Agent` header property.
+                httpUrlConnection.setRequestProperty("User-Agent", userAgentString);
+
+                // Add the `User-Agent` header to the string builder and format the text.
+                requestHeadersBuilder.append(System.getProperty("line.separator"));
+                if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
+                    requestHeadersBuilder.append("User-Agent", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                } else {  // Older versions not so much.
+                    oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.append("User-Agent");
+                    newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+                requestHeadersBuilder.append(":  ");
+                requestHeadersBuilder.append(userAgentString);
+
+
+                // Set the `Upgrade-Insecure-Requests` header property.
+                httpUrlConnection.setRequestProperty("Upgrade-Insecure-Requests", "1");
+
+                // Add the `Upgrade-Insecure-Requests` header to the string builder and format the text.
+                requestHeadersBuilder.append(System.getProperty("line.separator"));
+                if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
+                    requestHeadersBuilder.append("Upgrade-Insecure-Requests", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                } else {  // Older versions not so much.
+                    oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.append("Upgrade-Insecure_Requests");
+                    newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+                requestHeadersBuilder.append(":  1");
+
+
+                // Set the `x-requested-with` header property.
+                httpUrlConnection.setRequestProperty("x-requested-with", "");
+
+                // Add the `x-requested-with` header to the string builder and format the text.
+                requestHeadersBuilder.append(System.getProperty("line.separator"));
+                if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
+                    requestHeadersBuilder.append("x-requested-with", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                } else {  // Older versions not so much.
+                    oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.append("x-requested-with");
+                    newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+                requestHeadersBuilder.append(":  ");
+
+
+                // Get a handle for the shared preferences.
+                SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+
+                // Only populate `Do Not Track` if it is enabled.
+                if (sharedPreferences.getBoolean("do_not_track", false)) {
+                    // Set the `dnt` header property.
+                    httpUrlConnection.setRequestProperty("dnt", "1");
+
+                    // Add the `dnt` header to the string builder and format the text.
+                    requestHeadersBuilder.append(System.getProperty("line.separator"));
+                    if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
+                        requestHeadersBuilder.append("dnt", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    } else {  // Older versions not so much.
+                        oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                        requestHeadersBuilder.append("dnt");
+                        newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                        requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+                    requestHeadersBuilder.append(":  1");
+                }
+
+
+                // Set the `Accept` header property.
+                httpUrlConnection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
+
+                // Add the `Accept` header to the string builder and format the text.
+                requestHeadersBuilder.append(System.getProperty("line.separator"));
+                if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
+                    requestHeadersBuilder.append("Accept", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                } else {  // Older versions not so much.
+                    oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.append("Accept");
+                    newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+                requestHeadersBuilder.append(":  ");
+                requestHeadersBuilder.append("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
+
+
+                // Instantiate a locale string.
+                String localeString;
+
+                // Populate the locale string.
+                if (Build.VERSION.SDK_INT >= 24) {  // SDK >= 24 has a list of locales.
+                    // Get the list of locales.
+                    LocaleList localeList = getResources().getConfiguration().getLocales();
+
+                    // Initialize a string builder to extract the locales from the list.
+                    StringBuilder localesStringBuilder = new StringBuilder();
+
+                    // Initialize a `q` value, which is used by `WebView` to indicate the order of importance of the languages.
+                    int q = 10;
+
+                    // Populate the string builder with the contents of the locales list.
+                    for (int i = 0; i < localeList.size(); i++) {
+                        // Append a comma if there is already an item in the string builder.
+                        if (i > 0) {
+                            localesStringBuilder.append(",");
+                        }
+
+                        // Get the indicated locale from the list.
+                        localesStringBuilder.append(localeList.get(i));
+
+                        // If not the first locale, append `;q=0.i`, which drops by .1 for each removal from the main locale.
+                        if (q < 10) {
+                            localesStringBuilder.append(";q=0.");
+                            localesStringBuilder.append(q);
+                        }
+
+                        // Decrement `q`.
+                        q--;
+                    }
+
+                    // Store the populated string builder in the locale string.
+                    localeString = localesStringBuilder.toString();
+                } else {  // SDK < 24 only has a primary locale.
+                    // Store the locale in the locale string.
+                    localeString = Locale.getDefault().toString();
+                }
+
+                // Set the `Accept-Language` header property.
+                httpUrlConnection.setRequestProperty("Accept-Language", localeString);
+
+                // Add the `Accept-Language` header to the string builder and format the text.
+                requestHeadersBuilder.append(System.getProperty("line.separator"));
+                if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
+                    requestHeadersBuilder.append("Accept-Language", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                } else {  // Older versions not so much.
+                    oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.append("Accept-Language");
+                    newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+                requestHeadersBuilder.append(":  ");
+                requestHeadersBuilder.append(localeString);
+
+
+                // Get the cookies for the current domain.
+                String cookiesString = CookieManager.getInstance().getCookie(url.toString());
+
+                // Only process the cookies if they are not null.
+                if (cookiesString != null) {
+                    // Set the `Cookie` header property.
+                    httpUrlConnection.setRequestProperty("Cookie", cookiesString);
+
+                    // Add the `Cookie` header to the string builder and format the text.
+                    requestHeadersBuilder.append(System.getProperty("line.separator"));
+                    if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
+                        requestHeadersBuilder.append("Cookie", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    } else {  // Older versions not so much.
+                        oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                        requestHeadersBuilder.append("Cookie");
+                        newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                        requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+                    requestHeadersBuilder.append(":  ");
+                    requestHeadersBuilder.append(cookiesString);
+                }
+
+
+                // `HttpUrlConnection` sets `Accept-Encoding` to be `gzip` by default.  If the property is manually set, than `HttpUrlConnection` does not process the decoding.
+                // Add the `Accept-Encoding` header to the string builder and format the text.
+                requestHeadersBuilder.append(System.getProperty("line.separator"));
+                if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
+                    requestHeadersBuilder.append("Accept-Encoding", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                } else {  // Older versions not so much.
+                    oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.append("Accept-Encoding");
+                    newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+                    requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+                requestHeadersBuilder.append(":  gzip");
+
+
+                // The actual network request is in a `try` bracket so that `disconnect()` is run in the `finally` section even if an error is encountered in the main block.
+                try {
+                    // Initialize the string builders.
+                    responseMessageBuilder = new SpannableStringBuilder();
+                    responseHeadersBuilder = new SpannableStringBuilder();
+
+                    // Get the response code, which causes the connection to the server to be made.
+                    int responseCode = httpUrlConnection.getResponseCode();
+
+                    // Populate the response message string builder.
+                    if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
+                        responseMessageBuilder.append(String.valueOf(responseCode), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    } else {  // Older versions not so much.
+                        responseMessageBuilder.append(String.valueOf(responseCode));
+                        int newLength = responseMessageBuilder.length();
+                        responseMessageBuilder.setSpan(new StyleSpan(Typeface.BOLD), 0, newLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+                    responseMessageBuilder.append(":  ");
+                    responseMessageBuilder.append(httpUrlConnection.getResponseMessage());
+
+                    // Initialize the iteration variable.
+                    int i = 0;
+
+                    // Iterate through the received header fields.
+                    while (httpUrlConnection.getHeaderField(i) != null) {
+                        // Add a new line if there is already information in the string builder.
+                        if (i > 0) {
+                            responseHeadersBuilder.append(System.getProperty("line.separator"));
+                        }
+
+                        // Add the header to the string builder and format the text.
+                        if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
+                            responseHeadersBuilder.append(httpUrlConnection.getHeaderFieldKey(i), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                        } else {  // Older versions not so much.
+                            int oldLength = responseHeadersBuilder.length();
+                            responseHeadersBuilder.append(httpUrlConnection.getHeaderFieldKey(i));
+                            int newLength = responseHeadersBuilder.length();
+                            responseHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldLength + 1, newLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                        }
+                        responseHeadersBuilder.append(":  ");
+                        responseHeadersBuilder.append(httpUrlConnection.getHeaderField(i));
+
+                        // Increment the iteration variable.
+                        i++;
+                    }
+
+                    // Instantiate an input stream for the response body.
+                    InputStream inputStream;
+
+                    // Get the correct input stream based on the response code.
+                    if (responseCode == 404) {  // Get the error stream.
+                        inputStream = new BufferedInputStream(httpUrlConnection.getErrorStream());
+                    } else {  // Get the response body stream.
+                        inputStream = new BufferedInputStream(httpUrlConnection.getInputStream());
+                    }
+
+                    // Initialize the byte array output stream and the conversion buffer byte array.
+                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+                    byte[] conversionBufferByteArray = new byte[1024];
+
+                    // Instantiate the variable to track the buffer length.
+                    int bufferLength;
+
+                    try {
+                        // Attempt to read data from the input stream and store it in the conversion buffer byte array.  Also store the amount of data transferred in the buffer length variable.
+                        while ((bufferLength = inputStream.read(conversionBufferByteArray)) > 0) {  // Proceed while the amount of data stored in the buffer is > 0.
+                            // Write the contents of the conversion buffer to the byte array output stream.
+                            byteArrayOutputStream.write(conversionBufferByteArray, 0, bufferLength);
+                        }
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+
+                    // Close the input stream.
+                    inputStream.close();
+
+                    // Populate the response body string with the contents of the byte array output stream.
+                    responseBodyString = byteArrayOutputStream.toString();
+                } finally {
+                    // Disconnect `httpUrlConnection`.
+                    httpUrlConnection.disconnect();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+
+            // Return the response body string as the result.
+            return responseBodyString;
+        }
+
+        // `onPostExecute()` operates on the UI thread.
+        @Override
+        protected void onPostExecute(String responseBodyString){
+            // Get handles for the text views.
+            TextView requestHeadersTextView = findViewById(R.id.request_headers);
+            TextView responseMessageTextView = findViewById(R.id.response_message);
+            TextView responseHeadersTextView = findViewById(R.id.response_headers);
+            TextView responseBodyTextView = findViewById(R.id.response_body);
+            ProgressBar progressBar = findViewById(R.id.progress_bar);
+
+            // Populate the text views.
+            requestHeadersTextView.setText(requestHeadersBuilder);
+            responseMessageTextView.setText(responseMessageBuilder);
+            responseHeadersTextView.setText(responseHeadersBuilder);
+            responseBodyTextView.setText(responseBodyString);
+
+            // Hide the progress bar.
+            progressBar.setIndeterminate(false);
+            progressBar.setVisibility(View.GONE);
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/AboutViewSourceDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/AboutViewSourceDialog.java
new file mode 100644
index 00000000..4cd4e7fe
--- /dev/null
+++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/AboutViewSourceDialog.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2018 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.dialogs;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.os.Bundle;
+
+import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.activities.MainWebViewActivity;
+
+public class AboutViewSourceDialog extends DialogFragment {
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Use a builder to create the alert dialog.
+        AlertDialog.Builder dialogBuilder;
+
+        // Set the style and the icon according to the theme.
+        if (MainWebViewActivity.darkTheme) {
+            dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark);
+            dialogBuilder.setIcon(R.drawable.about_dark);
+        } else {
+            dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight);
+            dialogBuilder.setIcon(R.drawable.about_light);
+        }
+
+        // Set an `onClick` listener on the negative button.  Using `null` as the listener closes the dialog without doing anything else.
+        dialogBuilder.setNegativeButton(R.string.close, null);
+
+        // Set the title.
+        dialogBuilder.setTitle(R.string.about_view_source);
+
+        // Set the text.
+        dialogBuilder.setMessage(R.string.about_view_source_message);
+
+        // `onCreateDialog` requires the return of an `AlertDialog`.
+        return dialogBuilder.create();
+    }
+}
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java
index 8decfca9..6f9fa4d4 100644
--- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java
+++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
  *
@@ -51,7 +51,7 @@ public class ViewSslCertificateDialog extends DialogFragment {
         // Create a drawable version of the favorite icon.
         Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), MainWebViewActivity.favoriteIconBitmap);
 
-        // Use `AlertDialog.Builder` to create the `AlertDialog`.
+        // Use a builder to create the alert dialog.
         AlertDialog.Builder dialogBuilder;
 
         // Set the style according to the theme.
@@ -64,7 +64,7 @@ public class ViewSslCertificateDialog extends DialogFragment {
         // Set the icon.
         dialogBuilder.setIcon(favoriteIconDrawable);
 
-        // Set an `onClick` listener on the negative button.  Using `null` closes the dialog without doing anything else.
+        // Set an `onClick` listener on the negative button.  Using `null` as the listener closes the dialog without doing anything else.
         dialogBuilder.setNegativeButton(R.string.close, null);
 
         // Check to see if the website is encrypted.
@@ -75,14 +75,8 @@ public class ViewSslCertificateDialog extends DialogFragment {
             // Set the Layout.  The parent view is `null` because it will be assigned by `AlertDialog`.
             dialogBuilder.setView(layoutInflater.inflate(R.layout.unencrypted_website, null));
 
-            // Create an `AlertDialog` from the `AlertDialog.Builder`
-            final AlertDialog alertDialog = dialogBuilder.create();
-
-            // Show `alertDialog`.
-            alertDialog.show();
-
             // `onCreateDialog` requires the return of an `AlertDialog`.
-            return alertDialog;
+            return dialogBuilder.create();
 
         } else {  // Display the SSL certificate information
             // Set the title.
@@ -91,22 +85,22 @@ public class ViewSslCertificateDialog extends DialogFragment {
             // Set the layout.  The parent view is `null` because it will be assigned by `AlertDialog`.
             dialogBuilder.setView(layoutInflater.inflate(R.layout.view_ssl_certificate, null));
 
-            // Create an `AlertDialog` from the `AlertDialog.Builder`
+            // Create an alert dialog from the builder.
             final AlertDialog alertDialog = dialogBuilder.create();
 
-            // The `AlertDialog` must be shown before items in the layout can be modified.
+            // The alert dialog must be shown before items in the layout can be modified.
             alertDialog.show();
 
             // Get handles for the `TextViews`.
-            TextView domainTextView = (TextView) alertDialog.findViewById(R.id.domain);
-            TextView issuedToCNameTextView = (TextView) alertDialog.findViewById(R.id.issued_to_cname);
-            TextView issuedToONameTextView = (TextView) alertDialog.findViewById(R.id.issued_to_oname);
-            TextView issuedToUNameTextView = (TextView) alertDialog.findViewById(R.id.issued_to_uname);
-            TextView issuedByCNameTextView = (TextView) alertDialog.findViewById(R.id.issued_by_cname);
-            TextView issuedByONameTextView = (TextView) alertDialog.findViewById(R.id.issued_by_oname);
-            TextView issuedByUNameTextView = (TextView) alertDialog.findViewById(R.id.issued_by_uname);
-            TextView startDateTextView = (TextView) alertDialog.findViewById(R.id.start_date);
-            TextView endDateTextView = (TextView) alertDialog.findViewById(R.id.end_date);
+            TextView domainTextView = alertDialog.findViewById(R.id.domain);
+            TextView issuedToCNameTextView = alertDialog.findViewById(R.id.issued_to_cname);
+            TextView issuedToONameTextView = alertDialog.findViewById(R.id.issued_to_oname);
+            TextView issuedToUNameTextView = alertDialog.findViewById(R.id.issued_to_uname);
+            TextView issuedByCNameTextView = alertDialog.findViewById(R.id.issued_by_cname);
+            TextView issuedByONameTextView = alertDialog.findViewById(R.id.issued_by_oname);
+            TextView issuedByUNameTextView = alertDialog.findViewById(R.id.issued_by_uname);
+            TextView startDateTextView = alertDialog.findViewById(R.id.start_date);
+            TextView endDateTextView = alertDialog.findViewById(R.id.end_date);
 
             // Setup the labels.
             String domainLabel = getString(R.string.domain_label) + "  ";
diff --git a/app/src/main/res/drawable/about_dark.xml b/app/src/main/res/drawable/about_dark.xml
new file mode 100644
index 00000000..a1927d79
--- /dev/null
+++ b/app/src/main/res/drawable/about_dark.xml
@@ -0,0 +1,13 @@
+<!-- `about_dark.xml` comes from the Android Material icon set, where it is called `ic_info_outline`.  It is released under the Apache License 2.0. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0" >
+
+    <!-- A hard coded color must be used until API >= 21.  Then `@color` may be used. -->
+    <path
+        android:fillColor="#FFE0E0E0"
+        android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z" />
+</vector>
diff --git a/app/src/main/res/drawable/about.xml b/app/src/main/res/drawable/about_light.xml
similarity index 64%
rename from app/src/main/res/drawable/about.xml
rename to app/src/main/res/drawable/about_light.xml
index 8a20c678..1b8a7af7 100644
--- a/app/src/main/res/drawable/about.xml
+++ b/app/src/main/res/drawable/about_light.xml
@@ -1,4 +1,4 @@
-<!-- `about.xml` comes from the Android Material icon set, where it is called `ic_info_outline`.  It is released under the Apache License 2.0. -->
+<!-- `about_light.xml` comes from the Android Material icon set, where it is called `ic_info_outline`.  It is released under the Apache License 2.0. -->
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:height="24dp"
@@ -6,7 +6,7 @@
     android:viewportHeight="24.0"
     android:viewportWidth="24.0" >
 
-    <!-- We have to use a hard coded color until API >= 21.  Then we can use `@color`. -->
+    <!-- A hard coded color must be used until API >= 21.  Then `@color` may be used. -->
     <path
         android:fillColor="#FF000000"
         android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z" />
diff --git a/app/src/main/res/layout/domain_settings_fragment.xml b/app/src/main/res/layout/domain_settings_fragment.xml
index b113ca72..40e7cc78 100644
--- a/app/src/main/res/layout/domain_settings_fragment.xml
+++ b/app/src/main/res/layout/domain_settings_fragment.xml
@@ -54,7 +54,7 @@
                     android:layout_marginBottom="12dp"
                     android:layout_gravity="bottom"
                     android:src="@drawable/domains"
-                    android:tint="?attr/iconTintColor"
+                    android:tint="?attr/domainSettingsIconTintColor"
                     tools:ignore="contentDescription" />
 
                 <!-- `android.support.design.widget.TextInputLayout` makes the `android:hint` float above the `EditText`. -->
@@ -238,7 +238,7 @@
                     android:layout_marginEnd="10dp"
                     android:layout_gravity="center_vertical"
                     android:src="@drawable/user_agent_light"
-                    android:tint="?attr/iconTintColor"
+                    android:tint="?attr/domainSettingsIconTintColor"
                     android:contentDescription="@string/user_agent" />
 
                 <Spinner
@@ -285,7 +285,7 @@
                     android:layout_marginEnd="10dp"
                     android:layout_gravity="center_vertical"
                     android:src="@drawable/font_size_light"
-                    android:tint="?attr/iconTintColor"
+                    android:tint="?attr/domainSettingsIconTintColor"
                     android:contentDescription="@string/font_size" />
 
                 <Spinner
diff --git a/app/src/main/res/layout/main_drawerlayout.xml b/app/src/main/res/layout/main_drawerlayout.xml
index 6f1a839a..f5801791 100644
--- a/app/src/main/res/layout/main_drawerlayout.xml
+++ b/app/src/main/res/layout/main_drawerlayout.xml
@@ -25,9 +25,8 @@
     android:layout_height="match_parent"
     android:layout_width="match_parent" >
 
-    <!-- `android:fitsSystemWindows="true"` moves `root_coordinatorlayout` below the system status bar.
-         When it is specified, the theme should include `<item name="android:windowTranslucentStatus">true</item>`.
-         Setting the layout root to be `focusableInTouchMode` prevents `urlTextBox` from stealing focus on launch and opening the keyboard. -->
+    <!-- `android:fitsSystemWindows="true"` moves `root_coordinatorlayout` below the system status bar. When it is specified, the theme should include `<item name="android:windowTranslucentStatus">true</item>`.
+         Setting the CoordinatorLayout to be `focusableInTouchMode` prevents the URL text box from stealing focus on launch and opening the keyboard. -->
     <android.support.design.widget.CoordinatorLayout
         android:id="@+id/root_coordinatorlayout"
         xmlns:tools="http://schemas.android.com/tools"
@@ -63,12 +62,12 @@
 
                     <!-- `android:max` changes the maximum `ProgressBar` value from 10000 to 100 to match progress percentage.
                         `android:layout_height="2dp"` works best for API >= 23, but `3dp` is required for visibility on API <= 22.
-                        `tools:ignore="UnusedAttribute"` removes the lint waring about `progressTint` and `progressBackgroundTint` not applying to API < 21. -->
+                        `tools:ignore="UnusedAttribute"` removes the lint warning about `progressTint` and `progressBackgroundTint` not applying to API < 21. -->
                     <ProgressBar
                         android:id="@+id/progress_bar"
                         style="?android:attr/progressBarStyleHorizontal"
-                        android:layout_width="fill_parent"
                         android:layout_height="3dp"
+                        android:layout_width="match_parent"
                         android:layout_gravity="bottom"
                         android:max="100"
                         android:progressTint="?attr/progressTintColor"
diff --git a/app/src/main/res/layout/url_app_bar.xml b/app/src/main/res/layout/url_app_bar.xml
index 3a1dfc4e..a7397c35 100644
--- a/app/src/main/res/layout/url_app_bar.xml
+++ b/app/src/main/res/layout/url_app_bar.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright © 2015-2017 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2015-2018 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -27,8 +27,7 @@
     android:layout_width="match_parent"
     tools:context=".activities.MainWebViewActivity" >
 
-    <!-- Set `@drawable/world` as the initial `favoriteIcon`.
-        `layout_height` and `layout_width` of 26dp matches the `AppBar` icons. -->
+    <!-- Set `@drawable/world` as the initial as the initial favorite icon.  `layout_height` and `layout_width` of 26dp matches the `AppBar` icons. -->
     <ImageView
         android:id="@+id/favorite_icon"
         android:src="@drawable/world"
@@ -39,8 +38,7 @@
         android:onClick="viewSslCertificate"
         android:contentDescription="@string/favorite_icon" />
 
-    <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.
-        `android:inputType="textUri"` disables spell check in the `EditText`. -->
+    <!-- `android:imeOptions="actionGo"` sets the keyboard to have a go key instead of a new line key.  `android:inputType="textUri"` disables spell check in the `EditText`. -->
     <EditText
         android:id="@+id/url_edittext"
         android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/view_source_app_bar.xml b/app/src/main/res/layout/view_source_app_bar.xml
new file mode 100644
index 00000000..4f957a34
--- /dev/null
+++ b/app/src/main/res/layout/view_source_app_bar.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2015-2018 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+  Privacy Browser is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  Privacy Browser is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
+
+<!-- `RelativeLayout` is used instead of a `LinearLayout` because `supportAppBar` does not let `android:layout_weight="1"` cause `urlTextBox` to fill all the available space. -->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    tools:context=".activities.ViewSourceActivity" >
+
+    <ImageView
+        android:id="@+id/back_arrow"
+        android:src="@drawable/back"
+        android:tint="?attr/viewSourceIconTintColor"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginEnd="14dp"
+        android:contentDescription="@string/back"
+        android:onClick="goBack" />
+
+    <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.
+        `android:inputType="textUri"` disables spell check in the `EditText`. -->
+    <EditText
+        android:id="@+id/url_edittext"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_toEndOf="@id/back_arrow"
+        android:hint="@string/url"
+        android:imeOptions="actionGo"
+        android:inputType="textUri"
+        android:selectAllOnFocus="true" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_source_coordinatorlayout.xml b/app/src/main/res/layout/view_source_coordinatorlayout.xml
new file mode 100644
index 00000000..71a76676
--- /dev/null
+++ b/app/src/main/res/layout/view_source_coordinatorlayout.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2017-2018 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+  Privacy Browser is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  Privacy Browser is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
+
+<!-- `android:fitsSystemWindows="true"` moves the AppBar below the status bar.  When it is specified the theme should include `<item name="android:windowTranslucentStatus">true</item>` to make the status bar a transparent, darkened overlay.
+    Setting the layout root to be `focusableInTouchMode` prevents the URL toolbar from stealing focus on launch and opening the keyboard. -->
+<android.support.design.widget.CoordinatorLayout
+    android:id="@+id/view_source_coordinatorlayout"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:context="com.stoutner.privacybrowser.activities.ViewSourceActivity"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:fitsSystemWindows="true"
+    android:focusable="true"
+    android:focusableInTouchMode="true" >
+
+    <!-- The `LinearLayout` with `orientation="vertical"` moves the content below the app bar layout. -->
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:orientation="vertical" >
+
+        <!-- The `AppBarLayout` theme has to be defined here because the activity uses a `NoActionBar` theme. -->
+        <android.support.design.widget.AppBarLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:theme="@style/PrivacyBrowserAppBarLight" >
+
+            <!-- The `FrameLayout` allows the toolbar and the progress bar to occupy the same space. -->
+            <FrameLayout
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent" >
+
+                <android.support.v7.widget.Toolbar
+                    android:id="@+id/view_source_toolbar"
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent" />
+
+                <!-- Android automatically uses a different, skinnier drawable with padding for indeterminate horizontal progress bars in API >= 21.  They make this very difficult to override.  https://redmine.stoutner.com/issues/241
+                `tools:ignore="UnusedAttribute"` removes the lint warning about `progressTint` and `progressBackgroundTint` not applying to API < 21. -->
+                <ProgressBar
+                    android:id="@+id/progress_bar"
+                    style="?android:attr/progressBarStyleHorizontal"
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent"
+                    android:minHeight="3dp"
+                    android:layout_gravity="bottom"
+                    android:progressTint="?attr/progressTintColor"
+                    android:progressBackgroundTint="@color/transparent"
+                    android:visibility="gone"
+                    tools:ignore="UnusedAttribute" />
+            </FrameLayout>
+        </android.support.design.widget.AppBarLayout>
+
+        <ScrollView
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent" >
+
+            <LinearLayout
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:orientation="vertical"
+                android:layout_margin="10dp" >
+
+                <!-- Request headers. -->
+                <TextView
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent"
+                    android:text="@string/request_headers"
+                    android:textAlignment="center"
+                    android:textSize="18sp"
+                    android:textColor="@color/blue_600"
+                    android:textStyle="bold" />
+
+                <TextView
+                    android:id="@+id/request_headers"
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent"
+                    android:textIsSelectable="true"
+                    android:layout_marginBottom="8dp" />
+
+                <!-- Response message. -->
+                <TextView
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent"
+                    android:text="@string/response_message"
+                    android:textAlignment="center"
+                    android:textSize="18sp"
+                    android:textColor="@color/blue_600"
+                    android:textStyle="bold" />
+
+                <TextView
+                    android:id="@+id/response_message"
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent"
+                    android:textIsSelectable="true"
+                    android:layout_marginBottom="8dp" />
+
+                <!-- Response headers. -->
+                <TextView
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent"
+                    android:text="@string/response_headers"
+                    android:textAlignment="center"
+                    android:textSize="18sp"
+                    android:textColor="@color/blue_600"
+                    android:textStyle="bold" />
+
+                <TextView
+                    android:id="@+id/response_headers"
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent"
+                    android:textIsSelectable="true"
+                    android:layout_marginBottom="8dp" />
+
+                <!-- Response body. -->
+                <TextView
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent"
+                    android:text="@string/response_body"
+                    android:textAlignment="center"
+                    android:textSize="18sp"
+                    android:textColor="@color/blue_600"
+                    android:textStyle="bold" />
+
+                <TextView
+                    android:id="@+id/response_body"
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent"
+                    android:textIsSelectable="true" />
+            </LinearLayout>
+        </ScrollView>
+    </LinearLayout>
+</android.support.design.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/app/src/main/res/menu/bookmarks_options_menu.xml b/app/src/main/res/menu/bookmarks_options_menu.xml
index e5bf25cf..176c1bcc 100644
--- a/app/src/main/res/menu/bookmarks_options_menu.xml
+++ b/app/src/main/res/menu/bookmarks_options_menu.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright © 2015-2017 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2015-2018 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -22,6 +22,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
+    <!-- `android:iconTint` can be used once API >= 26 instead of including a separate drawable for each theme. -->
     <item
         android:id="@+id/options_menu_select_all_bookmarks"
         android:title="@string/select_all"
diff --git a/app/src/main/res/menu/view_source_options_menu.xml b/app/src/main/res/menu/view_source_options_menu.xml
new file mode 100644
index 00000000..ec50f16f
--- /dev/null
+++ b/app/src/main/res/menu/view_source_options_menu.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2018 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+  Privacy Browser is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  Privacy Browser is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
+
+<menu
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <!-- `android:iconTint` can be used once API >= 26 instead of including a separate drawable for each theme. -->
+    <item
+        android:id="@+id/about_view_source"
+        android:title="@string/about"
+        android:orderInCategory="10"
+        android:icon="?attr/aboutIcon"
+        app:showAsAction="ifRoom" />
+</menu>
\ No newline at end of file
diff --git a/app/src/main/res/menu/webview_navigation_menu.xml b/app/src/main/res/menu/webview_navigation_menu.xml
index 4063ba2c..eb30116c 100644
--- a/app/src/main/res/menu/webview_navigation_menu.xml
+++ b/app/src/main/res/menu/webview_navigation_menu.xml
@@ -79,7 +79,7 @@
         <item
             android:id="@+id/about"
             android:title="@string/about"
-            android:icon="@drawable/about"
+            android:icon="@drawable/about_light"
             android:orderInCategory="90" />
     </group>
 
diff --git a/app/src/main/res/menu/webview_options_menu.xml b/app/src/main/res/menu/webview_options_menu.xml
index 2761db63..a468a001 100644
--- a/app/src/main/res/menu/webview_options_menu.xml
+++ b/app/src/main/res/menu/webview_options_menu.xml
@@ -174,15 +174,21 @@
         android:orderInCategory="120"
         app:showAsAction="never" />
 
+    <item
+        android:id="@+id/view_source"
+        android:title="@string/view_source"
+        android:orderInCategory="130"
+        app:showAsAction="never" />
+
     <item
         android:id="@+id/add_to_homescreen"
         android:title="@string/add_to_home_screen"
-        android:orderInCategory="130"
+        android:orderInCategory="140"
         app:showAsAction="never" />
 
     <item
         android:id="@+id/refresh"
         android:title="@string/refresh"
-        android:orderInCategory="140"
+        android:orderInCategory="150"
         app:showAsAction="never" />
 </menu>
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index e6544ce1..dd6710b4 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -42,13 +42,12 @@
 
     <attr name="progressTintColor" format="reference" />
     <attr name="navigationIconTintColor" format="reference" />
-
     <attr name="findOnPageIconTintColor" format="reference" />
+    <attr name="domainSettingsIconTintColor" format="reference" />
+    <attr name="viewSourceIconTintColor" format="reference" />
 
     <attr name="listSelectorDrawable" format="reference" />
 
-    <attr name="iconTintColor" format="reference" />
-
     <attr name="userAgentIcon" format="reference" />
     <attr name="searchIcon" format="reference" />
     <attr name="homepageIcon" format="reference" />
@@ -60,4 +59,5 @@
     <attr name="selectAllIcon" format="reference" />
     <attr name="editIcon" format="reference" />
     <attr name="moveToFolderIcon" format="reference" />
+    <attr name="aboutIcon" format="reference" />
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8b7f03a6..960b3af9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -142,6 +142,7 @@
     <string name="share">Share</string>
     <string name="find_on_page">Find on Page</string>
     <string name="print">Print</string>
+    <string name="view_source">View Source</string>
     <string name="add_to_home_screen">Add to Home Screen</string>
         <string name="privacy_browser_web_page">Privacy Browser Web Page</string>
     <string name="refresh">Refresh</string>
@@ -160,6 +161,16 @@
     <string name="previous">Previous</string>
     <string name="next">Next</string>
 
+    <!-- View Source. -->
+    <string name="request_headers">Request Headers</string>
+    <string name="response_message">Response Message</string>
+    <string name="response_headers">Response Headers</string>
+    <string name="response_body">Response Body</string>
+    <string name="error_body">Error Body</string>
+    <string name="about_view_source">About View Source</string>
+    <string name="about_view_source_message">Because Android’s WebView does not expose the source information, a separate request was made using system tools to gather the information displayed in this activity.
+        There may be some differences between this data and that used by the WebView in the main activity. This limitation will be removed in the 4.x series with the release of Privacy WebView.</string>
+
     <!-- Create Home Screen Shortcut Alert Dialog. -->
     <string name="create_shortcut">Create Shortcut</string>
     <string name="shortcut_name">Shortcut name</string>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 8f9c5939..45f62497 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -33,10 +33,12 @@
         <item name="progressTintColor">@color/blue_700</item>
         <item name="navigationIconTintColor">@color/blue_800</item>
         <item name="findOnPageIconTintColor">@color/blue_800</item>
+        <item name="viewSourceIconTintColor">@color/black</item>
         <item name="sslHeader">@color/blue_700</item>
         <item name="sslTitle">@color/blue_900</item>
         <item name="urlHistoryText">@color/black</item>
         <item name="redText">@color/red_a700</item>
+        <item name="aboutIcon">@drawable/about_light</item>
         <item name="dialogTabLayoutTheme">@style/PrivacyBrowserTabLayoutDialogLight</item>
     </style>
 
@@ -54,7 +56,7 @@
         <item name="aboutTitle">@color/blue_900</item>
         <item name="aboutText">@color/blue_700</item>
         <item name="aboutBackground">@color/white</item>
-        <item name="iconTintColor">@color/blue_800</item>
+        <item name="domainSettingsIconTintColor">@color/blue_800</item>
         <item name="deleteIcon">@drawable/delete_light</item>
         <item name="addIcon">@drawable/add_light</item>
         <item name="addBookmarkIcon">@drawable/create_bookmark_light</item>
@@ -104,6 +106,10 @@
         <item name="colorAccent">@color/blue_700</item>
     </style>
 
+    <style name="PrivacyBrowserProgressBar" parent="Widget.AppCompat.ProgressBar.Horizontal" >
+        <item name="android:indeterminateDrawable">@android:drawable/progress_horizontal</item>
+    </style>
+
 
     <!-- Dark theme styles. -->
 
@@ -119,10 +125,12 @@
         <item name="progressTintColor">@color/blue_600</item>
         <item name="navigationIconTintColor">@color/blue_600</item>
         <item name="findOnPageIconTintColor">@color/blue_600</item>
+        <item name="viewSourceIconTintColor">@color/gray_300</item>
         <item name="sslHeader">@color/blue_400</item>
         <item name="sslTitle">@color/blue_700</item>
         <item name="urlHistoryText">@color/gray_200</item>
         <item name="redText">@color/red_900</item>
+        <item name="aboutIcon">@drawable/about_dark</item>
     </style>
 
     <!-- `windowActionModeOverlay` makes the contextual app bar cover the support app bar.  `colorPrimaryDark` goes behind the status bar, which is then darkened by the overlay.-->
@@ -138,7 +146,7 @@
         <item name="aboutText">@color/blue_400</item>
         <item name="aboutBackground">@color/gray_850</item>
         <item name="listSelectorDrawable">@drawable/list_selector_dark</item>
-        <item name="iconTintColor">@color/blue_600</item>
+        <item name="domainSettingsIconTintColor">@color/blue_600</item>
         <item name="deleteIcon">@drawable/delete_dark</item>
         <item name="addIcon">@drawable/add_dark</item>
         <item name="addBookmarkIcon">@drawable/create_bookmark_dark</item>
diff --git a/fastlane/metadata/android/en/images/phoneScreenshots/02-Full Screen-TranslucentBars.png b/fastlane/metadata/android/en/images/phoneScreenshots/02-FullScreen-TranslucentBars.png
similarity index 100%
rename from fastlane/metadata/android/en/images/phoneScreenshots/02-Full Screen-TranslucentBars.png
rename to fastlane/metadata/android/en/images/phoneScreenshots/02-FullScreen-TranslucentBars.png
-- 
2.47.2