RedditAuthChromeStyle.swift 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import Foundation
  2. enum RedditAuthChromeStyle {
  3. /// Hides Google sign-in on Reddit auth surfaces. Google OAuth does not work inside
  4. /// WKWebView subframes on macOS, so we remove the option instead of showing a broken flow.
  5. static let injectionScript = """
  6. (function() {
  7. if (window.__reddoraGoogleLoginHidden) return;
  8. window.__reddoraGoogleLoginHidden = true;
  9. const GOOGLE_PATTERNS = [
  10. /continue with google/i,
  11. /sign in with google/i,
  12. /sign in with google account/i
  13. ];
  14. const SELECTORS = 'button, a, [role="button"], faceplate-tracker, faceplate-button';
  15. function buttonLabel(node) {
  16. return (
  17. (node.textContent || '') + ' ' +
  18. (node.getAttribute('aria-label') || '') + ' ' +
  19. (node.getAttribute('title') || '')
  20. ).trim();
  21. }
  22. function isGoogleLoginButton(node) {
  23. return GOOGLE_PATTERNS.some(function(pattern) {
  24. return pattern.test(buttonLabel(node));
  25. });
  26. }
  27. function hideElement(node) {
  28. const target = node.closest('faceplate-tracker, faceplate-button') || node;
  29. target.style.setProperty('display', 'none', 'important');
  30. target.style.setProperty('visibility', 'hidden', 'important');
  31. target.setAttribute('aria-hidden', 'true');
  32. target.setAttribute('hidden', '');
  33. }
  34. function collectClickables(root, results) {
  35. if (!root) return;
  36. if (root.querySelectorAll) {
  37. root.querySelectorAll(SELECTORS).forEach(function(node) {
  38. results.push(node);
  39. });
  40. }
  41. const children = root.children || root.childNodes;
  42. for (let i = 0; i < children.length; i++) {
  43. const child = children[i];
  44. if (!(child instanceof Element)) continue;
  45. if (child.shadowRoot) {
  46. collectClickables(child.shadowRoot, results);
  47. }
  48. collectClickables(child, results);
  49. }
  50. }
  51. function hideGoogleLoginButtons(root) {
  52. const nodes = [];
  53. collectClickables(root || document.documentElement, nodes);
  54. for (let i = 0; i < nodes.length; i++) {
  55. const node = nodes[i];
  56. if (!isGoogleLoginButton(node)) continue;
  57. hideElement(node);
  58. }
  59. }
  60. document.addEventListener('click', function(event) {
  61. const path = event.composedPath ? event.composedPath() : [event.target];
  62. for (let i = 0; i < path.length; i++) {
  63. const node = path[i];
  64. if (!(node instanceof Element)) continue;
  65. if (!node.matches || !node.matches(SELECTORS)) continue;
  66. if (!isGoogleLoginButton(node)) continue;
  67. event.preventDefault();
  68. event.stopImmediatePropagation();
  69. return;
  70. }
  71. }, true);
  72. hideGoogleLoginButtons(document);
  73. const observer = new MutationObserver(function() {
  74. hideGoogleLoginButtons(document);
  75. });
  76. observer.observe(document.documentElement, { childList: true, subtree: true });
  77. [250, 750, 1500, 3000].forEach(function(delay) {
  78. setTimeout(function() {
  79. hideGoogleLoginButtons(document);
  80. }, delay);
  81. });
  82. })();
  83. """
  84. }