-
Notifications
You must be signed in to change notification settings - Fork 25
GOOWOO-447: Implement image proxying for generated images to bypass adblockers #3248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
AlejandroPerezMartin
wants to merge
26
commits into
develop
Choose a base branch
from
feature/GOOWOO-447-genai-adblocker-images
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
9ac207d
Implement image proxying for generated images to bypass adblockers
AlejandroPerezMartin fbaa04d
Apply suggestions from code review
AlejandroPerezMartin cdf7bb2
Remove domain restriction and update list of supported formats to jpg…
AlejandroPerezMartin 2679f9f
Rename function and use addqueryvars
AlejandroPerezMartin a2ea6f8
Merge branch 'feature/GOOWOO-447-genai-adblocker-images' of github.co…
AlejandroPerezMartin 8cbbbc7
Rename dependency comment
AlejandroPerezMartin 03273e7
Adding tests for image proxy controller
AlejandroPerezMartin 858253f
Merge branch 'feature/GOOWOO-383-genai-assets' into feature/GOOWOO-44…
jamesmorrison 3f5bd07
Fix broken tests.
jamesmorrison 850f4fa
PHPCS fixes.
jamesmorrison 2554e6c
Update permission callback; added nonce verification to getProxiedIma…
jamesmorrison d2d8531
Merge branch 'feature/GOOWOO-447-genai-adblocker-images' of github.co…
AlejandroPerezMartin d0f2254
Rename variable and sort imports
AlejandroPerezMartin 252f995
Add hook to detect adblocker, proxy images (wip)
AlejandroPerezMartin ab5c9cd
refactor(image-proxy): Consolidate ad blocker image logic
asvinb 306f3be
style: Sort imports
asvinb 558049f
refactor(images-selector): Refactor image selection and replacement l…
asvinb e411da8
Merge branch 'feature/GOOWOO-447-genai-adblocker-images' of github.co…
AlejandroPerezMartin 70e79fd
fix(adblock): Add DOM and fetch fallbacks for adblock detection
asvinb a05b71a
Merge branch 'feature/GOOWOO-447-genai-adblocker-images' of github.co…
AlejandroPerezMartin 6f6dd5f
Merge branch 'feature/GOOWOO-383-genai-assets' into feature/GOOWOO-44…
asvinb 19afaf6
chore: Update google-listings-and-ads.zip max size
asvinb 9ee6d5f
Merge branch 'feature/GOOWOO-383-genai-assets' of github.com:woocomme…
AlejandroPerezMartin 67c2df0
Merge branch 'feature/GOOWOO-447-genai-adblocker-images' of github.co…
AlejandroPerezMartin 7709122
Add list of allowed domains to fetch images, add test
AlejandroPerezMartin be924cd
Replace parse url function
AlejandroPerezMartin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| /** | ||
| * External dependencies | ||
| */ | ||
| import { detectAnyAdblocker } from 'just-detect-adblock'; | ||
| import { useCallback, useEffect, useRef, useState } from '@wordpress/element'; | ||
|
|
||
| /** | ||
| * Internal dependencies | ||
| */ | ||
| import getProxiedImageUrl from '~/utils/getProxiedImageUrl'; | ||
|
|
||
| /** | ||
| * Injects a hidden bait element with ad-like class names and checks if it gets | ||
| * hidden by cosmetic filtering (e.g. uBlock Origin). Resolves true if blocked. | ||
| * | ||
| * @return {Promise<boolean>} Resolves to true if an adblocker is detected, false otherwise. | ||
| */ | ||
| const detectViaDOM = () => | ||
| new Promise( ( resolve ) => { | ||
| const bait = document.createElement( 'div' ); | ||
| bait.className = 'adsbox pub_300x250 pub_300x250m'; | ||
| bait.style.cssText = | ||
| 'width:1px;height:1px;position:absolute;left:-9999px'; | ||
| document.body.appendChild( bait ); | ||
|
|
||
| setTimeout( () => { | ||
| const blocked = | ||
| bait.offsetHeight === 0 || | ||
| window.getComputedStyle( bait ).display === 'none'; | ||
| document.body.removeChild( bait ); | ||
| resolve( blocked ); | ||
| }, 100 ); | ||
| } ); | ||
|
|
||
| /** | ||
| * Fetches a known Google Ads URL and resolves true if the request is blocked. | ||
| * Uses no-cors + HEAD to avoid CORS errors on success. | ||
| * | ||
| * The hostname (tpc.googlesyndication.com) is what triggers blocklists — | ||
| * the path is irrelevant, but kept ad-like for consistency. | ||
| * | ||
| * @return {Promise<boolean>} Resolves to true if the request is blocked, false otherwise. | ||
| */ | ||
| const detectViaFetch = () => | ||
| fetch( | ||
| `https://tpc.googlesyndication.com/pimgad/pagead?timestamp=${ Date.now() }`, | ||
| { | ||
| method: 'HEAD', | ||
| mode: 'no-cors', | ||
| credentials: 'omit', | ||
| redirect: 'manual', | ||
| cache: 'no-store', | ||
| } | ||
| ) | ||
| .then( () => false ) | ||
| .catch( () => true ); | ||
|
|
||
| /** | ||
| * Hook that detects adblocker and returns a function to get display image URLs. | ||
| * | ||
| * This hook: | ||
| * - Detects adblocker on mount using just-detect-adblock library | ||
| * - Falls back to a DOM bait check to catch stubborn blockers | ||
| * (e.g. uBlock Origin, Privacy Badger) | ||
| * - Falls back to a fetch check against a known ad URL as a last resort, | ||
| * only if the DOM check also indicates blocking (to reduce false positives | ||
| * from network failures) | ||
| * - Returns a function that conditionally proxies URLs based on detection | ||
| * - Returns detection status (isDetected, isLoading) | ||
| * | ||
| * The returned function only proxies Google Ads images (tpc.googlesyndication.com) | ||
| * when an adblocker is detected. Otherwise, it returns the original URL to avoid | ||
| * unnecessary proxying. | ||
| * | ||
| * @return {Object} { getDisplayImageUrl: Function, isDetected: boolean, isLoading: boolean } | ||
| * | ||
| * @example | ||
| * const { getDisplayImageUrl, isDetected, isLoading } = useAdBlockImage(); | ||
| * | ||
| * // Use in render | ||
| * <img src={ getDisplayImageUrl( imageUrl ) } /> | ||
| */ | ||
| const useAdBlockImage = () => { | ||
| const [ isDetected, setIsDetected ] = useState( false ); | ||
| const [ isLoading, setIsLoading ] = useState( true ); | ||
| const hasDetectedRef = useRef( false ); | ||
|
|
||
| useEffect( () => { | ||
| if ( hasDetectedRef.current ) { | ||
| return; | ||
| } | ||
| hasDetectedRef.current = true; | ||
|
|
||
| const detectAdblock = async () => { | ||
| try { | ||
| const detected = await detectAnyAdblocker(); | ||
|
|
||
| if ( detected ) { | ||
| setIsDetected( true ); | ||
| return; | ||
| } | ||
|
|
||
| // Secondary trap: DOM bait catches cosmetic filters (e.g. uBlock Origin) | ||
| // that don't block network requests. | ||
| const domBlocked = await detectViaDOM(); | ||
|
|
||
| if ( domBlocked ) { | ||
| setIsDetected( true ); | ||
| return; | ||
| } | ||
|
|
||
| // Last resort: fetch check against a known ad URL. | ||
| // Only reached if DOM check passed — avoids false positives from | ||
| // network failures alone. | ||
| const fetchBlocked = await detectViaFetch(); | ||
|
|
||
| if ( fetchBlocked ) { | ||
| setIsDetected( true ); | ||
| } | ||
| } catch { | ||
| // If detectAnyAdblocker throws, assume blocked | ||
| setIsDetected( true ); | ||
| } finally { | ||
| setIsLoading( false ); | ||
| } | ||
| }; | ||
|
|
||
| detectAdblock(); | ||
| }, [] ); | ||
|
|
||
| const getDisplayImageUrl = useCallback( | ||
| ( imageUrl ) => { | ||
| const isGoogleAd = imageUrl?.startsWith( | ||
| 'https://tpc.googlesyndication.com/pimgad' | ||
| ); | ||
|
|
||
| if ( ! imageUrl || ! isDetected || ! isGoogleAd ) { | ||
| return imageUrl; | ||
| } | ||
|
|
||
| return getProxiedImageUrl( imageUrl ); | ||
| }, | ||
| [ isDetected ] | ||
| ); | ||
|
|
||
| return { getDisplayImageUrl, isDetected, isLoading }; | ||
| }; | ||
|
|
||
| export default useAdBlockImage; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| /** | ||
| * External dependencies | ||
| */ | ||
| import { addQueryArgs } from '@wordpress/url'; | ||
|
|
||
| /** | ||
| * Converts an external image URL to use the WordPress image proxy endpoint. | ||
| * This bypasses ad blockers that might block direct access to AI-generated images. | ||
| * | ||
| * Use this utility function when rendering images in the UI to ensure they load | ||
| * even when users have ad blockers enabled. The original URLs are preserved in | ||
| * state and only proxied at render time. | ||
| * | ||
| * The function includes a WordPress REST API nonce in the URL to authenticate | ||
| * requests made by img tags, which cannot send custom HTTP headers. | ||
| * | ||
| * @param {string} imageUrl - The original image URL to proxy. | ||
| * @return {string} The proxied URL through the WordPress REST API. | ||
| */ | ||
| export default function getProxiedImageUrl( imageUrl ) { | ||
| if ( ! imageUrl ) { | ||
| return imageUrl; | ||
| } | ||
|
|
||
| const nonce = window.wpApiSettings?.nonce || ''; | ||
| const root = window.wpApiSettings?.root || '/wp-json/'; | ||
| const baseUrl = `${ root }wc/gla/ads/assets/image-proxy`; | ||
|
|
||
| const params = { url: imageUrl }; | ||
| if ( nonce ) { | ||
| params._wpnonce = nonce; | ||
| } | ||
|
|
||
| return addQueryArgs( baseUrl, params ); | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.