The online textbooks that my School of Business uses are really annoying because they don’t let you copy-paste. I realized that this was a fixable problem. Behold: The textbook copy-inator!

This extension suppresses whatever nonsense they have to prevent you from copying quotes into essays and the annotation box as well. To turn it on for a page just click the icon extension when you want to use it and it’ll strip the problematic elements.

It’s a bit touchy, but it works!

Content

Code

background.js

chrome.action.onClicked.addListener((tab) => {
 
  chrome.scripting.executeScript({
 
    target: { tabId: tab.id },
 
    func: enableCopy
 
  });
 
});
 
  
 
function enableCopy() {
 
  // Function to aggressively enable copy on a document
 
  function enableCopyOnDoc(doc, win) {
 
    if (!doc || !doc.body) return;
 
    console.log('Enabling copy on document');
 
    // Remove annotation overlay and prevent it from showing
 
    function removeAnnotationOverlay() {
 
      // Remove the annotation context menu
 
      const overlays = doc.querySelectorAll('rdrx-annotations-context-menu, .cdk-overlay-container, .cdk-overlay-backdrop, .annotations-overlay-panel');
 
      overlays.forEach(el => {
 
        el.style.display = 'none';
 
        el.style.visibility = 'hidden';
 
        el.style.pointerEvents = 'none';
 
        el.remove();
 
      });
 
      // Also check in shadow DOM if present
 
      doc.querySelectorAll('*').forEach(el => {
 
        if (el.shadowRoot) {
 
          const shadowOverlays = el.shadowRoot.querySelectorAll('rdrx-annotations-context-menu, .cdk-overlay-container, .cdk-overlay-backdrop');
 
          shadowOverlays.forEach(overlay => {
 
            overlay.style.display = 'none';
 
            overlay.remove();
 
          });
 
        }
 
      });
 
    }
 
    // Initial removal
 
    removeAnnotationOverlay();
 
    // Add CSS to permanently hide annotation overlays
 
    const style = doc.createElement('style');
 
    style.id = 'copy-enabler-style';
 
    style.textContent = `
 
      /* Hide annotation overlays */
 
      rdrx-annotations-context-menu,
 
      .cdk-overlay-container,
 
      .cdk-overlay-backdrop,
 
      .annotations-overlay-panel,
 
      .annotation-context-menu-backdrop {
 
        display: none !important;
 
        visibility: hidden !important;
 
        pointer-events: none !important;
 
        opacity: 0 !important;
 
      }
 
      /* Enable text selection */
 
      * {
 
        user-select: text !important;
 
        -webkit-user-select: text !important;
 
        -moz-user-select: text !important;
 
        -ms-user-select: text !important;
 
        -webkit-touch-callout: default !important;
 
      }
 
      ::selection {
 
        background: #b3d4fc !important;
 
        color: #000 !important;
 
      }
 
      ::-moz-selection {
 
        background: #b3d4fc !important;
 
        color: #000 !important;
 
      }
 
      /* Ensure context menu works */
 
      body {
 
        pointer-events: auto !important;
 
      }
 
    `;
 
    if (doc.head) {
 
      doc.head.appendChild(style);
 
    }
 
    // Override execCommand to ensure clipboard access
 
    const originalExecCommand = doc.execCommand.bind(doc);
 
    doc.execCommand = function(command, showUI, value) {
 
      if (command === 'copy') {
 
        try {
 
          const selection = doc.getSelection();
 
          if (selection && selection.toString()) {
 
            navigator.clipboard.writeText(selection.toString()).then(() => {
 
              console.log('Copied:', selection.toString().substring(0, 50));
 
            }).catch(err => {
 
              return originalExecCommand(command, showUI, value);
 
            });
 
            return true;
 
          }
 
        } catch (e) {
 
          console.log('Error in copy override:', e);
 
        }
 
      }
 
      return originalExecCommand(command, showUI, value);
 
    };
 
    // Force enable clipboard events
 
    doc.addEventListener('copy', function(e) {
 
      const selection = doc.getSelection();
 
      if (selection && selection.toString()) {
 
        e.clipboardData?.setData('text/plain', selection.toString());
 
        console.log('Copy event intercepted');
 
      }
 
      e.stopPropagation();
 
    }, true);
 
    // Block all event prevention
 
    const blockableEvents = ['copy', 'cut', 'contextmenu', 'selectstart', 'select',
 
                            'mousedown', 'mouseup', 'keydown', 'keyup', 'click'];
 
    blockableEvents.forEach(eventType => {
 
      doc.addEventListener(eventType, function(e) {
 
        // Remove overlay on any interaction
 
        removeAnnotationOverlay();
 
        if (eventType === 'contextmenu') {
 
          e.stopPropagation();
 
          e.stopImmediatePropagation();
 
          return true;
 
        }
 
        if (['copy', 'cut'].includes(eventType)) {
 
          e.stopPropagation();
 
          e.stopImmediatePropagation();
 
        }
 
      }, true);
 
      doc.body['on' + eventType] = null;
 
      doc['on' + eventType] = null;
 
    });
 
    // Remove all inline event attributes
 
    const removeEventAttrs = () => {
 
      doc.querySelectorAll('*').forEach(el => {
 
        ['oncopy', 'oncut', 'oncontextmenu', 'onselectstart', 'onselect',
 
         'onmousedown', 'onmouseup', 'onclick', 'ondblclick'].forEach(attr => {
 
          if (el.hasAttribute(attr)) {
 
            el.removeAttribute(attr);
 
          }
 
          el[attr] = null;
 
        });
 
      });
 
    };
 
    removeEventAttrs();
 
    // Override Event.prototype methods
 
    const EventProto = win?.Event?.prototype || Event.prototype;
 
    const originalPreventDefault = EventProto.preventDefault;
 
    const originalStopPropagation = EventProto.stopPropagation;
 
    const originalStopImmediatePropagation = EventProto.stopImmediatePropagation;
 
    EventProto.preventDefault = function() {
 
      if (['copy', 'cut', 'contextmenu', 'selectstart'].includes(this.type)) {
 
        console.log('Blocked preventDefault on', this.type);
 
        return;
 
      }
 
      return originalPreventDefault.call(this);
 
    };
 
    EventProto.stopPropagation = function() {
 
      if (['copy', 'cut', 'contextmenu'].includes(this.type)) {
 
        console.log('Blocked stopPropagation on', this.type);
 
        return;
 
      }
 
      return originalStopPropagation.call(this);
 
    };
 
    EventProto.stopImmediatePropagation = function() {
 
      if (['copy', 'cut', 'contextmenu'].includes(this.type)) {
 
        console.log('Blocked stopImmediatePropagation on', this.type);
 
        return;
 
      }
 
      return originalStopImmediatePropagation.call(this);
 
    };
 
    // Watch for overlay being added back and remove it
 
    const observer = new MutationObserver((mutations) => {
 
      removeEventAttrs();
 
      removeAnnotationOverlay();
 
    });
 
    observer.observe(doc.body, {
 
      attributes: true,
 
      childList: true,
 
      subtree: true
 
    });
 
    // Add custom copy keyboard shortcut as backup
 
    doc.addEventListener('keydown', function(e) {
 
      if ((e.ctrlKey || e.metaKey) && e.key === 'c') {
 
        const selection = doc.getSelection();
 
        if (selection && selection.toString()) {
 
          navigator.clipboard.writeText(selection.toString()).then(() => {
 
            console.log('Manual copy successful');
 
          }).catch(err => {
 
            console.log('Manual copy failed:', err);
 
          });
 
        }
 
      }
 
    }, true);
 
  }
 
  // Enable on main document
 
  enableCopyOnDoc(document, window);
 
  // Process all iframes
 
  function processIframes() {
 
    const iframes = document.querySelectorAll('iframe');
 
    let processed = 0;
 
    iframes.forEach(iframe => {
 
      try {
 
        const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
 
        const iframeWin = iframe.contentWindow;
 
        if (iframeDoc && iframeWin) {
 
          enableCopyOnDoc(iframeDoc, iframeWin);
 
          processed++;
 
        }
 
      } catch (e) {
 
        console.log('Skipped iframe:', e.message);
 
      }
 
    });
 
    return processed;
 
  }
 
  // Initial processing with delay
 
  setTimeout(() => {
 
    const count = processIframes();
 
    console.log(`Processed ${count} iframes`);
 
  }, 100);
 
  // Keep watching for new iframes
 
  const mainObserver = new MutationObserver(() => {
 
    processIframes();
 
  });
 
  mainObserver.observe(document.body, {
 
    childList: true,
 
    subtree: true
 
  });
 
  // Aggressive periodic reprocessing
 
  setInterval(() => {
 
    processIframes();
 
  }, 2000);
 
  // Add manual trigger
 
  document.addEventListener('keydown', function(e) {
 
    if (e.ctrlKey && e.shiftKey && e.key === 'C') {
 
      const count = processIframes();
 
      alert(`Copy protection re-applied to ${count} iframe(s)!\nAnnotation overlay removed.`);
 
    }
 
  });
 
  alert('✓ Copy protection disabled!\n✓ Annotation overlay hidden!\n✓ Right-click menu enabled!\n\nPress Ctrl+Shift+C to re-apply if needed.');
 
}

manifest.json

{
 
  "manifest_version": 3,
 
  "name": "Copy Enabler",
 
  "version": "1.4",
 
  "description": "Enables copy-paste on protected pages",
 
  "permissions": ["scripting", "activeTab"],
 
  "action": {
 
    "default_title": "Enable Copy"
 
  },
 
  "background": {
 
    "service_worker": "background.js"
 
  },
 
  "icons": {
 
    "16": "icon16.png",
 
    "48": "icon48.png",
 
    "128": "icon128.png"
 
  }
 
}

icons

icon16.png

icon48.png

icon128.png