Skip to content

Commit

Permalink
Gmail fix (#107)
Browse files Browse the repository at this point in the history
* Fix for replacing in gmail draft emails

* Try to ignore other iframes

* Accept iframes from same host or blobs

* wip

* wip related to occurrences

* Fix faulty if condition for iframes

* Sort out iframe selector

* configure webpack
  • Loading branch information
forgetso authored Jan 27, 2024
1 parent 39f657b commit e678ff1
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 55 deletions.
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"permissions": ["activeTab", "storage", "notifications"],
"host_permissions": ["http://*/*", "https://*/*"],
"update_url": "http://clients2.google.com/service/update2/crx",
"version":"2.0.5",
"version":"2.0.6",
"options_page": "assets/options.html",
"icons": {
"16": "assets/icon-16.png",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "search_and_replace",
"version": "2.0.5",
"version": "2.0.6",
"resolutions": {
"author": "Chris Taylor <[email protected]>"
},
Expand Down
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const ELEMENT_FILTER = /(HTML|HEAD|SCRIPT|STYLE|IFRAME)/i
export const INPUT_TEXTAREA_FILTER = /(INPUT|TEXTAREA)/
export const HINTS: Record<string, Hint> = {
gmail: {
hint: 'Hint: Gmail detected. Check "Visible content only?" when editing draft emails.',
hint: 'Hint: Gmail detected. Check "Input fields only?" when editing draft emails.',
domain: 'mail.google.com',
selector: `meta[content="Gmail"]`,
name: 'gmail',
Expand Down
29 changes: 24 additions & 5 deletions src/elements.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Utils for dealing with Elements

import { HINTS } from './constants'
import { SearchReplaceResult } from './types'

export function getInputElements(
Expand All @@ -19,15 +20,33 @@ export function isBlobIframe(el: Element) {
}

export function getIframeElements(document: Document, blob = false): HTMLIFrameElement[] {
return Array.from(<NodeListOf<HTMLIFrameElement>>document.querySelectorAll('iframe')).filter(
(iframe) =>
// We don't want empty iframes
iframe.src.length &&
// we don't want to count iframes in gmail
if (document.querySelector(HINTS['gmail'].selector) || window.location.href.indexOf(HINTS['gmail'].domain) > -1) {
return []
}

return Array.from(<NodeListOf<HTMLIFrameElement>>document.querySelectorAll('iframe')).filter((iframe) => {
console.log('iframe.src.length', iframe.src.length)
console.log("iframe.src.startsWith('chrome-extension://')", iframe.src.startsWith('chrome-extension://'))
console.log('window location origin', window.location.origin)
console.log('iframe.src.startsWith(window.location.origin)', iframe.src.startsWith(window.location.origin))
console.log('blob', blob)
console.log('isBlobIframe(iframe)', isBlobIframe(iframe))
console.log(
'(blob ? isBlobIframe(iframe) : !isBlobIframe(iframe))',
blob ? isBlobIframe(iframe) : !isBlobIframe(iframe)
)
// We don't want empty iframes
const want =
iframe.src.length > 0 &&
// We don't want to count iframes injected by other chrome extensions
!iframe.src.startsWith('chrome-extension://') &&
// We only want to wait on iframes from the same origin OR blob iframes
iframe.src.indexOf(window.location.origin) > -1 &&
// We may or may not want blob iframes
(blob ? isBlobIframe(iframe) : !isBlobIframe(iframe))
)
return want
})
}

export function inIframe() {
Expand Down
100 changes: 53 additions & 47 deletions src/searchreplace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,11 @@ function containsAncestor(element: Element, results: Map<Element, SearchReplaceR
function countOccurrences(el: HTMLElement, config: SearchReplaceConfig): number {
let target = el[config.searchTarget]

if (config.hiddenContent && config.searchTarget === 'innerText' && 'textContent' in el) {
if (config.hiddenContent && config.searchTarget === 'innerText') {
// textContent contains text of visible and hidden elements
target = (el as HTMLElement).textContent
}
console.log('counting in', target)

const matches = target.match(config.globalSearchPattern) || []
return matches.length
Expand Down Expand Up @@ -377,7 +378,7 @@ function replaceInner(
}

const occurrences = countOccurrences(element, config)

console.log('occurrences', occurrences, element)
elementsChecked = updateResults(elementsChecked, element, false, occurrences, 0)

const ancestorChecked = containsAncestor(element, elementsChecked)
Expand Down Expand Up @@ -678,7 +679,7 @@ export async function searchReplace(
}
)
const searchableIframes = (await Promise.all(searchableIframePromises)).filter(notEmpty)

console.log('searchableIframes', searchableIframes)
result = replaceInHTML(
config,
document,
Expand All @@ -693,52 +694,57 @@ export async function searchReplace(

if (chrome && chrome.runtime && chrome.runtime.onMessage) {
chrome.runtime.onMessage.addListener(function (msg: SearchReplaceContentMessage, sender, sendResponse) {
const instance = msg.instance
const replaceAll = msg.action === 'count' ? true : instance.options.replaceAll
const action = msg.action
// are we in an iframe?
const isIframe = inIframe()
// get all iframes
const iframes = getIframeElements(window.document)

// Setup event listeners to communicate between iframes and parent
searchReplace(
action,
window,
instance.searchTerm,
instance.replaceTerm,
instance.options.inputFieldsOnly,
instance.options.isRegex,
instance.options.hiddenContent,
instance.options.wholeWord,
instance.options.matchCase,
instance.options.replaceHTML,
replaceAll,
isIframe,
iframes,
ELEMENT_FILTER
).then((result) => {
const response: SearchReplaceResponse = {
inIframe: inIframe(),
result: result.searchReplaceResult,
location: window.location.toString(),
action: 'searchReplaceResponseBackground',
hints: getHints(document),
iframes: iframes.length,
instance: instance,
backgroundReceived: 0,
host: window.location.host,
}
try {
const instance = msg.instance
const replaceAll = msg.action === 'count' ? true : instance.options.replaceAll
const action = msg.action
// are we in an iframe?
const isIframe = inIframe()
// get all iframes
const iframes = getIframeElements(window.document)

// Setup event listeners to communicate between iframes and parent
searchReplace(
action,
window,
instance.searchTerm,
instance.replaceTerm,
instance.options.inputFieldsOnly,
instance.options.isRegex,
instance.options.hiddenContent,
instance.options.wholeWord,
instance.options.matchCase,
instance.options.replaceHTML,
replaceAll,
isIframe,
iframes,
ELEMENT_FILTER
).then((result) => {
const response: SearchReplaceResponse = {
inIframe: inIframe(),
result: result.searchReplaceResult,
location: window.location.toString(),
action: 'searchReplaceResponseBackground',
hints: getHints(document),
iframes: iframes.length,
instance: instance,
backgroundReceived: 0,
host: window.location.host,
}

// Send the response to the background script for processing
console.log('sending response to background script')
chrome.runtime.sendMessage(response).then((r) => {
sendResponse({
action: 'searchReplaceResponsePopup',
msg: `Content script sent message to background with response ${r}`,
// Send the response to the background script for processing
console.log('sending response to background script', JSON.stringify(response, null, 4))
chrome.runtime.sendMessage(response).then((r) => {
sendResponse({
action: 'searchReplaceResponsePopup',
msg: `Content script sent message to background with response ${r}`,
})
})
return true
})
return true
})
} catch (err) {
console.log('Error in content script', err)
sendResponse({ action: 'searchReplaceResponsePopup', msg: err })
}
})
}

0 comments on commit e678ff1

Please sign in to comment.