Skip to content

Commit d5f7786

Browse files
authored
Merge branch 'main' into la/test-mac-screenY-fix
2 parents ec388a9 + a890ff8 commit d5f7786

File tree

3 files changed

+82
-16
lines changed

3 files changed

+82
-16
lines changed

integration-test/test-web-compat.js

+42-4
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,18 @@ describe('Viewport fixes', () => {
755755
expect(viewportValue).toBeUndefined()
756756
})
757757

758+
it('should respect forced zoom', async () => {
759+
await gotoAndWait(page, `http://localhost:${port}/blank.html`, {
760+
site: { enabledFeatures: ['webCompat'] },
761+
featureSettings: { webCompat: { viewportWidth: 'enabled' } },
762+
desktopModeEnabled: false,
763+
forcedZoomEnabled: true
764+
}, 'document.head.innerHTML += \'<meta name="viewport" content="width=device-width">\'')
765+
766+
const viewportValue = await page.evaluate(getViewportValue)
767+
expect(viewportValue).toEqual('initial-scale=1, user-scalable=yes, maximum-scale=10, width=device-width')
768+
})
769+
758770
describe('Desktop mode off', () => {
759771
it('should respect the forcedMobileValue config', async () => {
760772
await gotoAndWait(page, `http://localhost:${port}/blank.html`, {
@@ -775,7 +787,20 @@ describe('Viewport fixes', () => {
775787
const width = await page.evaluate('screen.width')
776788
const expectedWidth = width < 1280 ? 980 : 1280
777789
const viewportValue = await page.evaluate(getViewportValue)
778-
expect(viewportValue).toEqual(`width=${expectedWidth}, initial-scale=${(width / expectedWidth).toFixed(3)}`)
790+
expect(viewportValue).toEqual(`width=${expectedWidth}, initial-scale=${(width / expectedWidth).toFixed(3)}, user-scalable=yes`)
791+
})
792+
793+
it('should respect forced zoom', async () => {
794+
await gotoAndWait(page, `http://localhost:${port}/blank.html`, {
795+
site: { enabledFeatures: ['webCompat'] },
796+
featureSettings: { webCompat: { viewportWidth: 'enabled' } },
797+
desktopModeEnabled: false,
798+
forcedZoomEnabled: true
799+
})
800+
const width = await page.evaluate('screen.width')
801+
const expectedWidth = width < 1280 ? 980 : 1280
802+
const viewportValue = await page.evaluate(getViewportValue)
803+
expect(viewportValue).toEqual(`initial-scale=${(width / expectedWidth).toFixed(3)}, user-scalable=yes, maximum-scale=10, width=${expectedWidth}`)
779804
})
780805

781806
it('should fix the WebView edge case', async () => {
@@ -819,7 +844,7 @@ describe('Viewport fixes', () => {
819844
const width = await page.evaluate('screen.width')
820845
const expectedWidth = width < 1280 ? 980 : 1280
821846
const viewportValue = await page.evaluate(getViewportValue)
822-
expect(viewportValue).toEqual(`width=${expectedWidth}, initial-scale=${(width / expectedWidth).toFixed(3)}, something-something`)
847+
expect(viewportValue).toEqual(`width=${expectedWidth}, initial-scale=${(width / expectedWidth).toFixed(3)}, user-scalable=yes, something-something`)
823848
})
824849

825850
it('should force wide viewport, ignoring the viewport tag 2', async () => {
@@ -831,7 +856,20 @@ describe('Viewport fixes', () => {
831856
const width = await page.evaluate('screen.width')
832857
const expectedWidth = width < 1280 ? 980 : 1280
833858
const viewportValue = await page.evaluate(getViewportValue)
834-
expect(viewportValue).toEqual(`width=${expectedWidth}, initial-scale=${(width / expectedWidth).toFixed(3)},something-something`)
859+
expect(viewportValue).toEqual(`width=${expectedWidth}, initial-scale=${(width / expectedWidth).toFixed(3)}, user-scalable=yes, something-something`)
860+
})
861+
862+
it('should respect forced zoom', async () => {
863+
await gotoAndWait(page, `http://localhost:${port}/blank.html`, {
864+
site: { enabledFeatures: ['webCompat'] },
865+
featureSettings: { webCompat: { viewportWidth: 'enabled' } },
866+
desktopModeEnabled: true,
867+
forcedZoomEnabled: true
868+
}, 'document.head.innerHTML += \'<meta name="viewport" content="width=device-width, initial-scale=2, user-scalable=no, something-something">\'')
869+
const width = await page.evaluate('screen.width')
870+
const expectedWidth = width < 1280 ? 980 : 1280
871+
const viewportValue = await page.evaluate(getViewportValue)
872+
expect(viewportValue).toEqual(`initial-scale=${(width / expectedWidth).toFixed(3)}, user-scalable=yes, maximum-scale=10, width=${expectedWidth}, something-something`)
835873
})
836874

837875
it('should ignore the character case in the viewport tag', async () => {
@@ -843,7 +881,7 @@ describe('Viewport fixes', () => {
843881
const width = await page.evaluate('screen.width')
844882
const expectedWidth = width < 1280 ? 980 : 1280
845883
const viewportValue = await page.evaluate(getViewportValue)
846-
expect(viewportValue).toEqual(`width=${expectedWidth}, initial-scale=${(width / expectedWidth).toFixed(3)}, something-something`)
884+
expect(viewportValue).toEqual(`width=${expectedWidth}, initial-scale=${(width / expectedWidth).toFixed(3)}, user-scalable=yes, something-something`)
847885
})
848886
})
849887
})

src/content-feature.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export default class ContentFeature {
3838
/** @type {boolean} */
3939
#isDebugFlagSet = false
4040

41-
/** @type {{ debug?: boolean, desktopModeEnabled?: boolean, featureSettings?: Record<string, unknown>, assets?: AssetConfig | undefined, site: Site, messagingConfig?: import('@duckduckgo/messaging').MessagingConfig } | null} */
41+
/** @type {{ debug?: boolean, desktopModeEnabled?: boolean, forcedZoomEnabled?: boolean, featureSettings?: Record<string, unknown>, assets?: AssetConfig | undefined, site: Site, messagingConfig?: import('@duckduckgo/messaging').MessagingConfig } | null} */
4242
#args
4343

4444
constructor (featureName) {
@@ -55,6 +55,10 @@ export default class ContentFeature {
5555
return this.#args?.desktopModeEnabled || false
5656
}
5757

58+
get forcedZoomEnabled () {
59+
return this.#args?.forcedZoomEnabled || false
60+
}
61+
5862
/**
5963
* @param {import('./utils').Platform} platform
6064
*/

src/features/web-compat.js

+35-11
Original file line numberDiff line numberDiff line change
@@ -608,30 +608,40 @@ export default class WebCompat extends ContentFeature {
608608
// Chrome respects only the last viewport tag
609609
const viewportTag = viewportTags.length === 0 ? null : viewportTags[viewportTags.length - 1]
610610
const viewportContent = viewportTag?.getAttribute('content') || ''
611+
/** @type {readonly string[]} **/
611612
const viewportContentParts = viewportContent ? viewportContent.split(/,|;/) : []
613+
/** @type {readonly string[][]} **/
612614
const parsedViewportContent = viewportContentParts.map((part) => {
613615
const [key, value] = part.split('=').map(p => p.trim().toLowerCase())
614616
return [key, value]
615617
})
616618

619+
// first, check if there are any forced values
617620
const { forcedDesktopValue, forcedMobileValue } = this.getFeatureSetting('viewportWidth')
618621
if (typeof forcedDesktopValue === 'string' && this.desktopModeEnabled) {
619622
this.forceViewportTag(viewportTag, forcedDesktopValue)
623+
return
620624
} else if (typeof forcedMobileValue === 'string' && !this.desktopModeEnabled) {
621625
this.forceViewportTag(viewportTag, forcedMobileValue)
622-
} else if (!viewportTag || this.desktopModeEnabled) {
626+
return
627+
}
628+
629+
// otherwise, check for special cases
630+
const forcedValues = {}
631+
632+
if (this.forcedZoomEnabled) {
633+
forcedValues['initial-scale'] = 1
634+
forcedValues['user-scalable'] = 'yes'
635+
forcedValues['maximum-scale'] = 10
636+
}
637+
638+
if (!viewportTag || this.desktopModeEnabled) {
623639
// force wide viewport width
624-
const forcedWidth = screen.width >= 1280 ? 1280 : 980
640+
forcedValues.width = screen.width >= 1280 ? 1280 : 980
641+
forcedValues['initial-scale'] = (screen.width / forcedValues.width).toFixed(3)
625642
// Race condition: depending on the loading state of the page, initial scale may or may not be respected, so the page may look zoomed-in after applying this hack.
626643
// Usually this is just an annoyance, but it may be a bigger issue if user-scalable=no is set, so we remove it too.
627-
const forcedInitialScale = (screen.width / forcedWidth).toFixed(3)
628-
let newContent = `width=${forcedWidth}, initial-scale=${forcedInitialScale}`
629-
parsedViewportContent.forEach(([key], idx) => {
630-
if (!['width', 'initial-scale', 'user-scalable'].includes(key)) {
631-
newContent = newContent.concat(`,${viewportContentParts[idx]}`) // reuse the original values, not the parsed ones
632-
}
633-
})
634-
this.forceViewportTag(viewportTag, newContent)
644+
forcedValues['user-scalable'] = 'yes'
635645
} else { // mobile mode with a viewport tag
636646
// fix an edge case where WebView forces the wide viewport
637647
const widthPart = parsedViewportContent.find(([key]) => key === 'width')
@@ -640,10 +650,24 @@ export default class WebCompat extends ContentFeature {
640650
// Chromium accepts float values for initial-scale
641651
const parsedInitialScale = parseFloat(initialScalePart[1])
642652
if (parsedInitialScale !== 1) {
643-
this.forceViewportTag(viewportTag, `width=device-width, ${viewportContent}`)
653+
forcedValues.width = 'device-width'
644654
}
645655
}
646656
}
657+
658+
const newContent = []
659+
Object.keys(forcedValues).forEach((key) => {
660+
newContent.push(`${key}=${forcedValues[key]}`)
661+
})
662+
663+
if (newContent.length > 0) { // need to override at least one viewport component
664+
parsedViewportContent.forEach(([key], idx) => {
665+
if (!(key in forcedValues)) {
666+
newContent.push(viewportContentParts[idx].trim()) // reuse the original values, not the parsed ones
667+
}
668+
})
669+
this.forceViewportTag(viewportTag, newContent.join(', '))
670+
}
647671
}
648672
}
649673

0 commit comments

Comments
 (0)