diff --git a/3p/viqeoplayer.js b/3p/viqeoplayer.js index a04f8a1cbed2..a37336278cf4 100644 --- a/3p/viqeoplayer.js +++ b/3p/viqeoplayer.js @@ -15,7 +15,7 @@ */ import {getData} from '../src/event-helper'; import {loadScript} from './3p'; -import {tryDecodeUriComponent} from '../src/url'; +import {tryDecodeUriComponent} from '../src/core/types/string/url'; /** * @param {Window} global diff --git a/ads/alp/handler.js b/ads/alp/handler.js index 142e54c2e657..f27654ca1ff5 100644 --- a/ads/alp/handler.js +++ b/ads/alp/handler.js @@ -18,12 +18,12 @@ import { addParamToUrl, isLocalhostOrigin, isProxyOrigin, - parseQueryString, parseUrlDeprecated, } from '../../src/url'; import {closest, openWindowDialog} from '../../src/dom'; import {dev} from '../../src/log'; import {dict} from '../../src/core/types/object'; +import {parseQueryString} from '../../src/core/types/string/url'; import {urls} from '../../src/config'; /** diff --git a/ads/google/a4a/traffic-experiments.js b/ads/google/a4a/traffic-experiments.js index 6cf207146d6d..94982fdfdb42 100644 --- a/ads/google/a4a/traffic-experiments.js +++ b/ads/google/a4a/traffic-experiments.js @@ -28,7 +28,7 @@ import { mergeExperimentIds, } from './utils'; import {Services} from '../../../src/services'; -import {parseQueryString} from '../../../src/url'; +import {parseQueryString} from '../../../src/core/types/string/url'; /** @typedef {{ * control: string, diff --git a/ads/vendors/adventive.js b/ads/vendors/adventive.js index f3c1b6daf52a..e83389c49fa5 100644 --- a/ads/vendors/adventive.js +++ b/ads/vendors/adventive.js @@ -14,7 +14,7 @@ * limitations under the License. */ -import {addParamsToUrl} from '../../src/url.js'; +import {addParamsToUrl} from '../../src/url'; import {dict, hasOwn} from '../../src/core/types/object'; import {endsWith} from '../../src/core/types/string'; import {loadScript, validateData, writeScript} from '../../3p/3p'; diff --git a/ads/vendors/firstimpression.js b/ads/vendors/firstimpression.js index 98b00877b475..abad3fd0b021 100644 --- a/ads/vendors/firstimpression.js +++ b/ads/vendors/firstimpression.js @@ -14,7 +14,7 @@ * limitations under the License. */ -import {parseQueryString} from '../../src/url'; +import {parseQueryString} from '../../src/core/types/string/url'; import {validateData, writeScript} from '../../3p/3p'; /** diff --git a/ads/vendors/netletix.js b/ads/vendors/netletix.js index d3c8f4b9ff38..be16593ea247 100644 --- a/ads/vendors/netletix.js +++ b/ads/vendors/netletix.js @@ -14,7 +14,7 @@ * limitations under the License. */ -import {addParamsToUrl, assertHttpsUrl} from '../../src/url.js'; +import {addParamsToUrl, assertHttpsUrl} from '../../src/url'; import {dev} from '../../src/log.js'; import {dict} from '../../src/core/types/object'; import {loadScript, validateData, writeScript} from '../../3p/3p'; diff --git a/ads/vendors/videonow.js b/ads/vendors/videonow.js index be6199220100..5e5a35d57f15 100644 --- a/ads/vendors/videonow.js +++ b/ads/vendors/videonow.js @@ -16,7 +16,7 @@ import {loadScript, validateData} from '../../3p/3p'; import {parseJson} from '../../src/core/types/object/json'; -import {tryDecodeUriComponent} from '../../src/url'; +import {tryDecodeUriComponent} from '../../src/core/types/string/url'; /** * @param {!Window} global diff --git a/build-system/test-configs/dep-check-config.js b/build-system/test-configs/dep-check-config.js index f8d02f1cafb5..7b79dfaf1bac 100644 --- a/build-system/test-configs/dep-check-config.js +++ b/build-system/test-configs/dep-check-config.js @@ -91,6 +91,7 @@ exports.rules = [ '3p/**->src/core/types/index.js', '3p/**->src/core/types/object/index.js', '3p/**->src/core/types/string/index.js', + '3p/**->src/core/types/string/url.js', '3p/**->src/log.js', '3p/**->src/style.js', '3p/**->src/url.js', @@ -120,18 +121,19 @@ exports.rules = [ mustNotDependOn: 'src/**/*.js', allowlist: [ 'ads/**->src/utils/dom-fingerprint.js', + 'ads/**->src/core/constants/consent-state.js', 'ads/**->src/core/error.js', + 'ads/**->src/core/types/array.js', 'ads/**->src/core/types/function/index.js', 'ads/**->src/core/types/index.js', 'ads/**->src/core/types/object/index.js', 'ads/**->src/core/types/string/index.js', + 'ads/**->src/core/types/string/url.js', 'ads/**->src/log.js', 'ads/**->src/mode.js', 'ads/**->src/url.js', - 'ads/**->src/core/types/array.js', 'ads/**->src/static-template.js', 'ads/**->src/style.js', - 'ads/**->src/core/constants/consent-state.js', 'ads/**->src/internal-version.js', // ads/google/a4a doesn't contain 3P ad code and should probably move // somewhere else at some point diff --git a/build-system/test-configs/forbidden-terms.js b/build-system/test-configs/forbidden-terms.js index ff03dd21440c..ce6ab4e782f4 100644 --- a/build-system/test-configs/forbidden-terms.js +++ b/build-system/test-configs/forbidden-terms.js @@ -807,7 +807,7 @@ const forbiddenTermsSrcInclusive = { 'validator/js/engine/validator.js', 'validator/js/webui/webui.js', 'src/url.js', - 'src/url-try-decode-uri-component.js', + 'src/core/types/string/url.js', 'src/core/types/string/bytes.js', ], }, @@ -948,14 +948,6 @@ const forbiddenTermsSrcInclusive = { }, '\\.indexOf\\(.*===?.*\\.length': 'use endsWith helper in src/core/types/string', - '/url-parse-query-string': { - message: 'Import parseQueryString from `src/url.js`', - allowlist: [ - 'build-system/tasks/check-types.js', - 'src/mode.js', - 'src/url.js', - ], - }, '\\.trim(Left|Right)\\(\\)': { message: 'Unsupported on IE; use trim() or a helper instead.', allowlist: ['validator/js/engine/validator.js'], diff --git a/extensions/amp-a4a/0.1/amp-a4a.js b/extensions/amp-a4a/0.1/amp-a4a.js index f7a4c8fc91c1..7ebc37ae3a84 100644 --- a/extensions/amp-a4a/0.1/amp-a4a.js +++ b/extensions/amp-a4a/0.1/amp-a4a.js @@ -29,7 +29,7 @@ import { generateSentinel, getDefaultBootstrapBaseUrl, } from '../../../src/3p-frame'; -import {assertHttpsUrl, tryDecodeUriComponent} from '../../../src/url'; +import {assertHttpsUrl} from '../../../src/url'; import {cancellation, isCancellation} from '../../../src/error-reporting'; import {createElementWithAttributes} from '../../../src/dom'; import {createSecureDocSkeleton, createSecureFrame} from './secure-frame'; @@ -64,6 +64,7 @@ import { } from '../../../src/utils/intersection'; import {isAdPositionAllowed} from '../../../src/ad-helper'; import {isArray, isEnumValue, isObject} from '../../../src/core/types'; +import {tryDecodeUriComponent} from '../../../src/core/types/string/url'; import {listenOnce} from '../../../src/event-helper'; import { diff --git a/extensions/amp-access-scroll/0.1/scroll-impl.js b/extensions/amp-access-scroll/0.1/scroll-impl.js index 0118176b630c..e40c4aea132e 100644 --- a/extensions/amp-access-scroll/0.1/scroll-impl.js +++ b/extensions/amp-access-scroll/0.1/scroll-impl.js @@ -22,11 +22,12 @@ import {Relay} from './scroll-relay'; import {ScrollBar} from './scroll-bar'; import {Services} from '../../../src/services'; import {Sheet} from './scroll-sheet'; -import {addParamToUrl, isProxyOrigin, parseQueryString} from '../../../src/url'; +import {addParamToUrl, isProxyOrigin} from '../../../src/url'; import {buildUrl, connectHostname} from './scroll-url'; import {createElementWithAttributes} from '../../../src/dom'; import {dict} from '../../../src/core/types/object'; import {installStylesForDoc} from '../../../src/style-installer'; +import {parseQueryString} from '../../../src/core/types/string/url'; const TAG = 'amp-access-scroll-elt'; /** diff --git a/extensions/amp-access/0.1/amp-access-source.js b/extensions/amp-access/0.1/amp-access-source.js index 361867d277d9..866e9f06b205 100644 --- a/extensions/amp-access/0.1/amp-access-source.js +++ b/extensions/amp-access/0.1/amp-access-source.js @@ -22,12 +22,13 @@ import {AccessServerJwtAdapter} from './amp-access-server-jwt'; import {AccessVendorAdapter} from './amp-access-vendor'; import {Deferred} from '../../../src/core/data-structures/promise'; import {Services} from '../../../src/services'; -import {assertHttpsUrl, parseQueryString} from '../../../src/url'; +import {assertHttpsUrl} from '../../../src/url'; import {dev, user, userAssert} from '../../../src/log'; import {dict, getValueForExpr} from '../../../src/core/types/object'; import {getLoginUrl, openLoginDialog} from './login-dialog'; import {isExperimentOn} from '../../../src/experiments'; import {isObject} from '../../../src/core/types'; +import {parseQueryString} from '../../../src/core/types/string/url'; import {triggerAnalyticsEvent} from '../../../src/analytics'; /** @const */ diff --git a/extensions/amp-access/0.1/amp-login-done-dialog.js b/extensions/amp-access/0.1/amp-login-done-dialog.js index 12f030092f3b..bb8d567caad4 100644 --- a/extensions/amp-access/0.1/amp-login-done-dialog.js +++ b/extensions/amp-access/0.1/amp-login-done-dialog.js @@ -14,12 +14,12 @@ * limitations under the License. */ +import {assertAbsoluteHttpOrHttpsUrl} from '../../../src/url'; +import {listen} from '../../../src/event-helper'; import { - assertAbsoluteHttpOrHttpsUrl, parseQueryString, tryDecodeUriComponent, -} from '../../../src/url'; -import {listen} from '../../../src/event-helper'; +} from '../../../src/core/types/string/url'; /** * @private Visible for testing. diff --git a/extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js b/extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js index 90d4f1962d58..66aa25b36970 100644 --- a/extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js +++ b/extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js @@ -126,7 +126,7 @@ import { lineDelimitedStreamer, metaJsonCreativeGrouper, } from '../../../ads/google/a4a/line-delimited-response-handler'; -import {parseQueryString} from '../../../src/url'; +import {parseQueryString} from '../../../src/core/types/string/url'; import {stringHash32} from '../../../src/core/types/string'; import {tryParseJson} from '../../../src/core/types/object/json'; diff --git a/extensions/amp-analytics/0.1/linker-reader.js b/extensions/amp-analytics/0.1/linker-reader.js index 3257c2647cf0..77c3439cdc76 100644 --- a/extensions/amp-analytics/0.1/linker-reader.js +++ b/extensions/amp-analytics/0.1/linker-reader.js @@ -17,7 +17,8 @@ import {getService, registerServiceBuilder} from '../../../src/service'; import {hasOwn} from '../../../src/core/types/object'; import {parseLinker} from './linker'; -import {parseQueryString, removeParamsFromSearch} from '../../../src/url'; +import {parseQueryString} from '../../../src/core/types/string/url'; +import {removeParamsFromSearch} from '../../../src/url'; import {user} from '../../../src/log'; diff --git a/extensions/amp-auto-ads/0.1/firstimpression.io-network-config.js b/extensions/amp-auto-ads/0.1/firstimpression.io-network-config.js index a59252d51c3b..47eac0dbe36b 100644 --- a/extensions/amp-auto-ads/0.1/firstimpression.io-network-config.js +++ b/extensions/amp-auto-ads/0.1/firstimpression.io-network-config.js @@ -17,7 +17,7 @@ import {Services} from '../../../src/services'; import {buildUrl} from '../../../ads/google/a4a/shared/url-builder'; import {dict} from '../../../src/core/types/object'; -import {parseQueryString} from '../../../src/url'; +import {parseQueryString} from '../../../src/core/types/string/url'; /** * @implements {./ad-network-config.AdNetworkConfigDef} diff --git a/extensions/amp-consent/0.1/linker-reader.js b/extensions/amp-consent/0.1/linker-reader.js index f2ff1df2e628..7c5f3870b607 100644 --- a/extensions/amp-consent/0.1/linker-reader.js +++ b/extensions/amp-consent/0.1/linker-reader.js @@ -16,7 +16,7 @@ import {hasOwn} from '../../../src/core/types/object'; import {parseLinker} from './linker'; -import {parseQueryString} from '../../../src/url'; +import {parseQueryString} from '../../../src/core/types/string/url'; import {user} from '../../../src/log'; const TAG = 'amp-consent/linker-reader'; diff --git a/extensions/amp-dailymotion/0.1/amp-dailymotion.js b/extensions/amp-dailymotion/0.1/amp-dailymotion.js index c03116a78017..3efa66812233 100644 --- a/extensions/amp-dailymotion/0.1/amp-dailymotion.js +++ b/extensions/amp-dailymotion/0.1/amp-dailymotion.js @@ -18,11 +18,7 @@ import {Deferred} from '../../../src/core/data-structures/promise'; import {PauseHelper} from '../../../src/utils/pause-helper'; import {Services} from '../../../src/services'; import {VideoEvents} from '../../../src/video-interface'; -import { - addParamToUrl, - addParamsToUrl, - parseQueryString, -} from '../../../src/url'; +import {addParamToUrl, addParamsToUrl} from '../../../src/url'; import { createFrameFor, mutedOrUnmutedEvent, @@ -41,6 +37,7 @@ import { import {getData, listen} from '../../../src/event-helper'; import {installVideoManagerForDoc} from '../../../src/service/video-manager-impl'; import {isLayoutSizeDefined} from '../../../src/layout'; +import {parseQueryString} from '../../../src/core/types/string/url'; const TAG = 'amp-dailymotion'; diff --git a/extensions/amp-form/0.1/amp-form.js b/extensions/amp-form/0.1/amp-form.js index adcfb30e863d..2576dbc2b3f3 100644 --- a/extensions/amp-form/0.1/amp-form.js +++ b/extensions/amp-form/0.1/amp-form.js @@ -36,7 +36,6 @@ import { SOURCE_ORIGIN_PARAM, addParamsToUrl, isProxyOrigin, - parseQueryString, serializeQueryString, } from '../../../src/url'; import {Services} from '../../../src/services'; @@ -65,6 +64,7 @@ import {installFormProxy} from './form-proxy'; import {installStylesForDoc} from '../../../src/style-installer'; import {isAmp4Email} from '../../../src/format'; import {isArray, toArray} from '../../../src/core/types/array'; +import {parseQueryString} from '../../../src/core/types/string/url'; import { setupAMPCors, setupInit, diff --git a/extensions/amp-form/0.1/test/test-amp-form.js b/extensions/amp-form/0.1/test/test-amp-form.js index c502ada4a21f..e994f874e0a3 100644 --- a/extensions/amp-form/0.1/test/test-amp-form.js +++ b/extensions/amp-form/0.1/test/test-amp-form.js @@ -39,7 +39,7 @@ import { isFormDataWrapper, } from '../../../../src/form-data-wrapper'; import {fromIterator} from '../../../../src/core/types/array'; -import {parseQueryString} from '../../../../src/url.js'; +import {parseQueryString} from '../../../../src/core/types/string/url'; import { setCheckValiditySupportedForTesting, setReportValiditySupportedForTesting, diff --git a/extensions/amp-skimlinks/0.1/test/test-waypoint.js b/extensions/amp-skimlinks/0.1/test/test-waypoint.js index 888d150c231e..95938f64d38e 100644 --- a/extensions/amp-skimlinks/0.1/test/test-waypoint.js +++ b/extensions/amp-skimlinks/0.1/test/test-waypoint.js @@ -17,7 +17,8 @@ import {DEFAULT_SKIM_OPTIONS, pubcode} from './constants'; import {Waypoint} from '../waypoint'; import {XCUST_ATTRIBUTE_NAME} from '../constants'; -import {parseQueryString, parseUrlDeprecated} from '../../../../src/url'; +import {parseQueryString} from '../../../../src/core/types/string/url'; +import {parseUrlDeprecated} from '../../../../src/url'; import helpersFactory from './helpers'; describes.fakeWin( diff --git a/extensions/amp-social-share/0.1/amp-social-share.js b/extensions/amp-social-share/0.1/amp-social-share.js index 00aae2f2fbe6..e70a60cac382 100644 --- a/extensions/amp-social-share/0.1/amp-social-share.js +++ b/extensions/amp-social-share/0.1/amp-social-share.js @@ -17,11 +17,12 @@ import {CSS} from '../../../build/amp-social-share-0.1.css'; import {Keys} from '../../../src/core/constants/key-codes'; import {Services} from '../../../src/services'; -import {addParamsToUrl, parseQueryString} from '../../../src/url'; +import {addParamsToUrl} from '../../../src/url'; import {dev, devAssert, user, userAssert} from '../../../src/log'; import {dict} from '../../../src/core/types/object'; import {getDataParamsFromAttributes, openWindowDialog} from '../../../src/dom'; import {getSocialConfig} from './amp-social-share-config'; +import {parseQueryString} from '../../../src/core/types/string/url'; import {toggle} from '../../../src/style'; const TAG = 'amp-social-share'; diff --git a/extensions/amp-social-share/1.0/amp-social-share.js b/extensions/amp-social-share/1.0/amp-social-share.js index b566ee9e524d..32d42cd61394 100644 --- a/extensions/amp-social-share/1.0/amp-social-share.js +++ b/extensions/amp-social-share/1.0/amp-social-share.js @@ -19,11 +19,12 @@ import {Layout} from '../../../src/layout'; import {PreactBaseElement} from '../../../src/preact/base-element'; import {Services} from '../../../src/services'; import {SocialShare} from './social-share'; -import {addParamsToUrl, parseQueryString} from '../../../src/url'; +import {addParamsToUrl} from '../../../src/url'; import {dict} from '../../../src/core/types/object'; import {getDataParamsFromAttributes} from '../../../src/dom'; import {getSocialConfig} from './social-share-config'; import {isExperimentOn} from '../../../src/experiments'; +import {parseQueryString} from '../../../src/core/types/string/url'; import {toWin} from '../../../src/types'; import {toggle} from '../../../src/style'; import {userAssert} from '../../../src/log'; diff --git a/extensions/amp-social-share/1.0/social-share.js b/extensions/amp-social-share/1.0/social-share.js index 1f6081ce084b..5b2a844e3bc2 100644 --- a/extensions/amp-social-share/1.0/social-share.js +++ b/extensions/amp-social-share/1.0/social-share.js @@ -19,10 +19,11 @@ import * as Preact from '../../../src/preact'; import {Keys} from '../../../src/core/constants/key-codes'; import {SocialShareIcon} from './social-share-svgs'; import {Wrapper} from '../../../src/preact/component'; -import {addParamsToUrl, parseQueryString} from '../../../src/url'; +import {addParamsToUrl} from '../../../src/url'; import {dict} from '../../../src/core/types/object'; import {getSocialConfig} from './social-share-config'; import {openWindowDialog} from '../../../src/dom'; +import {parseQueryString} from '../../../src/core/types/string/url'; import {useResourcesNotify} from '../../../src/preact/utils'; import {useStyles} from './social-share.jss'; diff --git a/extensions/amp-story-dev-tools/0.1/amp-story-dev-tools.js b/extensions/amp-story-dev-tools/0.1/amp-story-dev-tools.js index dfa43389b97d..873f2bc22ba9 100644 --- a/extensions/amp-story-dev-tools/0.1/amp-story-dev-tools.js +++ b/extensions/amp-story-dev-tools/0.1/amp-story-dev-tools.js @@ -24,7 +24,7 @@ import { } from './amp-story-dev-tools-tab-preview'; import {CSS} from '../../../build/amp-story-dev-tools-0.1.css'; import {htmlFor} from '../../../src/static-template'; -import {parseQueryString} from '../../../src/url'; +import {parseQueryString} from '../../../src/core/types/string/url'; import {toggle} from '../../../src/style'; import {updateHash} from './utils'; diff --git a/extensions/amp-story-dev-tools/0.1/utils.js b/extensions/amp-story-dev-tools/0.1/utils.js index 3002bc85175e..c6b57b2a6c93 100644 --- a/extensions/amp-story-dev-tools/0.1/utils.js +++ b/extensions/amp-story-dev-tools/0.1/utils.js @@ -14,7 +14,7 @@ * limitations under the License. */ -import {parseQueryString} from '../../../src/url'; +import {parseQueryString} from '../../../src/core/types/string/url'; /** * Updates the hashString with the dictionary passed in diff --git a/extensions/amp-story/1.0/amp-story.js b/extensions/amp-story/1.0/amp-story.js index 610650d93571..9f8149a89cd7 100644 --- a/extensions/amp-story/1.0/amp-story.js +++ b/extensions/amp-story/1.0/amp-story.js @@ -100,7 +100,7 @@ import {getMode} from '../../../src/mode'; import {getState} from '../../../src/history'; import {isExperimentOn} from '../../../src/experiments'; import {isPageAttachmentUiV2ExperimentOn} from './amp-story-page-attachment-ui-v2'; -import {parseQueryString} from '../../../src/url'; +import {parseQueryString} from '../../../src/core/types/string/url'; import { removeAttributeInMutate, setAttributeInMutate, diff --git a/extensions/amp-story/1.0/embed-mode.js b/extensions/amp-story/1.0/embed-mode.js index df032cbde2da..3d0fb76b42f1 100644 --- a/extensions/amp-story/1.0/embed-mode.js +++ b/extensions/amp-story/1.0/embed-mode.js @@ -14,7 +14,7 @@ * limitations under the License. */ import {isEnumValue} from '../../../src/core/types'; -import {parseQueryString} from '../../../src/url'; +import {parseQueryString} from '../../../src/core/types/string/url'; /** * Embed mode for AMP story. See ../embed-modes.md for details. diff --git a/extensions/amp-story/1.0/prerender-active-page.js b/extensions/amp-story/1.0/prerender-active-page.js index 2e5c8a009e27..c3c7df8b1689 100644 --- a/extensions/amp-story/1.0/prerender-active-page.js +++ b/extensions/amp-story/1.0/prerender-active-page.js @@ -15,7 +15,7 @@ */ import {escapeCssSelectorIdent} from '../../../src/core/dom/css'; -import {parseQueryString} from '../../../src/url'; +import {parseQueryString} from '../../../src/core/types/string/url'; import {toWin} from '../../../src/types'; /** diff --git a/extensions/amp-subscriptions-google/0.1/amp-subscriptions-google.js b/extensions/amp-subscriptions-google/0.1/amp-subscriptions-google.js index a08f91d343fa..3599ad83c277 100644 --- a/extensions/amp-subscriptions-google/0.1/amp-subscriptions-google.js +++ b/extensions/amp-subscriptions-google/0.1/amp-subscriptions-google.js @@ -38,16 +38,13 @@ import {Services} from '../../../src/services'; import {SubscriptionsScoreFactor} from '../../amp-subscriptions/0.1/constants.js'; import {UrlBuilder} from '../../amp-subscriptions/0.1/url-builder'; import {WindowInterface} from '../../../src/window-interface'; -import { - assertHttpsUrl, - parseQueryString, - parseUrlDeprecated, -} from '../../../src/url'; +import {assertHttpsUrl, parseUrlDeprecated} from '../../../src/url'; import {experimentToggles, isExperimentOn} from '../../../src/experiments'; import {getData} from '../../../src/event-helper'; import {getMode} from '../../../src/mode'; import {getValueForExpr} from '../../../src/core/types/object'; import {installStylesForDoc} from '../../../src/style-installer'; +import {parseQueryString} from '../../../src/core/types/string/url'; import {devAssert, user, userAssert} from '../../../src/log'; diff --git a/extensions/amp-subscriptions/0.1/actions.js b/extensions/amp-subscriptions/0.1/actions.js index f9536c147b94..5f99207feb72 100644 --- a/extensions/amp-subscriptions/0.1/actions.js +++ b/extensions/amp-subscriptions/0.1/actions.js @@ -15,10 +15,11 @@ */ import {ActionStatus} from './analytics'; -import {assertHttpsUrl, parseQueryString} from '../../../src/url'; +import {assertHttpsUrl} from '../../../src/url'; import {dev, userAssert} from '../../../src/log'; import {dict} from '../../../src/core/types/object'; import {openLoginDialog} from '../../amp-access/0.1/login-dialog'; +import {parseQueryString} from '../../../src/core/types/string/url'; const TAG = 'amp-subscriptions'; const LOCAL = 'local'; diff --git a/extensions/amp-viewer-integration/0.1/highlight-handler.js b/extensions/amp-viewer-integration/0.1/highlight-handler.js index 7d04f52999c9..c4c09affdd66 100644 --- a/extensions/amp-viewer-integration/0.1/highlight-handler.js +++ b/extensions/amp-viewer-integration/0.1/highlight-handler.js @@ -22,7 +22,7 @@ import {listenOnce} from '../../../src/event-helper'; import {moveLayoutRect} from '../../../src/layout-rect'; import {once} from '../../../src/core/types/function'; import {parseJson} from '../../../src/core/types/object/json'; -import {parseQueryString} from '../../../src/url'; +import {parseQueryString} from '../../../src/core/types/string/url'; import {resetStyles, setInitialDisplay, setStyles} from '../../../src/style'; import {whenDocumentReady} from '../../../src/document-ready'; diff --git a/extensions/amp-web-push/0.1/amp-web-push-helper-frame.js b/extensions/amp-web-push/0.1/amp-web-push-helper-frame.js index aa58f789f1e6..cd86d654e40d 100644 --- a/extensions/amp-web-push/0.1/amp-web-push-helper-frame.js +++ b/extensions/amp-web-push/0.1/amp-web-push-helper-frame.js @@ -18,7 +18,7 @@ import {TAG} from './vars'; import {WindowMessenger} from './window-messenger'; import {getMode} from '../../../src/mode'; import {initLogConstructor, setReportError, user} from '../../../src/log'; -import {parseQueryString} from '../../../src/url.js'; +import {parseQueryString} from '../../../src/core/types/string/url'; import {reportError} from '../../../src/error-reporting'; initLogConstructor(); diff --git a/extensions/amp-web-push/0.1/amp-web-push-permission-dialog.js b/extensions/amp-web-push/0.1/amp-web-push-permission-dialog.js index 7da3e43519c4..f7235cc1cd84 100644 --- a/extensions/amp-web-push/0.1/amp-web-push-permission-dialog.js +++ b/extensions/amp-web-push/0.1/amp-web-push-permission-dialog.js @@ -20,9 +20,9 @@ import {escapeCssSelectorIdent} from '../../../src/core/dom/css'; import {getMode} from '../../../src/mode'; import { parseQueryString, - parseUrlDeprecated, tryDecodeUriComponent, -} from '../../../src/url.js'; +} from '../../../src/core/types/string/url'; +import {parseUrlDeprecated} from '../../../src/url.js'; /** @typedef {{ * debug: boolean, diff --git a/extensions/amp-web-push/0.1/web-push-service.js b/extensions/amp-web-push/0.1/web-push-service.js index 212775defe8d..0b7cc564f94c 100644 --- a/extensions/amp-web-push/0.1/web-push-service.js +++ b/extensions/amp-web-push/0.1/web-push-service.js @@ -32,7 +32,8 @@ import {getMode} from '../../../src/mode'; import {getServicePromiseForDoc} from '../../../src/service'; import {installStylesForDoc} from '../../../src/style-installer'; import {openWindowDialog} from '../../../src/dom'; -import {parseQueryString, parseUrlDeprecated} from '../../../src/url'; +import {parseQueryString} from '../../../src/core/types/string/url'; +import {parseUrlDeprecated} from '../../../src/url'; /** @typedef {{ * isControllingFrame: boolean, diff --git a/src/amp-story-player/amp-story-player-impl.js b/src/amp-story-player/amp-story-player-impl.js index 199db93d601e..806b3a10610a 100644 --- a/src/amp-story-player/amp-story-player-impl.js +++ b/src/amp-story-player/amp-story-player-impl.js @@ -24,7 +24,6 @@ import { addParamsToUrl, getFragment, isProxyOrigin, - parseQueryString, parseUrlWithA, removeFragment, removeSearch, @@ -34,6 +33,7 @@ import {applySandbox} from '../3p-frame'; import {createCustomEvent, listenOnce} from '../event-helper'; import {dict} from '../core/types/object'; import {isJsonScriptTag, tryFocus} from '../dom'; +import {parseQueryString} from '../core/types/string/url'; // Source for this constant is css/amp-story-player-iframe.css import {cssText} from '../../build/amp-story-player-iframe.css'; import {devAssertElement} from '../core/assert'; diff --git a/src/cookies.js b/src/cookies.js index ee86727a5c06..b3a968a68936 100644 --- a/src/cookies.js +++ b/src/cookies.js @@ -15,12 +15,8 @@ */ import {endsWith} from './core/types/string'; -import { - getSourceOrigin, - isProxyOrigin, - parseUrlDeprecated, - tryDecodeUriComponent, -} from './url'; +import {getSourceOrigin, isProxyOrigin, parseUrlDeprecated} from './url'; +import {tryDecodeUriComponent} from './core/types/string/url'; import {urls} from './config'; import {userAssert} from './log'; diff --git a/src/url-parse-query-string.js b/src/core/types/string/url.js similarity index 58% rename from src/url-parse-query-string.js rename to src/core/types/string/url.js index dacb8958171f..d6b980cf1e4e 100644 --- a/src/url-parse-query-string.js +++ b/src/core/types/string/url.js @@ -14,31 +14,44 @@ * limitations under the License. */ -import {tryDecodeUriComponent_} from './url-try-decode-uri-component'; +import {map} from '../object'; -const regex = /(?:^[#?]?|&)([^=&]+)(?:=([^&]*))?/g; +const QUERY_STRING_REGEX = /(?:^[#?]?|&)([^=&]+)(?:=([^&]*))?/g; + +/** + * Tries to decode a URI component, falling back to opt_fallback (or an empty + * string) + * + * @param {string} component + * @param {string=} fallback + * @return {string} + */ +export function tryDecodeUriComponent(component, fallback = '') { + try { + return decodeURIComponent(component); + } catch (e) { + return fallback; + } +} /** * Parses the query string of an URL. This method returns a simple key/value * map. If there are duplicate keys the latest value is returned. * - * DO NOT import the function from this file. Instead, import parseQueryString - * from `src/url.js`. - * * @param {string} queryString * @return {!JsonObject} */ -export function parseQueryString_(queryString) { - const params = /** @type {!JsonObject} */ (Object.create(null)); +export function parseQueryString(queryString) { + const params = map(); if (!queryString) { return params; } let match; - while ((match = regex.exec(queryString))) { - const name = tryDecodeUriComponent_(match[1], match[1]); + while ((match = QUERY_STRING_REGEX.exec(queryString))) { + const name = tryDecodeUriComponent(match[1], match[1]); const value = match[2] - ? tryDecodeUriComponent_(match[2].replace(/\+/g, ' '), match[2]) + ? tryDecodeUriComponent(match[2].replace(/\+/g, ' '), match[2]) : ''; params[name] = value; } diff --git a/src/experiments/index.js b/src/experiments/index.js index 671abd0e8472..28af2e1b92ae 100644 --- a/src/experiments/index.js +++ b/src/experiments/index.js @@ -26,7 +26,7 @@ import {getMode} from '../mode'; import {getTopWindow} from '../service'; import {hasOwn, map} from '../core/types/object'; import {isArray} from '../core/types'; -import {parseQueryString} from '../url'; +import {parseQueryString} from '../core/types/string/url'; // typedef imports import {ExperimentInfoDef} from './experiments.type'; diff --git a/src/impression.js b/src/impression.js index 9f0a11a197ef..5446b1851611 100644 --- a/src/impression.js +++ b/src/impression.js @@ -17,15 +17,11 @@ import {Deferred} from './core/data-structures/promise'; import {Services} from './services'; import {WindowInterface} from './window-interface'; -import { - addParamsToUrl, - isProxyOrigin, - parseQueryString, - parseUrlDeprecated, -} from './url'; +import {addParamsToUrl, isProxyOrigin, parseUrlDeprecated} from './url'; import {dev, user, userAssert} from './log'; import {getMode} from './mode'; import {isExperimentOn} from './experiments'; +import {parseQueryString} from './core/types/string/url'; const TIMEOUT_VALUE = 8000; diff --git a/src/mode.js b/src/mode.js index 07f38279c196..4768000d66c0 100644 --- a/src/mode.js +++ b/src/mode.js @@ -15,7 +15,7 @@ */ import {internalRuntimeVersion} from './internal-version'; -import {parseQueryString_} from './url-parse-query-string'; +import {parseQueryString} from './core/types/string/url'; /** * @typedef {{ @@ -73,7 +73,7 @@ function getMode_(win) { const runningTests = IS_FORTESTING && !!(AMP_CONFIG.test || win.__AMP_TEST || win['__karma__']); const isLocalDev = IS_FORTESTING && (!!AMP_CONFIG.localDev || runningTests); - const hashQuery = parseQueryString_( + const hashQuery = parseQueryString( // location.originalHash is set by the viewer when it removes the fragment // from the URL. win.location['originalHash'] || win.location.hash diff --git a/src/service/ampdoc-impl.js b/src/service/ampdoc-impl.js index f1aac5de5c93..9dc8e61095f5 100644 --- a/src/service/ampdoc-impl.js +++ b/src/service/ampdoc-impl.js @@ -33,7 +33,7 @@ import { import {isDocumentReady, whenDocumentReady} from '../document-ready'; import {iterateCursor, rootNodeFor, waitForBodyOpenPromise} from '../dom'; import {map} from '../core/types/object'; -import {parseQueryString} from '../url'; +import {parseQueryString} from '../core/types/string/url'; /** @const {string} */ const AMPDOC_PROP = '__AMPDOC'; diff --git a/src/service/document-info-impl.js b/src/service/document-info-impl.js index 215190ec0083..36be0e6cd4e1 100644 --- a/src/service/document-info-impl.js +++ b/src/service/document-info-impl.js @@ -14,16 +14,11 @@ * limitations under the License. */ -import { - getProxyServingType, - getSourceUrl, - parseQueryString, - parseUrlDeprecated, -} from '../url'; - +import {getProxyServingType, getSourceUrl, parseUrlDeprecated} from '../url'; import {getRandomString64} from './cid-impl'; import {isArray} from '../core/types'; import {map} from '../core/types/object'; +import {parseQueryString} from '../core/types/string/url'; import {registerServiceBuilderForDoc} from '../service'; /** @private @const {!Array} */ diff --git a/src/service/url-replacements-impl.js b/src/service/url-replacements-impl.js index 7eb8ab019e46..de9748b2aed3 100644 --- a/src/service/url-replacements-impl.js +++ b/src/service/url-replacements-impl.js @@ -23,28 +23,27 @@ import { getTimingDataAsync, getTimingDataSync, } from './variable-source'; +import {Expander} from './url-expander/expander'; +import {Services} from '../services'; +import {WindowInterface} from '../window-interface'; import { addMissingParamsToUrl, addParamsToUrl, getSourceUrl, isProtocolValid, - parseQueryString, parseUrlDeprecated, removeAmpJsParamsFromUrl, removeFragment, } from '../url'; import {dev, devAssert, user, userAssert} from '../log'; +import {getTrackImpressionPromise} from '../impression.js'; +import {hasOwn} from '../core/types/object'; import { installServiceInEmbedDoc, registerServiceBuilderForDoc, } from '../service'; - -import {Expander} from './url-expander/expander'; -import {Services} from '../services'; -import {WindowInterface} from '../window-interface'; -import {getTrackImpressionPromise} from '../impression.js'; -import {hasOwn} from '../core/types/object'; import {internalRuntimeVersion} from '../internal-version'; +import {parseQueryString} from '../core/types/string/url'; /** @private @const {string} */ const TAG = 'UrlReplacements'; diff --git a/src/service/viewer-impl.js b/src/service/viewer-impl.js index 80046e838d8f..8bb22516fd30 100644 --- a/src/service/viewer-impl.js +++ b/src/service/viewer-impl.js @@ -25,7 +25,6 @@ import {findIndex} from '../core/types/array'; import { getSourceOrigin, isProxyOrigin, - parseQueryString, parseUrlDeprecated, removeFragment, serializeQueryString, @@ -33,6 +32,7 @@ import { import {isIframed} from '../dom'; import {listen} from '../event-helper'; import {map} from '../core/types/object'; +import {parseQueryString} from '../core/types/string/url'; import {registerServiceBuilderForDoc} from '../service'; import {reportError} from '../error-reporting'; import {urls} from '../config'; diff --git a/src/url-try-decode-uri-component.js b/src/url-try-decode-uri-component.js deleted file mode 100644 index 63c1ded39988..000000000000 --- a/src/url-try-decode-uri-component.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2017 The AMP HTML Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS-IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Tries to decode a URI component, falling back to opt_fallback (or an empty - * string) - * - * DO NOT import the function from this file. Instead, import - * tryDecodeUriComponent from `src/url.js`. - * - * @param {string} component - * @param {string=} fallback - * @return {string} - */ -export function tryDecodeUriComponent_(component, fallback = '') { - try { - return decodeURIComponent(component); - } catch (e) { - return fallback; - } -} diff --git a/src/url.js b/src/url.js index 27659ccf301c..e2b84d227dbf 100644 --- a/src/url.js +++ b/src/url.js @@ -19,8 +19,7 @@ import {dict, hasOwn} from './core/types/object'; import {endsWith} from './core/types/string'; import {getMode} from './mode'; import {isArray} from './core/types'; -import {parseQueryString_} from './url-parse-query-string'; -import {tryDecodeUriComponent_} from './url-try-decode-uri-component'; +import {parseQueryString} from './core/types/string/url'; import {urls} from './config'; import {userAssert} from './log'; @@ -352,20 +351,6 @@ export function assertAbsoluteHttpOrHttpsUrl(urlString) { return parseUrlDeprecated(urlString).href; } -/** - * Parses the query string of an URL. This method returns a simple key/value - * map. If there are duplicate keys the latest value is returned. - * - * This function is implemented in a separate file to avoid a circular - * dependency. - * - * @param {string} queryString - * @return {!JsonObject} - */ -export function parseQueryString(queryString) { - return parseQueryString_(queryString); -} - /** * Returns the URL without fragment. If URL doesn't contain fragment, the same * string is returned. @@ -654,18 +639,6 @@ export function checkCorsUrl(url) { ); } -/** - * Tries to decode a URI component, falling back to opt_fallback (or an empty - * string) - * - * @param {string} component - * @param {string=} opt_fallback - * @return {string} - */ -export function tryDecodeUriComponent(component, opt_fallback) { - return tryDecodeUriComponent_(component, opt_fallback); -} - /** * Adds the path to the given url. * diff --git a/src/validator-integration.js b/src/validator-integration.js index a59ef7859198..c9d570a5de04 100644 --- a/src/validator-integration.js +++ b/src/validator-integration.js @@ -16,7 +16,7 @@ import {getMode} from './mode'; import {loadPromise} from './event-helper'; -import {parseQueryString} from './url'; +import {parseQueryString} from './core/types/string/url'; import {urls} from './config'; /** diff --git a/test/integration/test-amp-analytics.js b/test/integration/test-amp-analytics.js index 8e5dde6deb07..48fe38b1c8be 100644 --- a/test/integration/test-amp-analytics.js +++ b/test/integration/test-amp-analytics.js @@ -15,7 +15,7 @@ */ import {BrowserController, RequestBank} from '../../testing/test-helper'; -import {parseQueryString} from '../../src/url'; +import {parseQueryString} from '../../src/core/types/string/url'; // TODO(wg-analytics): These tests time out on Firefox and Safari. describes.sandboxed diff --git a/test/integration/test-amp-skimlinks.js b/test/integration/test-amp-skimlinks.js index cd832d5b3e11..cdc8d1bacb2a 100644 --- a/test/integration/test-amp-skimlinks.js +++ b/test/integration/test-amp-skimlinks.js @@ -16,7 +16,7 @@ import {BrowserController, RequestBank} from '../../testing/test-helper'; import {PLATFORM_NAME} from '../../extensions/amp-skimlinks/0.1/constants'; -import {parseQueryString} from '../../src/url'; +import {parseQueryString} from '../../src/core/types/string/url'; // Create fake test urls to replace skimlinks API urls. // RequestBank allow us to check if an API request has been made diff --git a/test/integration/test-amp-story-analytics.js b/test/integration/test-amp-story-analytics.js index c991c31c38be..d49e603fdb7a 100644 --- a/test/integration/test-amp-story-analytics.js +++ b/test/integration/test-amp-story-analytics.js @@ -15,7 +15,7 @@ */ import {BrowserController, RequestBank} from '../../testing/test-helper'; -import {parseQueryString} from '../../src/url'; +import {parseQueryString} from '../../src/core/types/string/url'; const config = describes.sandboxed.configure().skipEdge().skipSafari(); diff --git a/test/integration/test-amphtml-ads.js b/test/integration/test-amphtml-ads.js index dec1d1ebe0de..3d06093f0441 100644 --- a/test/integration/test-amphtml-ads.js +++ b/test/integration/test-amphtml-ads.js @@ -16,7 +16,7 @@ import {RequestBank} from '../../testing/test-helper'; import {maybeSwitchToCompiledJs} from '../../testing/iframe'; -import {parseQueryString} from '../../src/url'; +import {parseQueryString} from '../../src/core/types/string/url'; import {xhrServiceForTesting} from '../../src/service/xhr-impl'; // TODO(wg-monetization, #29112): Unskip on Safari. diff --git a/test/unit/core/types/string/test-url.js b/test/unit/core/types/string/test-url.js new file mode 100644 index 000000000000..173f77976c5f --- /dev/null +++ b/test/unit/core/types/string/test-url.js @@ -0,0 +1,71 @@ +/** + * Copyright 2021 The AMP HTML Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {parseQueryString} from '../../../../../src/core/types/string/url'; + +describes.sandboxed('type helpers - strings - urls', {}, () => { + describe('parseQueryString', () => { + it('should return empty params when query string is empty or null', () => { + expect(parseQueryString(null)).to.deep.equal({}); + expect(parseQueryString('')).to.deep.equal({}); + }); + it('should parse single key-value', () => { + expect(parseQueryString('a=1')).to.deep.equal({ + 'a': '1', + }); + }); + it('should parse two key-values', () => { + expect(parseQueryString('a=1&b=2')).to.deep.equal({ + 'a': '1', + 'b': '2', + }); + }); + it('should ignore leading ?', () => { + expect(parseQueryString('?a=1&b=2')).to.deep.equal({ + 'a': '1', + 'b': '2', + }); + }); + it('should ignore leading #', () => { + expect(parseQueryString('#a=1&b=2')).to.deep.equal({ + 'a': '1', + 'b': '2', + }); + }); + it('should parse empty value', () => { + expect(parseQueryString('a=&b=2')).to.deep.equal({ + 'a': '', + 'b': '2', + }); + expect(parseQueryString('a&b=2')).to.deep.equal({ + 'a': '', + 'b': '2', + }); + }); + it('should decode names and values', () => { + expect(parseQueryString('a%26=1%26&b=2')).to.deep.equal({ + 'a&': '1&', + 'b': '2', + }); + }); + it('should return last dupe', () => { + expect(parseQueryString('a=1&b=2&a=3')).to.deep.equal({ + 'a': '3', + 'b': '2', + }); + }); + }); +}); diff --git a/test/unit/test-url.js b/test/unit/test-url.js index 09a39f5e2f99..e1ba088332a1 100644 --- a/test/unit/test-url.js +++ b/test/unit/test-url.js @@ -31,7 +31,6 @@ import { isProtocolValid, isProxyOrigin, isSecureUrlDeprecated, - parseQueryString, parseUrlDeprecated, removeAmpJsParamsFromUrl, removeFragment, @@ -259,58 +258,6 @@ describes.sandboxed('parseUrlDeprecated', {}, () => { }); }); -describes.sandboxed('parseQueryString', {}, () => { - it('should return empty params when query string is empty or null', () => { - expect(parseQueryString(null)).to.deep.equal({}); - expect(parseQueryString('')).to.deep.equal({}); - }); - it('should parse single key-value', () => { - expect(parseQueryString('a=1')).to.deep.equal({ - 'a': '1', - }); - }); - it('should parse two key-values', () => { - expect(parseQueryString('a=1&b=2')).to.deep.equal({ - 'a': '1', - 'b': '2', - }); - }); - it('should ignore leading ?', () => { - expect(parseQueryString('?a=1&b=2')).to.deep.equal({ - 'a': '1', - 'b': '2', - }); - }); - it('should ignore leading #', () => { - expect(parseQueryString('#a=1&b=2')).to.deep.equal({ - 'a': '1', - 'b': '2', - }); - }); - it('should parse empty value', () => { - expect(parseQueryString('a=&b=2')).to.deep.equal({ - 'a': '', - 'b': '2', - }); - expect(parseQueryString('a&b=2')).to.deep.equal({ - 'a': '', - 'b': '2', - }); - }); - it('should decode names and values', () => { - expect(parseQueryString('a%26=1%26&b=2')).to.deep.equal({ - 'a&': '1&', - 'b': '2', - }); - }); - it('should return last dupe', () => { - expect(parseQueryString('a=1&b=2&a=3')).to.deep.equal({ - 'a': '3', - 'b': '2', - }); - }); -}); - describes.sandboxed('serializeQueryString', {}, () => { it('should return empty string for empty params', () => { expect(serializeQueryString({})).to.equal(''); diff --git a/test/unit/test-viewer.js b/test/unit/test-viewer.js index 28a67f7062cd..9ce78e850fb9 100644 --- a/test/unit/test-viewer.js +++ b/test/unit/test-viewer.js @@ -21,11 +21,8 @@ import {installDocService} from '../../src/service/ampdoc-impl'; import {installDocumentInfoServiceForDoc} from '../../src/service/document-info-impl'; import {installPlatformService} from '../../src/service/platform-impl'; import {installTimerService} from '../../src/service/timer-impl'; -import { - parseQueryString, - parseUrlDeprecated, - removeFragment, -} from '../../src/url'; +import {parseQueryString} from '../../src/core/types/string/url'; +import {parseUrlDeprecated, removeFragment} from '../../src/url'; describes.sandboxed('Viewer', {}, (env) => { let windowMock;