User-agent-customization.patch 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211
  1. From: uazo <uazo@users.noreply.github.com>
  2. Date: Mon, 26 Oct 2020 16:50:15 +0000
  3. Subject: User agent customization
  4. Add flag to always view the desktop site for all websites
  5. Add possibility to define a custom User agent for mobile and desktop mode.
  6. Add possibility to reactivate the metatag view for desktop mode, allowing users to choose
  7. to use the flag in the hamburger menu to navigate with a custom useragent leaving the standard navigation unchanged.
  8. ---
  9. base/base_switches.cc | 2 +
  10. base/base_switches.h | 2 +
  11. chrome/android/chrome_java_resources.gni | 2 +
  12. chrome/android/chrome_java_sources.gni | 1 +
  13. .../layout/custom_useragent_preferences.xml | 108 ++++++++++
  14. .../android/java/res/xml/main_preferences.xml | 5 +
  15. .../java/res/xml/useragent_preferences.xml | 31 +++
  16. .../chrome/browser/app/ChromeActivity.java | 20 +-
  17. .../init/ChromeBrowserInitializer.java | 3 +
  18. .../settings/PrivacyPreferencesManager.java | 35 ++++
  19. .../settings/UserAgentPreferences.java | 184 ++++++++++++++++++
  20. .../chromium/chrome/browser/tab/TabImpl.java | 83 +++++++-
  21. .../browser/tabmodel/TabWindowManager.java | 23 +++
  22. .../browser/android/content/content_utils.cc | 29 +++
  23. .../preferences/browser_prefs_android.cc | 7 +
  24. .../privacy_preferences_manager.cc | 113 +++++++++++
  25. chrome/browser/android/tab_android.cc | 5 +-
  26. chrome/browser/android/tab_android.h | 3 +-
  27. .../browser/chrome_content_browser_client.cc | 8 +
  28. .../preferences/ChromePreferenceKeys.java | 7 +-
  29. .../org/chromium/chrome/browser/tab/Tab.java | 2 +
  30. .../strings/android_chrome_strings.grd | 35 ++++
  31. chrome/common/pref_names.cc | 13 ++
  32. chrome/common/pref_names.h | 8 +
  33. .../widget/RadioButtonWithEditText.java | 11 ++
  34. .../navigation_controller_android.cc | 6 +-
  35. .../navigation_controller_android.h | 3 +-
  36. .../renderer_host/render_process_host_impl.cc | 1 +
  37. .../browser/web_contents/web_contents_impl.cc | 4 +-
  38. .../framehost/NavigationControllerImpl.java | 6 +-
  39. 30 files changed, 747 insertions(+), 13 deletions(-)
  40. create mode 100644 chrome/android/java/res/layout/custom_useragent_preferences.xml
  41. create mode 100644 chrome/android/java/res/xml/useragent_preferences.xml
  42. create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/settings/UserAgentPreferences.java
  43. diff --git a/base/base_switches.cc b/base/base_switches.cc
  44. --- a/base/base_switches.cc
  45. +++ b/base/base_switches.cc
  46. @@ -160,4 +160,6 @@ const char kForceFieldTrialParams[] = "force-fieldtrial-params";
  47. const char kEnableThreadInstructionCount[] = "enable-thread-instruction-count";
  48. #endif
  49. +const char kDesktopModeViewportMetaEnabled[] = "dm-viewport-meta-enabled";
  50. +
  51. } // namespace switches
  52. diff --git a/base/base_switches.h b/base/base_switches.h
  53. --- a/base/base_switches.h
  54. +++ b/base/base_switches.h
  55. @@ -59,6 +59,8 @@ extern const char kForceFieldTrialParams[];
  56. extern const char kEnableThreadInstructionCount[];
  57. #endif
  58. +extern const char kDesktopModeViewportMetaEnabled[];
  59. +
  60. } // namespace switches
  61. #endif // BASE_BASE_SWITCHES_H_
  62. diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
  63. --- a/chrome/android/chrome_java_resources.gni
  64. +++ b/chrome/android/chrome_java_resources.gni
  65. @@ -1068,4 +1068,6 @@ chrome_java_resources = [
  66. "java/res/xml/sync_and_services_preferences.xml",
  67. "java/res/xml/theme_preferences.xml",
  68. "java/res/xml/tracing_preferences.xml",
  69. + "java/res/xml/useragent_preferences.xml",
  70. + "java/res/layout/custom_useragent_preferences.xml",
  71. ]
  72. diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
  73. --- a/chrome/android/chrome_java_sources.gni
  74. +++ b/chrome/android/chrome_java_sources.gni
  75. @@ -1295,6 +1295,7 @@ chrome_java_sources = [
  76. "java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java",
  77. "java/src/org/chromium/chrome/browser/payments/ui/LineItem.java",
  78. "java/src/org/chromium/chrome/browser/payments/ui/PaymentAppComparator.java",
  79. + "java/src/org/chromium/chrome/browser/settings/UserAgentPreferences.java",
  80. "java/src/org/chromium/chrome/browser/payments/ui/PaymentInformation.java",
  81. "java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestBottomBar.java",
  82. "java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestHeader.java",
  83. diff --git a/chrome/android/java/res/layout/custom_useragent_preferences.xml b/chrome/android/java/res/layout/custom_useragent_preferences.xml
  84. new file mode 100644
  85. --- /dev/null
  86. +++ b/chrome/android/java/res/layout/custom_useragent_preferences.xml
  87. @@ -0,0 +1,108 @@
  88. +<?xml version="1.0" encoding="utf-8"?>
  89. +<!--
  90. + This file is part of Bromite.
  91. +
  92. + Bromite is free software: you can redistribute it and/or modify
  93. + it under the terms of the GNU General Public License as published by
  94. + the Free Software Foundation, either version 3 of the License, or
  95. + (at your option) any later version.
  96. +
  97. + Bromite is distributed in the hope that it will be useful,
  98. + but WITHOUT ANY WARRANTY; without even the implied warranty of
  99. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  100. + GNU General Public License for more details.
  101. +
  102. + You should have received a copy of the GNU General Public License
  103. + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
  104. +-->
  105. +
  106. +<!-- Layout used by the UserAgentPreferences. -->
  107. +
  108. +<ScrollView
  109. + xmlns:android="http://schemas.android.com/apk/res/android"
  110. + xmlns:app="http://schemas.android.com/apk/res-auto"
  111. + android:layout_weight="0"
  112. + android:gravity="top"
  113. + android:layout_width="match_parent"
  114. + android:layout_height="wrap_content">
  115. +
  116. + <LinearLayout
  117. + android:layout_width="match_parent"
  118. + android:layout_height="wrap_content"
  119. + android:focusable="false"
  120. + android:orientation="vertical"
  121. + android:divider="?android:dividerHorizontal">
  122. +
  123. + <TextView
  124. + android:layout_width="match_parent"
  125. + android:layout_height="wrap_content"
  126. + android:textAppearance="@style/TextAppearance.AccessibilityTextPreference"
  127. + android:background="@color/default_bg_color_secondary"
  128. + android:padding="16dp"
  129. + android:text="@string/custom_ua_text"/>
  130. +
  131. + <org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout
  132. + android:id="@+id/ua_radio_button_layout"
  133. + android:layout_width="match_parent"
  134. + android:layout_height="wrap_content">
  135. +
  136. + <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
  137. + android:id="@+id/default_ua_switch"
  138. + android:layout_width="match_parent"
  139. + android:layout_height="wrap_content"
  140. + android:paddingStart="?android:attr/listPreferredItemPaddingStart"
  141. + app:primaryText="@string/custom_ua_flag_off" />
  142. +
  143. + <org.chromium.components.browser_ui.widget.RadioButtonWithEditText
  144. + android:id="@+id/custom_ua_switch"
  145. + android:layout_width="match_parent"
  146. + android:layout_height="wrap_content"
  147. + android:paddingStart="?android:attr/listPreferredItemPaddingStart"
  148. + android:inputType="text"
  149. + android:hint="@string/custom_ua_placeholder"
  150. + app:descriptionText="@string/custom_ua_flag_on" />
  151. +
  152. + </org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout>
  153. +
  154. + <TextView
  155. + android:layout_width="match_parent"
  156. + android:layout_height="wrap_content"
  157. + android:textAppearance="@style/TextAppearance.AccessibilityTextPreference"
  158. + android:background="@color/default_bg_color_secondary"
  159. + android:padding="16dp"
  160. + android:text="@string/custom_desktop_ua_text"/>
  161. +
  162. + <org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout
  163. + android:id="@+id/ua_radio_button_layout_dm"
  164. + android:layout_width="match_parent"
  165. + android:layout_height="wrap_content">
  166. +
  167. + <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
  168. + android:id="@+id/default_ua_switch_dm"
  169. + android:layout_width="match_parent"
  170. + android:layout_height="wrap_content"
  171. + android:paddingStart="?android:attr/listPreferredItemPaddingStart"
  172. + app:primaryText="@string/custom_ua_flag_off" />
  173. +
  174. + <org.chromium.components.browser_ui.widget.RadioButtonWithEditText
  175. + android:id="@+id/custom_ua_switch_dm"
  176. + android:layout_width="match_parent"
  177. + android:layout_height="wrap_content"
  178. + android:paddingStart="?android:attr/listPreferredItemPaddingStart"
  179. + android:inputType="text"
  180. + android:hint="@string/custom_ua_placeholder"
  181. + app:descriptionText="@string/custom_ua_flag_on" />
  182. +
  183. + </org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout>
  184. +
  185. + <CheckBox
  186. + android:id="@+id/desktop_mode_viewportmeta"
  187. + android:layout_width="wrap_content"
  188. + android:layout_height="wrap_content"
  189. + android:layout_centerVertical="true"
  190. + android:layout_marginLeft="?android:attr/listPreferredItemPaddingStart"
  191. + android:text="@string/desktop_mode_viewportmeta_checkbox" />
  192. +
  193. + </LinearLayout>
  194. +
  195. +</ScrollView>
  196. diff --git a/chrome/android/java/res/xml/main_preferences.xml b/chrome/android/java/res/xml/main_preferences.xml
  197. --- a/chrome/android/java/res/xml/main_preferences.xml
  198. +++ b/chrome/android/java/res/xml/main_preferences.xml
  199. @@ -105,6 +105,11 @@
  200. android:key="content_settings"
  201. android:order="20"
  202. android:title="@string/prefs_site_settings"/>
  203. + <Preference
  204. + android:fragment="org.chromium.chrome.browser.settings.UserAgentPreferences"
  205. + android:key="useragent_settings"
  206. + android:order="20"
  207. + android:title="@string/prefs_useragent_settings"/>
  208. <Preference
  209. android:fragment="org.chromium.chrome.browser.language.settings.LanguageSettings"
  210. android:key="languages"
  211. diff --git a/chrome/android/java/res/xml/useragent_preferences.xml b/chrome/android/java/res/xml/useragent_preferences.xml
  212. new file mode 100644
  213. --- /dev/null
  214. +++ b/chrome/android/java/res/xml/useragent_preferences.xml
  215. @@ -0,0 +1,31 @@
  216. +<?xml version="1.0" encoding="utf-8"?>
  217. +<!--
  218. + This file is part of Bromite.
  219. +
  220. + Bromite is free software: you can redistribute it and/or modify
  221. + it under the terms of the GNU General Public License as published by
  222. + the Free Software Foundation, either version 3 of the License, or
  223. + (at your option) any later version.
  224. +
  225. + Bromite is distributed in the hope that it will be useful,
  226. + but WITHOUT ANY WARRANTY; without even the implied warranty of
  227. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  228. + GNU General Public License for more details.
  229. +
  230. + You should have received a copy of the GNU General Public License
  231. + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
  232. +-->
  233. +
  234. +<!-- Layout used by the UserAgentPreferences. -->
  235. +
  236. +<PreferenceScreen
  237. + xmlns:android="http://schemas.android.com/apk/res/android"
  238. + xmlns:app="http://schemas.android.com/apk/res-auto">
  239. +
  240. + <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
  241. + android:key="desktop_mode_switch"
  242. + android:title="@string/option_desktop_flag"
  243. + android:summaryOn="@string/option_desktop_flag_on"
  244. + android:summaryOff="@string/option_desktop_flag_off" />
  245. +
  246. +</PreferenceScreen>
  247. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
  248. --- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
  249. +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
  250. @@ -208,6 +208,13 @@ import org.chromium.ui.modaldialog.ModalDialogManager;
  251. import org.chromium.ui.widget.Toast;
  252. import org.chromium.url.Origin;
  253. import org.chromium.webapk.lib.client.WebApkNavigationClient;
  254. +import org.chromium.chrome.browser.tabmodel.TabWindowManager;
  255. +import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
  256. +import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
  257. +
  258. +import org.chromium.chrome.browser.tabmodel.TabWindowManager;
  259. +import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
  260. +import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
  261. import org.chromium.url.GURL;
  262. @@ -2038,11 +2045,18 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
  263. } else if (id == R.id.view_source_id) {
  264. currentTab.getWebContents().getNavigationController().loadUrl(new LoadUrlParams("view-source:"+currentTab.getUrlString()));
  265. } else if (id == R.id.request_desktop_site_id || id == R.id.request_desktop_site_check_id) {
  266. - final boolean reloadOnChange = !currentTab.isNativePage();
  267. final boolean usingDesktopUserAgent =
  268. currentTab.getWebContents().getNavigationController().getUseDesktopUserAgent();
  269. - currentTab.getWebContents().getNavigationController().setUseDesktopUserAgent(
  270. - !usingDesktopUserAgent, reloadOnChange);
  271. + SharedPreferencesManager.getInstance().writeBoolean(
  272. + ChromePreferenceKeys.USERAGENT_ALWAYS_DESKTOP_MODE, !usingDesktopUserAgent);
  273. +
  274. + final boolean stickyDesktopModeEnabled = SharedPreferencesManager.getInstance().readBoolean(
  275. + ChromePreferenceKeys.USERAGENT_STICKY_DESKTOP_MODE, false);
  276. + if (stickyDesktopModeEnabled) {
  277. + TabWindowManager.getInstance().SetOverrideUserAgentForAllTabs(!usingDesktopUserAgent);
  278. + } else {
  279. + currentTab.SetOverrideUserAgent(!usingDesktopUserAgent);
  280. + }
  281. RecordUserAction.record("MobileMenuRequestDesktopSite");
  282. } else if (id == R.id.reader_mode_prefs_id) {
  283. DomDistillerUIUtils.openSettings(currentTab.getWebContents());
  284. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
  285. --- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
  286. +++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
  287. @@ -48,6 +48,7 @@ import org.chromium.content_public.browser.SpeechRecognition;
  288. import org.chromium.content_public.browser.UiThreadTaskTraits;
  289. import org.chromium.net.NetworkChangeNotifier;
  290. import org.chromium.ui.resources.ResourceExtractor;
  291. +import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
  292. import java.io.File;
  293. import java.util.ArrayList;
  294. @@ -324,11 +325,13 @@ public class ChromeBrowserInitializer {
  295. @Override
  296. public void onSuccess() {
  297. + PrivacyPreferencesManager.getInstance().UpdateOverrideUserAgent();
  298. tasks.start(false);
  299. }
  300. });
  301. } else {
  302. startChromeBrowserProcessesSync();
  303. + PrivacyPreferencesManager.getInstance().UpdateOverrideUserAgent();
  304. tasks.start(true);
  305. }
  306. }
  307. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManager.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManager.java
  308. --- a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManager.java
  309. +++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManager.java
  310. @@ -315,6 +315,34 @@ public class PrivacyPreferencesManager implements CrashReportingPermissionManage
  311. return PrivacyPreferencesManagerJni.get().isMetricsReportingManaged();
  312. }
  313. + public void UpdateOverrideUserAgent() {
  314. + PrivacyPreferencesManagerJni.get().updateOverrideUserAgent();
  315. + }
  316. +
  317. + public boolean isOverrideUserAgentEnabled(boolean desktopMode) {
  318. + return PrivacyPreferencesManagerJni.get().isOverrideUserAgentEnabled(desktopMode);
  319. + }
  320. +
  321. + public void setOverrideUserAgentEnabled(boolean enabled, boolean desktopMode) {
  322. + PrivacyPreferencesManagerJni.get().setOverrideUserAgentEnabled(enabled, desktopMode);
  323. + }
  324. +
  325. + public String getOverrideUserAgentValue(boolean desktopMode) {
  326. + return PrivacyPreferencesManagerJni.get().getOverrideUserAgentValue(desktopMode);
  327. + }
  328. +
  329. + public void setOverrideUserAgentValue(String user_agent, boolean desktopMode) {
  330. + PrivacyPreferencesManagerJni.get().setOverrideUserAgentValue(user_agent, desktopMode);
  331. + }
  332. +
  333. + public boolean isDesktopModeViewportMetaEnabled() {
  334. + return PrivacyPreferencesManagerJni.get().isDesktopModeViewportMetaEnabled();
  335. + }
  336. +
  337. + public void setDesktopModeViewportMetaEnabled(boolean enabled) {
  338. + PrivacyPreferencesManagerJni.get().setDesktopModeViewportMetaEnabled(enabled);
  339. + }
  340. +
  341. @NativeMethods
  342. public interface Natives {
  343. boolean canPrefetchAndPrerender();
  344. @@ -325,5 +353,12 @@ public class PrivacyPreferencesManager implements CrashReportingPermissionManage
  345. boolean isMetricsReportingEnabled();
  346. void setMetricsReportingEnabled(boolean enabled);
  347. boolean isMetricsReportingManaged();
  348. + void updateOverrideUserAgent();
  349. + boolean isOverrideUserAgentEnabled(boolean desktopMode);
  350. + void setOverrideUserAgentEnabled(boolean enabled, boolean desktopMode);
  351. + String getOverrideUserAgentValue(boolean desktopMode);
  352. + void setOverrideUserAgentValue(String timezone, boolean desktopMode);
  353. + boolean isDesktopModeViewportMetaEnabled();
  354. + void setDesktopModeViewportMetaEnabled(boolean enabled);
  355. }
  356. }
  357. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/UserAgentPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/UserAgentPreferences.java
  358. new file mode 100644
  359. --- /dev/null
  360. +++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/UserAgentPreferences.java
  361. @@ -0,0 +1,184 @@
  362. +/*
  363. + This file is part of Bromite.
  364. +
  365. + Bromite is free software: you can redistribute it and/or modify
  366. + it under the terms of the GNU General Public License as published by
  367. + the Free Software Foundation, either version 3 of the License, or
  368. + (at your option) any later version.
  369. +
  370. + Bromite is distributed in the hope that it will be useful,
  371. + but WITHOUT ANY WARRANTY; without even the implied warranty of
  372. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  373. + GNU General Public License for more details.
  374. +
  375. + You should have received a copy of the GNU General Public License
  376. + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
  377. +*/
  378. +
  379. +package org.chromium.chrome.browser.settings;
  380. +
  381. +import android.os.Bundle;
  382. +import androidx.preference.Preference;
  383. +import androidx.preference.PreferenceFragmentCompat;
  384. +import androidx.preference.PreferenceViewHolder;
  385. +import androidx.annotation.NonNull;
  386. +import androidx.annotation.Nullable;
  387. +import android.view.LayoutInflater;
  388. +import android.widget.RadioGroup;
  389. +import android.content.Context;
  390. +import android.util.AttributeSet;
  391. +import android.view.View;
  392. +import android.view.ViewGroup;
  393. +import android.widget.LinearLayout;
  394. +import android.widget.ScrollView;
  395. +import android.widget.CheckBox;
  396. +import android.widget.CompoundButton;
  397. +import androidx.recyclerview.widget.RecyclerView;
  398. +
  399. +import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
  400. +import org.chromium.components.browser_ui.widget.RadioButtonWithDescription;
  401. +import org.chromium.components.browser_ui.widget.RadioButtonWithEditText;
  402. +import org.chromium.components.browser_ui.settings.SettingsUtils;
  403. +
  404. +import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
  405. +import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
  406. +
  407. +import org.chromium.chrome.browser.tabmodel.TabWindowManager;
  408. +import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
  409. +import org.chromium.chrome.R;
  410. +
  411. +/**
  412. + * Fragment that allows the user to configure User Agent related preferences.
  413. + */
  414. +public class UserAgentPreferences
  415. + extends PreferenceFragmentCompat implements RadioGroup.OnCheckedChangeListener {
  416. +
  417. + private static final String PREF_STICK_DESKTOP_MODE_SWITCH = "desktop_mode_switch";
  418. + private RadioButtonWithDescription useDefaultAgentSwitch;
  419. + private RadioButtonWithEditText useCustomAgentSwitch;
  420. + private RadioButtonWithDescription useDefaultAgentSwitchDesktopMode;
  421. + private RadioButtonWithEditText useCustomAgentSwitchDesktopMode;
  422. + private RadioGroup mRadioGroup;
  423. + private RadioGroup mRadioGroupDesktopMode;
  424. + private CheckBox mDesktopModeViewportmeta;
  425. +
  426. + @Override
  427. + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
  428. + getActivity().setTitle(R.string.useragent_settings_title);
  429. + SettingsUtils.addPreferencesFromResource(this, R.xml.useragent_preferences);
  430. +
  431. + ChromeSwitchPreference alwaysDesktopModeSwitch =
  432. + (ChromeSwitchPreference) findPreference(PREF_STICK_DESKTOP_MODE_SWITCH);
  433. + boolean enabled = SharedPreferencesManager.getInstance().readBoolean(
  434. + ChromePreferenceKeys.USERAGENT_STICKY_DESKTOP_MODE, false);
  435. + alwaysDesktopModeSwitch.setChecked(enabled);
  436. + alwaysDesktopModeSwitch.setOnPreferenceChangeListener((preference, newValue) -> {
  437. + SharedPreferencesManager.getInstance().writeBoolean(
  438. + ChromePreferenceKeys.USERAGENT_STICKY_DESKTOP_MODE, (boolean) newValue);
  439. + UpdateAllTabs();
  440. + return true;
  441. + });
  442. + }
  443. +
  444. + @Override
  445. + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
  446. + @Nullable Bundle savedInstanceState) {
  447. + LinearLayout viewGroup = (LinearLayout) super.onCreateView(inflater, container, savedInstanceState);
  448. + ScrollView view = (ScrollView) inflater.inflate(R.layout.custom_useragent_preferences, viewGroup, false);
  449. + viewGroup.addView(view);
  450. +
  451. + boolean enabledCustomUA = PrivacyPreferencesManager.getInstance().isOverrideUserAgentEnabled(false);
  452. + boolean enabledCustomUADesktopMode = PrivacyPreferencesManager.getInstance().isOverrideUserAgentEnabled(true);
  453. + boolean enabledDesktopModeViewportmeta = PrivacyPreferencesManager.getInstance().isDesktopModeViewportMetaEnabled();
  454. +
  455. + useDefaultAgentSwitch =
  456. + (RadioButtonWithDescription) view.findViewById(R.id.default_ua_switch);
  457. + useCustomAgentSwitch =
  458. + (RadioButtonWithEditText) view.findViewById(R.id.custom_ua_switch);
  459. + useDefaultAgentSwitchDesktopMode =
  460. + (RadioButtonWithDescription) view.findViewById(R.id.default_ua_switch_dm);
  461. + useCustomAgentSwitchDesktopMode =
  462. + (RadioButtonWithEditText) view.findViewById(R.id.custom_ua_switch_dm);
  463. +
  464. + mRadioGroup = (RadioGroup) view.findViewById(R.id.ua_radio_button_layout);
  465. + mRadioGroup.setOnCheckedChangeListener(this);
  466. +
  467. + mRadioGroupDesktopMode = (RadioGroup) view.findViewById(R.id.ua_radio_button_layout_dm);
  468. + mRadioGroupDesktopMode.setOnCheckedChangeListener(this);
  469. +
  470. + mDesktopModeViewportmeta = (CheckBox) view.findViewById(R.id.desktop_mode_viewportmeta);
  471. + mDesktopModeViewportmeta.setChecked(enabledDesktopModeViewportmeta);
  472. + mDesktopModeViewportmeta.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
  473. + @Override
  474. + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
  475. + PrivacyPreferencesManager.getInstance().setDesktopModeViewportMetaEnabled(
  476. + mDesktopModeViewportmeta.isChecked());
  477. + }
  478. + });
  479. +
  480. + useDefaultAgentSwitch.setChecked(!enabledCustomUA);
  481. + useCustomAgentSwitch.setChecked(enabledCustomUA);
  482. +
  483. + useDefaultAgentSwitchDesktopMode.setChecked(!enabledCustomUADesktopMode);
  484. + useCustomAgentSwitchDesktopMode.setChecked(enabledCustomUADesktopMode);
  485. +
  486. + useCustomAgentSwitch.setPrimaryText(
  487. + PrivacyPreferencesManager.getInstance().getOverrideUserAgentValue(false));
  488. + useCustomAgentSwitch.addTextChangeListener(new RadioButtonWithEditText.OnTextChangeListener() {
  489. + @Override
  490. + public void onTextChanged(CharSequence newText) {
  491. + PrivacyPreferencesManager.getInstance().setOverrideUserAgentValue(
  492. + newText.toString(), false);
  493. + }
  494. + });
  495. + useCustomAgentSwitch.setFocusChangeListener( hasFocus -> {
  496. + if( hasFocus )
  497. + PrivacyPreferencesManager.getInstance().setOverrideUserAgentEnabled(true, false);
  498. + });
  499. +
  500. + useCustomAgentSwitchDesktopMode.setPrimaryText(
  501. + PrivacyPreferencesManager.getInstance().getOverrideUserAgentValue(true));
  502. + useCustomAgentSwitchDesktopMode.addTextChangeListener(new RadioButtonWithEditText.OnTextChangeListener() {
  503. + @Override
  504. + public void onTextChanged(CharSequence newText) {
  505. + PrivacyPreferencesManager.getInstance().setOverrideUserAgentValue(
  506. + newText.toString(), true);
  507. + }
  508. + });
  509. + useCustomAgentSwitchDesktopMode.setFocusChangeListener( hasFocus -> {
  510. + if( hasFocus )
  511. + PrivacyPreferencesManager.getInstance().setOverrideUserAgentEnabled(true, true);
  512. + });
  513. +
  514. + return viewGroup;
  515. + }
  516. +
  517. + private void UpdateAllTabs() {
  518. + final boolean alwaysDesktopModeEnabled = SharedPreferencesManager.getInstance().readBoolean(
  519. + ChromePreferenceKeys.USERAGENT_ALWAYS_DESKTOP_MODE, false);
  520. + TabWindowManager.getInstance().SetOverrideUserAgentForAllTabs(alwaysDesktopModeEnabled);
  521. + }
  522. +
  523. + @Override
  524. + public void onCheckedChanged(RadioGroup group, int checkedId) {
  525. + if (useDefaultAgentSwitch.isChecked()) {
  526. + PrivacyPreferencesManager.getInstance().setOverrideUserAgentEnabled(false, false);
  527. + } else if (useCustomAgentSwitch.isChecked()) {
  528. + PrivacyPreferencesManager.getInstance().setOverrideUserAgentEnabled(true, false);
  529. + }
  530. +
  531. + if (useDefaultAgentSwitchDesktopMode.isChecked()) {
  532. + PrivacyPreferencesManager.getInstance().setOverrideUserAgentEnabled(false, true);
  533. + } else if (useCustomAgentSwitchDesktopMode.isChecked()) {
  534. + PrivacyPreferencesManager.getInstance().setOverrideUserAgentEnabled(true, true);
  535. + }
  536. +
  537. + UpdateAllTabs();
  538. + }
  539. +
  540. + @Override
  541. + public void onStop() {
  542. + super.onStop();
  543. + UpdateAllTabs();
  544. + }
  545. +}
  546. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
  547. --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
  548. +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
  549. @@ -56,6 +56,18 @@ import org.chromium.ui.base.WindowAndroid;
  550. import org.chromium.ui.util.ColorUtils;
  551. import org.chromium.url.GURL;
  552. import org.chromium.url.Origin;
  553. +import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
  554. +import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
  555. +import org.chromium.content_public.browser.navigation_controller.UserAgentOverrideOption;
  556. +import org.chromium.content_public.browser.NavigationController;
  557. +import org.chromium.components.embedder_support.util.UrlUtilities;
  558. +import org.chromium.components.url_formatter.UrlFormatter;
  559. +import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
  560. +import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
  561. +import org.chromium.content_public.browser.navigation_controller.UserAgentOverrideOption;
  562. +import org.chromium.content_public.browser.NavigationController;
  563. +import org.chromium.components.embedder_support.util.UrlUtilities;
  564. +import org.chromium.components.url_formatter.UrlFormatter;
  565. /**
  566. * Implementation of the interface {@link Tab}. Contains and manages a {@link ContentView}.
  567. @@ -448,6 +460,31 @@ public class TabImpl implements Tab, TabObscuringHandler.Observer {
  568. throw new RuntimeException("Tab.loadUrl called when no native side exists");
  569. }
  570. + final boolean stickyDesktopModeEnabled = SharedPreferencesManager.getInstance().readBoolean(
  571. + ChromePreferenceKeys.USERAGENT_STICKY_DESKTOP_MODE, false);
  572. + if (stickyDesktopModeEnabled) {
  573. + boolean alwaysDesktopModeEnabled = SharedPreferencesManager.getInstance().readBoolean(
  574. + ChromePreferenceKeys.USERAGENT_ALWAYS_DESKTOP_MODE, false);
  575. +
  576. + if (UrlUtilities.isInternalScheme(UrlFormatter.fixupUrl(params.getUrl()))) {
  577. + alwaysDesktopModeEnabled = false;
  578. + }
  579. +
  580. + WebContents webContents = this.getWebContents();
  581. + if (webContents != null) {
  582. + NavigationController navigationController = webContents.getNavigationController();
  583. + boolean currentUseDesktopUserAgent = navigationController.getUseDesktopUserAgent();
  584. + if (currentUseDesktopUserAgent != alwaysDesktopModeEnabled)
  585. + navigationController.setUseDesktopUserAgent(alwaysDesktopModeEnabled, false);
  586. + }
  587. +
  588. + if (alwaysDesktopModeEnabled) {
  589. + params.setOverrideUserAgent((int)UserAgentOverrideOption.TRUE);
  590. + } else {
  591. + params.setOverrideUserAgent((int)UserAgentOverrideOption.FALSE);
  592. + }
  593. + }
  594. +
  595. // We load the URL from the tab rather than directly from the ContentView so the tab has
  596. // a chance of using a prerenderer page is any.
  597. int loadType = TabImplJni.get().loadUrl(mNativeTabAndroid, TabImpl.this,
  598. @@ -460,7 +497,8 @@ public class TabImpl implements Tab, TabObscuringHandler.Observer {
  599. params.getReferrer() != null ? params.getReferrer().getPolicy() : 0,
  600. params.getIsRendererInitiated(), params.getShouldReplaceCurrentEntry(),
  601. params.getHasUserGesture(), params.getShouldClearHistoryList(),
  602. - params.getInputStartTimestamp(), params.getIntentReceivedTimestamp());
  603. + params.getInputStartTimestamp(), params.getIntentReceivedTimestamp(),
  604. + params.getUserAgentOverrideOption());
  605. for (TabObserver observer : mObservers) {
  606. observer.onLoadUrl(this, params, loadType);
  607. @@ -1402,6 +1440,10 @@ public class TabImpl implements Tab, TabObscuringHandler.Observer {
  608. if (mWebContents != null) mWebContents.getNavigationController().loadIfNecessary();
  609. mIsBeingRestored = true;
  610. for (TabObserver observer : mObservers) observer.onRestoreStarted(this);
  611. +
  612. + if(overrideUserAgentWhenUnFrozen != UserAgentOverrideOption.INHERIT) {
  613. + SetOverrideUserAgent(overrideUserAgentWhenUnFrozen == (int)UserAgentOverrideOption.TRUE ? true : false);
  614. + }
  615. } finally {
  616. TraceEvent.end("Tab.restoreIfNeeded");
  617. }
  618. @@ -1519,6 +1561,42 @@ public class TabImpl implements Tab, TabObscuringHandler.Observer {
  619. }
  620. }
  621. + int overrideUserAgentWhenUnFrozen = (int)UserAgentOverrideOption.INHERIT;
  622. +
  623. + public void SetOverrideUserAgent(boolean usingDesktopUserAgent) {
  624. + WebContents webContents = this.getWebContents();
  625. + overrideUserAgentWhenUnFrozen = UserAgentOverrideOption.INHERIT;
  626. +
  627. + if (usingDesktopUserAgent) {
  628. + GURL url = this.getUrl();
  629. + if (webContents == null && this.getPendingLoadParams() != null) {
  630. + url = UrlFormatter.fixupUrl(this.getPendingLoadParams().getUrl());
  631. + }
  632. + if (UrlUtilities.isInternalScheme(url) == true)
  633. + usingDesktopUserAgent = false;
  634. + }
  635. +
  636. + if (webContents != null) {
  637. + ContentUtils.setUserAgentOverride(webContents);
  638. +
  639. + NavigationController navigationController = webContents.getNavigationController();
  640. + navigationController.setUseDesktopUserAgent(
  641. + usingDesktopUserAgent, !this.isNativePage());
  642. + }
  643. + else if (this.getPendingLoadParams() != null) {
  644. + if (usingDesktopUserAgent) {
  645. + this.getPendingLoadParams().setOverrideUserAgent((int)UserAgentOverrideOption.TRUE);
  646. + }
  647. + else {
  648. + this.getPendingLoadParams().setOverrideUserAgent((int)UserAgentOverrideOption.FALSE);
  649. + }
  650. + }
  651. + else {
  652. + overrideUserAgentWhenUnFrozen = usingDesktopUserAgent ? UserAgentOverrideOption.TRUE :
  653. + UserAgentOverrideOption.FALSE;
  654. + }
  655. + }
  656. +
  657. @NativeMethods
  658. interface Natives {
  659. TabImpl fromWebContents(WebContents webContents);
  660. @@ -1540,7 +1618,8 @@ public class TabImpl implements Tab, TabObscuringHandler.Observer {
  661. String referrerUrl, int referrerPolicy, boolean isRendererInitiated,
  662. boolean shoulReplaceCurrentEntry, boolean hasUserGesture,
  663. boolean shouldClearHistoryList, long inputStartTimestamp,
  664. - long intentReceivedTimestamp);
  665. + long intentReceivedTimestamp,
  666. + int userAgentOverrideOption);
  667. void setActiveNavigationEntryTitleForUrl(
  668. long nativeTabAndroid, TabImpl caller, String url, String title);
  669. void loadOriginalImage(long nativeTabAndroid, TabImpl caller);
  670. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java
  671. --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java
  672. +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java
  673. @@ -28,6 +28,9 @@ import java.util.HashMap;
  674. import java.util.List;
  675. import java.util.Map;
  676. +import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
  677. +import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
  678. +
  679. /**
  680. * Manages multiple {@link TabModelSelector} instances, each owned by different {@link Activity}s.
  681. */
  682. @@ -268,4 +271,24 @@ public class TabWindowManager implements ActivityStateListener {
  683. false);
  684. }
  685. }
  686. +
  687. + public static void SetOverrideUserAgentForAllTabs(boolean usingDesktopUserAgent) {
  688. + TabWindowManager tabWindowManagerSingleton = TabWindowManager.getInstance();
  689. +
  690. + List<TabModelSelector> mSelectors = tabWindowManagerSingleton.mSelectors;
  691. + for (int selectorIndex = 0; selectorIndex < mSelectors.size(); selectorIndex++) {
  692. + TabModelSelector selector = mSelectors.get(selectorIndex);
  693. + if (selector != null) {
  694. + List<TabModel> models = selector.getModels();
  695. + for (int modelIndex = 0; modelIndex < models.size(); modelIndex++) {
  696. + TabModel model = models.get(modelIndex);
  697. +
  698. + for (int tabIdex = 0; tabIdex < model.getCount(); tabIdex++) {
  699. + Tab theTab = model.getTabAt(tabIdex);
  700. + theTab.SetOverrideUserAgent(usingDesktopUserAgent);
  701. + }
  702. + }
  703. + }
  704. + }
  705. + }
  706. }
  707. diff --git a/chrome/browser/android/content/content_utils.cc b/chrome/browser/android/content/content_utils.cc
  708. --- a/chrome/browser/android/content/content_utils.cc
  709. +++ b/chrome/browser/android/content/content_utils.cc
  710. @@ -9,6 +9,20 @@
  711. #include "content/public/browser/web_contents.h"
  712. #include "content/public/common/user_agent.h"
  713. +#include "base/android/jni_android.h"
  714. +#include "base/android/scoped_java_ref.h"
  715. +#include "chrome/browser/browser_process.h"
  716. +#include "components/prefs/pref_service.h"
  717. +#include "chrome/common/pref_names.h"
  718. +
  719. +using base::android::ConvertJavaStringToUTF8;
  720. +using base::android::ConvertUTF16ToJavaString;
  721. +using base::android::ConvertUTF8ToJavaString;
  722. +using base::android::JavaParamRef;
  723. +using base::android::JavaRef;
  724. +using base::android::ScopedJavaGlobalRef;
  725. +using base::android::ScopedJavaLocalRef;
  726. +
  727. static base::android::ScopedJavaLocalRef<jstring>
  728. JNI_ContentUtils_GetBrowserUserAgent(JNIEnv* env) {
  729. return base::android::ConvertUTF8ToJavaString(env, GetUserAgent());
  730. @@ -17,6 +31,21 @@ JNI_ContentUtils_GetBrowserUserAgent(JNIEnv* env) {
  731. static void JNI_ContentUtils_SetUserAgentOverride(
  732. JNIEnv* env,
  733. const base::android::JavaParamRef<jobject>& jweb_contents) {
  734. +
  735. + bool enabled =
  736. + g_browser_process->local_state()->GetBoolean(prefs::kOverrideUserAgentDesktopModeEnabled);
  737. +
  738. + if (enabled == true) {
  739. + std::string ua = g_browser_process->local_state()->GetString(prefs::kOverrideUserAgentDesktopMode);
  740. + blink::UserAgentOverride spoofed_ua;
  741. + spoofed_ua.ua_string_override = ua;
  742. +
  743. + content::WebContents* web_contents =
  744. + content::WebContents::FromJavaWebContents(jweb_contents);
  745. + web_contents->SetUserAgentOverride(spoofed_ua, false);
  746. + return;
  747. + }
  748. +
  749. const char kLinuxInfoStr[] = "X11; Linux x86_64";
  750. std::string product = version_info::GetProductNameAndVersionForUserAgent();
  751. diff --git a/chrome/browser/android/preferences/browser_prefs_android.cc b/chrome/browser/android/preferences/browser_prefs_android.cc
  752. --- a/chrome/browser/android/preferences/browser_prefs_android.cc
  753. +++ b/chrome/browser/android/preferences/browser_prefs_android.cc
  754. @@ -10,11 +10,18 @@
  755. #include "chrome/browser/notifications/notification_platform_bridge_android.h"
  756. #include "components/pref_registry/pref_registry_syncable.h"
  757. #include "components/prefs/pref_registry_simple.h"
  758. +#include "chrome/common/pref_names.h"
  759. namespace android {
  760. void RegisterPrefs(PrefRegistrySimple* registry) {
  761. RegisterClipboardAndroidPrefs(registry);
  762. +
  763. + registry->RegisterBooleanPref(prefs::kOverrideUserAgentEnabled, false);
  764. + registry->RegisterStringPref(prefs::kOverrideUserAgent, "");
  765. + registry->RegisterBooleanPref(prefs::kOverrideUserAgentDesktopModeEnabled, false);
  766. + registry->RegisterStringPref(prefs::kOverrideUserAgentDesktopMode, "");
  767. + registry->RegisterBooleanPref(prefs::kDesktopModeViewportMetaEnabled, false);
  768. }
  769. void RegisterUserProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
  770. diff --git a/chrome/browser/android/preferences/privacy_preferences_manager.cc b/chrome/browser/android/preferences/privacy_preferences_manager.cc
  771. --- a/chrome/browser/android/preferences/privacy_preferences_manager.cc
  772. +++ b/chrome/browser/android/preferences/privacy_preferences_manager.cc
  773. @@ -11,6 +11,25 @@
  774. #include "chrome/common/pref_names.h"
  775. #include "components/metrics/metrics_pref_names.h"
  776. #include "components/prefs/pref_service.h"
  777. +#include "base/command_line.h"
  778. +#include "base/base_switches.h"
  779. +#include "chrome/common/chrome_switches.h"
  780. +#include "content/browser/renderer_host/render_process_host_impl.h"
  781. +#include "content/common/renderer.mojom.h"
  782. +#include "chrome/browser/chrome_content_browser_client.h"
  783. +
  784. +#include "base/android/jni_android.h"
  785. +#include "base/android/jni_array.h"
  786. +#include "base/android/jni_string.h"
  787. +#include "base/android/scoped_java_ref.h"
  788. +
  789. +using base::android::ConvertJavaStringToUTF8;
  790. +using base::android::ConvertUTF16ToJavaString;
  791. +using base::android::ConvertUTF8ToJavaString;
  792. +using base::android::JavaParamRef;
  793. +using base::android::JavaRef;
  794. +using base::android::ScopedJavaGlobalRef;
  795. +using base::android::ScopedJavaLocalRef;
  796. namespace {
  797. @@ -74,3 +93,97 @@ JNI_PrivacyPreferencesManager_ObsoleteNetworkPredictionOptionsHasUserSetting(
  798. return GetPrefService()->GetUserPrefValue(prefs::kNetworkPredictionOptions) !=
  799. nullptr;
  800. }
  801. +
  802. +static void UpdateOverrideUserAgent() {
  803. + bool overrideUserAgentEnabled =
  804. + g_browser_process->local_state()->GetBoolean(prefs::kOverrideUserAgentEnabled);
  805. + std::string ua = g_browser_process->local_state()->GetString(prefs::kOverrideUserAgent);
  806. +
  807. + base::CommandLine* parsed_command_line =
  808. + base::CommandLine::ForCurrentProcess();
  809. + parsed_command_line->RemoveSwitch(switches::kUserAgent);
  810. + if (overrideUserAgentEnabled) {
  811. + parsed_command_line->AppendSwitchASCII(switches::kUserAgent, ua);
  812. + }
  813. +
  814. + for (auto iter = content::RenderProcessHost::AllHostsIterator(); !iter.IsAtEnd();
  815. + iter.Advance()) {
  816. + if (iter.GetCurrentValue()->IsInitializedAndNotDead()) {
  817. + if (overrideUserAgentEnabled) {
  818. + iter.GetCurrentValue()->GetRendererInterface()->SetUserAgent(ua);
  819. + } else {
  820. + iter.GetCurrentValue()->GetRendererInterface()->SetUserAgent(
  821. + ChromeContentBrowserClient().GetUserAgent());
  822. + }
  823. + }
  824. + }
  825. +
  826. + parsed_command_line->RemoveSwitch(switches::kDesktopModeViewportMetaEnabled);
  827. + if (g_browser_process->local_state()->GetBoolean(prefs::kDesktopModeViewportMetaEnabled))
  828. + parsed_command_line->AppendSwitch(switches::kDesktopModeViewportMetaEnabled);
  829. +}
  830. +
  831. +static void JNI_PrivacyPreferencesManager_UpdateOverrideUserAgent(
  832. + JNIEnv* env) {
  833. + UpdateOverrideUserAgent();
  834. +}
  835. +
  836. +static jboolean JNI_PrivacyPreferencesManager_IsOverrideUserAgentEnabled(
  837. + JNIEnv* env, jboolean desktopMode) {
  838. + if (desktopMode == false)
  839. + return g_browser_process->local_state()->GetBoolean(prefs::kOverrideUserAgentEnabled);
  840. + else
  841. + return g_browser_process->local_state()->GetBoolean(prefs::kOverrideUserAgentDesktopModeEnabled);
  842. +}
  843. +
  844. +static void JNI_PrivacyPreferencesManager_SetOverrideUserAgentEnabled(
  845. + JNIEnv* env,
  846. + jboolean enabled, jboolean desktopMode) {
  847. + if (desktopMode == false) {
  848. + g_browser_process->local_state()->SetBoolean(prefs::kOverrideUserAgentEnabled,
  849. + enabled);
  850. + UpdateOverrideUserAgent();
  851. + } else {
  852. + g_browser_process->local_state()->SetBoolean(prefs::kOverrideUserAgentDesktopModeEnabled,
  853. + enabled);
  854. + }
  855. +}
  856. +
  857. +static void JNI_PrivacyPreferencesManager_SetOverrideUserAgentValue(
  858. + JNIEnv* env,
  859. + const JavaParamRef<jstring>& ua, jboolean desktopMode) {
  860. + std::string new_ua = ConvertJavaStringToUTF8(env, ua);
  861. + if (desktopMode == false) {
  862. + g_browser_process->local_state()->SetString(prefs::kOverrideUserAgent,
  863. + new_ua);
  864. + UpdateOverrideUserAgent();
  865. + } else {
  866. + g_browser_process->local_state()->SetString(prefs::kOverrideUserAgentDesktopMode,
  867. + new_ua);
  868. + }
  869. +}
  870. +
  871. +static base::android::ScopedJavaLocalRef<jstring>
  872. + JNI_PrivacyPreferencesManager_GetOverrideUserAgentValue(
  873. + JNIEnv* env, jboolean desktopMode) {
  874. + if (desktopMode == false) {
  875. + std::string ua = g_browser_process->local_state()->GetString(prefs::kOverrideUserAgent);
  876. + return ConvertUTF8ToJavaString(env, ua);
  877. + } else {
  878. + std::string ua = g_browser_process->local_state()->GetString(prefs::kOverrideUserAgentDesktopMode);
  879. + return ConvertUTF8ToJavaString(env, ua);
  880. + }
  881. +}
  882. +
  883. +static jboolean JNI_PrivacyPreferencesManager_IsDesktopModeViewportMetaEnabled(
  884. + JNIEnv* env) {
  885. + return g_browser_process->local_state()->GetBoolean(prefs::kDesktopModeViewportMetaEnabled);
  886. +}
  887. +
  888. +static void JNI_PrivacyPreferencesManager_SetDesktopModeViewportMetaEnabled(
  889. + JNIEnv* env,
  890. + jboolean enabled) {
  891. + g_browser_process->local_state()->SetBoolean(prefs::kDesktopModeViewportMetaEnabled,
  892. + enabled);
  893. + UpdateOverrideUserAgent();
  894. +}
  895. diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
  896. --- a/chrome/browser/android/tab_android.cc
  897. +++ b/chrome/browser/android/tab_android.cc
  898. @@ -366,7 +366,8 @@ TabAndroid::TabLoadStatus TabAndroid::LoadUrl(
  899. jboolean has_user_gesture,
  900. jboolean should_clear_history_list,
  901. jlong input_start_timestamp,
  902. - jlong intent_received_timestamp) {
  903. + jlong intent_received_timestamp,
  904. + jint user_agent_override_option) {
  905. if (!web_contents())
  906. return PAGE_LOAD_FAILED;
  907. @@ -423,6 +424,8 @@ TabAndroid::TabLoadStatus TabAndroid::LoadUrl(
  908. load_params.input_start =
  909. base::TimeTicks::FromUptimeMillis(intent_received_timestamp);
  910. }
  911. + load_params.override_user_agent = static_cast<NavigationController::UserAgentOverrideOption>(
  912. + user_agent_override_option);
  913. web_contents()->GetController().LoadURLWithParams(load_params);
  914. }
  915. return DEFAULT_PAGE_LOAD;
  916. diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
  917. --- a/chrome/browser/android/tab_android.h
  918. +++ b/chrome/browser/android/tab_android.h
  919. @@ -157,7 +157,8 @@ class TabAndroid : public base::SupportsUserData {
  920. jboolean has_user_gesture,
  921. jboolean should_clear_history_list,
  922. jlong omnibox_input_received_timestamp,
  923. - jlong intent_received_timestamp);
  924. + jlong intent_received_timestamp,
  925. + jint user_agent_override_option);
  926. void SetActiveNavigationEntryTitleForUrl(
  927. JNIEnv* env,
  928. const base::android::JavaParamRef<jobject>& obj,
  929. diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
  930. --- a/chrome/browser/chrome_content_browser_client.cc
  931. +++ b/chrome/browser/chrome_content_browser_client.cc
  932. @@ -1236,6 +1236,13 @@ std::string GetUserAgent() {
  933. blink::UserAgentMetadata GetUserAgentMetadata() {
  934. blink::UserAgentMetadata metadata;
  935. + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  936. + if (command_line->HasSwitch(switches::kUserAgent)) {
  937. + std::string ua = command_line->GetSwitchValueASCII(switches::kUserAgent);
  938. +
  939. + return metadata;
  940. + }
  941. +
  942. metadata.brand_version_list = GetBrandVersionList();
  943. metadata.full_version = version_info::GetVersionNumber();
  944. metadata.platform = version_info::GetOSType();
  945. @@ -2310,6 +2317,7 @@ void ChromeContentBrowserClient::AppendExtraCommandLineSwitches(
  946. blink::switches::kUserAgentClientHintDisable);
  947. }
  948. +
  949. #if defined(OS_ANDROID)
  950. // Communicating to content/ for BackForwardCache.
  951. if (prefs->HasPrefPath(policy::policy_prefs::kBackForwardCacheEnabled) &&
  952. diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
  953. --- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
  954. +++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
  955. @@ -777,6 +777,9 @@ public final class ChromePreferenceKeys {
  956. public static final KeyPrefix KEY_ZERO_SUGGEST_HEADER_GROUP_COLLAPSED_BY_DEFAULT_PREFIX =
  957. new KeyPrefix("zero_suggest_header_group_collapsed_by_default*");
  958. + public static final String USERAGENT_STICKY_DESKTOP_MODE = "Chrome.UserAgent.StickyDesktopMode";
  959. + public static final String USERAGENT_ALWAYS_DESKTOP_MODE = "Chrome.UserAgent.AlwaysDesktopMode";
  960. +
  961. /**
  962. * These values are currently used as SharedPreferences keys, along with the keys in
  963. * {@link GrandfatheredChromePreferenceKeys#getKeysInUse()}. Add new SharedPreferences keys
  964. @@ -823,7 +826,9 @@ public final class ChromePreferenceKeys {
  965. SETTINGS_SAFETY_CHECK_LAST_RUN_TIMESTAMP,
  966. SETTINGS_SAFETY_CHECK_RUN_COUNTER,
  967. SIGNIN_PROMO_IMPRESSIONS_COUNT_NTP,
  968. - TWA_DISCLOSURE_SEEN_PACKAGES
  969. + TWA_DISCLOSURE_SEEN_PACKAGES,
  970. + USERAGENT_STICKY_DESKTOP_MODE,
  971. + USERAGENT_ALWAYS_DESKTOP_MODE
  972. );
  973. // clang-format on
  974. }
  975. diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
  976. --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
  977. +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
  978. @@ -266,6 +266,8 @@ public interface Tab extends TabLifecycle {
  979. */
  980. void setIsTabStateDirty(boolean isTabStateDirty);
  981. + void SetOverrideUserAgent(boolean usingDesktopUserAgent);
  982. +
  983. /**
  984. * If set to true, any future navigations in the tab automatically get
  985. * PageTransition.FROM_API_2 applied.
  986. diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
  987. --- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
  988. +++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
  989. @@ -228,6 +228,41 @@ CHAR-LIMIT guidelines:
  990. Visit help page
  991. </message>
  992. + <!-- User Agent settings -->
  993. + <message name="IDS_PREFS_USERAGENT_SETTINGS" desc="Title of the User Agent preference. [CHAR-LIMIT=32]">
  994. + User Agent
  995. + </message>
  996. + <message name="IDS_USERAGENT_SETTINGS_TITLE" desc="Title of the User Agent screen. [CHAR-LIMIT=32]">
  997. + Customize User Agent
  998. + </message>
  999. + <message name="IDS_OPTION_DESKTOP_FLAG" desc="The label of the option that allows users to sticky desktop mode view flag under hambuger menu.">
  1000. + Current behaviour for desktop mode toggle in hamburger menu
  1001. + </message>
  1002. + <message name="IDS_OPTION_DESKTOP_FLAG_ON" desc="The label of the option that allows users to sticky desktop mode view flag under hambuger menu. [CHAR-LIMIT=32]">
  1003. + Applies to all tabs (sticky mode)
  1004. + </message>
  1005. + <message name="IDS_OPTION_DESKTOP_FLAG_OFF" desc="The label of the option that revert the hambuger menu flag to actual behaviour. [CHAR-LIMIT=32]">
  1006. + Applies to current tab only
  1007. + </message>
  1008. + <message name="IDS_CUSTOM_UA_FLAG_ON" desc="The label of the option that allows users to define custom user agent.">
  1009. + Use custom user agent
  1010. + </message>
  1011. + <message name="IDS_CUSTOM_UA_FLAG_OFF" desc="The label of the option that revert the user agent to actual value.">
  1012. + Use standard user agent
  1013. + </message>
  1014. + <message name="IDS_CUSTOM_UA_PLACEHOLDER" desc="The label of the placeholder for user agent textbox.">
  1015. + Insert a valid user agent
  1016. + </message>
  1017. + <message name="IDS_CUSTOM_UA_TEXT" desc="The label of the placeholder for user agent textbox.">
  1018. + Mobile User Agent
  1019. + </message>
  1020. + <message name="IDS_CUSTOM_DESKTOP_UA_TEXT" desc="The label of the placeholder for user agent textbox.">
  1021. + Desktop Mode User Agent
  1022. + </message>
  1023. + <message name="IDS_DESKTOP_MODE_VIEWPORTMETA_CHECKBOX" desc="The label of the enable viewport meta checkbox for user desktop mode.">
  1024. + Enable processing of the viewport meta tag also for desktop mode
  1025. + </message>
  1026. +
  1027. <!-- Notification channels -->
  1028. <message name="IDS_NOTIFICATION_CATEGORY_GROUP_GENERAL" desc='Subheading for "General" section of a list of notification categories. [CHAR-LIMIT=32]'>
  1029. General
  1030. diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
  1031. --- a/chrome/common/pref_names.cc
  1032. +++ b/chrome/common/pref_names.cc
  1033. @@ -3084,4 +3084,17 @@ const char kIncognitoTabHistoryEnabled[] =
  1034. "incognito_tab_history_enabled";
  1035. #endif
  1036. +#if defined(OS_ANDROID)
  1037. +const char kOverrideUserAgentEnabled[] =
  1038. + "override_user_agent_enabled";
  1039. +const char kOverrideUserAgent[] =
  1040. + "override_user_agent";
  1041. +const char kOverrideUserAgentDesktopModeEnabled[] =
  1042. + "override_user_agent_dm_enabled";
  1043. +const char kOverrideUserAgentDesktopMode[] =
  1044. + "override_user_agent_dm";
  1045. +const char kDesktopModeViewportMetaEnabled[] =
  1046. + "dm-viewport-meta-enabled";
  1047. +#endif
  1048. +
  1049. } // namespace prefs
  1050. diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
  1051. --- a/chrome/common/pref_names.h
  1052. +++ b/chrome/common/pref_names.h
  1053. @@ -1082,6 +1082,14 @@ extern const char kShowCaretBrowsingDialog[];
  1054. extern const char kIncognitoTabHistoryEnabled[];
  1055. #endif
  1056. +#if defined(OS_ANDROID)
  1057. +extern const char kOverrideUserAgentEnabled[];
  1058. +extern const char kOverrideUserAgent[];
  1059. +extern const char kOverrideUserAgentDesktopModeEnabled[];
  1060. +extern const char kOverrideUserAgentDesktopMode[];
  1061. +extern const char kDesktopModeViewportMetaEnabled[];
  1062. +#endif
  1063. +
  1064. } // namespace prefs
  1065. #endif // CHROME_COMMON_PREF_NAMES_H_
  1066. diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditText.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditText.java
  1067. --- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditText.java
  1068. +++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditText.java
  1069. @@ -167,6 +167,17 @@ public class RadioButtonWithEditText extends RadioButtonWithDescription {
  1070. mEditText.setCursorVisible(false);
  1071. KeyboardVisibilityDelegate.getInstance().hideKeyboard(mEditText);
  1072. }
  1073. + if (mRadioButtonWithEditTextFocusListener != null) {
  1074. + mRadioButtonWithEditTextFocusListener.onRadioButtonWithEditTextFocusChanged(hasFocus);
  1075. + }
  1076. + }
  1077. +
  1078. + public interface RadioButtonWithEditTextFocusListener {
  1079. + void onRadioButtonWithEditTextFocusChanged(boolean hasFocus);
  1080. + }
  1081. + private RadioButtonWithEditTextFocusListener mRadioButtonWithEditTextFocusListener;
  1082. + public void setFocusChangeListener(RadioButtonWithEditTextFocusListener listener) {
  1083. + mRadioButtonWithEditTextFocusListener = listener;
  1084. }
  1085. /**
  1086. diff --git a/content/browser/renderer_host/navigation_controller_android.cc b/content/browser/renderer_host/navigation_controller_android.cc
  1087. --- a/content/browser/renderer_host/navigation_controller_android.cc
  1088. +++ b/content/browser/renderer_host/navigation_controller_android.cc
  1089. @@ -237,7 +237,8 @@ void NavigationControllerAndroid::LoadUrl(
  1090. const JavaParamRef<jstring>& data_url_as_string,
  1091. jboolean can_load_local_resources,
  1092. jboolean is_renderer_initiated,
  1093. - jboolean should_replace_current_entry) {
  1094. + jboolean should_replace_current_entry,
  1095. + jint user_agent_override_option) {
  1096. DCHECK(url);
  1097. NavigationController::LoadURLParams params(
  1098. GURL(ConvertJavaStringToUTF8(env, url)));
  1099. @@ -291,6 +292,9 @@ void NavigationControllerAndroid::LoadUrl(
  1100. Referrer::ConvertToPolicy(referrer_policy));
  1101. }
  1102. + params.override_user_agent = static_cast<NavigationController::UserAgentOverrideOption>(
  1103. + user_agent_override_option);
  1104. +
  1105. navigation_controller_->LoadURLWithParams(params);
  1106. }
  1107. diff --git a/content/browser/renderer_host/navigation_controller_android.h b/content/browser/renderer_host/navigation_controller_android.h
  1108. --- a/content/browser/renderer_host/navigation_controller_android.h
  1109. +++ b/content/browser/renderer_host/navigation_controller_android.h
  1110. @@ -80,7 +80,8 @@ class CONTENT_EXPORT NavigationControllerAndroid {
  1111. const base::android::JavaParamRef<jstring>& data_url_as_string,
  1112. jboolean can_load_local_resources,
  1113. jboolean is_renderer_initiated,
  1114. - jboolean should_replace_current_entry);
  1115. + jboolean should_replace_current_entry,
  1116. + jint user_agent_override_option);
  1117. void ClearSslPreferences(
  1118. JNIEnv* env,
  1119. const base::android::JavaParamRef<jobject>& /* obj */);
  1120. diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
  1121. --- a/content/browser/renderer_host/render_process_host_impl.cc
  1122. +++ b/content/browser/renderer_host/render_process_host_impl.cc
  1123. @@ -3498,6 +3498,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
  1124. switches::kIpcDumpDirectory,
  1125. switches::kIpcFuzzerTestcase,
  1126. #endif
  1127. + switches::kDesktopModeViewportMetaEnabled,
  1128. };
  1129. renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames,
  1130. base::size(kSwitchNames));
  1131. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
  1132. --- a/content/browser/web_contents/web_contents_impl.cc
  1133. +++ b/content/browser/web_contents/web_contents_impl.cc
  1134. @@ -38,6 +38,7 @@
  1135. #include "base/strings/string_util.h"
  1136. #include "base/strings/utf_string_conversions.h"
  1137. #include "base/system/sys_info.h"
  1138. +#include "base/base_switches.h"
  1139. #include "base/threading/thread_task_runner_handle.h"
  1140. #include "base/time/time.h"
  1141. #include "base/trace_event/optional_trace_event.h"
  1142. @@ -2487,7 +2488,8 @@ const blink::web_pref::WebPreferences WebContentsImpl::ComputeWebPreferences() {
  1143. prefs.viewport_enabled = command_line.HasSwitch(switches::kEnableViewport);
  1144. - if (IsOverridingUserAgent())
  1145. + if (IsOverridingUserAgent() &&
  1146. + !command_line.HasSwitch(switches::kDesktopModeViewportMetaEnabled))
  1147. prefs.viewport_meta_enabled = false;
  1148. prefs.main_frame_resizes_are_orientation_changes =
  1149. diff --git a/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
  1150. --- a/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
  1151. +++ b/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
  1152. @@ -168,7 +168,8 @@ import org.chromium.content_public.common.ResourceRequestBody;
  1153. params.getUserAgentOverrideOption(), params.getExtraHeadersString(),
  1154. params.getPostData(), params.getBaseUrl(), params.getVirtualUrlForDataUrl(),
  1155. params.getDataUrlAsString(), params.getCanLoadLocalResources(),
  1156. - params.getIsRendererInitiated(), params.getShouldReplaceCurrentEntry());
  1157. + params.getIsRendererInitiated(), params.getShouldReplaceCurrentEntry(),
  1158. + params.getUserAgentOverrideOption());
  1159. }
  1160. }
  1161. @@ -347,7 +348,8 @@ import org.chromium.content_public.common.ResourceRequestBody;
  1162. int referrerPolicy, int uaOverrideOption, String extraHeaders,
  1163. ResourceRequestBody postData, String baseUrlForDataUrl, String virtualUrlForDataUrl,
  1164. String dataUrlAsString, boolean canLoadLocalResources, boolean isRendererInitiated,
  1165. - boolean shouldReplaceCurrentEntry);
  1166. + boolean shouldReplaceCurrentEntry,
  1167. + int userAgentOverrideOption);
  1168. void clearHistory(long nativeNavigationControllerAndroid, NavigationControllerImpl caller);
  1169. int getNavigationHistory(long nativeNavigationControllerAndroid,
  1170. NavigationControllerImpl caller, Object history);
  1171. --
  1172. 2.17.1